001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018 019package org.apache.commons.compress.utils; 020 021import static java.nio.charset.StandardCharsets.US_ASCII; 022 023import java.util.Arrays; 024 025import org.apache.commons.compress.archivers.ArchiveEntry; 026 027/** 028 * Generic Archive utilities 029 */ 030public class ArchiveUtils { 031 032 private static final int MAX_SANITIZED_NAME_LENGTH = 255; 033 034 /** 035 * Returns true if the first N bytes of an array are all zero 036 * 037 * @param a 038 * The array to check 039 * @param size 040 * The number of characters to check (not the size of the array) 041 * @return true if the first N bytes are zero 042 */ 043 public static boolean isArrayZero(final byte[] a, final int size) { 044 for (int i = 0; i < size; i++) { 045 if (a[i] != 0) { 046 return false; 047 } 048 } 049 return true; 050 } 051 052 /** 053 * Compare byte buffers 054 * 055 * @param buffer1 the first buffer 056 * @param buffer2 the second buffer 057 * @return {@code true} if buffer1 and buffer2 have same contents 058 */ 059 public static boolean isEqual(final byte[] buffer1, final byte[] buffer2 ){ 060 return isEqual(buffer1, 0, buffer1.length, buffer2, 0, buffer2.length, false); 061 } 062 063 /** 064 * Compare byte buffers, optionally ignoring trailing nulls 065 * 066 * @param buffer1 the first buffer 067 * @param buffer2 the second buffer 068 * @param ignoreTrailingNulls whether to ignore trailing nulls 069 * @return {@code true} if buffer1 and buffer2 have same contents 070 */ 071 public static boolean isEqual(final byte[] buffer1, final byte[] buffer2, final boolean ignoreTrailingNulls){ 072 return isEqual(buffer1, 0, buffer1.length, buffer2, 0, buffer2.length, ignoreTrailingNulls); 073 } 074 075 /** 076 * Compare byte buffers 077 * 078 * @param buffer1 the first buffer 079 * @param offset1 the first offset 080 * @param length1 the first length 081 * @param buffer2 the second buffer 082 * @param offset2 the second offset 083 * @param length2 the second length 084 * @return {@code true} if buffer1 and buffer2 have same contents 085 */ 086 public static boolean isEqual( 087 final byte[] buffer1, final int offset1, final int length1, 088 final byte[] buffer2, final int offset2, final int length2){ 089 return isEqual(buffer1, offset1, length1, buffer2, offset2, length2, false); 090 } 091 092 /** 093 * Compare byte buffers, optionally ignoring trailing nulls 094 * 095 * @param buffer1 first buffer 096 * @param offset1 first offset 097 * @param length1 first length 098 * @param buffer2 second buffer 099 * @param offset2 second offset 100 * @param length2 second length 101 * @param ignoreTrailingNulls whether to ignore trailing nulls 102 * @return {@code true} if buffer1 and buffer2 have same contents, having regard to trailing nulls 103 */ 104 public static boolean isEqual( 105 final byte[] buffer1, final int offset1, final int length1, 106 final byte[] buffer2, final int offset2, final int length2, 107 final boolean ignoreTrailingNulls){ 108 final int minLen= Math.min(length1, length2); 109 for (int i=0; i < minLen; i++){ 110 if (buffer1[offset1+i] != buffer2[offset2+i]){ 111 return false; 112 } 113 } 114 if (length1 == length2){ 115 return true; 116 } 117 if (ignoreTrailingNulls){ 118 if (length1 > length2){ 119 for(int i = length2; i < length1; i++){ 120 if (buffer1[offset1+i] != 0){ 121 return false; 122 } 123 } 124 } else { 125 for(int i = length1; i < length2; i++){ 126 if (buffer2[offset2+i] != 0){ 127 return false; 128 } 129 } 130 } 131 return true; 132 } 133 return false; 134 } 135 136 /** 137 * Compare byte buffers, ignoring trailing nulls 138 * 139 * @param buffer1 the first buffer 140 * @param offset1 the first offset 141 * @param length1 the first length 142 * @param buffer2 the second buffer 143 * @param offset2 the second offset 144 * @param length2 the second length 145 * @return {@code true} if buffer1 and buffer2 have same contents, having regard to trailing nulls 146 */ 147 public static boolean isEqualWithNull( 148 final byte[] buffer1, final int offset1, final int length1, 149 final byte[] buffer2, final int offset2, final int length2){ 150 return isEqual(buffer1, offset1, length1, buffer2, offset2, length2, true); 151 } 152 153 /** 154 * Check if buffer contents matches Ascii String. 155 * 156 * @param expected the expected strin 157 * @param buffer the buffer 158 * @return {@code true} if buffer is the same as the expected string 159 */ 160 public static boolean matchAsciiBuffer(final String expected, final byte[] buffer){ 161 return matchAsciiBuffer(expected, buffer, 0, buffer.length); 162 } 163 164 /** 165 * Check if buffer contents matches Ascii String. 166 * 167 * @param expected expected string 168 * @param buffer the buffer 169 * @param offset offset to read from 170 * @param length length of the buffer 171 * @return {@code true} if buffer is the same as the expected string 172 */ 173 public static boolean matchAsciiBuffer( 174 final String expected, final byte[] buffer, final int offset, final int length){ 175 final byte[] buffer1; 176 buffer1 = expected.getBytes(US_ASCII); 177 return isEqual(buffer1, 0, buffer1.length, buffer, offset, length, false); 178 } 179 180 /** 181 * Returns a "sanitized" version of the string given as arguments, 182 * where sanitized means non-printable characters have been 183 * replaced with a question mark and the outcome is not longer 184 * than 255 chars. 185 * 186 * <p>This method is used to clean up file names when they are 187 * used in exception messages as they may end up in log files or 188 * as console output and may have been read from a corrupted 189 * input.</p> 190 * 191 * @param s the string to sanitize 192 * @return a sanitized version of the argument 193 * @since 1.12 194 */ 195 public static String sanitize(final String s) { 196 final char[] cs = s.toCharArray(); 197 final char[] chars = cs.length <= MAX_SANITIZED_NAME_LENGTH ? cs : Arrays.copyOf(cs, MAX_SANITIZED_NAME_LENGTH); 198 if (cs.length > MAX_SANITIZED_NAME_LENGTH) { 199 Arrays.fill(chars, MAX_SANITIZED_NAME_LENGTH - 3, MAX_SANITIZED_NAME_LENGTH, '.'); 200 } 201 final StringBuilder sb = new StringBuilder(); 202 for (final char c : chars) { 203 if (!Character.isISOControl(c)) { 204 final Character.UnicodeBlock block = Character.UnicodeBlock.of(c); 205 if (block != null && block != Character.UnicodeBlock.SPECIALS) { 206 sb.append(c); 207 continue; 208 } 209 } 210 sb.append('?'); 211 } 212 return sb.toString(); 213 } 214 215 /** 216 * Convert a string to Ascii bytes. 217 * Used for comparing "magic" strings which need to be independent of the default Locale. 218 * 219 * @param inputString string to convert 220 * @return the bytes 221 */ 222 public static byte[] toAsciiBytes(final String inputString){ 223 return inputString.getBytes(US_ASCII); 224 } 225 226 /** 227 * Convert an input byte array to a String using the ASCII character set. 228 * 229 * @param inputBytes bytes to convert 230 * @return the bytes, interpreted as an Ascii string 231 */ 232 public static String toAsciiString(final byte[] inputBytes){ 233 return new String(inputBytes, US_ASCII); 234 } 235 236 /** 237 * Convert an input byte array to a String using the ASCII character set. 238 * 239 * @param inputBytes input byte array 240 * @param offset offset within array 241 * @param length length of array 242 * @return the bytes, interpreted as an Ascii string 243 */ 244 public static String toAsciiString(final byte[] inputBytes, final int offset, final int length){ 245 return new String(inputBytes, offset, length, US_ASCII); 246 } 247 248 /** 249 * Generates a string containing the name, isDirectory setting and size of an entry. 250 * <p> 251 * For example: 252 * <pre> 253 * - 2000 main.c 254 * d 100 testfiles 255 * </pre> 256 * 257 * @param entry the entry 258 * @return the representation of the entry 259 */ 260 public static String toString(final ArchiveEntry entry){ 261 final StringBuilder sb = new StringBuilder(); 262 sb.append(entry.isDirectory()? 'd' : '-');// c.f. "ls -l" output 263 final String size = Long.toString(entry.getSize()); 264 sb.append(' '); 265 // Pad output to 7 places, leading spaces 266 for(int i=7; i > size.length(); i--){ 267 sb.append(' '); 268 } 269 sb.append(size); 270 sb.append(' ').append(entry.getName()); 271 return sb.toString(); 272 } 273 274 /** Private constructor to prevent instantiation of this utility class. */ 275 private ArchiveUtils(){ 276 } 277 278}