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 java.io.UnsupportedEncodingException; 022import java.util.Arrays; 023 024import org.apache.commons.compress.archivers.ArchiveEntry; 025 026/** 027 * Generic Archive utilities 028 */ 029public class ArchiveUtils { 030 031 private static final int MAX_SANITIZED_NAME_LENGTH = 255; 032 033 /** Private constructor to prevent instantiation of this utility class. */ 034 private ArchiveUtils(){ 035 } 036 037 /** 038 * Generates a string containing the name, isDirectory setting and size of an entry. 039 * <p> 040 * For example: 041 * <pre> 042 * - 2000 main.c 043 * d 100 testfiles 044 * </pre> 045 * 046 * @param entry the entry 047 * @return the representation of the entry 048 */ 049 public static String toString(final ArchiveEntry entry){ 050 final StringBuilder sb = new StringBuilder(); 051 sb.append(entry.isDirectory()? 'd' : '-');// c.f. "ls -l" output 052 final String size = Long.toString(entry.getSize()); 053 sb.append(' '); 054 // Pad output to 7 places, leading spaces 055 for(int i=7; i > size.length(); i--){ 056 sb.append(' '); 057 } 058 sb.append(size); 059 sb.append(' ').append(entry.getName()); 060 return sb.toString(); 061 } 062 063 /** 064 * Check if buffer contents matches Ascii String. 065 * 066 * @param expected expected string 067 * @param buffer the buffer 068 * @param offset offset to read from 069 * @param length length of the buffer 070 * @return {@code true} if buffer is the same as the expected string 071 */ 072 public static boolean matchAsciiBuffer( 073 final String expected, final byte[] buffer, final int offset, final int length){ 074 byte[] buffer1; 075 try { 076 buffer1 = expected.getBytes(CharsetNames.US_ASCII); 077 } catch (final UnsupportedEncodingException e) { 078 // Should not happen 079 throw new RuntimeException(e); //NOSONAR 080 } 081 return isEqual(buffer1, 0, buffer1.length, buffer, offset, length, false); 082 } 083 084 /** 085 * Check if buffer contents matches Ascii String. 086 * 087 * @param expected the expected strin 088 * @param buffer the buffer 089 * @return {@code true} if buffer is the same as the expected string 090 */ 091 public static boolean matchAsciiBuffer(final String expected, final byte[] buffer){ 092 return matchAsciiBuffer(expected, buffer, 0, buffer.length); 093 } 094 095 /** 096 * Convert a string to Ascii bytes. 097 * Used for comparing "magic" strings which need to be independent of the default Locale. 098 * 099 * @param inputString string to convert 100 * @return the bytes 101 */ 102 public static byte[] toAsciiBytes(final String inputString){ 103 try { 104 return inputString.getBytes(CharsetNames.US_ASCII); 105 } catch (final UnsupportedEncodingException e) { 106 // Should never happen 107 throw new RuntimeException(e); //NOSONAR 108 } 109 } 110 111 /** 112 * Convert an input byte array to a String using the ASCII character set. 113 * 114 * @param inputBytes bytes to convert 115 * @return the bytes, interpreted as an Ascii string 116 */ 117 public static String toAsciiString(final byte[] inputBytes){ 118 try { 119 return new String(inputBytes, CharsetNames.US_ASCII); 120 } catch (final UnsupportedEncodingException e) { 121 // Should never happen 122 throw new RuntimeException(e); //NOSONAR 123 } 124 } 125 126 /** 127 * Convert an input byte array to a String using the ASCII character set. 128 * 129 * @param inputBytes input byte array 130 * @param offset offset within array 131 * @param length length of array 132 * @return the bytes, interpreted as an Ascii string 133 */ 134 public static String toAsciiString(final byte[] inputBytes, final int offset, final int length){ 135 try { 136 return new String(inputBytes, offset, length, CharsetNames.US_ASCII); 137 } catch (final UnsupportedEncodingException e) { 138 // Should never happen 139 throw new RuntimeException(e); //NOSONAR 140 } 141 } 142 143 /** 144 * Compare byte buffers, optionally ignoring trailing nulls 145 * 146 * @param buffer1 first buffer 147 * @param offset1 first offset 148 * @param length1 first length 149 * @param buffer2 second buffer 150 * @param offset2 second offset 151 * @param length2 second length 152 * @param ignoreTrailingNulls whether to ignore trailing nulls 153 * @return {@code true} if buffer1 and buffer2 have same contents, having regard to trailing nulls 154 */ 155 public static boolean isEqual( 156 final byte[] buffer1, final int offset1, final int length1, 157 final byte[] buffer2, final int offset2, final int length2, 158 final boolean ignoreTrailingNulls){ 159 final int minLen=length1 < length2 ? length1 : length2; 160 for (int i=0; i < minLen; i++){ 161 if (buffer1[offset1+i] != buffer2[offset2+i]){ 162 return false; 163 } 164 } 165 if (length1 == length2){ 166 return true; 167 } 168 if (ignoreTrailingNulls){ 169 if (length1 > length2){ 170 for(int i = length2; i < length1; i++){ 171 if (buffer1[offset1+i] != 0){ 172 return false; 173 } 174 } 175 } else { 176 for(int i = length1; i < length2; i++){ 177 if (buffer2[offset2+i] != 0){ 178 return false; 179 } 180 } 181 } 182 return true; 183 } 184 return false; 185 } 186 187 /** 188 * Compare byte buffers 189 * 190 * @param buffer1 the first buffer 191 * @param offset1 the first offset 192 * @param length1 the first length 193 * @param buffer2 the second buffer 194 * @param offset2 the second offset 195 * @param length2 the second length 196 * @return {@code true} if buffer1 and buffer2 have same contents 197 */ 198 public static boolean isEqual( 199 final byte[] buffer1, final int offset1, final int length1, 200 final byte[] buffer2, final int offset2, final int length2){ 201 return isEqual(buffer1, offset1, length1, buffer2, offset2, length2, false); 202 } 203 204 /** 205 * Compare byte buffers 206 * 207 * @param buffer1 the first buffer 208 * @param buffer2 the second buffer 209 * @return {@code true} if buffer1 and buffer2 have same contents 210 */ 211 public static boolean isEqual(final byte[] buffer1, final byte[] buffer2 ){ 212 return isEqual(buffer1, 0, buffer1.length, buffer2, 0, buffer2.length, false); 213 } 214 215 /** 216 * Compare byte buffers, optionally ignoring trailing nulls 217 * 218 * @param buffer1 the first buffer 219 * @param buffer2 the second buffer 220 * @param ignoreTrailingNulls whether to ignore tariling nulls 221 * @return {@code true} if buffer1 and buffer2 have same contents 222 */ 223 public static boolean isEqual(final byte[] buffer1, final byte[] buffer2, final boolean ignoreTrailingNulls){ 224 return isEqual(buffer1, 0, buffer1.length, buffer2, 0, buffer2.length, ignoreTrailingNulls); 225 } 226 227 /** 228 * Compare byte buffers, ignoring trailing nulls 229 * 230 * @param buffer1 the first buffer 231 * @param offset1 the first offset 232 * @param length1 the first length 233 * @param buffer2 the second buffer 234 * @param offset2 the second offset 235 * @param length2 the second length 236 * @return {@code true} if buffer1 and buffer2 have same contents, having regard to trailing nulls 237 */ 238 public static boolean isEqualWithNull( 239 final byte[] buffer1, final int offset1, final int length1, 240 final byte[] buffer2, final int offset2, final int length2){ 241 return isEqual(buffer1, offset1, length1, buffer2, offset2, length2, true); 242 } 243 244 /** 245 * Returns true if the first N bytes of an array are all zero 246 * 247 * @param a 248 * The array to check 249 * @param size 250 * The number of characters to check (not the size of the array) 251 * @return true if the first N bytes are zero 252 */ 253 public static boolean isArrayZero(final byte[] a, final int size) { 254 for (int i = 0; i < size; i++) { 255 if (a[i] != 0) { 256 return false; 257 } 258 } 259 return true; 260 } 261 262 /** 263 * Returns a "sanitized" version of the string given as arguments, 264 * where sanitized means non-printable characters have been 265 * replaced with a question mark and the outcome is not longer 266 * than 255 chars. 267 * 268 * <p>This method is used to clean up file names when they are 269 * used in exception messages as they may end up in log files or 270 * as console output and may have been read from a corrupted 271 * input.</p> 272 * 273 * @param s the string to sanitize 274 * @return a sanitized version of the argument 275 * @since Compress 1.12 276 */ 277 public static String sanitize(final String s) { 278 final char[] cs = s.toCharArray(); 279 final char[] chars = cs.length <= MAX_SANITIZED_NAME_LENGTH ? cs : Arrays.copyOf(cs, MAX_SANITIZED_NAME_LENGTH); 280 if (cs.length > MAX_SANITIZED_NAME_LENGTH) { 281 for (int i = MAX_SANITIZED_NAME_LENGTH - 3; i < MAX_SANITIZED_NAME_LENGTH; i++) { 282 chars[i] = '.'; 283 } 284 } 285 final StringBuilder sb = new StringBuilder(); 286 for (final char c : chars) { 287 if (!Character.isISOControl(c)) { 288 final Character.UnicodeBlock block = Character.UnicodeBlock.of(c); 289 if (block != null && block != Character.UnicodeBlock.SPECIALS) { 290 sb.append(c); 291 continue; 292 } 293 } 294 sb.append('?'); 295 } 296 return sb.toString(); 297 } 298 299}