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