re PR bootstrap/15627 (Sun CC cannot bootstrap GCC)
[gcc.git] / fastjar / jartool.c
1 /*
2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002 Free Software Foundation
4 Copyright (C) 1999, 2000, 2001 Bryan Burns
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 /*
22 Revision 1.10 2002/01/03 04:57:56 rodrigc
23 2001-01-02 Craig Rodrigues <rodrigc@gcc.gnu.org>
24
25 PR bootstrap/5117
26 * configure.in (AC_CHECK_HEADERS): Check for stdlib.h.
27 * Makefile.am: Move grepjar to bin_PROGRAMS.
28 * config.h.in: Regenerated.
29 * Makefile.in: Regenerated.
30 * aclocal.m4: Regenerated.
31 * jargrep.c: Eliminate some signed/unsigned and default
32 uninitialized warnings. Use HAVE_STDLIB_H instead of
33 STDC_HEADERS macro.
34 * jartool.c: Likewise.
35 * compress.c: Likewise.
36
37 Revision 1.9 2001/10/12 00:49:42 bryce
38 * jatool.c (extract_jar): Account for null termination when
39 determining whether to expand "filename".
40
41 Revision 1.8 2001/08/29 01:35:31 apbianco
42 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
43
44 * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
45 Fixes PR java/3949.
46
47 (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
48
49 Revision 1.7 2001/08/27 23:09:37 tromey
50 * jartool.c (jarfile): Remove length limitation.
51 (main): Use jt_strdup when initializing jarfile.
52
53 Revision 1.6 2001/07/04 18:33:53 tromey
54 Modified from patch by Julian Hall <jules@acris.co.uk>:
55 * jartool.c (errno): Conditionally declare.
56 (O_BINARY): Conditionally define.
57 (main): Use open, not creat. Use O_BINARY everywhere.
58 (make_manifest): Use O_BINARY.
59 (add_to_jar): Likewise.
60
61 Revision 1.5 2001/05/03 21:40:47 danglin
62 * jartool.c (jt_strdup): New function.
63 (get_next_arg): Use jt_strdup instead of strdup.
64
65 Revision 1.4 2000/12/28 21:47:37 robertl
66 2000-12-28 Robert Lipe <robertl@sco.com>
67
68 * jartool.c (MAXPATHLEN): Provide if not defined.
69
70 Revision 1.3 2000/12/14 18:45:35 ghazi
71 Warning fixes:
72
73 * compress.c: Include stdlib.h and compress.h.
74 (rcsid): Delete.
75 (report_str_error): Make static.
76 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
77 (hrd_inflate_str): Likewise.
78
79 * compress.h (init_compression, end_compression, init_inflation,
80 end_inflation): Prototype void arguments.
81
82 * dostime.c (rcsid): Delete.
83
84 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
85 Make functions static. Cast ctype function argument to `unsigned
86 char'. Add parens in if-stmts. Constify.
87 (Usage): Change into a macro.
88 (jargrep): Remove unused parameter.
89
90 * jartool.c: Constify. Add parens in if-stmts. Align
91 signed/unsigned char pointers in functions calls using casts.
92 (rcsid): Delete.
93 (list_jar): Fix printf format specifier.
94 (usage): Chop long string into bits. Reformat.
95
96 * pushback.c (rcsid): Delete.
97
98 Revision 1.2 2000/12/13 18:11:57 tromey
99 * jartool.c (extract_jar): Use strchr, not index.
100
101 Revision 1.1 2000/12/09 03:08:23 apbianco
102 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
103
104 * fastjar: Imported.
105
106 Revision 1.5 2000/08/24 15:01:27 cory
107 Made certain that fastjar opened the jar file before trying to update it
108 with the -u option.
109
110 Revision 1.4 2000/08/24 13:39:21 cory
111 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
112 when byte swapping. Better safe than sorry.
113
114 Revision 1.3 2000/08/23 19:42:17 cory
115 Added support for more Unix platforms. The following code has been hacked
116 to work on AIX, Solaris, True 64, and HP-UX.
117 Added bigendian check. Probably works on most big and little endian platforms
118 now.
119
120 Revision 1.2 1999/12/06 07:38:28 toast
121 fixed recursive archiving bug
122
123 Revision 1.1.1.1 1999/12/06 03:09:34 toast
124 initial checkin..
125
126
127
128 Revision 1.22 1999/10/12 19:45:13 burnsbr
129 adding patch to fix compat problem
130
131 Revision 1.21 1999/05/10 09:15:49 burnsbr
132 fixed manifest file version info
133
134 Revision 1.20 1999/05/10 08:53:16 burnsbr
135 *** empty log message ***
136
137 Revision 1.19 1999/05/10 08:30:39 burnsbr
138 added extract / listing code
139
140 Revision 1.18 1999/04/28 04:24:29 burnsbr
141 updated version
142
143 Revision 1.17 1999/04/28 04:21:23 burnsbr
144 added support for -C dir-changing flag.. Updated total compression display
145
146 Revision 1.16 1999/04/27 10:28:22 burnsbr
147 updated version string
148
149 Revision 1.15 1999/04/27 10:04:06 burnsbr
150 configure support
151
152 Revision 1.14 1999/04/27 08:56:14 burnsbr
153 added -V flag, better error messages
154
155 Revision 1.13 1999/04/26 02:35:21 burnsbr
156 changed all sorts of stuff.. compression now works 100%
157
158 Revision 1.12 1999/04/23 12:00:45 burnsbr
159 90% done with compression code
160
161 Revision 1.11 1999/04/22 04:12:57 burnsbr
162 finished first round of Manifest file support..
163 might need to do more, digest etc..
164
165 Revision 1.10 1999/04/22 02:35:23 burnsbr
166 added more manifest support, about 75% done now. Replaced all the
167 redundant shifts and bit-logic with a macro or two, making the code
168 easier to read.
169
170 Revision 1.9 1999/04/21 09:55:16 burnsbr
171 pulled out printfs
172
173 Revision 1.8 1999/04/21 02:58:01 burnsbr
174 started manifest code
175
176 Revision 1.7 1999/04/20 23:15:28 burnsbr
177 added patch sent by John Bley <jbb6@acpub.duke.edu>
178
179 Revision 1.6 1999/04/20 08:56:02 burnsbr
180 added GPL comment
181
182 Revision 1.5 1999/04/20 08:16:09 burnsbr
183 fixed verbose flag, did some optimization
184
185 Revision 1.4 1999/04/20 05:09:59 burnsbr
186 added rcsid variable
187
188 Revision 1.3 1999/04/20 05:08:54 burnsbr
189 fixed Log statement
190
191 */
192
193 #include "config.h"
194
195 #include <zlib.h>
196
197 #ifdef HAVE_STDLIB_H
198 #include <stdlib.h>
199 #endif
200
201 #ifdef HAVE_UNISTD_H
202 #include <unistd.h>
203 #endif
204
205 #include <stdio.h>
206 #include <sys/stat.h>
207 #include <sys/types.h>
208
209 #ifdef HAVE_SYS_PARAM_H
210 #include <sys/param.h>
211 #endif
212
213 #ifndef MAXPATHLEN
214 #define MAXPATHLEN 1024
215 #endif
216
217 #ifdef HAVE_DIRENT_H
218 #include <dirent.h>
219 #endif
220
221 #ifdef HAVE_FCNTL_H
222 #include <fcntl.h>
223 #endif
224
225 #include <string.h>
226 #include <errno.h>
227
228 #ifdef TM_IN_SYS_TIME
229 #include <sys/time.h>
230 #else
231 #include <time.h>
232 #endif
233
234 #include <getopt.h>
235
236 #include "jartool.h"
237 #include "zipfile.h"
238 #include "dostime.h"
239 #include "pushback.h"
240 #include "compress.h"
241
242 /* Some systems have mkdir that takes a single argument. */
243 #ifdef MKDIR_TAKES_ONE_ARG
244 # define mkdir(a,b) mkdir(a)
245 #endif
246
247
248 #ifdef WORDS_BIGENDIAN
249
250 #define L2BI(l) ((l & 0xff000000) >> 24) | \
251 ((l & 0x00ff0000) >> 8) | \
252 ((l & 0x0000ff00) << 8) | \
253 ((l & 0x000000ff) << 24);
254
255 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
256
257 #endif
258
259 #ifndef errno
260 extern int errno;
261 #endif
262
263 #ifndef O_BINARY
264 #define O_BINARY 0
265 #endif
266
267 void usage(const char*);
268 void help(const char *);
269 void version(void);
270 void add_entry(struct zipentry *);
271 void init_headers(void);
272
273 int consume(pb_file *, int);
274 int list_jar(int, char**, int);
275 int extract_jar(int, char**, int);
276 int add_file_to_jar(int, int, const char*, struct stat*);
277 int add_to_jar(int, const char*);
278 int add_to_jar_with_dir(int, const char*, const char*);
279 int create_central_header(int);
280 int make_manifest(int, const char*);
281 static void init_args(char **, int);
282 static char *get_next_arg (void);
283 static char *jt_strdup (char*);
284 static void expand_options (int *argcp, char ***argvp);
285
286 /* global variables */
287 ub1 file_header[30];
288 ub1 data_descriptor[16];
289 int do_compress;
290 int seekable;
291 int verbose;
292 char *jarfile;
293
294 /* If non zero, then don't recurse in directory. Instead, add the
295 directory entry and relie on an explicit list of files to populate
296 the archive. This option isn't supported by the original jar tool. */
297 int use_explicit_list_only;
298
299 /* If non zero, then read the entry names from stdin. This option
300 isn't supported by the original jar tool. */
301 int read_names_from_stdin;
302
303 zipentry *ziplist; /* linked list of entries */
304 zipentry *ziptail; /* tail of the linked list */
305
306 int number_of_entries; /* number of entries in the linked list */
307
308 /* This is used to mark options with no short value. */
309 #define LONG_OPT(Num) ((Num) + 128)
310
311 #define OPT_HELP LONG_OPT (0)
312
313 /* This holds all options. */
314 #define OPTION_STRING "-ctxuvVf:m:C:0ME@"
315
316 /* Define the MANIFEST content here to have it easier with calculations
317 below. This is for the case we create an empty MANIFEST.MF. */
318 #define MANIFEST_STR "Manifest-Version: 1.0\nCreated-By: "
319 #define MANIFEST_END "\n\n"
320
321 static const struct option options[] =
322 {
323 { "help", no_argument, NULL, OPT_HELP },
324 { "version", no_argument, NULL, 'V' },
325 { NULL, no_argument, NULL, 0 }
326 };
327
328 int main(int argc, char **argv){
329
330 char *mfile = NULL;
331
332 int action = ACTION_NONE;
333 int manifest = TRUE;
334 int opt;
335
336 int jarfd = -1;
337
338 /* These are used to collect file names and `-C' options for the
339 second pass through the command line. */
340 int new_argc;
341 char **new_argv;
342
343 do_compress = TRUE;
344 verbose = FALSE;
345
346 ziplist = NULL;
347
348 number_of_entries = 0;
349
350 if(argc < 2)
351 usage(argv[0]);
352
353 new_argc = 0;
354 new_argv = (char **) malloc (argc * sizeof (char *));
355
356 expand_options (&argc, &argv);
357 while ((opt = getopt_long (argc, argv, OPTION_STRING,
358 options, NULL)) != -1) {
359 switch(opt){
360 case 'C':
361 new_argv[new_argc++] = (char *) "-C";
362 /* ... fall through ... */
363 case 1:
364 /* File name or unparsed option, due to RETURN_IN_ORDER. */
365 new_argv[new_argc++] = optarg;
366 break;
367 case 'c':
368 action = ACTION_CREATE;
369 break;
370 case 't':
371 action = ACTION_LIST;
372 break;
373 case 'x':
374 action = ACTION_EXTRACT;
375 break;
376 case 'u':
377 action = ACTION_UPDATE;
378 break;
379 case 'v':
380 verbose = TRUE;
381 break;
382 case 'V':
383 version();
384 exit(0);
385 case 'f':
386 jarfile = optarg;
387 break;
388 case 'm':
389 mfile = optarg;
390 break;
391 case '0':
392 do_compress = FALSE;
393 break;
394 case 'M':
395 manifest = FALSE;
396 break;
397
398 case OPT_HELP:
399 help(argv[0]);
400 break;
401
402 /* The following options aren't supported by the original jar tool. */
403 case 'E':
404 use_explicit_list_only = TRUE;
405 break;
406 case '@':
407 read_names_from_stdin = TRUE;
408 break;
409 default:
410 usage(argv[0]);
411 }
412 }
413
414 /* We might have seen `--'. In this case we want to make sure that
415 all following options are handled as file names. */
416 while (optind < argc)
417 new_argv[new_argc++] = argv[optind++];
418 new_argv[new_argc] = NULL;
419
420 if(action == ACTION_NONE){
421 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
422 usage(argv[0]);
423 }
424
425 if(action == ACTION_UPDATE){
426 fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
427 exit(1);
428 }
429
430 /* Verify unsupported combinations and warn of the use of non
431 standard features */
432 if(verbose && use_explicit_list_only)
433 fprintf (stderr, "Warning: using non standard '-E' option\n");
434 if(verbose && read_names_from_stdin)
435 fprintf (stderr, "Warning: using non standard '-@' option\n");
436 if(read_names_from_stdin
437 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
438 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
439 usage(argv[0]);
440 }
441
442 /* create the jarfile */
443 if(action == ACTION_CREATE){
444 if(jarfile){
445 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
446
447 if(jarfd < 0){
448 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
449 perror(jarfile);
450 exit(1);
451 }
452
453 /* We assume that the file is seekable */
454 seekable = TRUE;
455
456 } else {
457
458 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
459
460 /* standard out is not seekable */
461 seekable = FALSE;
462
463 /* don't want our output to be part of the jar file.. figured this one
464 out the hard way.. =P */
465 verbose = FALSE;
466 }
467 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
468
469 if(jarfile){
470 jarfd = open(jarfile, O_RDONLY | O_BINARY);
471
472 if(jarfd < 0){
473 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
474 perror(jarfile);
475 exit(1);
476 }
477
478 seekable = TRUE;
479 } else {
480 jarfd = STDIN_FILENO; /* jarfd is standard in */
481
482 /* we assume that the stream isn't seekable for safety */
483 seekable = FALSE;
484 }
485 }
486
487 if(action == ACTION_CREATE || action == ACTION_UPDATE){
488 const char *arg;
489 init_headers();
490
491 if((action == ACTION_UPDATE) && jarfile) {
492 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
493 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
494 perror(jarfile);
495 exit(1);
496 }
497 }
498
499 if(do_compress)
500 init_compression();
501
502
503 /* Add the META-INF/ directory and the manifest */
504 if(manifest && mfile)
505 make_manifest(jarfd, mfile);
506 else if(manifest)
507 make_manifest(jarfd, NULL);
508
509 init_args (new_argv, 0);
510 /* now we add the files to the archive */
511 while ((arg = get_next_arg ())){
512
513 if(!strcmp(arg, "-C")){
514 const char *dir_to_change = get_next_arg ();
515 const char *file_to_add = get_next_arg ();
516 if (!dir_to_change || !file_to_add) {
517 fprintf(stderr, "Error: missing argument for -C.\n");
518 exit(1);
519 }
520 if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add)) {
521 fprintf(stderr,
522 "Error adding %s (in directory %s) to jar archive!\n",
523 file_to_add, dir_to_change);
524 exit(1);
525 }
526 } else {
527 if(add_to_jar(jarfd, arg)){
528 fprintf(stderr, "Error adding %s to jar archive!\n", arg);
529 exit(1);
530 }
531 }
532 }
533 /* de-initialize the compression DS */
534 if(do_compress)
535 end_compression();
536
537 create_central_header(jarfd);
538
539 if (close(jarfd) != 0) {
540 fprintf(stderr, "Error closing jar archive!\n");
541 }
542 } else if(action == ACTION_LIST){
543 list_jar(jarfd, &new_argv[0], new_argc);
544 } else if(action == ACTION_EXTRACT){
545 extract_jar(jarfd, &new_argv[0], new_argc);
546 }
547
548 exit(0);
549 }
550
551 static int args_current_g;
552 static char **args_g;
553
554 static void
555 init_args(args, current)
556 char **args;
557 int current;
558 {
559 if(!read_names_from_stdin)
560 {
561 args_g = args;
562 args_current_g = current;
563 }
564 }
565
566 static char *
567 get_next_arg ()
568 {
569 static int reached_end = 0;
570
571 if (reached_end)
572 return NULL;
573
574 if (args_g)
575 {
576 if (!args_g [args_current_g])
577 {
578 reached_end = 1;
579 return NULL;
580 }
581 return args_g [args_current_g++];
582 }
583 else
584 {
585 /* Read the name from stdin. Delimiters are '\n' and
586 '\r'. Reading EOF indicates that we don't have anymore file
587 names characters to read. */
588
589 char s [MAXPATHLEN];
590 int pos = 0;
591
592 /* Get rid of '\n' and '\r' first. */
593 while (1)
594 {
595 int c = getc (stdin);
596 if (c == '\n' || c == '\r')
597 continue;
598 else
599 {
600 if (c == EOF)
601 return NULL;
602 ungetc (c, stdin);
603 break;
604 }
605 }
606
607 while (1)
608 {
609 int c = getc (stdin);
610 /* Exit when we get a delimiter or don't have any characters
611 to read */
612 if (c == '\n'|| c == '\r'|| c == EOF)
613 break;
614 s [pos++] = (char) c;
615 }
616
617 if (pos)
618 {
619 s [pos] = '\0';
620 return jt_strdup (s);
621 }
622 else
623 return NULL;
624 }
625 }
626
627 void init_headers(){
628 /* packing file header */
629 /* magic number */
630 file_header[0] = 0x50;
631 file_header[1] = 0x4b;
632 file_header[2] = 0x03;
633 file_header[3] = 0x04;
634 /* version number (Unix 1.0)*/
635 file_header[4] = 10;
636 file_header[5] = 0;
637 /* bit flag (normal deflation)*/
638 file_header[6] = 0x00;
639
640 file_header[7] = 0x00;
641 /* do_compression method (deflation) */
642 file_header[8] = 0;
643 file_header[9] = 0;
644
645 /* last mod file time (MS-DOS format) */
646 file_header[10] = 0;
647 file_header[11] = 0;
648 /* last mod file date (MS-DOS format) */
649 file_header[12] = 0;
650 file_header[13] = 0;
651 /* CRC 32 */
652 file_header[14] = 0;
653 file_header[15] = 0;
654 file_header[16] = 0;
655 file_header[17] = 0;
656 /* compressed size */
657 file_header[18] = 0;
658 file_header[19] = 0;
659 file_header[20] = 0;
660 file_header[21] = 0;
661 /* uncompressed size */
662 file_header[22] = 0;
663 file_header[23] = 0;
664 file_header[24] = 0;
665 file_header[25] = 0;
666 /* filename length */
667 file_header[26] = 0;
668 file_header[27] = 0;
669 /* extra field length */
670 file_header[28] = 0;
671 file_header[29] = 0;
672
673 /* Initialize the compression DS */
674 PACK_UB4(data_descriptor, 0, 0x08074b50);
675
676 }
677
678 void add_entry(struct zipentry *ze){
679
680 if(ziplist == NULL){
681 ziplist = ze;
682 ziptail = ziplist;
683 } else {
684 ziplist->next_entry = ze;
685 ziplist = ze;
686 }
687
688 number_of_entries++;
689 }
690
691 int make_manifest(int jfd, const char *mf_name){
692 time_t current_time;
693 int nlen; /* length of file name */
694 int mod_time; /* file modification time */
695 struct zipentry *ze;
696
697 nlen = 9; /* trust me on this one */
698
699 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
700
701 current_time = time(NULL);
702 if(current_time == (time_t)-1){
703 perror("time");
704 exit(1);
705 }
706
707 mod_time = unix2dostime(&current_time);
708
709 PACK_UB2(file_header, LOC_EXTRA, 0);
710 PACK_UB2(file_header, LOC_COMP, 0);
711 PACK_UB2(file_header, LOC_FNLEN, nlen);
712 PACK_UB4(file_header, LOC_MODTIME, mod_time);
713
714 if(verbose)
715 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
716
717 ze = (zipentry*)malloc(sizeof(zipentry));
718 if(ze == NULL){
719 perror("malloc");
720 exit(1);
721 }
722
723 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
724 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
725 strcpy(ze->filename, "META-INF/");
726 ze->filename[nlen] = '\0';
727
728 ze->offset = lseek(jfd, 0, SEEK_CUR);
729 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
730 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
731 ze->compressed = FALSE;
732
733 add_entry(ze);
734
735 write(jfd, file_header, 30);
736 write(jfd, "META-INF/", nlen);
737
738 /* if the user didn't specify an external manifest file... */
739 if(mf_name == NULL){
740
741 int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
742 char *mf;
743
744 if((mf = (char *) malloc(mf_len + 1))) {
745 uLong crc;
746
747 sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
748
749 crc = crc32(0L, Z_NULL, 0);
750
751 crc = crc32(crc, (const unsigned char *)mf, mf_len);
752
753 nlen = 20; /* once again, trust me */
754
755 PACK_UB2(file_header, LOC_EXTRA, 0);
756 PACK_UB2(file_header, LOC_COMP, 0);
757 PACK_UB2(file_header, LOC_FNLEN, nlen);
758 PACK_UB4(file_header, LOC_USIZE, mf_len);
759
760 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
761
762 PACK_UB4(file_header, LOC_CRC, crc);
763
764 if(verbose)
765 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
766
767 ze = (zipentry*)malloc(sizeof(zipentry));
768 if(ze == NULL){
769 perror("malloc");
770 exit(1);
771 }
772
773 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
774 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
775 strcpy(ze->filename, "META-INF/MANIFEST.MF");
776 ze->filename[nlen] = '\0';
777
778 ze->offset = lseek(jfd, 0, SEEK_CUR);
779 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
780 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
781 ze->crc = crc;
782 ze->csize = mf_len;
783 ze->usize = ze->csize;
784 ze->compressed = FALSE;
785
786 add_entry(ze);
787
788 write(jfd, file_header, 30);
789 write(jfd, "META-INF/MANIFEST.MF", nlen);
790 write(jfd, mf, mf_len);
791 free(mf);
792 }
793 else {
794 printf("malloc errror\n");
795 exit(-1);
796 }
797 } else {
798 int mfd;
799 struct stat statbuf;
800
801 stat(mf_name, &statbuf);
802
803 if(!S_ISREG(statbuf.st_mode)){
804 fprintf(stderr, "Invalid manifest file specified.\n");
805 exit(1);
806 }
807
808 mfd = open(mf_name, O_RDONLY | O_BINARY);
809
810 if(mfd < 0){
811 fprintf(stderr, "Error opening %s.\n", mf_name);
812 exit(1);
813 }
814
815 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
816 perror("error writing to jar");
817 exit(1);
818 }
819
820 }
821
822 return 0;
823 }
824
825 /* Implements -C by wrapping add_to_jar. new_dir is the directory
826 to switch to. */
827 int
828 add_to_jar_with_dir (int fd, const char* new_dir, const char* file)
829 {
830 int retval;
831 char old_dir[MAXPATHLEN];
832 if (getcwd(old_dir, MAXPATHLEN) == NULL) {
833 perror("getcwd");
834 return 1;
835 }
836 if (chdir(new_dir) == -1) {
837 perror(new_dir);
838 return 1;
839 }
840 retval=add_to_jar(fd, file);
841 if (chdir(old_dir) == -1) {
842 perror(old_dir);
843 return 1;
844 }
845 return retval;
846 }
847
848 int
849 add_to_jar (int fd, const char *file) {
850 struct stat statbuf;
851 DIR *dir;
852 struct dirent *de;
853 zipentry *ze;
854 int stat_return;
855
856 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
857 * It fixes this:
858 * "normal" jar : org/apache/java/io/LogRecord.class
859 * fastjar : ./org/apache/java/io/LogRecord.class
860 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
861 * with both kaffe-1.0b4 and JDK.
862 */
863 while (*file=='.' && *(file+1)=='/')
864 file+=2;
865
866 if(jarfile && !strcmp(file, jarfile)){
867 if(verbose)
868 printf("skipping: %s\n", file);
869 return 0; /* we don't want to add ourselves.. */
870 }
871
872 stat_return = stat(file, &statbuf);
873
874 if(stat_return == -1){
875 perror(file);
876 return 1;
877 } else if(S_ISDIR(statbuf.st_mode)){
878 char *fullname;
879 char *t_ptr;
880 int nlen;
881 unsigned long mod_time;
882
883 dir = opendir(file);
884
885 if(dir == NULL){
886 perror("opendir");
887 return 1;
888 }
889
890 nlen = strlen(file) + 256;
891 fullname = (char*)malloc(nlen * sizeof(char));
892 memset(fullname, 0, (nlen * sizeof(char)));
893
894 if(fullname == NULL){
895 fprintf(stderr, "Filename is NULL!\n");
896 return 1;
897 }
898
899 strcpy(fullname, file);
900 nlen = strlen(file);
901
902 if(fullname[nlen - 1] != '/'){
903 fullname[nlen] = '/';
904 t_ptr = (fullname + nlen + 1);
905 } else
906 t_ptr = (fullname + nlen);
907
908
909 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
910
911 nlen = (t_ptr - fullname);
912
913 mod_time = unix2dostime(&statbuf.st_mtime);
914
915 PACK_UB2(file_header, LOC_EXTRA, 0);
916 PACK_UB2(file_header, LOC_COMP, 0);
917 PACK_UB2(file_header, LOC_FNLEN, nlen);
918 PACK_UB4(file_header, LOC_MODTIME, mod_time);
919
920 if(verbose)
921 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
922
923 ze = (zipentry*)malloc(sizeof(zipentry));
924 if(ze == NULL){
925 perror("malloc");
926 exit(1);
927 }
928
929 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
930 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
931 strcpy(ze->filename, fullname);
932 ze->filename[nlen] = '\0';
933
934 ze->offset = lseek(fd, 0, SEEK_CUR);
935 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
936 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
937 ze->compressed = FALSE;
938
939 add_entry(ze);
940
941 write(fd, file_header, 30);
942 write(fd, fullname, nlen);
943
944 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
945 if(de->d_name[0] == '.')
946 continue;
947 if(jarfile && !strcmp(de->d_name, jarfile)){
948 /* we don't want to add ourselves. Believe me */
949 if(verbose)
950 printf("skipping: %s\n", de->d_name);
951 continue;
952 }
953
954 strcpy(t_ptr, de->d_name);
955
956 if (add_to_jar(fd, fullname)) {
957 fprintf(stderr, "Error adding file to jar!\n");
958 return 1;
959 }
960 }
961
962 free(fullname);
963 closedir(dir);
964
965 } else if(S_ISREG(statbuf.st_mode)){
966 int add_fd;
967
968 add_fd = open(file, O_RDONLY | O_BINARY);
969 if(add_fd < 0){
970 fprintf(stderr, "Error opening %s.\n", file);
971 return 1;
972 }
973
974 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
975 fprintf(stderr, "Error adding file to jar!\n");
976 return 1;
977 }
978
979 } else {
980 fprintf(stderr, "Illegal file specified: %s\n", file);
981 }
982 return 0;
983 }
984
985 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
986
987 unsigned short file_name_length;
988 unsigned long mod_time;
989 ub1 rd_buff[RDSZ];
990 uLong crc = 0;
991 off_t offset = 0;
992 int rdamt;
993 struct zipentry *ze;
994
995 mod_time = unix2dostime(&(statbuf->st_mtime));
996 file_name_length = strlen(fname);
997
998 if(!seekable && !do_compress){
999 crc = crc32(0L, Z_NULL, 0);
1000
1001 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
1002 crc = crc32(crc, rd_buff, rdamt);
1003
1004 lseek(ffd, 0, SEEK_SET);
1005 }
1006
1007 /* data descriptor */
1008 if(!seekable && do_compress){
1009 PACK_UB2(file_header, LOC_EXTRA, 8);
1010 } else {
1011 PACK_UB2(file_header, LOC_EXTRA, 0);
1012 }
1013
1014 if(do_compress){
1015 PACK_UB2(file_header, LOC_COMP, 8);
1016 } else {
1017 PACK_UB2(file_header, LOC_COMP, 0);
1018 }
1019
1020 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1021 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1022
1023 if(!seekable && !do_compress){
1024 PACK_UB4(file_header, LOC_CRC, crc);
1025 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1026 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1027 } else
1028 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1029
1030 ze = (zipentry*)malloc(sizeof(zipentry));
1031 if(ze == NULL){
1032 perror("malloc");
1033 exit(1);
1034 }
1035
1036 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1037 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1038 strcpy(ze->filename, fname);
1039
1040 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1041 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1042
1043 if(!seekable && !do_compress)
1044 ze->crc = crc;
1045
1046 ze->csize = statbuf->st_size;
1047 ze->usize = ze->csize;
1048 ze->offset = lseek(jfd, 0, SEEK_CUR);
1049 if(do_compress)
1050 ze->compressed = TRUE;
1051 else
1052 ze->compressed = FALSE;
1053
1054 add_entry(ze);
1055
1056 /* Write the local header */
1057 write(jfd, file_header, 30);
1058
1059 /* write the file name to the zip file */
1060 write(jfd, fname, file_name_length);
1061
1062
1063 if(verbose){
1064 printf("adding: %s ", fname);
1065 fflush(stdout);
1066 }
1067
1068 if(do_compress){
1069 /* compress the file */
1070 compress_file(ffd, jfd, ze);
1071 } else {
1072 /* Write the contents of the file (uncompressed) to the zip file */
1073 /* calculate the CRC as we go along */
1074 ze->crc = crc32(0L, Z_NULL, 0);
1075
1076 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1077 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1078 if(write(jfd, rd_buff, rdamt) != rdamt){
1079 perror("write");
1080 return 0;
1081 }
1082 }
1083 }
1084 close(ffd);
1085
1086 /* write out data descriptor */
1087 PACK_UB4(data_descriptor, 4, ze->crc);
1088 PACK_UB4(data_descriptor, 8, ze->csize);
1089 PACK_UB4(data_descriptor, 12, ze->usize);
1090
1091 /* we need to seek back and fill the header */
1092 if(seekable){
1093 offset = (ze->csize + strlen(ze->filename) + 16);
1094
1095 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1096 perror("lseek");
1097 exit(1);
1098 }
1099
1100 if(write(jfd, (data_descriptor + 4), 12) != 12){
1101 perror("write");
1102 return 0;
1103 }
1104
1105 offset -= 12;
1106
1107 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1108 perror("lseek");
1109 exit(1);
1110 }
1111 } else if(do_compress){
1112 /* Sun's jar tool will only allow a data descriptor if the entry is
1113 compressed, but we'll save 16 bytes/entry if we only use it when
1114 we can't seek back on the file */
1115
1116 if(write(jfd, data_descriptor, 16) != 16){
1117 perror("write");
1118 return 0;
1119 }
1120 }
1121
1122 if(verbose)
1123 printf("(in=%d) (out=%d) (%s %d%%)\n",
1124 (int)ze->usize, (int)ze->csize,
1125 (do_compress ? "deflated" : "stored"),
1126 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1127
1128 return 0;
1129 }
1130
1131 int create_central_header(int fd){
1132 ub1 header[46];
1133 ub1 end_header[22];
1134 int start_offset;
1135 int dir_size;
1136 int total_in = 0, total_out = 22;
1137
1138 zipentry *ze;
1139
1140 /* magic number */
1141 header[0] = 'P';
1142 header[1] = 'K';
1143 header[2] = 1;
1144 header[3] = 2;
1145 /* version made by */
1146 header[4] = 10;
1147 header[5] = 0;
1148 /* version needed to extract */
1149 header[6] = 10;
1150 header[7] = 0;
1151 /* bit flag */
1152 header[8] = 0;
1153 header[9] = 0;
1154 /* compression method */
1155 header[10] = 0;
1156 header[11] = 0;
1157 /* file mod time */
1158 header[12] = 0;
1159 header[13] = 0;
1160 /* file mod date */
1161 header[14] = 0;
1162 header[15] = 0;
1163 /* crc 32 */
1164 header[16] = 0;
1165 header[17] = 0;
1166 header[18] = 0;
1167 header[19] = 0;
1168 /* compressed size */
1169 header[20] = 0;
1170 header[21] = 0;
1171 header[22] = 0;
1172 header[23] = 0;
1173 /* uncompressed size */
1174 header[24] = 0;
1175 header[25] = 0;
1176 header[26] = 0;
1177 header[27] = 0;
1178 /* filename length */
1179 header[28] = 0;
1180 header[29] = 0;
1181 /* extra field length */
1182 header[30] = 0;
1183 header[31] = 0;
1184 /* file comment length */
1185 header[32] = 0;
1186 header[33] = 0;
1187 /* disk number start */
1188 header[34] = 0;
1189 header[35] = 0;
1190 /* internal file attribs */
1191 header[36] = 0;
1192 header[37] = 0;
1193 /* external file attribs */
1194 header[38] = 0;
1195 header[39] = 0;
1196 header[40] = 0;
1197 header[41] = 0;
1198 /* relative offset of local header */
1199 header[42] = 0;
1200 header[43] = 0;
1201 header[44] = 0;
1202 header[45] = 0;
1203
1204 start_offset = lseek(fd, 0, SEEK_CUR);
1205
1206 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1207
1208 total_in += ze->usize;
1209 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1210
1211 if(ze->compressed){
1212 PACK_UB2(header, CEN_COMP, 8);
1213 } else {
1214 PACK_UB2(header, CEN_COMP, 0);
1215 }
1216
1217 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1218 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1219 PACK_UB4(header, CEN_CRC, ze->crc);
1220 PACK_UB4(header, CEN_CSIZE, ze->csize);
1221 PACK_UB4(header, CEN_USIZE, ze->usize);
1222 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1223 PACK_UB4(header, CEN_OFFSET, ze->offset);
1224
1225 write(fd, header, 46);
1226
1227 write(fd, ze->filename, strlen(ze->filename));
1228 }
1229
1230 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1231
1232 /* magic number */
1233 end_header[0] = 0x50;
1234 end_header[1] = 0x4b;
1235 end_header[2] = 0x05;
1236 end_header[3] = 0x06;
1237 /* number of this disk */
1238 end_header[4] = 0;
1239 end_header[5] = 0;
1240 /* number of disk w/ start of central header */
1241 end_header[6] = 0;
1242 end_header[7] = 0;
1243 /* total number of entries in central dir on this disk*/
1244 PACK_UB2(end_header, 8, number_of_entries);
1245 /* total number of entries in central dir*/
1246 PACK_UB2(end_header, 10, number_of_entries);
1247 /* size of central dir. */
1248 PACK_UB4(end_header, 12, dir_size);
1249 /* offset of start of central dir */
1250 PACK_UB4(end_header, 16, start_offset);
1251 /* zipfile comment length */
1252 end_header[20] = 0;
1253 end_header[21] = 0;
1254
1255 write(fd, end_header, 22);
1256
1257 if(verbose)
1258 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1259 total_in,
1260 total_out,
1261 (do_compress ? "deflated" : "stored"),
1262 (int)((1 - (total_out / (float)total_in)) * 100)
1263 );
1264
1265 return 0;
1266 }
1267
1268 int extract_jar(int fd, char **files, int file_num){
1269 int rdamt;
1270 int out_a, in_a;
1271 ub4 signature;
1272 ub4 csize;
1273 ub4 crc;
1274 ub2 fnlen;
1275 ub2 eflen;
1276 ub2 flags;
1277 ub2 method;
1278 ub1 *filename = NULL;
1279 int filename_len = 0;
1280 ub4 rd_buff[RDSZ];
1281 pb_file pbf;
1282 ub1 scratch[16];
1283 zipentry ze;
1284 int f_fd;
1285 int dir;
1286 int handle;
1287 int j;
1288
1289 init_inflation();
1290
1291 pb_init(&pbf, fd);
1292
1293 for(;;){
1294 f_fd = 0;
1295 crc = 0;
1296 ze.crc = 0;
1297
1298 dir = FALSE; /* by default, the file isn't a dir */
1299 handle = TRUE; /* by default we'll extract/create the file */
1300
1301 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1302 perror("read");
1303 break;
1304 }
1305
1306 signature = UNPACK_UB4(scratch, 0);
1307
1308 #ifdef DEBUG
1309 printf("signature is %x\n", signature);
1310 #endif
1311 if(signature == 0x08074b50){
1312 #ifdef DEBUG
1313 printf("skipping data descriptor\n");
1314 #endif
1315 pb_read(&pbf, scratch, 12);
1316 continue;
1317 } else if(signature == 0x02014b50){
1318 #ifdef DEBUG
1319 printf("Central header reached.. we're all done!\n");
1320 #endif
1321 break;
1322 }else if(signature != 0x04034b50){
1323 printf("Ick! %#x\n", signature);
1324 break;
1325 }
1326
1327 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1328 perror("read");
1329 break;
1330 }
1331
1332 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1333 #ifdef DEBUG
1334 printf("Compressed size is %u\n", csize);
1335 #endif
1336
1337 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1338 #ifdef DEBUG
1339 printf("Filename length is %hu\n", fnlen);
1340 #endif
1341
1342 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1343 #ifdef DEBUG
1344 printf("Extra field length is %hu\n", eflen);
1345 #endif
1346
1347 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1348 #ifdef DEBUG
1349 printf("Flags are %#hx\n", flags);
1350 #endif
1351
1352 method = UNPACK_UB2(file_header, LOC_COMP);
1353 #ifdef DEBUG
1354 printf("Compression method is %#hx\n", method);
1355 #endif
1356
1357 /* if there isn't a data descriptor */
1358 if(!(flags & 0x0008)){
1359 crc = UNPACK_UB4(file_header, LOC_CRC);
1360 #ifdef DEBUG
1361 printf("CRC is %x\n", crc);
1362 #endif
1363 }
1364
1365 if(filename_len < fnlen + 1){
1366 if(filename != NULL)
1367 free(filename);
1368
1369 filename = malloc(sizeof(ub1) * (fnlen + 1));
1370 filename_len = fnlen + 1;
1371 }
1372
1373 pb_read(&pbf, filename, fnlen);
1374 filename[fnlen] = '\0';
1375
1376 #ifdef DEBUG
1377 printf("filename is %s\n", filename);
1378 #endif
1379
1380 if(file_num > 0){
1381 handle = FALSE;
1382
1383 for(j = 0; j < file_num; j++)
1384 if(strcmp(files[j], (const char *)filename) == 0){
1385 handle = TRUE;
1386 break;
1387 }
1388 }
1389
1390 if(!handle)
1391 f_fd = -1;
1392
1393 /* OK, there is some directory information in the file. Nothing to do
1394 but ensure the directory(s) exist, and create them if they don't.
1395 What a pain! */
1396 if(strchr((const char *)filename, '/') != NULL && handle){
1397 /* Loop through all the directories in the path, (everything w/ a '/') */
1398 const ub1 *start = filename;
1399 char *tmp_buff;
1400 struct stat sbuf;
1401
1402 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1403
1404 for(;;){
1405 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1406
1407 if(idx == NULL)
1408 break;
1409 else if(idx == start){
1410 start++;
1411 continue;
1412 }
1413 start = idx + 1;
1414
1415 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1416 tmp_buff[(idx - filename)] = '\0';
1417
1418 #ifdef DEBUG
1419 printf("checking the existance of %s\n", tmp_buff);
1420 #endif
1421
1422 if(stat(tmp_buff, &sbuf) < 0){
1423 if(errno != ENOENT){
1424 perror("stat");
1425 exit(1);
1426 }
1427
1428 } else if(S_ISDIR(sbuf.st_mode)){
1429 #ifdef DEBUG
1430 printf("Directory exists\n");
1431 #endif
1432 continue;
1433 }else {
1434 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1435 tmp_buff);
1436 exit(1);
1437 }
1438
1439 #ifdef DEBUG
1440 printf("Making directory..\n");
1441 #endif
1442 if(mkdir(tmp_buff, 0755) < 0){
1443 perror("mkdir");
1444 exit(1);
1445 }
1446 if(verbose && handle)
1447 printf("%10s: %s/\n", "created", tmp_buff);
1448
1449 }
1450
1451 /* only a directory */
1452 if(strlen((const char *)start) == 0)
1453 dir = TRUE;
1454
1455 #ifdef DEBUG
1456 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1457 #endif
1458
1459 /* If the entry was just a directory, don't write to file, etc */
1460 if(strlen((const char *)start) == 0)
1461 f_fd = -1;
1462
1463 free(tmp_buff);
1464 }
1465
1466 if(f_fd != -1 && handle){
1467 f_fd = open((const char *)filename,
1468 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1469
1470 if(f_fd < 0){
1471 fprintf(stderr, "Error extracting JAR archive!\n");
1472 perror((const char *)filename);
1473 exit(1);
1474 }
1475 }
1476
1477 if(method != 8 && flags & 0x0008){
1478 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1479 exit(1);
1480 }
1481
1482 if(method == 8 || flags & 0x0008){
1483 consume(&pbf, eflen);
1484
1485 inflate_file(&pbf, f_fd, &ze);
1486 } else {
1487
1488 #ifdef DEBUG
1489 printf("writing stored data.. (%d bytes)\n", csize);
1490 #endif
1491
1492 out_a = 0;
1493 in_a = csize;
1494
1495 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1496
1497 while(out_a < (int)csize){
1498 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1499 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1500 perror("read");
1501 exit(1);
1502 }
1503
1504 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1505
1506 if(f_fd >= 0)
1507 write(f_fd, rd_buff, rdamt);
1508
1509 out_a += rdamt;
1510 in_a -= rdamt;
1511
1512 #ifdef DEBUG
1513 printf("%d bytes written\n", out_a);
1514 #endif
1515 }
1516
1517 consume(&pbf, eflen);
1518 }
1519
1520 /* if there is a data descriptor left, compare the CRC */
1521 if(flags & 0x0008){
1522
1523 if(pb_read(&pbf, scratch, 16) != 16){
1524 perror("read");
1525 exit(1);
1526 }
1527
1528 signature = UNPACK_UB4(scratch, 0);
1529
1530 if(signature != 0x08074b50){
1531 fprintf(stderr, "Error! Missing data descriptor!\n");
1532 exit(1);
1533 }
1534
1535 crc = UNPACK_UB4(scratch, 4);
1536
1537 }
1538
1539 if(crc != ze.crc){
1540 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1541 ze.crc, crc);
1542 exit(1);
1543 }
1544
1545 close(f_fd);
1546
1547 if(verbose && dir == FALSE && handle)
1548 printf("%10s: %s\n",
1549 (method == 8 ? "inflated" : "extracted"),
1550 filename);
1551 }
1552
1553 return 0;
1554 }
1555
1556 int list_jar(int fd, char **files, int file_num){
1557 ub4 signature;
1558 ub4 csize;
1559 ub4 usize;
1560 ub4 mdate;
1561 ub4 tmp;
1562 ub2 fnlen;
1563 ub2 eflen;
1564 ub2 clen;
1565 ub2 flags;
1566 ub2 method;
1567 ub2 cen_size;
1568 ub1 *filename = NULL;
1569 ub1 scratch[16];
1570 ub1 cen_header[46];
1571 int filename_len = 0;
1572 off_t size;
1573 int i, j;
1574 time_t tdate;
1575 struct tm *s_tm;
1576 char ascii_date[31];
1577 zipentry ze;
1578
1579 #ifdef DEBUG
1580 printf("Listing jar file, looking for %d files\n", file_num);
1581 #endif
1582
1583 /* This should be the start of the central-header-end section */
1584 if(seekable){
1585 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1586 perror("lseek");
1587 exit(1);
1588 }
1589
1590 if(read(fd, &tmp, sizeof(ub4)) != 4){
1591 perror("read");
1592 exit(1);
1593 }
1594
1595 #ifdef WORDS_BIGENDIAN
1596 tmp = L2BI(tmp);
1597 #endif
1598
1599 if(tmp != 0x06054b50){
1600 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1601 exit(1);
1602 }
1603
1604 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1605 perror("lseek");
1606 exit(1);
1607 }
1608
1609 if(read(fd, &cen_size, 2) != 2){
1610 perror("read");
1611 exit(1);
1612 }
1613
1614 #ifdef WORDS_BIGENDIAN
1615 cen_size = L2BS(cen_size);
1616 #endif
1617
1618 /* printf("%hu entries in central header\n", cen_size); */
1619
1620 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1621 perror("lseek");
1622 exit(1);
1623 }
1624
1625 if(read(fd, &tmp, 4) != 4){
1626 perror("read");
1627 exit(1);
1628 }
1629
1630 #ifdef WORDS_BIGENDIAN
1631 tmp = L2BI(tmp);
1632 #endif
1633
1634 /* printf("Central header offset = %d\n", tmp); */
1635
1636 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1637 perror("lseek");
1638 exit(1);
1639 }
1640
1641 /* Loop through the entries in the central header */
1642 for(i = 0; i < cen_size; i++){
1643
1644 if(read(fd, &cen_header, 46) != 46){
1645 perror("read");
1646 exit(1);
1647 }
1648
1649 signature = UNPACK_UB4(cen_header, 0);
1650 if(signature != 0x02014b50){
1651 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1652 exit(1);
1653 }
1654
1655 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1656 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1657 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1658 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1659
1660 /* If we're providing verbose output, we need to make an ASCII
1661 * formatted version of the date. */
1662 if(verbose){
1663 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1664 tdate = dos2unixtime(mdate);
1665 s_tm = localtime(&tdate);
1666 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1667 ascii_date[30] = '\0';
1668 }
1669
1670 if(filename_len < fnlen + 1){
1671 if(filename != NULL)
1672 free(filename);
1673
1674 filename = malloc(sizeof(ub1) * (fnlen + 1));
1675 filename_len = fnlen + 1;
1676 }
1677
1678 if(read(fd, filename, fnlen) != fnlen){
1679 perror("read");
1680 exit(1);
1681 }
1682 filename[fnlen] = '\0';
1683
1684 /* if the user specified a list of files on the command line,
1685 we'll only display those, otherwise we'll display everything */
1686 if(file_num > 0){
1687 for(j = 0; j < file_num; j++)
1688 if(strcmp(files[j], (const char *)filename) == 0){
1689 if(verbose)
1690 printf("%6d %s %s\n", usize, ascii_date, filename);
1691 else
1692 printf("%s\n", filename);
1693 break;
1694 }
1695 } else {
1696 if(verbose)
1697 printf("%6d %s %s\n", usize, ascii_date, filename);
1698 else
1699 printf("%s\n", filename);
1700 }
1701
1702 size = eflen + clen;
1703 if(size > 0){
1704 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1705 perror("lseek");
1706 exit(1);
1707 }
1708 }
1709 }
1710 } else {
1711 /* the file isn't seekable.. evil! */
1712 pb_file pbf;
1713
1714 pb_init(&pbf, fd);
1715
1716 init_inflation();
1717
1718 for(;;){
1719 if(pb_read(&pbf, scratch, 4) != 4){
1720 perror("read");
1721 break;
1722 }
1723
1724 signature = UNPACK_UB4(scratch, 0);
1725
1726 #ifdef DEBUG
1727 printf("signature is %x\n", signature);
1728 #endif
1729
1730 if(signature == 0x08074b50){
1731 #ifdef DEBUG
1732 printf("skipping data descriptor\n");
1733 #endif
1734 pb_read(&pbf, scratch, 12);
1735 continue;
1736 } else if(signature == 0x02014b50){
1737 #ifdef DEBUG
1738 printf("Central header reached.. we're all done!\n");
1739 #endif
1740 break;
1741 }else if(signature != 0x04034b50){
1742 #ifdef DEBUG
1743 printf("Ick! %#x\n", signature);
1744 #endif
1745 break;
1746 }
1747
1748 if(pb_read(&pbf, (file_header + 4), 26) != 26){
1749 perror("read");
1750 break;
1751 }
1752
1753 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1754 #ifdef DEBUG
1755 printf("Compressed size is %u\n", csize);
1756 #endif
1757
1758 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1759 #ifdef DEBUG
1760 printf("Filename length is %hu\n", fnlen);
1761 #endif
1762
1763 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1764 #ifdef DEBUG
1765 printf("Extra field length is %hu\n", eflen);
1766 #endif
1767
1768 method = UNPACK_UB2(file_header, LOC_COMP);
1769 #ifdef DEBUG
1770 printf("Compression method is %#hx\n", method);
1771 #endif
1772
1773 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1774 #ifdef DEBUG
1775 printf("Flags are %#hx\n", flags);
1776 #endif
1777
1778 usize = UNPACK_UB4(file_header, LOC_USIZE);
1779
1780 /* If we're providing verbose output, we need to make an ASCII
1781 * formatted version of the date. */
1782 if(verbose){
1783 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1784 tdate = dos2unixtime(mdate);
1785 s_tm = localtime(&tdate);
1786 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1787 }
1788
1789 if(filename_len < fnlen + 1){
1790 if(filename != NULL)
1791 free(filename);
1792
1793 filename = malloc(sizeof(ub1) * (fnlen + 1));
1794 ascii_date[30] = '\0';
1795 filename_len = fnlen + 1;
1796 }
1797
1798 pb_read(&pbf, filename, fnlen);
1799 filename[fnlen] = '\0';
1800
1801 /* the header is at the end. In a JAR file, this means that the data
1802 happens to be compressed. We have no choice but to inflate the
1803 data */
1804 if(flags & 0x0008){
1805
1806 size = eflen;
1807
1808 if(size > 0)
1809 consume(&pbf, size);
1810
1811 if(method == 8){
1812 #ifdef DEBUG
1813 printf("inflating %s\n", filename);
1814 #endif
1815 inflate_file(&pbf, -1, &ze);
1816
1817 usize = ze.usize;
1818 } else
1819 printf("We're shit outta luck!\n");
1820
1821 } else {
1822 size = csize + (eflen > 0 ? eflen : 0);
1823
1824
1825 #ifdef DEBUG
1826 printf("Skipping %ld bytes\n", (long)size);
1827 #endif
1828
1829 consume(&pbf, size);
1830 }
1831 /* print out the listing */
1832 if(file_num > 0){
1833 for(j = 0; j < file_num; j++)
1834 if(strcmp(files[j], (const char *)filename) == 0){
1835 if(verbose)
1836 printf("%6d %s %s\n", usize, ascii_date, filename);
1837 else
1838 printf("%s\n", filename);
1839 break;
1840 }
1841 } else {
1842 if(verbose)
1843 printf("%6d %s %s\n", usize, ascii_date, filename);
1844 else
1845 printf("%s\n", filename);
1846 }
1847 }
1848 }
1849 return 0;
1850 }
1851
1852 int consume(pb_file *pbf, int amt){
1853 int tc = 0; /* total amount consumed */
1854 ub1 buff[RDSZ];
1855 int rdamt;
1856
1857 #ifdef DEBUG
1858 printf("Consuming %d bytes\n", amt);
1859 #endif
1860
1861 if (seekable){
1862 if (amt <= (int)pbf->buff_amt)
1863 pb_read(pbf, buff, amt);
1864 else {
1865 lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
1866 pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
1867 }
1868 } else
1869 while(tc < amt){
1870 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1871 #ifdef DEBUG
1872 printf("got %d bytes\n", rdamt);
1873 #endif
1874 tc += rdamt;
1875 }
1876
1877 #ifdef DEBUG
1878 printf("%d bytes consumed\n", amt);
1879 #endif
1880
1881 return 0;
1882 }
1883
1884 void usage(const char *filename){
1885 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1886 exit (1);
1887 }
1888
1889 void version ()
1890 {
1891 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1892 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1893 printf("Copyright 2002 Free Software Foundation\n");
1894 printf("\
1895 This is free software; see the source for copying conditions. There is NO\n\
1896 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1897 exit (0);
1898 }
1899
1900 void help(const char *filename)
1901 {
1902 printf("\
1903 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1904 \n\
1905 Store many files together in a single `jar' file.\n\
1906 \n\
1907 -c create new archive\n\
1908 -t list table of contents for archive\n\
1909 -x extract named (or all) files from archive\n\
1910 -u update existing archive\n\
1911 ", filename);
1912 printf("\n\
1913 -@ read names from stdin\n\
1914 -0 store only; use no ZIP compression\n\
1915 -C DIR FILE change to the specified directory and include\n\
1916 the following file\n\
1917 -E don't include the files found in a directory\n\
1918 -f FILE specify archive file name\n\
1919 --help print this help, then exit\n\
1920 -m FILE include manifest information from specified manifest file\n\
1921 -M Do not create a manifest file for the entries\n\
1922 -v generate verbose output on standard output\n\
1923 -V, --version display version information\n\
1924 ");
1925 printf("\n\
1926 If any file is a directory then it is processed recursively.\n\
1927 The manifest file name and the archive file name needs to be specified\n\
1928 in the same order the 'm' and 'f' flags are specified.\n\
1929 \n\
1930 Example 1: to archive two class files into an archive called classes.jar: \n\
1931 jar cvf classes.jar Foo.class Bar.class \n\
1932 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1933 files in the foo/ directory into 'classes.jar': \n\
1934 jar cvfm classes.jar mymanifest -C foo/ .\n\
1935 ");
1936
1937 exit(0);
1938 }
1939
1940 static char *
1941 jt_strdup(s)
1942 char *s;
1943 {
1944 char *result = (char*)malloc(strlen(s) + 1);
1945 if (result == (char*)0)
1946 return (char*)0;
1947 strcpy(result, s);
1948 return result;
1949 }
1950
1951 /* Convert "tar-style" first argument to a form expected by getopt.
1952 This idea and the code comes from GNU tar. This can allocate a new
1953 argument vector. This might leak some memory, but we don't care. */
1954 static void
1955 expand_options (int *argcp, char ***argvp)
1956 {
1957 int argc = *argcp;
1958 char **argv = *argvp;
1959
1960 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
1961 if a long argument (like "--help") is detected. */
1962 if (argc > 1 && argv[1][1] != '-')
1963 {
1964 char buf[3];
1965 char **new_argv;
1966 int new_argc;
1967 int args_to_expand;
1968 char *p;
1969 char **in, **out;
1970
1971 buf[0] = '-';
1972 buf[2] = '\0';
1973
1974 args_to_expand = strlen (argv[1]);
1975 if (argv[1][0] == '-')
1976 --args_to_expand;
1977
1978 new_argc = argc - 1 + args_to_expand;
1979 new_argv = (char **) malloc (new_argc * sizeof (char *));
1980 in = argv;
1981 out = new_argv;
1982
1983 *out++ = *in++;
1984 p = *in++;
1985 if (*p == '-')
1986 p++;
1987 while (*p != '\0')
1988 {
1989 char *opt;
1990 buf[1] = *p;
1991 *out++ = jt_strdup (buf);
1992 /* If the option takes an argument, move the next argument
1993 to just after this option. */
1994 opt = strchr (OPTION_STRING, *p);
1995 if (opt && opt[1] == ':')
1996 {
1997 if (in < argv + argc)
1998 *out++ = *in++;
1999 else
2000 {
2001 fprintf(stderr, "%s: option `%s' requires an argument.\n",
2002 argv[0], buf);
2003 usage(argv[0]);
2004 }
2005 }
2006 ++p;
2007 }
2008
2009 /* Copy remaining options. */
2010 while (in < argv + argc)
2011 *out++ = *in++;
2012
2013 *argcp = new_argc;
2014 *argvp = new_argv;
2015 }
2016 }