1/*
2 * Copyright (c) 2003, 2016, 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
61int 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
70NOT_PRODUCT(static THRTYPE uThread = -1;)
71
72unpacker* unpacker::non_mt_current = null;
73unpacker* unpacker::current() {
74  //assert(uThread == THREAD_SELF);
75  return non_mt_current;
76}
77static 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.
84static 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
119enum { EOF_MAGIC = 0, BAD_MAGIC = -1 };
120static 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
134static void setup_gzin(unpacker* u) {
135  gunzip* gzin = NEW(gunzip, 1);
136  gzin->init(u);
137}
138
139static const char* nbasename(const char* progname) {
140  const char* slash = strrchr(progname, '/');
141  if (slash != null)  progname = ++slash;
142  return progname;
143}
144
145#define USAGE_HEADER "Usage:  %s [-opt... | --option=value]... x.pack[.gz] y.jar\n"
146#define USAGE_OPTIONS \
147    "\n" \
148    "Unpacking Options\n" \
149    "  -H{h}, --deflate-hint={h}     override transmitted deflate hint:\n" \
150    "                                true, false, or keep (default)\n" \
151    "  -r, --remove-pack-file        remove input file after unpacking\n" \
152    "  -v, --verbose                 increase program verbosity\n" \
153    "  -q, --quiet                   set verbosity to lowest level\n" \
154    "  -l{F}, --log-file={F}         output to the given log file,\n" \
155    "                                or '-' for standard output (default)\n" \
156    "  -?, -h, --help                print this message\n" \
157    "  -V, --version                 print program version\n" \
158    "\n" \
159    "Exit Status:\n" \
160    "  0 if successful, >0 if an error occurred\n"
161
162static void usage(unpacker* u, const char* progname, bool full = false) {
163  // WinMain does not set argv[0] to the progrname
164  progname = (progname != null) ? nbasename(progname) : "unpack200";
165
166  fprintf(u->errstrm, USAGE_HEADER, progname);
167  if (full) {
168    fprintf(u->errstrm, USAGE_OPTIONS);
169  } else {
170    fprintf(u->errstrm, "(For more information, run %s --help .)\n", progname);
171  }
172}
173
174// argument parsing
175static char** init_args(int argc, char** argv, int &envargc) {
176  const char* env = getenv("UNPACK200_FLAGS");
177  ptrlist envargs;
178  envargs.init();
179  if (env != null) {
180    char* buf = (char*) strdup(env);
181    const char* delim = "\n\t ";
182    for (char* p = strtok(buf, delim); p != null; p = strtok(null, delim)) {
183      envargs.add(p);
184    }
185  }
186  // allocate extra margin at both head and tail
187  char** argp = NEW(char*, envargs.length()+argc+1);
188  char** argp0 = argp;
189  int i;
190  for (i = 0; i < envargs.length(); i++) {
191    *argp++ = (char*) envargs.get(i);
192  }
193  for (i = 1; i < argc; i++) {
194    // note: skip argv[0] (program name)
195    *argp++ = (char*) strdup(argv[i]);  // make a scratch copy
196  }
197  *argp = null; // sentinel
198  envargc = envargs.length();  // report this count to next_arg
199  envargs.free();
200  return argp0;
201}
202
203static int strpcmp(const char* str, const char* pfx) {
204  return strncmp(str, pfx, strlen(pfx));
205}
206
207static const char flag_opts[] = "vqrVh?";
208static const char string_opts[] = "HlJ";
209
210static int next_arg(char** &argp) {
211  char* arg = *argp;
212  if (arg == null || arg[0] != '-') { // end of option list
213    return 0;
214  }
215  //printf("opt: %s\n", arg);
216  char ach = arg[1];
217  if (ach == '\0') {
218    // ++argp;  // do not pop this arg
219    return 0;  // bare "-" is stdin/stdout
220  } else if (arg[1] == '-') {  // --foo option
221    static const char* keys[] = {
222      "Hdeflate-hint=",
223      "vverbose",
224      "qquiet",
225      "rremove-pack-file",
226      "llog-file=",
227      "Vversion",
228      "hhelp",
229      null };
230    if (arg[2] == '\0') {  // end of option list
231      ++argp;  // pop the "--"
232      return 0;
233    }
234    for (int i = 0; keys[i] != null; i++) {
235      const char* key = keys[i];
236      char kch = *key++;
237      if (strchr(key, '=') == null) {
238        if (!strcmp(arg+2, key)) {
239          ++argp;  // pop option arg
240          return kch;
241        }
242      } else {
243        if (!strpcmp(arg+2, key)) {
244          *argp += 2 + strlen(key);  // remove "--"+key from arg
245          return kch;
246        }
247      }
248    }
249  } else if (strchr(flag_opts, ach) != null) {  // plain option
250    if (arg[2] == '\0') {
251      ++argp;
252    } else {
253      // in-place edit of "-vxyz" to "-xyz"
254      arg += 1;  // skip original '-'
255      arg[0] = '-';
256      *argp = arg;
257    }
258    //printf("  key => %c\n", ach);
259    return ach;
260  } else if (strchr(string_opts, ach) != null) {  // argument-bearing option
261    if (arg[2] == '\0') {
262      if (argp[1] == null)  return -1;  // no next arg
263      ++argp;  // leave the argument in place
264    } else {
265      // in-place edit of "-Hxyz" to "xyz"
266      arg += 2;  // skip original '-H'
267      *argp = arg;
268    }
269    //printf("  key => %c\n", ach);
270    return ach;
271  }
272  return -1;  // bad argument
273}
274
275static const char sccsver[] = "1.30, 07/05/05";
276
277// Usage:  unpackage input.pack output.jar
278int unpacker::run(int argc, char **argv) {
279  unpacker u;
280  u.init(read_input_via_stdio);
281  set_current_unpacker(&u);
282
283  jar jarout;
284  jarout.init(&u);
285
286  int envargc = 0;
287  char** argbuf = init_args(argc, argv, envargc);
288  char** arg0 = argbuf+envargc;
289  char** argp = argbuf;
290
291  int verbose = 0;
292  char* logfile = null;
293
294  for (;;) {
295    const char* arg = (*argp == null)? "": u.saveStr(*argp);
296    bool isenvarg = (argp < arg0);
297    int ach = next_arg(argp);
298    bool hasoptarg = (ach != 0 && strchr(string_opts, ach) != null);
299    if (ach == 0 && argp >= arg0)  break;
300    if (isenvarg && argp == arg0 && hasoptarg)  ach = 0;  // don't pull from cmdline
301    switch (ach) {
302    case 'H':  u.set_option(UNPACK_DEFLATE_HINT,*argp++); break;
303    case 'v':  ++verbose; break;
304    case 'q':  verbose = 0; break;
305    case 'r':  u.set_option(UNPACK_REMOVE_PACKFILE,"1"); break;
306    case 'l':  logfile = *argp++; break;
307    case 'J':  argp += 1; break;  // skip ignored -Jxxx parameter
308
309    case 'V':
310      fprintf(u.errstrm, VERSION_STRING, nbasename(argv[0]), sccsver);
311      exit(0);
312
313    case 'h':
314    case '?':
315      usage(&u, argv[0], true);
316      exit(1);
317
318    default:
319      const char* inenv = isenvarg? " in ${UNPACK200_FLAGS}": "";
320      if (hasoptarg)
321        fprintf(u.errstrm, "Missing option string%s: %s\n", inenv, arg);
322      else
323        fprintf(u.errstrm, "Unrecognized argument%s: %s\n", inenv, arg);
324      usage(&u, argv[0]);
325      exit(2);
326    }
327  }
328
329  if (verbose != 0) {
330    u.set_option(DEBUG_VERBOSE, u.saveIntStr(verbose));
331  }
332  if (logfile != null) {
333    u.set_option(UNPACK_LOG_FILE, logfile);
334  }
335
336  u.redirect_stdio();
337
338  const char* source_file      = *argp++;
339  const char* destination_file = *argp++;
340
341  if (source_file == null || destination_file == null || *argp != null) {
342    usage(&u, argv[0]);
343    exit(2);
344  }
345
346  if (verbose != 0) {
347    fprintf(u.errstrm,
348            "Unpacking from %s to %s\n", source_file, destination_file);
349  }
350  bool& remove_source = u.remove_packfile;
351
352  if (strcmp(source_file, "-") == 0) {
353    remove_source = false;
354    u.infileno = fileno(stdin);
355  } else {
356    u.infileptr = fopen(source_file, "rb");
357    if (u.infileptr == null) {
358       fprintf(u.errstrm,
359               "Error: Could not open input file: %s\n", source_file);
360       exit(3); // Called only from the native standalone unpacker
361    }
362  }
363
364  if (strcmp(destination_file, "-") == 0) {
365    jarout.jarfp = stdout;
366    jarout.jarname = null;
367    if (u.errstrm == stdout) // do not mix output
368      u.set_option(UNPACK_LOG_FILE, LOGFILE_STDERR);
369  } else {
370    jarout.openJarFile(destination_file);
371    assert(jarout.jarfp != null);
372  }
373
374  if (verbose != 0)
375    u.dump_options();
376
377  char peek[4];
378  int magic;
379
380  // check for GZIP input
381  magic = read_magic(&u, peek, (int)sizeof(peek));
382  if ((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC) {
383    // Oops; must slap an input filter on this data.
384    setup_gzin(&u);
385    u.gzin->start(magic);
386    u.gzin->gzcrc = 0;
387    u.gzin->gzlen = 0;
388    if (!u.aborting()) {
389      u.start();
390    }
391  } else {
392    u.start(peek, sizeof(peek));
393  }
394
395  // Note:  The checks to u.aborting() are necessary to gracefully
396  // terminate processing when the first segment throws an error.
397
398  for (;;) {
399    if (u.aborting())  break;
400
401    // Each trip through this loop unpacks one segment
402    // and then resets the unpacker.
403    for (unpacker::file* filep; (filep = u.get_next_file()) != null; ) {
404      if (u.aborting())  break;
405      u.write_file_to_jar(filep);
406    }
407    if (u.aborting())  break;
408
409    // Peek ahead for more data.
410    magic = read_magic(&u, peek, (int)sizeof(peek));
411    if (magic != (int)JAVA_PACKAGE_MAGIC) {
412      if (magic != EOF_MAGIC)
413        u.abort("garbage after end of pack archive");
414      break;   // all done
415    }
416
417    // Release all storage from parsing the old segment.
418    u.reset();
419
420    // Restart, beginning with the peek-ahead.
421    u.start(peek, sizeof(peek));
422  }
423
424  int status = 0;
425  if (u.aborting()) {
426    fprintf(u.errstrm, "Error: %s\n", u.get_abort_message());
427    status = 1;
428  }
429
430  if (u.infileptr != null) {
431    fclose(u.infileptr);
432    u.infileptr = null;
433  }
434
435  if (!u.aborting() && remove_source)
436    remove(source_file);
437
438  if (verbose != 0) {
439    fprintf(u.errstrm, "unpacker completed with status=%d\n", status);
440  }
441
442  u.finish();
443
444  u.free();  // tidy up malloc blocks
445  set_current_unpacker(null);  // clean up global pointer
446
447  return status;
448}
449