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 throw new RuntimeException(e); // Should not happen 079 } 080 return isEqual(buffer1, 0, buffer1.length, buffer, offset, length, false); 081 } 082 083 /** 084 * Check if buffer contents matches Ascii String. 085 * 086 * @param expected the expected strin 087 * @param buffer the buffer 088 * @return {@code true} if buffer is the same as the expected string 089 */ 090 public static boolean matchAsciiBuffer(final String expected, final byte[] buffer){ 091 return matchAsciiBuffer(expected, buffer, 0, buffer.length); 092 } 093 094 /** 095 * Convert a string to Ascii bytes. 096 * Used for comparing "magic" strings which need to be independent of the default Locale. 097 * 098 * @param inputString string to convert 099 * @return the bytes 100 */ 101 public static byte[] toAsciiBytes(final String inputString){ 102 try { 103 return inputString.getBytes(CharsetNames.US_ASCII); 104 } catch (final UnsupportedEncodingException e) { 105 throw new RuntimeException(e); // Should never happen 106 } 107 } 108 109 /** 110 * Convert an input byte array to a String using the ASCII character set. 111 * 112 * @param inputBytes bytes to convert 113 * @return the bytes, interpreted as an Ascii string 114 */ 115 public static String toAsciiString(final byte[] inputBytes){ 116 try { 117 return new String(inputBytes, CharsetNames.US_ASCII); 118 } catch (final UnsupportedEncodingException e) { 119 throw new RuntimeException(e); // Should never happen 120 } 121 } 122 123 /** 124 * Convert an input byte array to a String using the ASCII character set. 125 * 126 * @param inputBytes input byte array 127 * @param offset offset within array 128 * @param length length of array 129 * @return the bytes, interpreted as an Ascii string 130 */ 131 public static String toAsciiString(final byte[] inputBytes, final int offset, final int length){ 132 try { 133 return new String(inputBytes, offset, length, CharsetNames.US_ASCII); 134 } catch (final UnsupportedEncodingException e) { 135 throw new RuntimeException(e); // Should never happen 136 } 137 } 138 139 /** 140 * Compare byte buffers, optionally ignoring trailing nulls 141 * 142 * @param buffer1 first buffer 143 * @param offset1 first offset 144 * @param length1 first length 145 * @param buffer2 second buffer 146 * @param offset2 second offset 147 * @param length2 second length 148 * @param ignoreTrailingNulls whether to ignore trailing nulls 149 * @return {@code true} if buffer1 and buffer2 have same contents, having regard to trailing nulls 150 */ 151 public static boolean isEqual( 152 final byte[] buffer1, final int offset1, final int length1, 153 final byte[] buffer2, final int offset2, final int length2, 154 final boolean ignoreTrailingNulls){ 155 final int minLen=length1 < length2 ? length1 : length2; 156 for (int i=0; i < minLen; i++){ 157 if (buffer1[offset1+i] != buffer2[offset2+i]){ 158 return false; 159 } 160 } 161 if (length1 == length2){ 162 return true; 163 } 164 if (ignoreTrailingNulls){ 165 if (length1 > length2){ 166 for(int i = length2; i < length1; i++){ 167 if (buffer1[offset1+i] != 0){ 168 return false; 169 } 170 } 171 } else { 172 for(int i = length1; i < length2; i++){ 173 if (buffer2[offset2+i] != 0){ 174 return false; 175 } 176 } 177 } 178 return true; 179 } 180 return false; 181 } 182 183 /** 184 * Compare byte buffers 185 * 186 * @param buffer1 the first buffer 187 * @param offset1 the first offset 188 * @param length1 the first length 189 * @param buffer2 the second buffer 190 * @param offset2 the second offset 191 * @param length2 the second length 192 * @return {@code true} if buffer1 and buffer2 have same contents 193 */ 194 public static boolean isEqual( 195 final byte[] buffer1, final int offset1, final int length1, 196 final byte[] buffer2, final int offset2, final int length2){ 197 return isEqual(buffer1, offset1, length1, buffer2, offset2, length2, false); 198 } 199 200 /** 201 * Compare byte buffers 202 * 203 * @param buffer1 the first buffer 204 * @param buffer2 the second buffer 205 * @return {@code true} if buffer1 and buffer2 have same contents 206 */ 207 public static boolean isEqual(final byte[] buffer1, final byte[] buffer2 ){ 208 return isEqual(buffer1, 0, buffer1.length, buffer2, 0, buffer2.length, false); 209 } 210 211 /** 212 * Compare byte buffers, optionally ignoring trailing nulls 213 * 214 * @param buffer1 the first buffer 215 * @param buffer2 the second buffer 216 * @param ignoreTrailingNulls whether to ignore tariling nulls 217 * @return {@code true} if buffer1 and buffer2 have same contents 218 */ 219 public static boolean isEqual(final byte[] buffer1, final byte[] buffer2, final boolean ignoreTrailingNulls){ 220 return isEqual(buffer1, 0, buffer1.length, buffer2, 0, buffer2.length, ignoreTrailingNulls); 221 } 222 223 /** 224 * Compare byte buffers, ignoring trailing nulls 225 * 226 * @param buffer1 the first buffer 227 * @param offset1 the first offset 228 * @param length1 the first length 229 * @param buffer2 the second buffer 230 * @param offset2 the second offset 231 * @param length2 the second length 232 * @return {@code true} if buffer1 and buffer2 have same contents, having regard to trailing nulls 233 */ 234 public static boolean isEqualWithNull( 235 final byte[] buffer1, final int offset1, final int length1, 236 final byte[] buffer2, final int offset2, final int length2){ 237 return isEqual(buffer1, offset1, length1, buffer2, offset2, length2, true); 238 } 239 240 /** 241 * Returns true if the first N bytes of an array are all zero 242 * 243 * @param a 244 * The array to check 245 * @param size 246 * The number of characters to check (not the size of the array) 247 * @return true if the first N bytes are zero 248 */ 249 public static boolean isArrayZero(final byte[] a, final int size) { 250 for (int i = 0; i < size; i++) { 251 if (a[i] != 0) { 252 return false; 253 } 254 } 255 return true; 256 } 257 258 /** 259 * Returns a "sanitized" version of the string given as arguments, 260 * where sanitized means non-printable characters have been 261 * replaced with a question mark and the outcome is not longer 262 * than 255 chars. 263 * 264 * <p>This method is used to clean up file names when they are 265 * used in exception messages as they may end up in log files or 266 * as console output and may have been read from a corrupted 267 * input.</p> 268 * 269 * @param s the string to sanitize 270 * @return a sanitized version of the argument 271 * @since Compress 1.12 272 */ 273 public static String sanitize(String s) { 274 final char[] cs = s.toCharArray(); 275 final char[] chars = cs.length <= MAX_SANITIZED_NAME_LENGTH ? cs : Arrays.copyOf(cs, MAX_SANITIZED_NAME_LENGTH); 276 if (cs.length > MAX_SANITIZED_NAME_LENGTH) { 277 for (int i = MAX_SANITIZED_NAME_LENGTH - 3; i < MAX_SANITIZED_NAME_LENGTH; i++) { 278 chars[i] = '.'; 279 } 280 } 281 final int len = chars.length; 282 final StringBuilder sb = new StringBuilder(); 283 for (int i = 0; i < len; i++) { 284 final char c = chars[i]; 285 if (!Character.isISOControl(c)) { 286 Character.UnicodeBlock block = Character.UnicodeBlock.of(c); 287 if (block != null && block != Character.UnicodeBlock.SPECIALS) { 288 sb.append(c); 289 continue; 290 } 291 } 292 sb.append('?'); 293 } 294 return sb.toString(); 295 } 296 297}