1 /* 2 * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nio.zipfs; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 import java.util.Arrays; 31 import java.util.Date; 32 import java.util.regex.PatternSyntaxException; 33 import java.util.concurrent.TimeUnit; 34 35 /** 36 * 37 * @author Xueming Shen 38 */ 39 40 class ZipUtils { 41 42 /* 43 * Writes a 16-bit short to the output stream in little-endian byte order. 44 */ 45 public static void writeShort(OutputStream os, int v) throws IOException { 46 os.write(v & 0xff); 47 os.write((v >>> 8) & 0xff); 48 } 49 50 /* 51 * Writes a 32-bit int to the output stream in little-endian byte order. 52 */ 53 public static void writeInt(OutputStream os, long v) throws IOException { 54 os.write((int)(v & 0xff)); 55 os.write((int)((v >>> 8) & 0xff)); 56 os.write((int)((v >>> 16) & 0xff)); 57 os.write((int)((v >>> 24) & 0xff)); 58 } 59 60 /* 61 * Writes a 64-bit int to the output stream in little-endian byte order. 62 */ 63 public static void writeLong(OutputStream os, long v) throws IOException { 64 os.write((int)(v & 0xff)); 65 os.write((int)((v >>> 8) & 0xff)); 66 os.write((int)((v >>> 16) & 0xff)); 67 os.write((int)((v >>> 24) & 0xff)); 68 os.write((int)((v >>> 32) & 0xff)); 69 os.write((int)((v >>> 40) & 0xff)); 70 os.write((int)((v >>> 48) & 0xff)); 71 os.write((int)((v >>> 56) & 0xff)); 72 } 73 74 /* 75 * Writes an array of bytes to the output stream. 76 */ 77 public static void writeBytes(OutputStream os, byte[] b) 78 throws IOException 79 { 80 os.write(b, 0, b.length); 81 } 82 83 /* 84 * Writes an array of bytes to the output stream. 85 */ 86 public static void writeBytes(OutputStream os, byte[] b, int off, int len) 87 throws IOException 88 { 89 os.write(b, off, len); 90 } 91 92 /* 93 * Append a slash at the end, if it does not have one yet 94 */ 95 public static byte[] toDirectoryPath(byte[] dir) { 96 if (dir.length != 0 && dir[dir.length - 1] != '/') { 97 dir = Arrays.copyOf(dir, dir.length + 1); 98 dir[dir.length - 1] = '/'; 99 } 100 return dir; 101 } 102 103 /* 104 * Converts DOS time to Java time (number of milliseconds since epoch). 105 */ 106 @SuppressWarnings("deprecation") 107 public static long dosToJavaTime(long dtime) { 108 Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80), 109 (int)(((dtime >> 21) & 0x0f) - 1), 110 (int)((dtime >> 16) & 0x1f), 111 (int)((dtime >> 11) & 0x1f), 112 (int)((dtime >> 5) & 0x3f), 113 (int)((dtime << 1) & 0x3e)); 114 return d.getTime(); 115 } 116 117 /* 118 * Converts Java time to DOS time. 119 */ 120 @SuppressWarnings("deprecation") 121 public static long javaToDosTime(long time) { 122 Date d = new Date(time); 123 int year = d.getYear() + 1900; 124 if (year < 1980) { 125 return (1 << 21) | (1 << 16); 126 } 127 return (year - 1980) << 25 | (d.getMonth() + 1) << 21 | 128 d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 | 129 d.getSeconds() >> 1; 130 } 131 132 133 // used to adjust values between Windows and java epoch 134 private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L; 135 public static final long winToJavaTime(long wtime) { 136 return TimeUnit.MILLISECONDS.convert( 137 wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS); 138 } 139 140 public static final long javaToWinTime(long time) { 141 return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS) 142 - WINDOWS_EPOCH_IN_MICROSECONDS) * 10; 143 } 144 145 public static final long unixToJavaTime(long utime) { 146 return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS); 147 } 148 149 public static final long javaToUnixTime(long time) { 150 return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS); 151 } 152 153 private static final String regexMetaChars = ".^$+{[]|()"; 154 private static final String globMetaChars = "\\*?[{"; 155 private static boolean isRegexMeta(char c) { 156 return regexMetaChars.indexOf(c) != -1; 157 } 158 private static boolean isGlobMeta(char c) { 159 return globMetaChars.indexOf(c) != -1; 160 } 161 private static char EOL = 0; //TBD 162 private static char next(String glob, int i) { 163 if (i < glob.length()) { 164 return glob.charAt(i); 165 } 166 return EOL; 167 } 168 169 /* 170 * Creates a regex pattern from the given glob expression. 171 * 172 * @throws PatternSyntaxException 173 */ 174 public static String toRegexPattern(String globPattern) { 175 boolean inGroup = false; 176 StringBuilder regex = new StringBuilder("^"); 177 178 int i = 0; 179 while (i < globPattern.length()) { 180 char c = globPattern.charAt(i++); 181 switch (c) { 182 case '\\': 183 // escape special characters 184 if (i == globPattern.length()) { 185 throw new PatternSyntaxException("No character to escape", 186 globPattern, i - 1); 187 } 188 char next = globPattern.charAt(i++); 189 if (isGlobMeta(next) || isRegexMeta(next)) { 190 regex.append('\\'); 191 } 192 regex.append(next); 193 break; 194 case '/': 195 regex.append(c); 196 break; 197 case '[': 198 // don't match name separator in class 199 regex.append("[[^/]&&["); 200 if (next(globPattern, i) == '^') { 201 // escape the regex negation char if it appears 202 regex.append("\\^"); 203 i++; 204 } else { 205 // negation 206 if (next(globPattern, i) == '!') { 207 regex.append('^'); 208 i++; 209 } 210 // hyphen allowed at start 211 if (next(globPattern, i) == '-') { 212 regex.append('-'); 213 i++; 214 } 215 } 216 boolean hasRangeStart = false; 217 char last = 0; 218 while (i < globPattern.length()) { 219 c = globPattern.charAt(i++); 220 if (c == ']') { 221 break; 222 } 223 if (c == '/') { 224 throw new PatternSyntaxException("Explicit 'name separator' in class", 225 globPattern, i - 1); 226 } 227 // TBD: how to specify ']' in a class? 228 if (c == '\\' || c == '[' || 229 c == '&' && next(globPattern, i) == '&') { 230 // escape '\', '[' or "&&" for regex class 231 regex.append('\\'); 232 } 233 regex.append(c); 234 235 if (c == '-') { 236 if (!hasRangeStart) { 237 throw new PatternSyntaxException("Invalid range", 238 globPattern, i - 1); 239 } 240 if ((c = next(globPattern, i++)) == EOL || c == ']') { 241 break; 242 } 243 if (c < last) { 244 throw new PatternSyntaxException("Invalid range", 245 globPattern, i - 3); 246 } 247 regex.append(c); 248 hasRangeStart = false; 249 } else { 250 hasRangeStart = true; 251 last = c; 252 } 253 } 254 if (c != ']') { 255 throw new PatternSyntaxException("Missing ']", globPattern, i - 1); 256 } 257 regex.append("]]"); 258 break; 259 case '{': 260 if (inGroup) { 261 throw new PatternSyntaxException("Cannot nest groups", 262 globPattern, i - 1); 263 } 264 regex.append("(?:(?:"); 265 inGroup = true; 266 break; 267 case '}': 268 if (inGroup) { 269 regex.append("))"); 270 inGroup = false; 271 } else { 272 regex.append('}'); 273 } 274 break; 275 case ',': 276 if (inGroup) { 277 regex.append(")|(?:"); 278 } else { 279 regex.append(','); 280 } 281 break; 282 case '*': 283 if (next(globPattern, i) == '*') { 284 // crosses directory boundaries 285 regex.append(".*"); 286 i++; 287 } else { 288 // within directory boundary 289 regex.append("[^/]*"); 290 } 291 break; 292 case '?': 293 regex.append("[^/]"); 294 break; 295 default: 296 if (isRegexMeta(c)) { 297 regex.append('\\'); 298 } 299 regex.append(c); 300 } 301 } 302 if (inGroup) { 303 throw new PatternSyntaxException("Missing '}", globPattern, i - 1); 304 } 305 return regex.append('$').toString(); 306 } 307 }