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