1 /* 2 * Copyright (c) 2003, 2012, 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 #ifdef _ALLBSD_SOURCE 26 #include <stdint.h> 27 #define THRTYPE intptr_t 28 #else 29 #define THRTYPE int 30 #endif 31 32 #include <sys/types.h> 33 34 #include <stdio.h> 35 #include <string.h> 36 #include <stdlib.h> 37 #include <stdarg.h> 38 #include <errno.h> 39 40 #include <limits.h> 41 #include <time.h> 42 43 #if defined(unix) && !defined(PRODUCT) 44 #include "pthread.h" 45 #define THREAD_SELF ((THRTYPE)pthread_self()) 46 #endif 47 48 #include "defines.h" 49 #include "bytes.h" 50 #include "utils.h" 51 #include "coding.h" 52 #include "bands.h" 53 54 #include "constants.h" 55 56 #include "zip.h" 57 58 #include "unpack.h" 59 60 61 int main(int argc, char **argv) { 62 return unpacker::run(argc, argv); 63 } 64 65 // Single-threaded, implementation, not reentrant. 66 // Includes a weak error check against MT access. 67 #ifndef THREAD_SELF 68 #define THREAD_SELF ((THRTYPE) 0) 69 #endif 70 NOT_PRODUCT(static THRTYPE uThread = -1;) 71 72 unpacker* unpacker::non_mt_current = null; 73 unpacker* unpacker::current() { 74 assert(uThread == THREAD_SELF); 75 return non_mt_current; 76 } 77 static void set_current_unpacker(unpacker* u) { 78 unpacker::non_mt_current = u; 79 assert(((uThread = (u == null) ? (THRTYPE) -1 : THREAD_SELF), 80 true)); 81 } 82 83 // Callback for fetching data, Unix style. 84 static jlong read_input_via_stdio(unpacker* u, 85 void* buf, jlong minlen, jlong maxlen) { 86 assert(minlen <= maxlen); // don't talk nonsense 87 jlong numread = 0; 88 char* bufptr = (char*) buf; 89 while (numread < minlen) { 90 // read available input, up to buf.length or maxlen 91 int readlen = (1<<16); 92 if (readlen > (maxlen - numread)) 93 readlen = (int)(maxlen - numread); 94 int nr = 0; 95 if (u->infileptr != null) { 96 nr = (int)fread(bufptr, 1, readlen, u->infileptr); 97 } else { 98 #ifndef WIN32 99 // we prefer unbuffered inputs 100 nr = (int)read(u->infileno, bufptr, readlen); 101 #else 102 nr = (int)fread(bufptr, 1, readlen, stdin); 103 #endif 104 } 105 if (nr <= 0) { 106 if (errno != EINTR) 107 break; 108 nr = 0; 109 } 110 numread += nr; 111 bufptr += nr; 112 assert(numread <= maxlen); 113 } 114 //fprintf(u->errstrm, "readInputFn(%d,%d) => %d\n", 115 // (int)minlen, (int)maxlen, (int)numread); 116 return numread; 117 } 118 119 enum { EOF_MAGIC = 0, BAD_MAGIC = -1 }; 120 static int read_magic(unpacker* u, char peek[], int peeklen) { 121 assert(peeklen == 4); // magic numbers are always 4 bytes 122 jlong nr = (u->read_input_fn)(u, peek, peeklen, peeklen); 123 if (nr != peeklen) { 124 return (nr == 0) ? EOF_MAGIC : BAD_MAGIC; 125 } 126 int magic = 0; 127 for (int i = 0; i < peeklen; i++) { 128 magic <<= 8; 129 magic += peek[i] & 0xFF; 130 } 131 return magic; 132 } 133 134 static void setup_gzin(unpacker* u) { 135 gunzip* gzin = NEW(gunzip, 1); 136 gzin->init(u); 137 } 138 139 static const char* nbasename(const char* progname) { 140 const char* slash = strrchr(progname, '/'); 141 if (slash != null) progname = ++slash; 142 return progname; 143 } 144 145 static const char* usage_lines[] = { 146 "Usage: %s [-opt... | --option=value]... x.pack[.gz] y.jar\n", 147 "\n", 148 "Unpacking Options\n", 149 " -H{h}, --deflate-hint={h} override transmitted deflate hint: true, false, or keep (default)\n", 150 " -r, --remove-pack-file remove input file after unpacking\n", 151 " -v, --verbose increase program verbosity\n", 152 " -q, --quiet set verbosity to lowest level\n", 153 " -l{F}, --log-file={F} output to the given log file, or '-' for standard output (default)\n", 154 " -?, -h, --help print this message\n", 155 " -V, --version print program version\n", 156 " -J{X} Java VM argument (ignored)\n", 157 null 158 }; 159 160 static void usage(unpacker* u, const char* progname, bool full = false) { 161 // WinMain does not set argv[0] to the progrname 162 progname = (progname != null) ? nbasename(progname) : "unpack200"; 163 for (int i = 0; usage_lines[i] != null; i++) { 164 fprintf(u->errstrm, usage_lines[i], progname); 165 if (!full) { 166 fprintf(u->errstrm, 167 "(For more information, run %s --help .)\n", progname); 168 break; 169 } 170 } 171 } 172 173 // argument parsing 174 static char** init_args(int argc, char** argv, int &envargc) { 175 const char* env = getenv("UNPACK200_FLAGS"); 176 ptrlist envargs; 177 envargs.init(); 178 if (env != null) { 179 char* buf = (char*) strdup(env); 180 const char* delim = "\n\t "; 181 for (char* p = strtok(buf, delim); p != null; p = strtok(null, delim)) { 182 envargs.add(p); 183 } 184 } 185 // allocate extra margin at both head and tail 186 char** argp = NEW(char*, envargs.length()+argc+1); 187 char** argp0 = argp; 188 int i; 189 for (i = 0; i < envargs.length(); i++) { 190 *argp++ = (char*) envargs.get(i); 191 } 192 for (i = 1; i < argc; i++) { 193 // note: skip argv[0] (program name) 194 *argp++ = (char*) strdup(argv[i]); // make a scratch copy 195 } 196 *argp = null; // sentinel 197 envargc = envargs.length(); // report this count to next_arg 198 envargs.free(); 199 return argp0; 200 } 201 202 static int strpcmp(const char* str, const char* pfx) { 203 return strncmp(str, pfx, strlen(pfx)); 204 } 205 206 static const char flag_opts[] = "vqrVh?"; 207 static const char string_opts[] = "HlJ"; 208 209 static int next_arg(char** &argp) { 210 char* arg = *argp; 211 if (arg == null || arg[0] != '-') { // end of option list 212 return 0; 213 } 214 //printf("opt: %s\n", arg); 215 char ach = arg[1]; 216 if (ach == '\0') { 217 // ++argp; // do not pop this arg 218 return 0; // bare "-" is stdin/stdout 219 } else if (arg[1] == '-') { // --foo option 220 static const char* keys[] = { 221 "Hdeflate-hint=", 222 "vverbose", 223 "qquiet", 224 "rremove-pack-file", 225 "llog-file=", 226 "Vversion", 227 "hhelp", 228 null }; 229 if (arg[2] == '\0') { // end of option list 230 ++argp; // pop the "--" 231 return 0; 232 } 233 for (int i = 0; keys[i] != null; i++) { 234 const char* key = keys[i]; 235 char kch = *key++; 236 if (strchr(key, '=') == null) { 237 if (!strcmp(arg+2, key)) { 238 ++argp; // pop option arg 239 return kch; 240 } 241 } else { 242 if (!strpcmp(arg+2, key)) { 243 *argp += 2 + strlen(key); // remove "--"+key from arg 244 return kch; 245 } 246 } 247 } 248 } else if (strchr(flag_opts, ach) != null) { // plain option 249 if (arg[2] == '\0') { 250 ++argp; 251 } else { 252 // in-place edit of "-vxyz" to "-xyz" 253 arg += 1; // skip original '-' 254 arg[0] = '-'; 255 *argp = arg; 256 } 257 //printf(" key => %c\n", ach); 258 return ach; 259 } else if (strchr(string_opts, ach) != null) { // argument-bearing option 260 if (arg[2] == '\0') { 261 if (argp[1] == null) return -1; // no next arg 262 ++argp; // leave the argument in place 263 } else { 264 // in-place edit of "-Hxyz" to "xyz" 265 arg += 2; // skip original '-H' 266 *argp = arg; 267 } 268 //printf(" key => %c\n", ach); 269 return ach; 270 } 271 return -1; // bad argument 272 } 273 274 static const char sccsver[] = "1.30, 07/05/05"; 275 276 // Usage: unpackage input.pack output.jar 277 int unpacker::run(int argc, char **argv) { 278 unpacker u; 279 u.init(read_input_via_stdio); 280 set_current_unpacker(&u); 281 282 jar jarout; 283 jarout.init(&u); 284 285 int envargc = 0; 286 char** argbuf = init_args(argc, argv, envargc); 287 char** arg0 = argbuf+envargc; 288 char** argp = argbuf; 289 290 int verbose = 0; 291 char* logfile = null; 292 293 for (;;) { 294 const char* arg = (*argp == null)? "": u.saveStr(*argp); 295 bool isenvarg = (argp < arg0); 296 int ach = next_arg(argp); 297 bool hasoptarg = (ach != 0 && strchr(string_opts, ach) != null); 298 if (ach == 0 && argp >= arg0) break; 299 if (isenvarg && argp == arg0 && hasoptarg) ach = 0; // don't pull from cmdline 300 switch (ach) { 301 case 'H': u.set_option(UNPACK_DEFLATE_HINT,*argp++); break; 302 case 'v': ++verbose; break; 303 case 'q': verbose = 0; break; 304 case 'r': u.set_option(UNPACK_REMOVE_PACKFILE,"1"); break; 305 case 'l': logfile = *argp++; break; 306 case 'J': argp += 1; break; // skip ignored -Jxxx parameter 307 308 case 'V': 309 fprintf(u.errstrm, VERSION_STRING, nbasename(argv[0]), sccsver); 310 exit(0); 311 312 case 'h': 313 case '?': 314 usage(&u, argv[0], true); 315 exit(1); 316 317 default: 318 const char* inenv = isenvarg? " in ${UNPACK200_FLAGS}": ""; 319 if (hasoptarg) 320 fprintf(u.errstrm, "Missing option string%s: %s\n", inenv, arg); 321 else 322 fprintf(u.errstrm, "Unrecognized argument%s: %s\n", inenv, arg); 323 usage(&u, argv[0]); 324 exit(2); 325 } 326 } 327 328 if (verbose != 0) { 329 u.set_option(DEBUG_VERBOSE, u.saveIntStr(verbose)); 330 } 331 if (logfile != null) { 332 u.set_option(UNPACK_LOG_FILE, logfile); 333 } 334 335 u.redirect_stdio(); 336 337 const char* source_file = *argp++; 338 const char* destination_file = *argp++; 339 340 if (source_file == null || destination_file == null || *argp != null) { 341 usage(&u, argv[0]); 342 exit(2); 343 } 344 345 if (verbose != 0) { 346 fprintf(u.errstrm, 347 "Unpacking from %s to %s\n", source_file, destination_file); 348 } 349 bool& remove_source = u.remove_packfile; 350 351 if (strcmp(source_file, "-") == 0) { 352 remove_source = false; 353 u.infileno = fileno(stdin); 354 } else { 355 u.infileptr = fopen(source_file, "rb"); 356 if (u.infileptr == null) { 357 fprintf(u.errstrm, 358 "Error: Could not open input file: %s\n", source_file); 359 exit(3); // Called only from the native standalone unpacker 360 } 361 } 362 363 if (strcmp(destination_file, "-") == 0) { 364 jarout.jarfp = stdout; 365 if (u.errstrm == stdout) // do not mix output 366 u.set_option(UNPACK_LOG_FILE, LOGFILE_STDERR); 367 } else { 368 jarout.openJarFile(destination_file); 369 assert(jarout.jarfp != null); 370 } 371 372 if (verbose != 0) 373 u.dump_options(); 374 375 char peek[4]; 376 int magic; 377 378 // check for GZIP input 379 magic = read_magic(&u, peek, (int)sizeof(peek)); 380 if ((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC) { 381 // Oops; must slap an input filter on this data. 382 setup_gzin(&u); 383 u.gzin->start(magic); 384 if (!u.aborting()) { 385 u.start(); 386 } 387 } else { 388 u.start(peek, sizeof(peek)); 389 } 390 391 // Note: The checks to u.aborting() are necessary to gracefully 392 // terminate processing when the first segment throws an error. 393 394 for (;;) { 395 if (u.aborting()) break; 396 397 // Each trip through this loop unpacks one segment 398 // and then resets the unpacker. 399 for (unpacker::file* filep; (filep = u.get_next_file()) != null; ) { 400 if (u.aborting()) break; 401 u.write_file_to_jar(filep); 402 } 403 if (u.aborting()) break; 404 405 // Peek ahead for more data. 406 magic = read_magic(&u, peek, (int)sizeof(peek)); 407 if (magic != (int)JAVA_PACKAGE_MAGIC) { 408 if (magic != EOF_MAGIC) 409 u.abort("garbage after end of pack archive"); 410 break; // all done 411 } 412 413 // Release all storage from parsing the old segment. 414 u.reset(); 415 416 // Restart, beginning with the peek-ahead. 417 u.start(peek, sizeof(peek)); 418 } 419 420 421 422 int status = 0; 423 if (u.aborting()) { 424 fprintf(u.errstrm, "Error: %s\n", u.get_abort_message()); 425 status = 1; 426 } 427 428 if (u.infileptr != null) { 429 fclose(u.infileptr); 430 u.infileptr = null; 431 } 432 433 if (!u.aborting() && remove_source) 434 remove(source_file); 435 436 if (verbose != 0) { 437 fprintf(u.errstrm, "unpacker completed with status=%d\n", status); 438 } 439 440 u.finish(); 441 442 u.free(); // tidy up malloc blocks 443 set_current_unpacker(null); // clean up global pointer 444 445 return status; 446 }