source: branches/libc-0.6/src/gcc/fastjar/jartool.c

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

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