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