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