2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / fastjar / jartool.c
blob83454d2746ce9bfccb574eb86ba7fe55d3fa9a55
1 /*
2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002 Free Software Foundation
4 Copyright (C) 1999, 2000, 2001 Bryan Burns
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.
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.
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.
22 Revision 1.10 2002/01/03 04:57:56 rodrigc
23 2001-01-02 Craig Rodrigues <rodrigc@gcc.gnu.org>
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.
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".
41 Revision 1.8 2001/08/29 01:35:31 apbianco
42 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
44 * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
45 Fixes PR java/3949.
47 (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
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.
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.
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.
65 Revision 1.4 2000/12/28 21:47:37 robertl
66 2000-12-28 Robert Lipe <robertl@sco.com>
68 * jartool.c (MAXPATHLEN): Provide if not defined.
70 Revision 1.3 2000/12/14 18:45:35 ghazi
71 Warning fixes:
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.
79 * compress.h (init_compression, end_compression, init_inflation,
80 end_inflation): Prototype void arguments.
82 * dostime.c (rcsid): Delete.
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.
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.
96 * pushback.c (rcsid): Delete.
98 Revision 1.2 2000/12/13 18:11:57 tromey
99 * jartool.c (extract_jar): Use strchr, not index.
101 Revision 1.1 2000/12/09 03:08:23 apbianco
102 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
104 * fastjar: Imported.
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.
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.
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.
120 Revision 1.2 1999/12/06 07:38:28 toast
121 fixed recursive archiving bug
123 Revision 1.1.1.1 1999/12/06 03:09:34 toast
124 initial checkin..
128 Revision 1.22 1999/10/12 19:45:13 burnsbr
129 adding patch to fix compat problem
131 Revision 1.21 1999/05/10 09:15:49 burnsbr
132 fixed manifest file version info
134 Revision 1.20 1999/05/10 08:53:16 burnsbr
135 *** empty log message ***
137 Revision 1.19 1999/05/10 08:30:39 burnsbr
138 added extract / listing code
140 Revision 1.18 1999/04/28 04:24:29 burnsbr
141 updated version
143 Revision 1.17 1999/04/28 04:21:23 burnsbr
144 added support for -C dir-changing flag.. Updated total compression display
146 Revision 1.16 1999/04/27 10:28:22 burnsbr
147 updated version string
149 Revision 1.15 1999/04/27 10:04:06 burnsbr
150 configure support
152 Revision 1.14 1999/04/27 08:56:14 burnsbr
153 added -V flag, better error messages
155 Revision 1.13 1999/04/26 02:35:21 burnsbr
156 changed all sorts of stuff.. compression now works 100%
158 Revision 1.12 1999/04/23 12:00:45 burnsbr
159 90% done with compression code
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..
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.
170 Revision 1.9 1999/04/21 09:55:16 burnsbr
171 pulled out printfs
173 Revision 1.8 1999/04/21 02:58:01 burnsbr
174 started manifest code
176 Revision 1.7 1999/04/20 23:15:28 burnsbr
177 added patch sent by John Bley <jbb6@acpub.duke.edu>
179 Revision 1.6 1999/04/20 08:56:02 burnsbr
180 added GPL comment
182 Revision 1.5 1999/04/20 08:16:09 burnsbr
183 fixed verbose flag, did some optimization
185 Revision 1.4 1999/04/20 05:09:59 burnsbr
186 added rcsid variable
188 Revision 1.3 1999/04/20 05:08:54 burnsbr
189 fixed Log statement
193 #include "config.h"
195 #include <zlib.h>
197 #ifdef HAVE_STDLIB_H
198 #include <stdlib.h>
199 #endif
201 #ifdef HAVE_UNISTD_H
202 #include <unistd.h>
203 #endif
205 #include <stdio.h>
206 #include <sys/stat.h>
207 #include <sys/types.h>
209 #ifdef HAVE_SYS_PARAM_H
210 #include <sys/param.h>
211 #endif
213 #ifndef MAXPATHLEN
214 #define MAXPATHLEN 1024
215 #endif
217 #ifdef HAVE_DIRENT_H
218 #include <dirent.h>
219 #endif
221 #ifdef HAVE_FCNTL_H
222 #include <fcntl.h>
223 #endif
225 #include <string.h>
226 #include <errno.h>
228 #ifdef TM_IN_SYS_TIME
229 #include <sys/time.h>
230 #else
231 #include <time.h>
232 #endif
234 #include <getopt.h>
236 #include "jartool.h"
237 #include "zipfile.h"
238 #include "dostime.h"
239 #include "pushback.h"
240 #include "compress.h"
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
248 #ifdef WORDS_BIGENDIAN
250 #define L2BI(l) ((l & 0xff000000) >> 24) | \
251 ((l & 0x00ff0000) >> 8) | \
252 ((l & 0x0000ff00) << 8) | \
253 ((l & 0x000000ff) << 24);
255 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
257 #endif
259 #ifndef errno
260 extern int errno;
261 #endif
263 #ifndef O_BINARY
264 #define O_BINARY 0
265 #endif
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);
273 int consume(pb_file *, int);
274 int list_jar(int, char**, int);
275 int extract_jar(int, char**, int);
276 int add_file_to_jar(int, int, const char*, struct stat*);
277 int add_to_jar(int, const char*);
278 int add_to_jar_with_dir(int, const char*, const char*);
279 int create_central_header(int);
280 int make_manifest(int, const char*);
281 static void init_args(char **, int);
282 static char *get_next_arg (void);
283 static char *jt_strdup (char*);
284 static void expand_options (int *argcp, char ***argvp);
286 /* global variables */
287 ub1 file_header[30];
288 ub1 data_descriptor[16];
289 int do_compress;
290 int seekable;
291 int verbose;
292 char *jarfile;
294 /* If non zero, then don't recurse in directory. Instead, add the
295 directory entry and relie on an explicit list of files to populate
296 the archive. This option isn't supported by the original jar tool. */
297 int use_explicit_list_only;
299 /* If non zero, then read the entry names from stdin. This option
300 isn't supported by the original jar tool. */
301 int read_names_from_stdin;
303 zipentry *ziplist; /* linked list of entries */
304 zipentry *ziptail; /* tail of the linked list */
306 int number_of_entries; /* number of entries in the linked list */
308 /* This is used to mark options with no short value. */
309 #define LONG_OPT(Num) ((Num) + 128)
311 #define OPT_HELP LONG_OPT (0)
313 /* This holds all options. */
314 #define OPTION_STRING "-ctxuvVf:m:C:0ME@"
316 static const struct option options[] =
318 { "help", no_argument, NULL, OPT_HELP },
319 { "version", no_argument, NULL, 'V' },
320 { NULL, no_argument, NULL, 0 }
323 int main(int argc, char **argv){
325 char *mfile = NULL;
327 int action = ACTION_NONE;
328 int manifest = TRUE;
329 int opt;
331 int jarfd = -1;
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;
338 do_compress = TRUE;
339 verbose = FALSE;
341 ziplist = NULL;
343 number_of_entries = 0;
345 if(argc < 2)
346 usage(argv[0]);
348 new_argc = 0;
349 new_argv = (char **) malloc (argc * sizeof (char *));
351 expand_options (&argc, &argv);
352 while ((opt = getopt_long (argc, argv, OPTION_STRING,
353 options, NULL)) != -1) {
354 switch(opt){
355 case 'C':
356 new_argv[new_argc++] = (char *) "-C";
357 /* ... fall through ... */
358 case 1:
359 /* File name or unparsed option, due to RETURN_IN_ORDER. */
360 new_argv[new_argc++] = optarg;
361 break;
362 case 'c':
363 action = ACTION_CREATE;
364 break;
365 case 't':
366 action = ACTION_LIST;
367 break;
368 case 'x':
369 action = ACTION_EXTRACT;
370 break;
371 case 'u':
372 action = ACTION_UPDATE;
373 break;
374 case 'v':
375 verbose = TRUE;
376 break;
377 case 'V':
378 version();
379 exit(0);
380 case 'f':
381 jarfile = optarg;
382 break;
383 case 'm':
384 mfile = optarg;
385 break;
386 case '0':
387 do_compress = FALSE;
388 break;
389 case 'M':
390 manifest = FALSE;
391 break;
393 case OPT_HELP:
394 help(argv[0]);
395 break;
397 /* The following options aren't supported by the original jar tool. */
398 case 'E':
399 use_explicit_list_only = TRUE;
400 break;
401 case '@':
402 read_names_from_stdin = TRUE;
403 break;
404 default:
405 usage(argv[0]);
409 /* We might have seen `--'. In this case we want to make sure that
410 all following options are handled as file names. */
411 while (optind < argc)
412 new_argv[new_argc++] = argv[optind++];
413 new_argv[new_argc] = NULL;
415 if(action == ACTION_NONE){
416 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
417 usage(argv[0]);
420 if(action == ACTION_UPDATE){
421 fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
422 exit(1);
425 /* Verify unsupported combinations and warn of the use of non
426 standard features */
427 if(verbose && use_explicit_list_only)
428 fprintf (stderr, "Warning: using non standard '-E' option\n");
429 if(verbose && read_names_from_stdin)
430 fprintf (stderr, "Warning: using non standard '-@' option\n");
431 if(read_names_from_stdin
432 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
433 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
434 usage(argv[0]);
437 /* create the jarfile */
438 if(action == ACTION_CREATE){
439 if(jarfile){
440 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
442 if(jarfd < 0){
443 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
444 perror(jarfile);
445 exit(1);
448 /* We assume that the file is seekable */
449 seekable = TRUE;
451 } else {
453 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
455 /* standard out is not seekable */
456 seekable = FALSE;
458 /* don't want our output to be part of the jar file.. figured this one
459 out the hard way.. =P */
460 verbose = FALSE;
462 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
464 if(jarfile){
465 jarfd = open(jarfile, O_RDONLY | O_BINARY);
467 if(jarfd < 0){
468 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
469 perror(jarfile);
470 exit(1);
473 seekable = TRUE;
474 } else {
475 jarfd = STDIN_FILENO; /* jarfd is standard in */
477 /* we assume that the stream isn't seekable for safety */
478 seekable = FALSE;
482 if(action == ACTION_CREATE || action == ACTION_UPDATE){
483 const char *arg;
484 init_headers();
486 if((action == ACTION_UPDATE) && jarfile) {
487 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
488 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
489 perror(jarfile);
490 exit(1);
494 if(do_compress)
495 init_compression();
498 /* Add the META-INF/ directory and the manifest */
499 if(manifest && mfile)
500 make_manifest(jarfd, mfile);
501 else if(manifest)
502 make_manifest(jarfd, NULL);
504 init_args (new_argv, 0);
505 /* now we add the files to the archive */
506 while ((arg = get_next_arg ())){
508 if(!strcmp(arg, "-C")){
509 const char *dir_to_change = get_next_arg ();
510 const char *file_to_add = get_next_arg ();
511 if (!dir_to_change || !file_to_add) {
512 fprintf(stderr, "Error: missing argument for -C.\n");
513 exit(1);
515 if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add)) {
516 fprintf(stderr,
517 "Error adding %s (in directory %s) to jar archive!\n",
518 file_to_add, dir_to_change);
519 exit(1);
521 } else {
522 if(add_to_jar(jarfd, arg)){
523 fprintf(stderr, "Error adding %s to jar archive!\n", arg);
524 exit(1);
528 /* de-initialize the compression DS */
529 if(do_compress)
530 end_compression();
532 create_central_header(jarfd);
534 if (close(jarfd) != 0) {
535 fprintf(stderr, "Error closing jar archive!\n");
537 } else if(action == ACTION_LIST){
538 list_jar(jarfd, &new_argv[0], new_argc);
539 } else if(action == ACTION_EXTRACT){
540 extract_jar(jarfd, &new_argv[0], new_argc);
543 exit(0);
546 static int args_current_g;
547 static char **args_g;
549 static void
550 init_args(args, current)
551 char **args;
552 int current;
554 if(!read_names_from_stdin)
556 args_g = args;
557 args_current_g = current;
561 static char *
562 get_next_arg ()
564 static int reached_end = 0;
566 if (reached_end)
567 return NULL;
569 if (args_g)
571 if (!args_g [args_current_g])
573 reached_end = 1;
574 return NULL;
576 return args_g [args_current_g++];
578 else
580 /* Read the name from stdin. Delimiters are '\n' and
581 '\r'. Reading EOF indicates that we don't have anymore file
582 names characters to read. */
584 char s [MAXPATHLEN];
585 int pos = 0;
587 /* Get rid of '\n' and '\r' first. */
588 while (1)
590 int c = getc (stdin);
591 if (c == '\n' || c == '\r')
592 continue;
593 else
595 if (c == EOF)
596 return NULL;
597 ungetc (c, stdin);
598 break;
602 while (1)
604 int c = getc (stdin);
605 /* Exit when we get a delimiter or don't have any characters
606 to read */
607 if (c == '\n'|| c == '\r'|| c == EOF)
608 break;
609 s [pos++] = (char) c;
612 if (pos)
614 s [pos] = '\0';
615 return jt_strdup (s);
617 else
618 return NULL;
622 void init_headers(){
623 /* packing file header */
624 /* magic number */
625 file_header[0] = 0x50;
626 file_header[1] = 0x4b;
627 file_header[2] = 0x03;
628 file_header[3] = 0x04;
629 /* version number (Unix 1.0)*/
630 file_header[4] = 10;
631 file_header[5] = 0;
632 /* bit flag (normal deflation)*/
633 file_header[6] = 0x00;
635 file_header[7] = 0x00;
636 /* do_compression method (deflation) */
637 file_header[8] = 0;
638 file_header[9] = 0;
640 /* last mod file time (MS-DOS format) */
641 file_header[10] = 0;
642 file_header[11] = 0;
643 /* last mod file date (MS-DOS format) */
644 file_header[12] = 0;
645 file_header[13] = 0;
646 /* CRC 32 */
647 file_header[14] = 0;
648 file_header[15] = 0;
649 file_header[16] = 0;
650 file_header[17] = 0;
651 /* compressed size */
652 file_header[18] = 0;
653 file_header[19] = 0;
654 file_header[20] = 0;
655 file_header[21] = 0;
656 /* uncompressed size */
657 file_header[22] = 0;
658 file_header[23] = 0;
659 file_header[24] = 0;
660 file_header[25] = 0;
661 /* filename length */
662 file_header[26] = 0;
663 file_header[27] = 0;
664 /* extra field length */
665 file_header[28] = 0;
666 file_header[29] = 0;
668 /* Initialize the compression DS */
669 PACK_UB4(data_descriptor, 0, 0x08074b50);
673 void add_entry(struct zipentry *ze){
675 if(ziplist == NULL){
676 ziplist = ze;
677 ziptail = ziplist;
678 } else {
679 ziplist->next_entry = ze;
680 ziplist = ze;
683 number_of_entries++;
686 int make_manifest(int jfd, const char *mf_name){
687 time_t current_time;
688 int nlen; /* length of file name */
689 int mod_time; /* file modification time */
690 struct zipentry *ze;
692 nlen = 9; /* trust me on this one */
694 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
696 current_time = time(NULL);
697 if(current_time == (time_t)-1){
698 perror("time");
699 exit(1);
702 mod_time = unix2dostime(&current_time);
704 PACK_UB2(file_header, LOC_EXTRA, 0);
705 PACK_UB2(file_header, LOC_COMP, 0);
706 PACK_UB2(file_header, LOC_FNLEN, nlen);
707 PACK_UB4(file_header, LOC_MODTIME, mod_time);
709 if(verbose)
710 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
712 ze = (zipentry*)malloc(sizeof(zipentry));
713 if(ze == NULL){
714 perror("malloc");
715 exit(1);
718 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
719 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
720 strcpy(ze->filename, "META-INF/");
721 ze->filename[nlen] = '\0';
723 ze->offset = lseek(jfd, 0, SEEK_CUR);
724 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
725 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
726 ze->compressed = FALSE;
728 add_entry(ze);
730 write(jfd, file_header, 30);
731 write(jfd, "META-INF/", nlen);
733 /* if the user didn't specify an external manifest file... */
734 if(mf_name == NULL){
735 int mf_len = 37 + strlen(VERSION);
736 char *mf;
738 if((mf = (char *) malloc(mf_len + 1))) {
739 uLong crc;
741 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
743 crc = crc32(0L, Z_NULL, 0);
745 crc = crc32(crc, (const unsigned char *)mf, mf_len);
747 nlen = 20; /* once again, trust me */
749 PACK_UB2(file_header, LOC_EXTRA, 0);
750 PACK_UB2(file_header, LOC_COMP, 0);
751 PACK_UB2(file_header, LOC_FNLEN, nlen);
752 PACK_UB4(file_header, LOC_USIZE, mf_len);
754 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
756 PACK_UB4(file_header, LOC_CRC, crc);
758 if(verbose)
759 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
761 ze = (zipentry*)malloc(sizeof(zipentry));
762 if(ze == NULL){
763 perror("malloc");
764 exit(1);
767 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
768 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
769 strcpy(ze->filename, "META-INF/MANIFEST.MF");
770 ze->filename[nlen] = '\0';
772 ze->offset = lseek(jfd, 0, SEEK_CUR);
773 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
774 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
775 ze->crc = crc;
776 ze->csize = mf_len;
777 ze->usize = ze->csize;
778 ze->compressed = FALSE;
780 add_entry(ze);
782 write(jfd, file_header, 30);
783 write(jfd, "META-INF/MANIFEST.MF", nlen);
784 write(jfd, mf, mf_len);
785 free(mf);
787 else {
788 printf("malloc errror\n");
789 exit(-1);
791 } else {
792 int mfd;
793 struct stat statbuf;
795 stat(mf_name, &statbuf);
797 if(!S_ISREG(statbuf.st_mode)){
798 fprintf(stderr, "Invalid manifest file specified.\n");
799 exit(1);
802 mfd = open(mf_name, O_RDONLY | O_BINARY);
804 if(mfd < 0){
805 fprintf(stderr, "Error opening %s.\n", mf_name);
806 exit(1);
809 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
810 perror("error writing to jar");
811 exit(1);
816 return 0;
819 /* Implements -C by wrapping add_to_jar. new_dir is the directory
820 to switch to. */
821 int
822 add_to_jar_with_dir (int fd, const char* new_dir, const char* file)
824 int retval;
825 char old_dir[MAXPATHLEN];
826 if (getcwd(old_dir, MAXPATHLEN) == NULL) {
827 perror("getcwd");
828 return 1;
830 if (chdir(new_dir) == -1) {
831 perror(new_dir);
832 return 1;
834 retval=add_to_jar(fd, file);
835 if (chdir(old_dir) == -1) {
836 perror(old_dir);
837 return 1;
839 return retval;
842 int
843 add_to_jar (int fd, const char *file) {
844 struct stat statbuf;
845 DIR *dir;
846 struct dirent *de;
847 zipentry *ze;
848 int stat_return;
850 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
851 * It fixes this:
852 * "normal" jar : org/apache/java/io/LogRecord.class
853 * fastjar : ./org/apache/java/io/LogRecord.class
854 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
855 * with both kaffe-1.0b4 and JDK.
857 while (*file=='.' && *(file+1)=='/')
858 file+=2;
860 if(jarfile && !strcmp(file, jarfile)){
861 if(verbose)
862 printf("skipping: %s\n", file);
863 return 0; /* we don't want to add ourselves.. */
866 stat_return = stat(file, &statbuf);
868 if(stat_return == -1){
869 perror(file);
870 return 1;
871 } else if(S_ISDIR(statbuf.st_mode)){
872 char *fullname;
873 char *t_ptr;
874 int nlen;
875 unsigned long mod_time;
877 dir = opendir(file);
879 if(dir == NULL){
880 perror("opendir");
881 return 1;
884 nlen = strlen(file) + 256;
885 fullname = (char*)malloc(nlen * sizeof(char));
886 memset(fullname, 0, (nlen * sizeof(char)));
888 if(fullname == NULL){
889 fprintf(stderr, "Filename is NULL!\n");
890 return 1;
893 strcpy(fullname, file);
894 nlen = strlen(file);
896 if(fullname[nlen - 1] != '/'){
897 fullname[nlen] = '/';
898 t_ptr = (fullname + nlen + 1);
899 } else
900 t_ptr = (fullname + nlen);
903 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
905 nlen = (t_ptr - fullname);
907 mod_time = unix2dostime(&statbuf.st_mtime);
909 PACK_UB2(file_header, LOC_EXTRA, 0);
910 PACK_UB2(file_header, LOC_COMP, 0);
911 PACK_UB2(file_header, LOC_FNLEN, nlen);
912 PACK_UB4(file_header, LOC_MODTIME, mod_time);
914 if(verbose)
915 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
917 ze = (zipentry*)malloc(sizeof(zipentry));
918 if(ze == NULL){
919 perror("malloc");
920 exit(1);
923 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
924 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
925 strcpy(ze->filename, fullname);
926 ze->filename[nlen] = '\0';
928 ze->offset = lseek(fd, 0, SEEK_CUR);
929 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
930 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
931 ze->compressed = FALSE;
933 add_entry(ze);
935 write(fd, file_header, 30);
936 write(fd, fullname, nlen);
938 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
939 if(de->d_name[0] == '.')
940 continue;
941 if(jarfile && !strcmp(de->d_name, jarfile)){
942 /* we don't want to add ourselves. Believe me */
943 if(verbose)
944 printf("skipping: %s\n", de->d_name);
945 continue;
948 strcpy(t_ptr, de->d_name);
950 if (add_to_jar(fd, fullname)) {
951 fprintf(stderr, "Error adding file to jar!\n");
952 return 1;
956 free(fullname);
957 closedir(dir);
959 } else if(S_ISREG(statbuf.st_mode)){
960 int add_fd;
962 add_fd = open(file, O_RDONLY | O_BINARY);
963 if(add_fd < 0){
964 fprintf(stderr, "Error opening %s.\n", file);
965 return 1;
968 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
969 fprintf(stderr, "Error adding file to jar!\n");
970 return 1;
973 } else {
974 fprintf(stderr, "Illegal file specified: %s\n", file);
976 return 0;
979 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
981 unsigned short file_name_length;
982 unsigned long mod_time;
983 ub1 rd_buff[RDSZ];
984 uLong crc = 0;
985 off_t offset = 0;
986 int rdamt;
987 struct zipentry *ze;
989 mod_time = unix2dostime(&(statbuf->st_mtime));
990 file_name_length = strlen(fname);
992 if(!seekable && !do_compress){
993 crc = crc32(0L, Z_NULL, 0);
995 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
996 crc = crc32(crc, rd_buff, rdamt);
998 lseek(ffd, 0, SEEK_SET);
1001 /* data descriptor */
1002 if(!seekable && do_compress){
1003 PACK_UB2(file_header, LOC_EXTRA, 8);
1004 } else {
1005 PACK_UB2(file_header, LOC_EXTRA, 0);
1008 if(do_compress){
1009 PACK_UB2(file_header, LOC_COMP, 8);
1010 } else {
1011 PACK_UB2(file_header, LOC_COMP, 0);
1014 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1015 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1017 if(!seekable && !do_compress){
1018 PACK_UB4(file_header, LOC_CRC, crc);
1019 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1020 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1021 } else
1022 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1024 ze = (zipentry*)malloc(sizeof(zipentry));
1025 if(ze == NULL){
1026 perror("malloc");
1027 exit(1);
1030 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1031 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1032 strcpy(ze->filename, fname);
1034 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1035 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1037 if(!seekable && !do_compress)
1038 ze->crc = crc;
1040 ze->csize = statbuf->st_size;
1041 ze->usize = ze->csize;
1042 ze->offset = lseek(jfd, 0, SEEK_CUR);
1043 if(do_compress)
1044 ze->compressed = TRUE;
1045 else
1046 ze->compressed = FALSE;
1048 add_entry(ze);
1050 /* Write the local header */
1051 write(jfd, file_header, 30);
1053 /* write the file name to the zip file */
1054 write(jfd, fname, file_name_length);
1057 if(verbose){
1058 printf("adding: %s ", fname);
1059 fflush(stdout);
1062 if(do_compress){
1063 /* compress the file */
1064 compress_file(ffd, jfd, ze);
1065 } else {
1066 /* Write the contents of the file (uncompressed) to the zip file */
1067 /* calculate the CRC as we go along */
1068 ze->crc = crc32(0L, Z_NULL, 0);
1070 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1071 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1072 if(write(jfd, rd_buff, rdamt) != rdamt){
1073 perror("write");
1074 return 0;
1078 close(ffd);
1080 /* write out data descriptor */
1081 PACK_UB4(data_descriptor, 4, ze->crc);
1082 PACK_UB4(data_descriptor, 8, ze->csize);
1083 PACK_UB4(data_descriptor, 12, ze->usize);
1085 /* we need to seek back and fill the header */
1086 if(seekable){
1087 offset = (ze->csize + strlen(ze->filename) + 16);
1089 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1090 perror("lseek");
1091 exit(1);
1094 if(write(jfd, (data_descriptor + 4), 12) != 12){
1095 perror("write");
1096 return 0;
1099 offset -= 12;
1101 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1102 perror("lseek");
1103 exit(1);
1105 } else if(do_compress){
1106 /* Sun's jar tool will only allow a data descriptor if the entry is
1107 compressed, but we'll save 16 bytes/entry if we only use it when
1108 we can't seek back on the file */
1110 if(write(jfd, data_descriptor, 16) != 16){
1111 perror("write");
1112 return 0;
1116 if(verbose)
1117 printf("(in=%d) (out=%d) (%s %d%%)\n",
1118 (int)ze->usize, (int)ze->csize,
1119 (do_compress ? "deflated" : "stored"),
1120 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1122 return 0;
1125 int create_central_header(int fd){
1126 ub1 header[46];
1127 ub1 end_header[22];
1128 int start_offset;
1129 int dir_size;
1130 int total_in = 0, total_out = 22;
1132 zipentry *ze;
1134 /* magic number */
1135 header[0] = 'P';
1136 header[1] = 'K';
1137 header[2] = 1;
1138 header[3] = 2;
1139 /* version made by */
1140 header[4] = 10;
1141 header[5] = 0;
1142 /* version needed to extract */
1143 header[6] = 10;
1144 header[7] = 0;
1145 /* bit flag */
1146 header[8] = 0;
1147 header[9] = 0;
1148 /* compression method */
1149 header[10] = 0;
1150 header[11] = 0;
1151 /* file mod time */
1152 header[12] = 0;
1153 header[13] = 0;
1154 /* file mod date */
1155 header[14] = 0;
1156 header[15] = 0;
1157 /* crc 32 */
1158 header[16] = 0;
1159 header[17] = 0;
1160 header[18] = 0;
1161 header[19] = 0;
1162 /* compressed size */
1163 header[20] = 0;
1164 header[21] = 0;
1165 header[22] = 0;
1166 header[23] = 0;
1167 /* uncompressed size */
1168 header[24] = 0;
1169 header[25] = 0;
1170 header[26] = 0;
1171 header[27] = 0;
1172 /* filename length */
1173 header[28] = 0;
1174 header[29] = 0;
1175 /* extra field length */
1176 header[30] = 0;
1177 header[31] = 0;
1178 /* file comment length */
1179 header[32] = 0;
1180 header[33] = 0;
1181 /* disk number start */
1182 header[34] = 0;
1183 header[35] = 0;
1184 /* internal file attribs */
1185 header[36] = 0;
1186 header[37] = 0;
1187 /* external file attribs */
1188 header[38] = 0;
1189 header[39] = 0;
1190 header[40] = 0;
1191 header[41] = 0;
1192 /* relative offset of local header */
1193 header[42] = 0;
1194 header[43] = 0;
1195 header[44] = 0;
1196 header[45] = 0;
1198 start_offset = lseek(fd, 0, SEEK_CUR);
1200 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1202 total_in += ze->usize;
1203 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1205 if(ze->compressed){
1206 PACK_UB2(header, CEN_COMP, 8);
1207 } else {
1208 PACK_UB2(header, CEN_COMP, 0);
1211 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1212 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1213 PACK_UB4(header, CEN_CRC, ze->crc);
1214 PACK_UB4(header, CEN_CSIZE, ze->csize);
1215 PACK_UB4(header, CEN_USIZE, ze->usize);
1216 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1217 PACK_UB4(header, CEN_OFFSET, ze->offset);
1219 write(fd, header, 46);
1221 write(fd, ze->filename, strlen(ze->filename));
1224 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1226 /* magic number */
1227 end_header[0] = 0x50;
1228 end_header[1] = 0x4b;
1229 end_header[2] = 0x05;
1230 end_header[3] = 0x06;
1231 /* number of this disk */
1232 end_header[4] = 0;
1233 end_header[5] = 0;
1234 /* number of disk w/ start of central header */
1235 end_header[6] = 0;
1236 end_header[7] = 0;
1237 /* total number of entries in central dir on this disk*/
1238 PACK_UB2(end_header, 8, number_of_entries);
1239 /* total number of entries in central dir*/
1240 PACK_UB2(end_header, 10, number_of_entries);
1241 /* size of central dir. */
1242 PACK_UB4(end_header, 12, dir_size);
1243 /* offset of start of central dir */
1244 PACK_UB4(end_header, 16, start_offset);
1245 /* zipfile comment length */
1246 end_header[20] = 0;
1247 end_header[21] = 0;
1249 write(fd, end_header, 22);
1251 if(verbose)
1252 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1253 total_in,
1254 total_out,
1255 (do_compress ? "deflated" : "stored"),
1256 (int)((1 - (total_out / (float)total_in)) * 100)
1259 return 0;
1262 int extract_jar(int fd, char **files, int file_num){
1263 int rdamt;
1264 int out_a, in_a;
1265 ub4 signature;
1266 ub4 csize;
1267 ub4 crc;
1268 ub2 fnlen;
1269 ub2 eflen;
1270 ub2 flags;
1271 ub2 method;
1272 ub1 *filename = NULL;
1273 int filename_len = 0;
1274 ub4 rd_buff[RDSZ];
1275 pb_file pbf;
1276 ub1 scratch[16];
1277 zipentry ze;
1278 int f_fd;
1279 int dir;
1280 int handle;
1281 int j;
1283 init_inflation();
1285 pb_init(&pbf, fd);
1287 for(;;){
1288 f_fd = 0;
1289 crc = 0;
1290 ze.crc = 0;
1292 dir = FALSE; /* by default, the file isn't a dir */
1293 handle = TRUE; /* by default we'll extract/create the file */
1295 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1296 perror("read");
1297 break;
1300 signature = UNPACK_UB4(scratch, 0);
1302 #ifdef DEBUG
1303 printf("signature is %x\n", signature);
1304 #endif
1305 if(signature == 0x08074b50){
1306 #ifdef DEBUG
1307 printf("skipping data descriptor\n");
1308 #endif
1309 pb_read(&pbf, scratch, 12);
1310 continue;
1311 } else if(signature == 0x02014b50){
1312 #ifdef DEBUG
1313 printf("Central header reached.. we're all done!\n");
1314 #endif
1315 break;
1316 }else if(signature != 0x04034b50){
1317 printf("Ick! %#x\n", signature);
1318 break;
1321 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1322 perror("read");
1323 break;
1326 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1327 #ifdef DEBUG
1328 printf("Compressed size is %u\n", csize);
1329 #endif
1331 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1332 #ifdef DEBUG
1333 printf("Filename length is %hu\n", fnlen);
1334 #endif
1336 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1337 #ifdef DEBUG
1338 printf("Extra field length is %hu\n", eflen);
1339 #endif
1341 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1342 #ifdef DEBUG
1343 printf("Flags are %#hx\n", flags);
1344 #endif
1346 method = UNPACK_UB2(file_header, LOC_COMP);
1347 #ifdef DEBUG
1348 printf("Compression method is %#hx\n", method);
1349 #endif
1351 /* if there isn't a data descriptor */
1352 if(!(flags & 0x0008)){
1353 crc = UNPACK_UB4(file_header, LOC_CRC);
1354 #ifdef DEBUG
1355 printf("CRC is %x\n", crc);
1356 #endif
1359 if(filename_len < fnlen + 1){
1360 if(filename != NULL)
1361 free(filename);
1363 filename = malloc(sizeof(ub1) * (fnlen + 1));
1364 filename_len = fnlen + 1;
1367 pb_read(&pbf, filename, fnlen);
1368 filename[fnlen] = '\0';
1370 #ifdef DEBUG
1371 printf("filename is %s\n", filename);
1372 #endif
1374 if(file_num > 0){
1375 handle = FALSE;
1377 for(j = 0; j < file_num; j++)
1378 if(strcmp(files[j], (const char *)filename) == 0){
1379 handle = TRUE;
1380 break;
1384 if(!handle)
1385 f_fd = -1;
1387 /* OK, there is some directory information in the file. Nothing to do
1388 but ensure the directory(s) exist, and create them if they don't.
1389 What a pain! */
1390 if(strchr((const char *)filename, '/') != NULL && handle){
1391 /* Loop through all the directories in the path, (everything w/ a '/') */
1392 const ub1 *start = filename;
1393 char *tmp_buff;
1394 struct stat sbuf;
1396 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1398 for(;;){
1399 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1401 if(idx == NULL)
1402 break;
1403 else if(idx == start){
1404 start++;
1405 continue;
1407 start = idx + 1;
1409 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1410 tmp_buff[(idx - filename)] = '\0';
1412 #ifdef DEBUG
1413 printf("checking the existance of %s\n", tmp_buff);
1414 #endif
1416 if(stat(tmp_buff, &sbuf) < 0){
1417 if(errno != ENOENT){
1418 perror("stat");
1419 exit(1);
1422 } else if(S_ISDIR(sbuf.st_mode)){
1423 #ifdef DEBUG
1424 printf("Directory exists\n");
1425 #endif
1426 continue;
1427 }else {
1428 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1429 tmp_buff);
1430 exit(1);
1433 #ifdef DEBUG
1434 printf("Making directory..\n");
1435 #endif
1436 if(mkdir(tmp_buff, 0755) < 0){
1437 perror("mkdir");
1438 exit(1);
1440 if(verbose && handle)
1441 printf("%10s: %s/\n", "created", tmp_buff);
1445 /* only a directory */
1446 if(strlen((const char *)start) == 0)
1447 dir = TRUE;
1449 #ifdef DEBUG
1450 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1451 #endif
1453 /* If the entry was just a directory, don't write to file, etc */
1454 if(strlen((const char *)start) == 0)
1455 f_fd = -1;
1457 free(tmp_buff);
1460 if(f_fd != -1 && handle){
1461 f_fd = open((const char *)filename,
1462 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1464 if(f_fd < 0){
1465 fprintf(stderr, "Error extracting JAR archive!\n");
1466 perror((const char *)filename);
1467 exit(1);
1471 if(method != 8 && flags & 0x0008){
1472 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1473 exit(1);
1476 if(method == 8 || flags & 0x0008){
1477 consume(&pbf, eflen);
1479 inflate_file(&pbf, f_fd, &ze);
1480 } else {
1482 #ifdef DEBUG
1483 printf("writing stored data.. (%d bytes)\n", csize);
1484 #endif
1486 out_a = 0;
1487 in_a = csize;
1489 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1491 while(out_a < (int)csize){
1492 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1493 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1494 perror("read");
1495 exit(1);
1498 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1500 if(f_fd >= 0)
1501 write(f_fd, rd_buff, rdamt);
1503 out_a += rdamt;
1504 in_a -= rdamt;
1506 #ifdef DEBUG
1507 printf("%d bytes written\n", out_a);
1508 #endif
1511 consume(&pbf, eflen);
1514 /* if there is a data descriptor left, compare the CRC */
1515 if(flags & 0x0008){
1517 if(pb_read(&pbf, scratch, 16) != 16){
1518 perror("read");
1519 exit(1);
1522 signature = UNPACK_UB4(scratch, 0);
1524 if(signature != 0x08074b50){
1525 fprintf(stderr, "Error! Missing data descriptor!\n");
1526 exit(1);
1529 crc = UNPACK_UB4(scratch, 4);
1533 if(crc != ze.crc){
1534 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1535 ze.crc, crc);
1536 exit(1);
1539 close(f_fd);
1541 if(verbose && dir == FALSE && handle)
1542 printf("%10s: %s\n",
1543 (method == 8 ? "inflated" : "extracted"),
1544 filename);
1547 return 0;
1550 int list_jar(int fd, char **files, int file_num){
1551 ub4 signature;
1552 ub4 csize;
1553 ub4 usize;
1554 ub4 mdate;
1555 ub4 tmp;
1556 ub2 fnlen;
1557 ub2 eflen;
1558 ub2 clen;
1559 ub2 flags;
1560 ub2 method;
1561 ub2 cen_size;
1562 ub1 *filename = NULL;
1563 ub1 scratch[16];
1564 ub1 cen_header[46];
1565 int filename_len = 0;
1566 off_t size;
1567 int i, j;
1568 time_t tdate;
1569 struct tm *s_tm;
1570 char ascii_date[31];
1571 zipentry ze;
1573 #ifdef DEBUG
1574 printf("Listing jar file, looking for %d files\n", file_num);
1575 #endif
1577 /* This should be the start of the central-header-end section */
1578 if(seekable){
1579 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1580 perror("lseek");
1581 exit(1);
1584 if(read(fd, &tmp, sizeof(ub4)) != 4){
1585 perror("read");
1586 exit(1);
1589 #ifdef WORDS_BIGENDIAN
1590 tmp = L2BI(tmp);
1591 #endif
1593 if(tmp != 0x06054b50){
1594 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1595 exit(1);
1598 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1599 perror("lseek");
1600 exit(1);
1603 if(read(fd, &cen_size, 2) != 2){
1604 perror("read");
1605 exit(1);
1608 #ifdef WORDS_BIGENDIAN
1609 cen_size = L2BS(cen_size);
1610 #endif
1612 /* printf("%hu entries in central header\n", cen_size); */
1614 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1615 perror("lseek");
1616 exit(1);
1619 if(read(fd, &tmp, 4) != 4){
1620 perror("read");
1621 exit(1);
1624 #ifdef WORDS_BIGENDIAN
1625 tmp = L2BI(tmp);
1626 #endif
1628 /* printf("Central header offset = %d\n", tmp); */
1630 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1631 perror("lseek");
1632 exit(1);
1635 /* Loop through the entries in the central header */
1636 for(i = 0; i < cen_size; i++){
1638 if(read(fd, &cen_header, 46) != 46){
1639 perror("read");
1640 exit(1);
1643 signature = UNPACK_UB4(cen_header, 0);
1644 if(signature != 0x02014b50){
1645 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1646 exit(1);
1649 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1650 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1651 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1652 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1654 /* If we're providing verbose output, we need to make an ASCII
1655 * formatted version of the date. */
1656 if(verbose){
1657 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1658 tdate = dos2unixtime(mdate);
1659 s_tm = localtime(&tdate);
1660 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1661 ascii_date[30] = '\0';
1664 if(filename_len < fnlen + 1){
1665 if(filename != NULL)
1666 free(filename);
1668 filename = malloc(sizeof(ub1) * (fnlen + 1));
1669 filename_len = fnlen + 1;
1672 if(read(fd, filename, fnlen) != fnlen){
1673 perror("read");
1674 exit(1);
1676 filename[fnlen] = '\0';
1678 /* if the user specified a list of files on the command line,
1679 we'll only display those, otherwise we'll display everything */
1680 if(file_num > 0){
1681 for(j = 0; j < file_num; j++)
1682 if(strcmp(files[j], (const char *)filename) == 0){
1683 if(verbose)
1684 printf("%6d %s %s\n", usize, ascii_date, filename);
1685 else
1686 printf("%s\n", filename);
1687 break;
1689 } else {
1690 if(verbose)
1691 printf("%6d %s %s\n", usize, ascii_date, filename);
1692 else
1693 printf("%s\n", filename);
1696 size = eflen + clen;
1697 if(size > 0){
1698 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1699 perror("lseek");
1700 exit(1);
1704 } else {
1705 /* the file isn't seekable.. evil! */
1706 pb_file pbf;
1708 pb_init(&pbf, fd);
1710 init_inflation();
1712 for(;;){
1713 if(pb_read(&pbf, scratch, 4) != 4){
1714 perror("read");
1715 break;
1718 signature = UNPACK_UB4(scratch, 0);
1720 #ifdef DEBUG
1721 printf("signature is %x\n", signature);
1722 #endif
1724 if(signature == 0x08074b50){
1725 #ifdef DEBUG
1726 printf("skipping data descriptor\n");
1727 #endif
1728 pb_read(&pbf, scratch, 12);
1729 continue;
1730 } else if(signature == 0x02014b50){
1731 #ifdef DEBUG
1732 printf("Central header reached.. we're all done!\n");
1733 #endif
1734 break;
1735 }else if(signature != 0x04034b50){
1736 #ifdef DEBUG
1737 printf("Ick! %#x\n", signature);
1738 #endif
1739 break;
1742 if(pb_read(&pbf, (file_header + 4), 26) != 26){
1743 perror("read");
1744 break;
1747 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1748 #ifdef DEBUG
1749 printf("Compressed size is %u\n", csize);
1750 #endif
1752 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1753 #ifdef DEBUG
1754 printf("Filename length is %hu\n", fnlen);
1755 #endif
1757 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1758 #ifdef DEBUG
1759 printf("Extra field length is %hu\n", eflen);
1760 #endif
1762 method = UNPACK_UB2(file_header, LOC_COMP);
1763 #ifdef DEBUG
1764 printf("Compression method is %#hx\n", method);
1765 #endif
1767 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1768 #ifdef DEBUG
1769 printf("Flags are %#hx\n", flags);
1770 #endif
1772 usize = UNPACK_UB4(file_header, LOC_USIZE);
1774 /* If we're providing verbose output, we need to make an ASCII
1775 * formatted version of the date. */
1776 if(verbose){
1777 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1778 tdate = dos2unixtime(mdate);
1779 s_tm = localtime(&tdate);
1780 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1783 if(filename_len < fnlen + 1){
1784 if(filename != NULL)
1785 free(filename);
1787 filename = malloc(sizeof(ub1) * (fnlen + 1));
1788 ascii_date[30] = '\0';
1789 filename_len = fnlen + 1;
1792 pb_read(&pbf, filename, fnlen);
1793 filename[fnlen] = '\0';
1795 /* the header is at the end. In a JAR file, this means that the data
1796 happens to be compressed. We have no choice but to inflate the
1797 data */
1798 if(flags & 0x0008){
1800 size = eflen;
1802 if(size > 0)
1803 consume(&pbf, size);
1805 if(method == 8){
1806 #ifdef DEBUG
1807 printf("inflating %s\n", filename);
1808 #endif
1809 inflate_file(&pbf, -1, &ze);
1811 usize = ze.usize;
1812 } else
1813 printf("We're shit outta luck!\n");
1815 } else {
1816 size = csize + (eflen > 0 ? eflen : 0);
1819 #ifdef DEBUG
1820 printf("Skipping %ld bytes\n", (long)size);
1821 #endif
1823 consume(&pbf, size);
1825 /* print out the listing */
1826 if(file_num > 0){
1827 for(j = 0; j < file_num; j++)
1828 if(strcmp(files[j], (const char *)filename) == 0){
1829 if(verbose)
1830 printf("%6d %s %s\n", usize, ascii_date, filename);
1831 else
1832 printf("%s\n", filename);
1833 break;
1835 } else {
1836 if(verbose)
1837 printf("%6d %s %s\n", usize, ascii_date, filename);
1838 else
1839 printf("%s\n", filename);
1843 return 0;
1846 int consume(pb_file *pbf, int amt){
1847 int tc = 0; /* total amount consumed */
1848 ub1 buff[RDSZ];
1849 int rdamt;
1851 #ifdef DEBUG
1852 printf("Consuming %d bytes\n", amt);
1853 #endif
1855 if (seekable){
1856 if (amt <= (int)pbf->buff_amt)
1857 pb_read(pbf, buff, amt);
1858 else {
1859 lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
1860 pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
1862 } else
1863 while(tc < amt){
1864 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1865 #ifdef DEBUG
1866 printf("got %d bytes\n", rdamt);
1867 #endif
1868 tc += rdamt;
1871 #ifdef DEBUG
1872 printf("%d bytes consumed\n", amt);
1873 #endif
1875 return 0;
1878 void usage(const char *filename){
1879 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1880 exit (1);
1883 void version ()
1885 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1886 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1887 printf("Copyright 2002 Free Software Foundation\n");
1888 printf("\
1889 This is free software; see the source for copying conditions. There is NO\n\
1890 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1891 exit (0);
1894 void help(const char *filename)
1896 printf("\
1897 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1899 Store many files together in a single `jar' file.\n\
1901 -c create new archive\n\
1902 -t list table of contents for archive\n\
1903 -x extract named (or all) files from archive\n\
1904 -u update existing archive\n\
1905 ", filename);
1906 printf("\n\
1907 -@ read names from stdin\n\
1908 -0 store only; use no ZIP compression\n\
1909 -C DIR FILE change to the specified directory and include\n\
1910 the following file\n\
1911 -E don't include the files found in a directory\n\
1912 -f FILE specify archive file name\n\
1913 --help print this help, then exit\n\
1914 -m FILE include manifest information from specified manifest file\n\
1915 -M Do not create a manifest file for the entries\n\
1916 -v generate verbose output on standard output\n\
1917 -V, --version display version information\n\
1919 printf("\n\
1920 If any file is a directory then it is processed recursively.\n\
1921 The manifest file name and the archive file name needs to be specified\n\
1922 in the same order the 'm' and 'f' flags are specified.\n\
1924 Example 1: to archive two class files into an archive called classes.jar: \n\
1925 jar cvf classes.jar Foo.class Bar.class \n\
1926 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1927 files in the foo/ directory into 'classes.jar': \n\
1928 jar cvfm classes.jar mymanifest -C foo/ .\n\
1931 exit(0);
1934 static char *
1935 jt_strdup(s)
1936 char *s;
1938 char *result = (char*)malloc(strlen(s) + 1);
1939 if (result == (char*)0)
1940 return (char*)0;
1941 strcpy(result, s);
1942 return result;
1945 /* Convert "tar-style" first argument to a form expected by getopt.
1946 This idea and the code comes from GNU tar. This can allocate a new
1947 argument vector. This might leak some memory, but we don't care. */
1948 static void
1949 expand_options (int *argcp, char ***argvp)
1951 int argc = *argcp;
1952 char **argv = *argvp;
1954 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
1955 if a long argument (like "--help") is detected. */
1956 if (argc > 1 && argv[1][1] != '-')
1958 char buf[3];
1959 char **new_argv;
1960 int new_argc;
1961 int args_to_expand;
1962 char *p;
1963 char **in, **out;
1965 buf[0] = '-';
1966 buf[2] = '\0';
1968 args_to_expand = strlen (argv[1]);
1969 if (argv[1][0] == '-')
1970 --args_to_expand;
1972 new_argc = argc - 1 + args_to_expand;
1973 new_argv = (char **) malloc (new_argc * sizeof (char *));
1974 in = argv;
1975 out = new_argv;
1977 *out++ = *in++;
1978 p = *in++;
1979 if (*p == '-')
1980 p++;
1981 while (*p != '\0')
1983 char *opt;
1984 buf[1] = *p;
1985 *out++ = jt_strdup (buf);
1986 /* If the option takes an argument, move the next argument
1987 to just after this option. */
1988 opt = strchr (OPTION_STRING, *p);
1989 if (opt && opt[1] == ':')
1991 if (in < argv + argc)
1992 *out++ = *in++;
1993 else
1995 fprintf(stderr, "%s: option `%s' requires an argument.\n",
1996 argv[0], buf);
1997 usage(argv[0]);
2000 ++p;
2003 /* Copy remaining options. */
2004 while (in < argv + argc)
2005 *out++ = *in++;
2007 *argcp = new_argc;
2008 *argvp = new_argv;