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