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