* xref.c (FILE_NAME_ABSOLUTE_P): Add parenthesis.
[official-gcc.git] / fastjar / jartool.c
blob25ce2abf9b1ec13c23658b37e7ab48b4ef93f528
1 /*
2 jartool.c - main functions for fastjar utility
3 Copyright (C) 1999, 2000, 2001 Bryan Burns
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 /* $Id: jartool.c,v 1.9 2001/10/12 00:49:42 bryce Exp $
22 $Log: jartool.c,v $
23 Revision 1.9 2001/10/12 00:49:42 bryce
24 * jatool.c (extract_jar): Account for null termination when
25 determining whether to expand "filename".
27 Revision 1.8 2001/08/29 01:35:31 apbianco
28 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
30 * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
31 Fixes PR java/3949.
33 (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
35 Revision 1.7 2001/08/27 23:09:37 tromey
36 * jartool.c (jarfile): Remove length limitation.
37 (main): Use jt_strdup when initializing jarfile.
39 Revision 1.6 2001/07/04 18:33:53 tromey
40 Modified from patch by Julian Hall <jules@acris.co.uk>:
41 * jartool.c (errno): Conditionally declare.
42 (O_BINARY): Conditionally define.
43 (main): Use open, not creat. Use O_BINARY everywhere.
44 (make_manifest): Use O_BINARY.
45 (add_to_jar): Likewise.
47 Revision 1.5 2001/05/03 21:40:47 danglin
48 * jartool.c (jt_strdup): New function.
49 (get_next_arg): Use jt_strdup instead of strdup.
51 Revision 1.4 2000/12/28 21:47:37 robertl
52 2000-12-28 Robert Lipe <robertl@sco.com>
54 * jartool.c (MAXPATHLEN): Provide if not defined.
56 Revision 1.3 2000/12/14 18:45:35 ghazi
57 Warning fixes:
59 * compress.c: Include stdlib.h and compress.h.
60 (rcsid): Delete.
61 (report_str_error): Make static.
62 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
63 (hrd_inflate_str): Likewise.
65 * compress.h (init_compression, end_compression, init_inflation,
66 end_inflation): Prototype void arguments.
68 * dostime.c (rcsid): Delete.
70 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
71 Make functions static. Cast ctype function argument to `unsigned
72 char'. Add parens in if-stmts. Constify.
73 (Usage): Change into a macro.
74 (jargrep): Remove unused parameter.
76 * jartool.c: Constify. Add parens in if-stmts. Align
77 signed/unsigned char pointers in functions calls using casts.
78 (rcsid): Delete.
79 (list_jar): Fix printf format specifier.
80 (usage): Chop long string into bits. Reformat.
82 * pushback.c (rcsid): Delete.
84 Revision 1.2 2000/12/13 18:11:57 tromey
85 * jartool.c (extract_jar): Use strchr, not index.
87 Revision 1.1 2000/12/09 03:08:23 apbianco
88 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
90 * fastjar: Imported.
92 Revision 1.5 2000/08/24 15:01:27 cory
93 Made certain that fastjar opened the jar file before trying to update it
94 with the -u option.
96 Revision 1.4 2000/08/24 13:39:21 cory
97 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
98 when byte swapping. Better safe than sorry.
100 Revision 1.3 2000/08/23 19:42:17 cory
101 Added support for more Unix platforms. The following code has been hacked
102 to work on AIX, Solaris, True 64, and HP-UX.
103 Added bigendian check. Probably works on most big and little endian platforms
104 now.
106 Revision 1.2 1999/12/06 07:38:28 toast
107 fixed recursive archiving bug
109 Revision 1.1.1.1 1999/12/06 03:09:34 toast
110 initial checkin..
114 Revision 1.22 1999/10/12 19:45:13 burnsbr
115 adding patch to fix compat problem
117 Revision 1.21 1999/05/10 09:15:49 burnsbr
118 fixed manifest file version info
120 Revision 1.20 1999/05/10 08:53:16 burnsbr
121 *** empty log message ***
123 Revision 1.19 1999/05/10 08:30:39 burnsbr
124 added extract / listing code
126 Revision 1.18 1999/04/28 04:24:29 burnsbr
127 updated version
129 Revision 1.17 1999/04/28 04:21:23 burnsbr
130 added support for -C dir-changing flag.. Updated total compression display
132 Revision 1.16 1999/04/27 10:28:22 burnsbr
133 updated version string
135 Revision 1.15 1999/04/27 10:04:06 burnsbr
136 configure support
138 Revision 1.14 1999/04/27 08:56:14 burnsbr
139 added -V flag, better error messages
141 Revision 1.13 1999/04/26 02:35:21 burnsbr
142 changed all sorts of stuff.. compression now works 100%
144 Revision 1.12 1999/04/23 12:00:45 burnsbr
145 90% done with compression code
147 Revision 1.11 1999/04/22 04:12:57 burnsbr
148 finished first round of Manifest file support..
149 might need to do more, digest etc..
151 Revision 1.10 1999/04/22 02:35:23 burnsbr
152 added more manifest support, about 75% done now. Replaced all the
153 redundant shifts and bit-logic with a macro or two, making the code
154 easier to read.
156 Revision 1.9 1999/04/21 09:55:16 burnsbr
157 pulled out printfs
159 Revision 1.8 1999/04/21 02:58:01 burnsbr
160 started manifest code
162 Revision 1.7 1999/04/20 23:15:28 burnsbr
163 added patch sent by John Bley <jbb6@acpub.duke.edu>
165 Revision 1.6 1999/04/20 08:56:02 burnsbr
166 added GPL comment
168 Revision 1.5 1999/04/20 08:16:09 burnsbr
169 fixed verbose flag, did some optimization
171 Revision 1.4 1999/04/20 05:09:59 burnsbr
172 added rcsid variable
174 Revision 1.3 1999/04/20 05:08:54 burnsbr
175 fixed Log statement
179 #include "config.h"
181 #include <zlib.h>
183 #ifdef HAVE_STDLIB_H
184 #include <stdlib.h>
185 #endif
187 #ifdef HAVE_UNISTD_H
188 #include <unistd.h>
189 #endif
191 #include <stdio.h>
192 #include <sys/stat.h>
193 #include <sys/types.h>
195 #ifdef HAVE_SYS_PARAM_H
196 #include <sys/param.h>
197 #endif
199 #ifndef MAXPATHLEN
200 #define MAXPATHLEN 1024
201 #endif
203 #ifdef HAVE_DIRENT_H
204 #include <dirent.h>
205 #endif
207 #ifdef HAVE_FCNTL_H
208 #include <fcntl.h>
209 #endif
211 #include <string.h>
212 #include <errno.h>
214 #ifdef TM_IN_SYS_TIME
215 #include <sys/time.h>
216 #else
217 #include <time.h>
218 #endif
220 #include "jartool.h"
221 #include "zipfile.h"
222 #include "dostime.h"
223 #include "pushback.h"
224 #include "compress.h"
226 #ifdef WORDS_BIGENDIAN
228 #define L2BI(l) ((l & 0xff000000) >> 24) | \
229 ((l & 0x00ff0000) >> 8) | \
230 ((l & 0x0000ff00) << 8) | \
231 ((l & 0x000000ff) << 24);
233 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
235 #endif
237 static char version_string[] = VERSION;
239 #ifndef errno
240 extern int errno;
241 #endif
243 #ifndef O_BINARY
244 #define O_BINARY 0
245 #endif
247 void usage(const char*);
248 void add_entry(struct zipentry *);
249 void init_headers(void);
251 int consume(pb_file *, int);
252 int list_jar(int, char**, int);
253 int extract_jar(int, char**, int);
254 int add_file_to_jar(int, int, const char*, struct stat*);
255 int add_to_jar(int, const char*, const char*);
256 int create_central_header(int);
257 int make_manifest(int, const char*);
258 static void init_args(char **, int);
259 static char *get_next_arg (void);
260 static char *jt_strdup (char*);
262 /* global variables */
263 ub1 file_header[30];
264 ub1 data_descriptor[16];
265 int do_compress;
266 int seekable;
267 int verbose;
268 char *jarfile;
270 /* If non zero, then don't recurse in directory. Instead, add the
271 directory entry and relie on an explicit list of files to populate
272 the archive. This option isn't supported by the original jar tool. */
273 int use_explicit_list_only;
275 /* If non zero, then read the entry names from stdin. This option
276 isn't supported by the original jar tool. */
277 int read_names_from_stdin;
279 zipentry *ziplist; /* linked list of entries */
280 zipentry *ziptail; /* tail of the linked list */
282 int number_of_entries; /* number of entries in the linked list */
284 int main(int argc, char **argv){
286 char mfile[256];
288 int action = ACTION_NONE;
289 int manifest = TRUE;
290 int manifest_file = FALSE;
291 int file = FALSE;
292 int file_first = FALSE;
294 int i, j;
295 int jarfd = -1;
297 do_compress = TRUE;
298 verbose = FALSE;
300 ziplist = NULL;
302 number_of_entries = 0;
304 if(argc < 2)
305 usage(argv[0]);
307 j = strlen(argv[1]);
309 for(i = 0; i < j; i++){
310 switch(argv[1][i]){
311 case 'c':
312 action = ACTION_CREATE;
313 break;
314 case 't':
315 action = ACTION_LIST;
316 break;
317 case 'x':
318 action = ACTION_EXTRACT;
319 break;
320 case 'u':
321 action = ACTION_UPDATE;
322 break;
323 case 'v':
324 verbose = TRUE;
325 break;
326 case 'V':
327 printf("%s\n", version_string);
328 exit(0);
329 case 'f':
330 file = TRUE;
331 if(!manifest_file)
332 file_first = TRUE;
333 else
334 file_first = FALSE;
335 break;
336 case 'm':
337 manifest_file = TRUE;
338 break;
339 case '0':
340 do_compress = FALSE;
341 break;
342 case 'M':
343 manifest = FALSE;
344 break;
345 case '-':
346 break;
347 /* The following options aren't supported by the original jar tool. */
348 case 'E':
349 use_explicit_list_only = TRUE;
350 break;
351 case '@':
352 read_names_from_stdin = TRUE;
353 break;
354 default:
355 fprintf(stderr, "Illegal option: %c\n", argv[1][i]);
356 usage(argv[0]);
360 if(action == ACTION_NONE){
361 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
362 usage(argv[0]);
365 /* Verify unsupported combinations and warn of the use of non
366 standard features */
367 if(verbose && use_explicit_list_only)
368 fprintf (stderr, "Warning: using non standard '-E' option\n");
369 if(verbose && read_names_from_stdin)
370 fprintf (stderr, "Warning: using non standard '-@' option\n");
371 if(read_names_from_stdin
372 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
373 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
374 usage(argv[0]);
377 i = 2;
379 /* get the jarfile and manifest file (if any) */
380 if(file && file_first){
381 if(i >= argc)
382 usage(argv[0]);
384 jarfile = jt_strdup (argv[i++]);
386 if(manifest_file){
387 if(i >= argc)
388 usage(argv[0]);
390 strncpy(mfile, argv[i++], 256);
393 if(file && !file_first){
394 if(i >= argc)
395 usage(argv[0]);
397 jarfile = jt_strdup (argv[i++]);
400 /* create the jarfile */
401 if(action == ACTION_CREATE){
402 if(file){
403 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
404 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
406 if(jarfd < 0){
407 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
408 perror(jarfile);
409 exit(1);
412 /* We assume that the file is seekable */
413 seekable = TRUE;
415 } else {
417 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
419 /* standard out is not seekable */
420 seekable = FALSE;
422 /* don't want our output to be part of the jar file.. figured this one
423 out the hard way.. =P */
424 verbose = FALSE;
426 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
428 if(file){
429 jarfd = open(jarfile, O_RDONLY | O_BINARY);
431 if(jarfd < 0){
432 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
433 perror(jarfile);
434 exit(1);
437 seekable = TRUE;
438 } else {
439 jarfd = STDIN_FILENO; /* jarfd is standard in */
441 /* we assume that the stream isn't seekable for safety */
442 seekable = FALSE;
446 if(action == ACTION_CREATE || action == ACTION_UPDATE){
447 const char *arg;
448 init_headers();
450 if((action == ACTION_UPDATE) && file) {
451 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
452 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
453 perror(jarfile);
454 exit(1);
458 if(do_compress)
459 init_compression();
462 /* Add the META-INF/ directory and the manifest */
463 if(manifest && manifest_file)
464 make_manifest(jarfd, mfile);
465 else if(manifest)
466 make_manifest(jarfd, NULL);
468 init_args (argv, i);
469 /* now we add the files to the archive */
470 while ((arg = get_next_arg ())){
472 if(!strcmp(arg, "-C")){
473 const char *dir_to_change = get_next_arg ();
474 const char *file_to_add = get_next_arg ();
475 if(!dir_to_change
476 || !file_to_add
477 || add_to_jar(jarfd, dir_to_change, file_to_add)){
478 printf("Error adding %s to jar archive!\n", arg);
479 exit(1);
481 } else {
482 if(add_to_jar(jarfd, NULL, arg)){
483 printf("Error adding %s to jar archive!\n", arg);
484 exit(1);
488 /* de-initialize the compression DS */
489 if(do_compress)
490 end_compression();
492 create_central_header(jarfd);
494 if (close(jarfd) != 0) {
495 fprintf(stderr, "Error closing jar archive!\n");
497 } else if(action == ACTION_LIST){
498 list_jar(jarfd, &argv[i], (argc - i));
499 } else if(action == ACTION_EXTRACT){
500 extract_jar(jarfd, &argv[i], (argc - i));
503 exit(0);
506 static int args_current_g;
507 static char **args_g;
509 static void
510 init_args(args, current)
511 char **args;
512 int current;
514 if(!read_names_from_stdin)
516 args_g = args;
517 args_current_g = current;
521 static char *
522 get_next_arg ()
524 static int reached_end = 0;
526 if (reached_end)
527 return NULL;
529 if (args_g)
531 if (!args_g [args_current_g])
533 reached_end = 1;
534 return NULL;
536 return args_g [args_current_g++];
538 else
540 /* Read the name from stdin. Delimiters are '\n' and
541 '\r'. Reading EOF indicates that we don't have anymore file
542 names characters to read. */
544 char s [MAXPATHLEN];
545 int pos = 0;
547 /* Get rid of '\n' and '\r' first. */
548 while (1)
550 int c = getc (stdin);
551 if (c == '\n' || c == '\r')
552 continue;
553 else
555 if (c == EOF)
556 return NULL;
557 ungetc (c, stdin);
558 break;
562 while (1)
564 int c = getc (stdin);
565 /* Exit when we get a delimiter or don't have any characters
566 to read */
567 if (c == '\n'|| c == '\r'|| c == EOF)
568 break;
569 s [pos++] = (char) c;
572 if (pos)
574 s [pos] = '\0';
575 return jt_strdup (s);
577 else
578 return NULL;
582 void init_headers(){
583 /* packing file header */
584 /* magic number */
585 file_header[0] = 0x50;
586 file_header[1] = 0x4b;
587 file_header[2] = 0x03;
588 file_header[3] = 0x04;
589 /* version number (Unix 1.0)*/
590 file_header[4] = 10;
591 file_header[5] = 0;
592 /* bit flag (normal deflation)*/
593 file_header[6] = 0x00;
595 file_header[7] = 0x00;
596 /* do_compression method (deflation) */
597 file_header[8] = 0;
598 file_header[9] = 0;
600 /* last mod file time (MS-DOS format) */
601 file_header[10] = 0;
602 file_header[11] = 0;
603 /* last mod file date (MS-DOS format) */
604 file_header[12] = 0;
605 file_header[13] = 0;
606 /* CRC 32 */
607 file_header[14] = 0;
608 file_header[15] = 0;
609 file_header[16] = 0;
610 file_header[17] = 0;
611 /* compressed size */
612 file_header[18] = 0;
613 file_header[19] = 0;
614 file_header[20] = 0;
615 file_header[21] = 0;
616 /* uncompressed size */
617 file_header[22] = 0;
618 file_header[23] = 0;
619 file_header[24] = 0;
620 file_header[25] = 0;
621 /* filename length */
622 file_header[26] = 0;
623 file_header[27] = 0;
624 /* extra field length */
625 file_header[28] = 0;
626 file_header[29] = 0;
628 /* Initialize the compression DS */
629 PACK_UB4(data_descriptor, 0, 0x08074b50);
633 void add_entry(struct zipentry *ze){
635 if(ziplist == NULL){
636 ziplist = ze;
637 ziptail = ziplist;
638 } else {
639 ziplist->next_entry = ze;
640 ziplist = ze;
643 number_of_entries++;
646 int make_manifest(int jfd, const char *mf_name){
647 time_t current_time;
648 int nlen; /* length of file name */
649 int mod_time; /* file modification time */
650 struct zipentry *ze;
652 nlen = 9; /* trust me on this one */
654 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
656 current_time = time(NULL);
657 if(current_time == (time_t)-1){
658 perror("time");
659 exit(1);
662 mod_time = unix2dostime(&current_time);
664 PACK_UB2(file_header, LOC_EXTRA, 0);
665 PACK_UB2(file_header, LOC_COMP, 0);
666 PACK_UB2(file_header, LOC_FNLEN, nlen);
667 PACK_UB4(file_header, LOC_MODTIME, mod_time);
669 if(verbose)
670 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
672 ze = (zipentry*)malloc(sizeof(zipentry));
673 if(ze == NULL){
674 perror("malloc");
675 exit(1);
678 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
679 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
680 strcpy(ze->filename, "META-INF/");
681 ze->filename[nlen] = '\0';
683 ze->offset = lseek(jfd, 0, SEEK_CUR);
684 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
685 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
686 ze->compressed = FALSE;
688 add_entry(ze);
690 write(jfd, file_header, 30);
691 write(jfd, "META-INF/", nlen);
693 /* if the user didn't specify an external manifest file... */
694 if(mf_name == NULL){
695 int mf_len = 37 + strlen(VERSION);
696 char *mf;
698 if((mf = (char *) malloc(mf_len + 1))) {
699 uLong crc;
701 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
703 crc = crc32(0L, Z_NULL, 0);
705 crc = crc32(crc, (const unsigned char *)mf, mf_len);
707 nlen = 20; /* once again, trust me */
709 PACK_UB2(file_header, LOC_EXTRA, 0);
710 PACK_UB2(file_header, LOC_COMP, 0);
711 PACK_UB2(file_header, LOC_FNLEN, nlen);
712 PACK_UB4(file_header, LOC_USIZE, mf_len);
714 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
716 PACK_UB4(file_header, LOC_CRC, crc);
718 if(verbose)
719 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
721 ze = (zipentry*)malloc(sizeof(zipentry));
722 if(ze == NULL){
723 perror("malloc");
724 exit(1);
727 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
728 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
729 strcpy(ze->filename, "META-INF/MANIFEST.MF");
730 ze->filename[nlen] = '\0';
732 ze->offset = lseek(jfd, 0, SEEK_CUR);
733 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
734 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
735 ze->crc = crc;
736 ze->csize = mf_len;
737 ze->usize = ze->csize;
738 ze->compressed = FALSE;
740 add_entry(ze);
742 write(jfd, file_header, 30);
743 write(jfd, "META-INF/MANIFEST.MF", nlen);
744 write(jfd, mf, mf_len);
745 free(mf);
747 else {
748 printf("malloc errror\n");
749 exit(-1);
751 } else {
752 int mfd;
753 struct stat statbuf;
755 stat(mf_name, &statbuf);
757 if(!S_ISREG(statbuf.st_mode)){
758 fprintf(stderr, "Invalid manifest file specified.\n");
759 exit(1);
762 mfd = open(mf_name, O_RDONLY | O_BINARY);
764 if(mfd < 0){
765 fprintf(stderr, "Error opening %s.\n", mf_name);
766 exit(1);
769 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
770 perror("error writing to jar");
771 exit(1);
776 return 0;
779 int add_to_jar(int fd, const char *new_dir, const char *file){
780 struct stat statbuf;
781 DIR *dir;
782 struct dirent *de;
783 zipentry *ze;
784 int stat_return;
785 char *old_dir = NULL;
787 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
788 * It fixes this:
789 * "normal" jar : org/apache/java/io/LogRecord.class
790 * fastjar : ./org/apache/java/io/LogRecord.class
791 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
792 * with both kaffe-1.0b4 and JDK.
794 while (*file=='.' && *(file+1)=='/')
795 file+=2;
797 /* If new_dir isn't null, we need to change to that directory. However,
798 we also need to return to the old directory when we're done */
799 if(new_dir != NULL){
800 old_dir = getcwd(NULL, 0);
802 if(chdir(new_dir) == -1){
803 perror(new_dir);
804 return 1;
808 if(!strcmp(file, jarfile)){
809 if(verbose)
810 printf("skipping: %s\n", file);
811 return 0; /* we don't want to add ourselves.. */
814 stat_return = stat(file, &statbuf);
816 if(stat_return == -1){
817 perror(file);
818 return 1;
819 } else if(S_ISDIR(statbuf.st_mode)){
820 char *fullname;
821 char *t_ptr;
822 int nlen;
823 unsigned long mod_time;
825 dir = opendir(file);
827 if(dir == NULL){
828 perror("opendir");
829 return 1;
832 nlen = strlen(file) + 256;
833 fullname = (char*)malloc(nlen * sizeof(char));
834 memset(fullname, 0, (nlen * sizeof(char)));
836 if(fullname == NULL){
837 fprintf(stderr, "Filename is NULL!\n");
838 return 1;
841 strcpy(fullname, file);
842 nlen = strlen(file);
844 if(fullname[nlen - 1] != '/'){
845 fullname[nlen] = '/';
846 t_ptr = (fullname + nlen + 1);
847 } else
848 t_ptr = (fullname + nlen);
851 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
853 nlen = (t_ptr - fullname);
855 mod_time = unix2dostime(&statbuf.st_mtime);
857 PACK_UB2(file_header, LOC_EXTRA, 0);
858 PACK_UB2(file_header, LOC_COMP, 0);
859 PACK_UB2(file_header, LOC_FNLEN, nlen);
860 PACK_UB4(file_header, LOC_MODTIME, mod_time);
862 if(verbose)
863 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
865 ze = (zipentry*)malloc(sizeof(zipentry));
866 if(ze == NULL){
867 perror("malloc");
868 exit(1);
871 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
872 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
873 strcpy(ze->filename, fullname);
874 ze->filename[nlen] = '\0';
876 ze->offset = lseek(fd, 0, SEEK_CUR);
877 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
878 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
879 ze->compressed = FALSE;
881 add_entry(ze);
883 write(fd, file_header, 30);
884 write(fd, fullname, nlen);
886 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
887 if(de->d_name[0] == '.')
888 continue;
889 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
890 if(verbose)
891 printf("skipping: %s\n", de->d_name);
892 continue;
895 strcpy(t_ptr, de->d_name);
897 if(add_to_jar(fd, NULL, fullname)){
898 fprintf(stderr, "Error adding file to jar!\n");
899 return 1;
903 free(fullname);
904 closedir(dir);
906 } else if(S_ISREG(statbuf.st_mode)){
907 int add_fd;
909 add_fd = open(file, O_RDONLY | O_BINARY);
910 if(add_fd < 0){
911 fprintf(stderr, "Error opening %s.\n", file);
912 return 0;
915 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
916 fprintf(stderr, "Error adding file to jar!\n");
917 return 1;
920 } else {
921 fprintf(stderr, "Illegal file specified: %s\n", file);
924 if(old_dir != NULL){
925 if(chdir(old_dir))
926 perror(old_dir);
928 free(old_dir);
931 return 0;
934 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
936 unsigned short file_name_length;
937 unsigned long mod_time;
938 ub1 rd_buff[RDSZ];
939 uLong crc = 0;
940 off_t offset = 0;
941 int rdamt;
942 struct zipentry *ze;
944 mod_time = unix2dostime(&(statbuf->st_mtime));
945 file_name_length = strlen(fname);
947 if(!seekable && !do_compress){
948 crc = crc32(0L, Z_NULL, 0);
950 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
951 crc = crc32(crc, rd_buff, rdamt);
953 lseek(ffd, 0, SEEK_SET);
956 /* data descriptor */
957 if(!seekable && do_compress){
958 PACK_UB2(file_header, LOC_EXTRA, 8);
959 } else {
960 PACK_UB2(file_header, LOC_EXTRA, 0);
963 if(do_compress){
964 PACK_UB2(file_header, LOC_COMP, 8);
965 } else {
966 PACK_UB2(file_header, LOC_COMP, 0);
969 PACK_UB4(file_header, LOC_MODTIME, mod_time);
970 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
972 if(!seekable && !do_compress){
973 PACK_UB4(file_header, LOC_CRC, crc);
974 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
975 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
976 } else
977 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
979 ze = (zipentry*)malloc(sizeof(zipentry));
980 if(ze == NULL){
981 perror("malloc");
982 exit(1);
985 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
986 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
987 strcpy(ze->filename, fname);
989 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
990 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
992 if(!seekable && !do_compress)
993 ze->crc = crc;
995 ze->csize = statbuf->st_size;
996 ze->usize = ze->csize;
997 ze->offset = lseek(jfd, 0, SEEK_CUR);
998 if(do_compress)
999 ze->compressed = TRUE;
1000 else
1001 ze->compressed = FALSE;
1003 add_entry(ze);
1005 /* Write the local header */
1006 write(jfd, file_header, 30);
1008 /* write the file name to the zip file */
1009 write(jfd, fname, file_name_length);
1012 if(verbose){
1013 printf("adding: %s ", fname);
1014 fflush(stdout);
1017 if(do_compress){
1018 /* compress the file */
1019 compress_file(ffd, jfd, ze);
1020 } else {
1021 /* Write the contents of the file (uncompressed) to the zip file */
1022 /* calculate the CRC as we go along */
1023 ze->crc = crc32(0L, Z_NULL, 0);
1025 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1026 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1027 if(write(jfd, rd_buff, rdamt) != rdamt){
1028 perror("write");
1029 return 0;
1033 close(ffd);
1035 /* write out data descriptor */
1036 PACK_UB4(data_descriptor, 4, ze->crc);
1037 PACK_UB4(data_descriptor, 8, ze->csize);
1038 PACK_UB4(data_descriptor, 12, ze->usize);
1040 /* we need to seek back and fill the header */
1041 if(seekable){
1042 offset = (ze->csize + strlen(ze->filename) + 16);
1044 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1045 perror("lseek");
1046 exit(1);
1049 if(write(jfd, (data_descriptor + 4), 12) != 12){
1050 perror("write");
1051 return 0;
1054 offset -= 12;
1056 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1057 perror("lseek");
1058 exit(1);
1060 } else if(do_compress){
1061 /* Sun's jar tool will only allow a data descriptor if the entry is
1062 compressed, but we'll save 16 bytes/entry if we only use it when
1063 we can't seek back on the file */
1065 if(write(jfd, data_descriptor, 16) != 16){
1066 perror("write");
1067 return 0;
1071 if(verbose)
1072 printf("(in=%d) (out=%d) (%s %d%%)\n",
1073 (int)ze->usize, (int)ze->csize,
1074 (do_compress ? "deflated" : "stored"),
1075 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1077 return 0;
1080 int create_central_header(int fd){
1081 ub1 header[46];
1082 ub1 end_header[22];
1083 int start_offset;
1084 int dir_size;
1085 int *iheader;
1086 int total_in = 0, total_out = 22;
1088 zipentry *ze;
1090 iheader = (int*)header;
1092 /* magic number */
1093 header[0] = 'P';
1094 header[1] = 'K';
1095 header[2] = 1;
1096 header[3] = 2;
1097 /* version made by */
1098 header[4] = 10;
1099 header[5] = 0;
1100 /* version needed to extract */
1101 header[6] = 10;
1102 header[7] = 0;
1103 /* bit flag */
1104 header[8] = 0;
1105 header[9] = 0;
1106 /* compression method */
1107 header[10] = 0;
1108 header[11] = 0;
1109 /* file mod time */
1110 header[12] = 0;
1111 header[13] = 0;
1112 /* file mod date */
1113 header[14] = 0;
1114 header[15] = 0;
1115 /* crc 32 */
1116 header[16] = 0;
1117 header[17] = 0;
1118 header[18] = 0;
1119 header[19] = 0;
1120 /* compressed size */
1121 header[20] = 0;
1122 header[21] = 0;
1123 header[22] = 0;
1124 header[23] = 0;
1125 /* uncompressed size */
1126 header[24] = 0;
1127 header[25] = 0;
1128 header[26] = 0;
1129 header[27] = 0;
1130 /* filename length */
1131 header[28] = 0;
1132 header[29] = 0;
1133 /* extra field length */
1134 header[30] = 0;
1135 header[31] = 0;
1136 /* file comment length */
1137 header[32] = 0;
1138 header[33] = 0;
1139 /* disk number start */
1140 header[34] = 0;
1141 header[35] = 0;
1142 /* internal file attribs */
1143 header[36] = 0;
1144 header[37] = 0;
1145 /* external file attribs */
1146 header[38] = 0;
1147 header[39] = 0;
1148 header[40] = 0;
1149 header[41] = 0;
1150 /* relative offset of local header */
1151 header[42] = 0;
1152 header[43] = 0;
1153 header[44] = 0;
1154 header[45] = 0;
1156 start_offset = lseek(fd, 0, SEEK_CUR);
1158 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1160 total_in += ze->usize;
1161 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1163 if(ze->compressed){
1164 PACK_UB2(header, CEN_COMP, 8);
1165 } else {
1166 PACK_UB2(header, CEN_COMP, 0);
1169 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1170 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1171 PACK_UB4(header, CEN_CRC, ze->crc);
1172 PACK_UB4(header, CEN_CSIZE, ze->csize);
1173 PACK_UB4(header, CEN_USIZE, ze->usize);
1174 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1175 PACK_UB4(header, CEN_OFFSET, ze->offset);
1177 write(fd, header, 46);
1179 write(fd, ze->filename, strlen(ze->filename));
1182 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1184 /* magic number */
1185 end_header[0] = 0x50;
1186 end_header[1] = 0x4b;
1187 end_header[2] = 0x05;
1188 end_header[3] = 0x06;
1189 /* number of this disk */
1190 end_header[4] = 0;
1191 end_header[5] = 0;
1192 /* number of disk w/ start of central header */
1193 end_header[6] = 0;
1194 end_header[7] = 0;
1195 /* total number of entries in central dir on this disk*/
1196 PACK_UB2(end_header, 8, number_of_entries);
1197 /* total number of entries in central dir*/
1198 PACK_UB2(end_header, 10, number_of_entries);
1199 /* size of central dir. */
1200 PACK_UB4(end_header, 12, dir_size);
1201 /* offset of start of central dir */
1202 PACK_UB4(end_header, 16, start_offset);
1203 /* zipfile comment length */
1204 end_header[20] = 0;
1205 end_header[21] = 0;
1207 write(fd, end_header, 22);
1209 if(verbose)
1210 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1211 total_in,
1212 total_out,
1213 (do_compress ? "deflated" : "stored"),
1214 (int)((1 - (total_out / (float)total_in)) * 100)
1217 return 0;
1220 int extract_jar(int fd, char **files, int file_num){
1221 int rdamt;
1222 int out_a, in_a;
1223 ub4 signature;
1224 ub4 csize;
1225 ub4 crc;
1226 ub2 fnlen;
1227 ub2 eflen;
1228 ub2 flags;
1229 ub2 method;
1230 ub1 *filename = NULL;
1231 int filename_len = 0;
1232 ub4 rd_buff[RDSZ];
1233 pb_file pbf;
1234 ub1 scratch[16];
1235 zipentry ze;
1236 int f_fd;
1237 int dir;
1238 int handle;
1239 int j;
1241 init_inflation();
1243 pb_init(&pbf, fd);
1245 for(;;){
1246 f_fd = 0;
1247 crc = 0;
1248 ze.crc = 0;
1250 dir = FALSE; /* by default, the file isn't a dir */
1251 handle = TRUE; /* by default we'll extract/create the file */
1253 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1254 perror("read");
1255 break;
1258 signature = UNPACK_UB4(scratch, 0);
1260 #ifdef DEBUG
1261 printf("signature is %x\n", signature);
1262 #endif
1263 if(signature == 0x08074b50){
1264 #ifdef DEBUG
1265 printf("skipping data descriptor\n");
1266 #endif
1267 pb_read(&pbf, scratch, 12);
1268 continue;
1269 } else if(signature == 0x02014b50){
1270 #ifdef DEBUG
1271 printf("Central header reached.. we're all done!\n");
1272 #endif
1273 break;
1274 }else if(signature != 0x04034b50){
1275 printf("Ick! %#x\n", signature);
1276 break;
1279 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1280 perror("read");
1281 break;
1284 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1285 #ifdef DEBUG
1286 printf("Compressed size is %u\n", csize);
1287 #endif
1289 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1290 #ifdef DEBUG
1291 printf("Filename length is %hu\n", fnlen);
1292 #endif
1294 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1295 #ifdef DEBUG
1296 printf("Extra field length is %hu\n", eflen);
1297 #endif
1299 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1300 #ifdef DEBUG
1301 printf("Flags are %#hx\n", flags);
1302 #endif
1304 method = UNPACK_UB2(file_header, LOC_COMP);
1305 #ifdef DEBUG
1306 printf("Compression method is %#hx\n", method);
1307 #endif
1309 /* if there isn't a data descriptor */
1310 if(!(flags & 0x0008)){
1311 crc = UNPACK_UB4(file_header, LOC_CRC);
1312 #ifdef DEBUG
1313 printf("CRC is %x\n", crc);
1314 #endif
1317 if(filename_len < fnlen + 1){
1318 if(filename != NULL)
1319 free(filename);
1321 filename = malloc(sizeof(ub1) * (fnlen + 1));
1322 filename_len = fnlen + 1;
1325 pb_read(&pbf, filename, fnlen);
1326 filename[fnlen] = '\0';
1328 #ifdef DEBUG
1329 printf("filename is %s\n", filename);
1330 #endif
1332 if(file_num > 0){
1333 handle = FALSE;
1335 for(j = 0; j < file_num; j++)
1336 if(strcmp(files[j], (const char *)filename) == 0){
1337 handle = TRUE;
1338 break;
1342 if(!handle)
1343 f_fd = -1;
1345 /* OK, there is some directory information in the file. Nothing to do
1346 but ensure the directory(s) exist, and create them if they don't.
1347 What a pain! */
1348 if(strchr((const char *)filename, '/') != NULL && handle){
1349 /* Loop through all the directories in the path, (everything w/ a '/') */
1350 const ub1 *start = filename;
1351 char *tmp_buff;
1352 struct stat sbuf;
1354 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1356 for(;;){
1357 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1359 if(idx == NULL)
1360 break;
1361 else if(idx == start){
1362 start++;
1363 continue;
1365 start = idx + 1;
1367 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1368 tmp_buff[(idx - filename)] = '\0';
1370 #ifdef DEBUG
1371 printf("checking the existance of %s\n", tmp_buff);
1372 #endif
1374 if(stat(tmp_buff, &sbuf) < 0){
1375 if(errno != ENOENT){
1376 perror("stat");
1377 exit(1);
1380 } else if(S_ISDIR(sbuf.st_mode)){
1381 #ifdef DEBUG
1382 printf("Directory exists\n");
1383 #endif
1384 continue;
1385 }else {
1386 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1387 tmp_buff);
1388 exit(1);
1391 #ifdef DEBUG
1392 printf("Making directory..\n");
1393 #endif
1394 if(mkdir(tmp_buff, 0755) < 0){
1395 perror("mkdir");
1396 exit(1);
1398 if(verbose && handle)
1399 printf("%10s: %s/\n", "created", tmp_buff);
1403 /* only a directory */
1404 if(strlen((const char *)start) == 0)
1405 dir = TRUE;
1407 #ifdef DEBUG
1408 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1409 #endif
1411 /* If the entry was just a directory, don't write to file, etc */
1412 if(strlen((const char *)start) == 0)
1413 f_fd = -1;
1415 free(tmp_buff);
1418 if(f_fd != -1 && handle){
1419 f_fd = creat((const char *)filename, 00644);
1421 if(f_fd < 0){
1422 fprintf(stderr, "Error extracting JAR archive!\n");
1423 perror((const char *)filename);
1424 exit(1);
1428 if(method != 8 && flags & 0x0008){
1429 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1430 exit(1);
1433 if(method == 8 || flags & 0x0008){
1434 if(seekable)
1435 lseek(fd, eflen, SEEK_CUR);
1436 else
1437 consume(&pbf, eflen);
1439 inflate_file(&pbf, f_fd, &ze);
1440 } else {
1442 #ifdef DEBUG
1443 printf("writing stored data.. (%d bytes)\n", csize);
1444 #endif
1446 out_a = 0;
1447 in_a = csize;
1449 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1451 while(out_a < (int)csize){
1452 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1453 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1454 perror("read");
1455 exit(1);
1458 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1460 if(f_fd >= 0)
1461 write(f_fd, rd_buff, rdamt);
1463 out_a += rdamt;
1464 in_a -= rdamt;
1466 #ifdef DEBUG
1467 printf("%d bytes written\n", out_a);
1468 #endif
1471 if(seekable)
1472 lseek(fd, eflen, SEEK_CUR);
1473 else
1474 consume(&pbf, eflen);
1477 /* if there is a data descriptor left, compare the CRC */
1478 if(flags & 0x0008){
1480 if(pb_read(&pbf, scratch, 16) != 16){
1481 perror("read");
1482 exit(1);
1485 signature = UNPACK_UB4(scratch, 0);
1487 if(signature != 0x08074b50){
1488 fprintf(stderr, "Error! Missing data descriptor!\n");
1489 exit(1);
1492 crc = UNPACK_UB4(scratch, 4);
1496 if(crc != ze.crc){
1497 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1498 ze.crc, crc);
1499 exit(1);
1502 close(f_fd);
1504 if(verbose && dir == FALSE && handle)
1505 printf("%10s: %s\n",
1506 (method == 8 ? "inflated" : "extracted"),
1507 filename);
1510 return 0;
1513 int list_jar(int fd, char **files, int file_num){
1514 int rdamt;
1515 ub4 signature;
1516 ub4 csize;
1517 ub4 usize;
1518 ub4 mdate;
1519 ub4 tmp;
1520 ub2 fnlen;
1521 ub2 eflen;
1522 ub2 clen;
1523 ub2 flags;
1524 ub2 method;
1525 ub2 cen_size;
1526 ub1 *filename = NULL;
1527 ub1 scratch[16];
1528 ub1 cen_header[46];
1529 int filename_len = 0;
1530 off_t size;
1531 int i, j;
1532 time_t tdate;
1533 struct tm *s_tm;
1534 char ascii_date[30];
1535 zipentry ze;
1537 #ifdef DEBUG
1538 printf("Listing jar file, looking for %d files\n", file_num);
1539 #endif
1541 /* This should be the start of the central-header-end section */
1542 if(seekable){
1543 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1544 perror("lseek");
1545 exit(1);
1548 if(read(fd, &tmp, sizeof(ub4)) != 4){
1549 perror("read");
1550 exit(1);
1553 #ifdef WORDS_BIGENDIAN
1554 tmp = L2BI(tmp);
1555 #endif
1557 if(tmp != 0x06054b50){
1558 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1559 exit(1);
1562 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1563 perror("lseek");
1564 exit(1);
1567 if(read(fd, &cen_size, 2) != 2){
1568 perror("read");
1569 exit(1);
1572 #ifdef WORDS_BIGENDIAN
1573 cen_size = L2BS(cen_size);
1574 #endif
1576 /* printf("%hu entries in central header\n", cen_size); */
1578 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1579 perror("lseek");
1580 exit(1);
1583 if(read(fd, &tmp, 4) != 4){
1584 perror("read");
1585 exit(1);
1588 #ifdef WORDS_BIGENDIAN
1589 tmp = L2BI(tmp);
1590 #endif
1592 /* printf("Central header offset = %d\n", tmp); */
1594 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1595 perror("lseek");
1596 exit(1);
1599 /* Loop through the entries in the central header */
1600 for(i = 0; i < cen_size; i++){
1602 if(read(fd, &cen_header, 46) != 46){
1603 perror("read");
1604 exit(1);
1607 signature = UNPACK_UB4(cen_header, 0);
1608 if(signature != 0x02014b50){
1609 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1610 exit(1);
1613 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1614 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1615 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1616 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1618 /* If we're providing verbose output, we need to make an ASCII
1619 * formatted version of the date. */
1620 if(verbose){
1621 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1622 tdate = dos2unixtime(mdate);
1623 s_tm = localtime(&tdate);
1624 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1627 if(filename_len < fnlen){
1628 if(filename != NULL)
1629 free(filename);
1631 filename = malloc(sizeof(ub1) * (fnlen + 1));
1632 filename_len = fnlen + 1;
1635 if(read(fd, filename, fnlen) != fnlen){
1636 perror("read");
1637 exit(1);
1639 filename[fnlen] = '\0';
1641 /* if the user specified a list of files on the command line,
1642 we'll only display those, otherwise we'll display everything */
1643 if(file_num > 0){
1644 for(j = 0; j < file_num; j++)
1645 if(strcmp(files[j], (const char *)filename) == 0){
1646 if(verbose)
1647 printf("%6d %s %s\n", usize, ascii_date, filename);
1648 else
1649 printf("%s\n", filename);
1650 break;
1652 } else {
1653 if(verbose)
1654 printf("%6d %s %s\n", usize, ascii_date, filename);
1655 else
1656 printf("%s\n", filename);
1659 size = eflen + clen;
1660 if(size > 0){
1661 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1662 perror("lseek");
1663 exit(1);
1667 } else {
1668 /* the file isn't seekable.. evil! */
1669 pb_file pbf;
1671 pb_init(&pbf, fd);
1673 init_inflation();
1675 for(;;){
1676 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1677 perror("read");
1678 break;
1681 signature = UNPACK_UB4(scratch, 0);
1683 #ifdef DEBUG
1684 printf("signature is %x\n", signature);
1685 #endif
1687 if(signature == 0x08074b50){
1688 #ifdef DEBUG
1689 printf("skipping data descriptor\n");
1690 #endif
1691 pb_read(&pbf, scratch, 12);
1692 continue;
1693 } else if(signature == 0x02014b50){
1694 #ifdef DEBUG
1695 printf("Central header reached.. we're all done!\n");
1696 #endif
1697 break;
1698 }else if(signature != 0x04034b50){
1699 #ifdef DEBUG
1700 printf("Ick! %#x\n", signature);
1701 #endif
1702 break;
1705 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1706 perror("read");
1707 break;
1710 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1711 #ifdef DEBUG
1712 printf("Compressed size is %u\n", csize);
1713 #endif
1715 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1716 #ifdef DEBUG
1717 printf("Filename length is %hu\n", fnlen);
1718 #endif
1720 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1721 #ifdef DEBUG
1722 printf("Extra field length is %hu\n", eflen);
1723 #endif
1725 method = UNPACK_UB2(file_header, LOC_COMP);
1726 #ifdef DEBUG
1727 printf("Compression method is %#hx\n", method);
1728 #endif
1730 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1731 #ifdef DEBUG
1732 printf("Flags are %#hx\n", flags);
1733 #endif
1735 usize = UNPACK_UB4(file_header, LOC_USIZE);
1737 /* If we're providing verbose output, we need to make an ASCII
1738 * formatted version of the date. */
1739 if(verbose){
1740 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1741 tdate = dos2unixtime(mdate);
1742 s_tm = localtime(&tdate);
1743 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1746 if(filename_len < fnlen){
1747 if(filename != NULL)
1748 free(filename);
1750 filename = malloc(sizeof(ub1) * (fnlen + 1));
1751 filename_len = fnlen + 1;
1754 pb_read(&pbf, filename, fnlen);
1755 filename[fnlen] = '\0';
1757 /* the header is at the end. In a JAR file, this means that the data
1758 happens to be compressed. We have no choice but to inflate the
1759 data */
1760 if(flags & 0x0008){
1762 size = eflen;
1764 if(size > 0)
1765 consume(&pbf, size);
1767 if(method == 8){
1768 #ifdef DEBUG
1769 printf("inflating %s\n", filename);
1770 #endif
1771 inflate_file(&pbf, -1, &ze);
1773 usize = ze.usize;
1774 } else
1775 printf("We're shit outta luck!\n");
1777 } else {
1778 size = csize + (eflen > 0 ? eflen : 0);
1781 #ifdef DEBUG
1782 printf("Skipping %ld bytes\n", (long)size);
1783 #endif
1785 consume(&pbf, size);
1787 /* print out the listing */
1788 if(file_num > 0){
1789 for(j = 0; j < file_num; j++)
1790 if(strcmp(files[j], (const char *)filename) == 0){
1791 if(verbose)
1792 printf("%6d %s %s\n", usize, ascii_date, filename);
1793 else
1794 printf("%s\n", filename);
1795 break;
1797 } else {
1798 if(verbose)
1799 printf("%6d %s %s\n", usize, ascii_date, filename);
1800 else
1801 printf("%s\n", filename);
1805 return 0;
1808 int consume(pb_file *pbf, int amt){
1809 int tc = 0; /* total amount consumed */
1810 ub1 buff[RDSZ];
1811 int rdamt;
1813 #ifdef DEBUG
1814 printf("Consuming %d bytes\n", amt);
1815 #endif
1817 while(tc < amt){
1818 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1819 #ifdef DEBUG
1820 printf("got %d bytes\n", rdamt);
1821 #endif
1822 tc += rdamt;
1825 #ifdef DEBUG
1826 printf("%d bytes consumed\n", tc);
1827 #endif
1829 return 0;
1832 void usage(const char *filename){
1833 fprintf(stderr, "\
1834 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1835 Options\n\
1836 -c create new archive\n\
1837 -t list table of contents for archive\n\
1838 -x extract named (or all) files from archive\n\
1839 ", filename);
1840 fprintf(stderr, "\
1841 -u update existing archive\n\
1842 -V display version information\n\
1843 -v generate verbose output on standard output\n\
1844 -f specify archive file name\n\
1845 -m include manifest information from specified manifest file\n\
1846 -0 store only; use no ZIP compression\n\
1847 -M Do not create a manifest file for the entries\n\
1848 -C change to the specified directory and include the following file\n\
1849 -E don't include the files found in a directory\n\
1850 -@ Read names from stdin\n\
1852 fprintf(stderr, "\
1853 If any file is a directory then it is processed recursively.\n\
1854 The manifest file name and the archive file name needs to be specified\n\
1855 in the same order the 'm' and 'f' flags are specified.\n\
1857 Example 1: to archive two class files into an archive called classes.jar: \n\
1858 jar cvf classes.jar Foo.class Bar.class \n\
1859 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1860 files in the foo/ directory into 'classes.jar': \n\
1861 jar cvfm classes.jar mymanifest -C foo/ .\n\
1864 exit(1);
1867 static char *
1868 jt_strdup(s)
1869 char *s;
1871 char *result = (char*)malloc(strlen(s) + 1);
1872 if (result == (char*)0)
1873 return (char*)0;
1874 strcpy(result, s);
1875 return result;