* trans.c (tree_transform): Adjust call of expand_start_stmt_expr.
[official-gcc.git] / fastjar / jartool.c
blobd4d15e9dae3bc6c5da5b08c4ef94165fb6b17fc5
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.8 2001/08/29 01:35:31 apbianco Exp $
22 $Log: jartool.c,v $
23 Revision 1.8 2001/08/29 01:35:31 apbianco
24 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
26 * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
27 Fixes PR java/3949.
29 (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
31 Revision 1.7 2001/08/27 23:09:37 tromey
32 * jartool.c (jarfile): Remove length limitation.
33 (main): Use jt_strdup when initializing jarfile.
35 Revision 1.6 2001/07/04 18:33:53 tromey
36 Modified from patch by Julian Hall <jules@acris.co.uk>:
37 * jartool.c (errno): Conditionally declare.
38 (O_BINARY): Conditionally define.
39 (main): Use open, not creat. Use O_BINARY everywhere.
40 (make_manifest): Use O_BINARY.
41 (add_to_jar): Likewise.
43 Revision 1.5 2001/05/03 21:40:47 danglin
44 * jartool.c (jt_strdup): New function.
45 (get_next_arg): Use jt_strdup instead of strdup.
47 Revision 1.4 2000/12/28 21:47:37 robertl
48 2000-12-28 Robert Lipe <robertl@sco.com>
50 * jartool.c (MAXPATHLEN): Provide if not defined.
52 Revision 1.3 2000/12/14 18:45:35 ghazi
53 Warning fixes:
55 * compress.c: Include stdlib.h and compress.h.
56 (rcsid): Delete.
57 (report_str_error): Make static.
58 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
59 (hrd_inflate_str): Likewise.
61 * compress.h (init_compression, end_compression, init_inflation,
62 end_inflation): Prototype void arguments.
64 * dostime.c (rcsid): Delete.
66 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
67 Make functions static. Cast ctype function argument to `unsigned
68 char'. Add parens in if-stmts. Constify.
69 (Usage): Change into a macro.
70 (jargrep): Remove unused parameter.
72 * jartool.c: Constify. Add parens in if-stmts. Align
73 signed/unsigned char pointers in functions calls using casts.
74 (rcsid): Delete.
75 (list_jar): Fix printf format specifier.
76 (usage): Chop long string into bits. Reformat.
78 * pushback.c (rcsid): Delete.
80 Revision 1.2 2000/12/13 18:11:57 tromey
81 * jartool.c (extract_jar): Use strchr, not index.
83 Revision 1.1 2000/12/09 03:08:23 apbianco
84 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
86 * fastjar: Imported.
88 Revision 1.5 2000/08/24 15:01:27 cory
89 Made certain that fastjar opened the jar file before trying to update it
90 with the -u option.
92 Revision 1.4 2000/08/24 13:39:21 cory
93 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
94 when byte swapping. Better safe than sorry.
96 Revision 1.3 2000/08/23 19:42:17 cory
97 Added support for more Unix platforms. The following code has been hacked
98 to work on AIX, Solaris, True 64, and HP-UX.
99 Added bigendian check. Probably works on most big and little endian platforms
100 now.
102 Revision 1.2 1999/12/06 07:38:28 toast
103 fixed recursive archiving bug
105 Revision 1.1.1.1 1999/12/06 03:09:34 toast
106 initial checkin..
110 Revision 1.22 1999/10/12 19:45:13 burnsbr
111 adding patch to fix compat problem
113 Revision 1.21 1999/05/10 09:15:49 burnsbr
114 fixed manifest file version info
116 Revision 1.20 1999/05/10 08:53:16 burnsbr
117 *** empty log message ***
119 Revision 1.19 1999/05/10 08:30:39 burnsbr
120 added extract / listing code
122 Revision 1.18 1999/04/28 04:24:29 burnsbr
123 updated version
125 Revision 1.17 1999/04/28 04:21:23 burnsbr
126 added support for -C dir-changing flag.. Updated total compression display
128 Revision 1.16 1999/04/27 10:28:22 burnsbr
129 updated version string
131 Revision 1.15 1999/04/27 10:04:06 burnsbr
132 configure support
134 Revision 1.14 1999/04/27 08:56:14 burnsbr
135 added -V flag, better error messages
137 Revision 1.13 1999/04/26 02:35:21 burnsbr
138 changed all sorts of stuff.. compression now works 100%
140 Revision 1.12 1999/04/23 12:00:45 burnsbr
141 90% done with compression code
143 Revision 1.11 1999/04/22 04:12:57 burnsbr
144 finished first round of Manifest file support..
145 might need to do more, digest etc..
147 Revision 1.10 1999/04/22 02:35:23 burnsbr
148 added more manifest support, about 75% done now. Replaced all the
149 redundant shifts and bit-logic with a macro or two, making the code
150 easier to read.
152 Revision 1.9 1999/04/21 09:55:16 burnsbr
153 pulled out printfs
155 Revision 1.8 1999/04/21 02:58:01 burnsbr
156 started manifest code
158 Revision 1.7 1999/04/20 23:15:28 burnsbr
159 added patch sent by John Bley <jbb6@acpub.duke.edu>
161 Revision 1.6 1999/04/20 08:56:02 burnsbr
162 added GPL comment
164 Revision 1.5 1999/04/20 08:16:09 burnsbr
165 fixed verbose flag, did some optimization
167 Revision 1.4 1999/04/20 05:09:59 burnsbr
168 added rcsid variable
170 Revision 1.3 1999/04/20 05:08:54 burnsbr
171 fixed Log statement
175 #include "config.h"
177 #include <zlib.h>
179 #ifdef STDC_HEADERS
180 #include <stdlib.h>
181 #endif
183 #ifdef HAVE_UNISTD_H
184 #include <unistd.h>
185 #endif
187 #include <stdio.h>
188 #include <sys/stat.h>
189 #include <sys/types.h>
191 #ifdef HAVE_SYS_PARAM_H
192 #include <sys/param.h>
193 #endif
195 #ifndef MAXPATHLEN
196 #define MAXPATHLEN 1024
197 #endif
199 #ifdef HAVE_DIRENT_H
200 #include <dirent.h>
201 #endif
203 #ifdef HAVE_FCNTL_H
204 #include <fcntl.h>
205 #endif
207 #include <string.h>
208 #include <errno.h>
210 #ifdef TM_IN_SYS_TIME
211 #include <sys/time.h>
212 #else
213 #include <time.h>
214 #endif
216 #include "jartool.h"
217 #include "zipfile.h"
218 #include "dostime.h"
219 #include "pushback.h"
220 #include "compress.h"
222 #ifdef WORDS_BIGENDIAN
224 #define L2BI(l) ((l & 0xff000000) >> 24) | \
225 ((l & 0x00ff0000) >> 8) | \
226 ((l & 0x0000ff00) << 8) | \
227 ((l & 0x000000ff) << 24);
229 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
231 #endif
233 static char version_string[] = VERSION;
235 #ifndef errno
236 extern int errno;
237 #endif
239 #ifndef O_BINARY
240 #define O_BINARY 0
241 #endif
243 void usage(const char*);
244 void add_entry(struct zipentry *);
245 void init_headers(void);
247 int consume(pb_file *, int);
248 int list_jar(int, char**, int);
249 int extract_jar(int, char**, int);
250 int add_file_to_jar(int, int, const char*, struct stat*);
251 int add_to_jar(int, const char*, const char*);
252 int create_central_header(int);
253 int make_manifest(int, const char*);
254 static void init_args(char **, int);
255 static char *get_next_arg (void);
256 static char *jt_strdup (char*);
258 /* global variables */
259 ub1 file_header[30];
260 ub1 data_descriptor[16];
261 int do_compress;
262 int seekable;
263 int verbose;
264 char *jarfile;
266 /* If non zero, then don't recurse in directory. Instead, add the
267 directory entry and relie on an explicit list of files to populate
268 the archive. This option isn't supported by the original jar tool. */
269 int use_explicit_list_only;
271 /* If non zero, then read the entry names from stdin. This option
272 isn't supported by the original jar tool. */
273 int read_names_from_stdin;
275 zipentry *ziplist; /* linked list of entries */
276 zipentry *ziptail; /* tail of the linked list */
278 int number_of_entries; /* number of entries in the linked list */
280 int main(int argc, char **argv){
282 char mfile[256];
284 int action = ACTION_NONE;
285 int manifest = TRUE;
286 int manifest_file = FALSE;
287 int file = FALSE;
288 int file_first = FALSE;
290 int i, j;
291 int jarfd = -1;
293 do_compress = TRUE;
294 verbose = FALSE;
296 ziplist = NULL;
298 number_of_entries = 0;
300 if(argc < 2)
301 usage(argv[0]);
303 j = strlen(argv[1]);
305 for(i = 0; i < j; i++){
306 switch(argv[1][i]){
307 case 'c':
308 action = ACTION_CREATE;
309 break;
310 case 't':
311 action = ACTION_LIST;
312 break;
313 case 'x':
314 action = ACTION_EXTRACT;
315 break;
316 case 'u':
317 action = ACTION_UPDATE;
318 break;
319 case 'v':
320 verbose = TRUE;
321 break;
322 case 'V':
323 printf("%s\n", version_string);
324 exit(0);
325 case 'f':
326 file = TRUE;
327 if(!manifest_file)
328 file_first = TRUE;
329 else
330 file_first = FALSE;
331 break;
332 case 'm':
333 manifest_file = TRUE;
334 break;
335 case '0':
336 do_compress = FALSE;
337 break;
338 case 'M':
339 manifest = FALSE;
340 break;
341 case '-':
342 break;
343 /* The following options aren't supported by the original jar tool. */
344 case 'E':
345 use_explicit_list_only = TRUE;
346 break;
347 case '@':
348 read_names_from_stdin = TRUE;
349 break;
350 default:
351 fprintf(stderr, "Illegal option: %c\n", argv[1][i]);
352 usage(argv[0]);
356 if(action == ACTION_NONE){
357 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
358 usage(argv[0]);
361 /* Verify unsupported combinations and warn of the use of non
362 standard features */
363 if(verbose && use_explicit_list_only)
364 fprintf (stderr, "Warning: using non standard '-E' option\n");
365 if(verbose && read_names_from_stdin)
366 fprintf (stderr, "Warning: using non standard '-@' option\n");
367 if(read_names_from_stdin
368 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
369 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
370 usage(argv[0]);
373 i = 2;
375 /* get the jarfile and manifest file (if any) */
376 if(file && file_first){
377 if(i >= argc)
378 usage(argv[0]);
380 jarfile = jt_strdup (argv[i++]);
382 if(manifest_file){
383 if(i >= argc)
384 usage(argv[0]);
386 strncpy(mfile, argv[i++], 256);
389 if(file && !file_first){
390 if(i >= argc)
391 usage(argv[0]);
393 jarfile = jt_strdup (argv[i++]);
396 /* create the jarfile */
397 if(action == ACTION_CREATE){
398 if(file){
399 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
400 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
402 if(jarfd < 0){
403 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
404 perror(jarfile);
405 exit(1);
408 /* We assume that the file is seekable */
409 seekable = TRUE;
411 } else {
413 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
415 /* standard out is not seekable */
416 seekable = FALSE;
418 /* don't want our output to be part of the jar file.. figured this one
419 out the hard way.. =P */
420 verbose = FALSE;
422 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
424 if(file){
425 jarfd = open(jarfile, O_RDONLY | O_BINARY);
427 if(jarfd < 0){
428 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
429 perror(jarfile);
430 exit(1);
433 seekable = TRUE;
434 } else {
435 jarfd = STDIN_FILENO; /* jarfd is standard in */
437 /* we assume that the stream isn't seekable for safety */
438 seekable = FALSE;
442 if(action == ACTION_CREATE || action == ACTION_UPDATE){
443 const char *arg;
444 init_headers();
446 if((action == ACTION_UPDATE) && file) {
447 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
448 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
449 perror(jarfile);
450 exit(1);
454 if(do_compress)
455 init_compression();
458 /* Add the META-INF/ directory and the manifest */
459 if(manifest && manifest_file)
460 make_manifest(jarfd, mfile);
461 else if(manifest)
462 make_manifest(jarfd, NULL);
464 init_args (argv, i);
465 /* now we add the files to the archive */
466 while ((arg = get_next_arg ())){
468 if(!strcmp(arg, "-C")){
469 const char *dir_to_change = get_next_arg ();
470 const char *file_to_add = get_next_arg ();
471 if(!dir_to_change
472 || !file_to_add
473 || add_to_jar(jarfd, dir_to_change, file_to_add)){
474 printf("Error adding %s to jar archive!\n", arg);
475 exit(1);
477 } else {
478 if(add_to_jar(jarfd, NULL, arg)){
479 printf("Error adding %s to jar archive!\n", arg);
480 exit(1);
484 /* de-initialize the compression DS */
485 if(do_compress)
486 end_compression();
488 create_central_header(jarfd);
490 if (close(jarfd) != 0) {
491 fprintf(stderr, "Error closing jar archive!\n");
493 } else if(action == ACTION_LIST){
494 list_jar(jarfd, &argv[i], (argc - i));
495 } else if(action == ACTION_EXTRACT){
496 extract_jar(jarfd, &argv[i], (argc - i));
499 exit(0);
502 static int args_current_g;
503 static char **args_g;
505 static void
506 init_args(args, current)
507 char **args;
508 int current;
510 if(!read_names_from_stdin)
512 args_g = args;
513 args_current_g = current;
517 static char *
518 get_next_arg ()
520 static int reached_end = 0;
522 if (reached_end)
523 return NULL;
525 if (args_g)
527 if (!args_g [args_current_g])
529 reached_end = 1;
530 return NULL;
532 return args_g [args_current_g++];
534 else
536 /* Read the name from stdin. Delimiters are '\n' and
537 '\r'. Reading EOF indicates that we don't have anymore file
538 names characters to read. */
540 char s [MAXPATHLEN];
541 int pos = 0;
543 /* Get rid of '\n' and '\r' first. */
544 while (1)
546 int c = getc (stdin);
547 if (c == '\n' || c == '\r')
548 continue;
549 else
551 if (c == EOF)
552 return NULL;
553 ungetc (c, stdin);
554 break;
558 while (1)
560 int c = getc (stdin);
561 /* Exit when we get a delimiter or don't have any characters
562 to read */
563 if (c == '\n'|| c == '\r'|| c == EOF)
564 break;
565 s [pos++] = (char) c;
568 if (pos)
570 s [pos] = '\0';
571 return jt_strdup (s);
573 else
574 return NULL;
578 void init_headers(){
579 /* packing file header */
580 /* magic number */
581 file_header[0] = 0x50;
582 file_header[1] = 0x4b;
583 file_header[2] = 0x03;
584 file_header[3] = 0x04;
585 /* version number (Unix 1.0)*/
586 file_header[4] = 10;
587 file_header[5] = 0;
588 /* bit flag (normal deflation)*/
589 file_header[6] = 0x00;
591 file_header[7] = 0x00;
592 /* do_compression method (deflation) */
593 file_header[8] = 0;
594 file_header[9] = 0;
596 /* last mod file time (MS-DOS format) */
597 file_header[10] = 0;
598 file_header[11] = 0;
599 /* last mod file date (MS-DOS format) */
600 file_header[12] = 0;
601 file_header[13] = 0;
602 /* CRC 32 */
603 file_header[14] = 0;
604 file_header[15] = 0;
605 file_header[16] = 0;
606 file_header[17] = 0;
607 /* compressed size */
608 file_header[18] = 0;
609 file_header[19] = 0;
610 file_header[20] = 0;
611 file_header[21] = 0;
612 /* uncompressed size */
613 file_header[22] = 0;
614 file_header[23] = 0;
615 file_header[24] = 0;
616 file_header[25] = 0;
617 /* filename length */
618 file_header[26] = 0;
619 file_header[27] = 0;
620 /* extra field length */
621 file_header[28] = 0;
622 file_header[29] = 0;
624 /* Initialize the compression DS */
625 PACK_UB4(data_descriptor, 0, 0x08074b50);
629 void add_entry(struct zipentry *ze){
631 if(ziplist == NULL){
632 ziplist = ze;
633 ziptail = ziplist;
634 } else {
635 ziplist->next_entry = ze;
636 ziplist = ze;
639 number_of_entries++;
642 int make_manifest(int jfd, const char *mf_name){
643 time_t current_time;
644 int nlen; /* length of file name */
645 int mod_time; /* file modification time */
646 struct zipentry *ze;
648 nlen = 9; /* trust me on this one */
650 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
652 current_time = time(NULL);
653 if(current_time == (time_t)-1){
654 perror("time");
655 exit(1);
658 mod_time = unix2dostime(&current_time);
660 PACK_UB2(file_header, LOC_EXTRA, 0);
661 PACK_UB2(file_header, LOC_COMP, 0);
662 PACK_UB2(file_header, LOC_FNLEN, nlen);
663 PACK_UB4(file_header, LOC_MODTIME, mod_time);
665 if(verbose)
666 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
668 ze = (zipentry*)malloc(sizeof(zipentry));
669 if(ze == NULL){
670 perror("malloc");
671 exit(1);
674 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
675 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
676 strcpy(ze->filename, "META-INF/");
677 ze->filename[nlen] = '\0';
679 ze->offset = lseek(jfd, 0, SEEK_CUR);
680 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
681 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
682 ze->compressed = FALSE;
684 add_entry(ze);
686 write(jfd, file_header, 30);
687 write(jfd, "META-INF/", nlen);
689 /* if the user didn't specify an external manifest file... */
690 if(mf_name == NULL){
691 int mf_len = 37 + strlen(VERSION);
692 char *mf;
694 if((mf = (char *) malloc(mf_len + 1))) {
695 uLong crc;
697 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
699 crc = crc32(0L, Z_NULL, 0);
701 crc = crc32(crc, (const unsigned char *)mf, mf_len);
703 nlen = 20; /* once again, trust me */
705 PACK_UB2(file_header, LOC_EXTRA, 0);
706 PACK_UB2(file_header, LOC_COMP, 0);
707 PACK_UB2(file_header, LOC_FNLEN, nlen);
708 PACK_UB4(file_header, LOC_USIZE, mf_len);
710 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
712 PACK_UB4(file_header, LOC_CRC, crc);
714 if(verbose)
715 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
717 ze = (zipentry*)malloc(sizeof(zipentry));
718 if(ze == NULL){
719 perror("malloc");
720 exit(1);
723 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
724 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
725 strcpy(ze->filename, "META-INF/MANIFEST.MF");
726 ze->filename[nlen] = '\0';
728 ze->offset = lseek(jfd, 0, SEEK_CUR);
729 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
730 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
731 ze->crc = crc;
732 ze->csize = mf_len;
733 ze->usize = ze->csize;
734 ze->compressed = FALSE;
736 add_entry(ze);
738 write(jfd, file_header, 30);
739 write(jfd, "META-INF/MANIFEST.MF", nlen);
740 write(jfd, mf, mf_len);
741 free(mf);
743 else {
744 printf("malloc errror\n");
745 exit(-1);
747 } else {
748 int mfd;
749 struct stat statbuf;
751 stat(mf_name, &statbuf);
753 if(!S_ISREG(statbuf.st_mode)){
754 fprintf(stderr, "Invalid manifest file specified.\n");
755 exit(1);
758 mfd = open(mf_name, O_RDONLY | O_BINARY);
760 if(mfd < 0){
761 fprintf(stderr, "Error opening %s.\n", mf_name);
762 exit(1);
765 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
766 perror("error writing to jar");
767 exit(1);
772 return 0;
775 int add_to_jar(int fd, const char *new_dir, const char *file){
776 struct stat statbuf;
777 DIR *dir;
778 struct dirent *de;
779 zipentry *ze;
780 int stat_return;
781 char *old_dir = NULL;
783 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
784 * It fixes this:
785 * "normal" jar : org/apache/java/io/LogRecord.class
786 * fastjar : ./org/apache/java/io/LogRecord.class
787 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
788 * with both kaffe-1.0b4 and JDK.
790 while (*file=='.' && *(file+1)=='/')
791 file+=2;
793 /* If new_dir isn't null, we need to change to that directory. However,
794 we also need to return to the old directory when we're done */
795 if(new_dir != NULL){
796 old_dir = getcwd(NULL, 0);
798 if(chdir(new_dir) == -1){
799 perror(new_dir);
800 return 1;
804 if(!strcmp(file, jarfile)){
805 if(verbose)
806 printf("skipping: %s\n", file);
807 return 0; /* we don't want to add ourselves.. */
810 stat_return = stat(file, &statbuf);
812 if(stat_return == -1){
813 perror(file);
814 return 1;
815 } else if(S_ISDIR(statbuf.st_mode)){
816 char *fullname;
817 char *t_ptr;
818 int nlen;
819 unsigned long mod_time;
821 dir = opendir(file);
823 if(dir == NULL){
824 perror("opendir");
825 return 1;
828 nlen = strlen(file) + 256;
829 fullname = (char*)malloc(nlen * sizeof(char));
830 memset(fullname, 0, (nlen * sizeof(char)));
832 if(fullname == NULL){
833 fprintf(stderr, "Filename is NULL!\n");
834 return 1;
837 strcpy(fullname, file);
838 nlen = strlen(file);
840 if(fullname[nlen - 1] != '/'){
841 fullname[nlen] = '/';
842 t_ptr = (fullname + nlen + 1);
843 } else
844 t_ptr = (fullname + nlen);
847 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
849 nlen = (t_ptr - fullname);
851 mod_time = unix2dostime(&statbuf.st_mtime);
853 PACK_UB2(file_header, LOC_EXTRA, 0);
854 PACK_UB2(file_header, LOC_COMP, 0);
855 PACK_UB2(file_header, LOC_FNLEN, nlen);
856 PACK_UB4(file_header, LOC_MODTIME, mod_time);
858 if(verbose)
859 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
861 ze = (zipentry*)malloc(sizeof(zipentry));
862 if(ze == NULL){
863 perror("malloc");
864 exit(1);
867 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
868 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
869 strcpy(ze->filename, fullname);
870 ze->filename[nlen] = '\0';
872 ze->offset = lseek(fd, 0, SEEK_CUR);
873 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
874 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
875 ze->compressed = FALSE;
877 add_entry(ze);
879 write(fd, file_header, 30);
880 write(fd, fullname, nlen);
882 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
883 if(de->d_name[0] == '.')
884 continue;
885 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
886 if(verbose)
887 printf("skipping: %s\n", de->d_name);
888 continue;
891 strcpy(t_ptr, de->d_name);
893 if(add_to_jar(fd, NULL, fullname)){
894 fprintf(stderr, "Error adding file to jar!\n");
895 return 1;
899 free(fullname);
900 closedir(dir);
902 } else if(S_ISREG(statbuf.st_mode)){
903 int add_fd;
905 add_fd = open(file, O_RDONLY | O_BINARY);
906 if(add_fd < 0){
907 fprintf(stderr, "Error opening %s.\n", file);
908 return 0;
911 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
912 fprintf(stderr, "Error adding file to jar!\n");
913 return 1;
916 } else {
917 fprintf(stderr, "Illegal file specified: %s\n", file);
920 if(old_dir != NULL){
921 if(chdir(old_dir))
922 perror(old_dir);
924 free(old_dir);
927 return 0;
930 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
932 unsigned short file_name_length;
933 unsigned long mod_time;
934 ub1 rd_buff[RDSZ];
935 uLong crc = 0;
936 off_t offset = 0;
937 int rdamt;
938 struct zipentry *ze;
940 mod_time = unix2dostime(&(statbuf->st_mtime));
941 file_name_length = strlen(fname);
943 if(!seekable && !do_compress){
944 crc = crc32(0L, Z_NULL, 0);
946 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
947 crc = crc32(crc, rd_buff, rdamt);
949 lseek(ffd, 0, SEEK_SET);
952 /* data descriptor */
953 if(!seekable && do_compress){
954 PACK_UB2(file_header, LOC_EXTRA, 8);
955 } else {
956 PACK_UB2(file_header, LOC_EXTRA, 0);
959 if(do_compress){
960 PACK_UB2(file_header, LOC_COMP, 8);
961 } else {
962 PACK_UB2(file_header, LOC_COMP, 0);
965 PACK_UB4(file_header, LOC_MODTIME, mod_time);
966 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
968 if(!seekable && !do_compress){
969 PACK_UB4(file_header, LOC_CRC, crc);
970 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
971 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
972 } else
973 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
975 ze = (zipentry*)malloc(sizeof(zipentry));
976 if(ze == NULL){
977 perror("malloc");
978 exit(1);
981 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
982 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
983 strcpy(ze->filename, fname);
985 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
986 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
988 if(!seekable && !do_compress)
989 ze->crc = crc;
991 ze->csize = statbuf->st_size;
992 ze->usize = ze->csize;
993 ze->offset = lseek(jfd, 0, SEEK_CUR);
994 if(do_compress)
995 ze->compressed = TRUE;
996 else
997 ze->compressed = FALSE;
999 add_entry(ze);
1001 /* Write the local header */
1002 write(jfd, file_header, 30);
1004 /* write the file name to the zip file */
1005 write(jfd, fname, file_name_length);
1008 if(verbose){
1009 printf("adding: %s ", fname);
1010 fflush(stdout);
1013 if(do_compress){
1014 /* compress the file */
1015 compress_file(ffd, jfd, ze);
1016 } else {
1017 /* Write the contents of the file (uncompressed) to the zip file */
1018 /* calculate the CRC as we go along */
1019 ze->crc = crc32(0L, Z_NULL, 0);
1021 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1022 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1023 if(write(jfd, rd_buff, rdamt) != rdamt){
1024 perror("write");
1025 return 0;
1029 close(ffd);
1031 /* write out data descriptor */
1032 PACK_UB4(data_descriptor, 4, ze->crc);
1033 PACK_UB4(data_descriptor, 8, ze->csize);
1034 PACK_UB4(data_descriptor, 12, ze->usize);
1036 /* we need to seek back and fill the header */
1037 if(seekable){
1038 offset = (ze->csize + strlen(ze->filename) + 16);
1040 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1041 perror("lseek");
1042 exit(1);
1045 if(write(jfd, (data_descriptor + 4), 12) != 12){
1046 perror("write");
1047 return 0;
1050 offset -= 12;
1052 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1053 perror("lseek");
1054 exit(1);
1056 } else if(do_compress){
1057 /* Sun's jar tool will only allow a data descriptor if the entry is
1058 compressed, but we'll save 16 bytes/entry if we only use it when
1059 we can't seek back on the file */
1061 if(write(jfd, data_descriptor, 16) != 16){
1062 perror("write");
1063 return 0;
1067 if(verbose)
1068 printf("(in=%d) (out=%d) (%s %d%%)\n",
1069 (int)ze->usize, (int)ze->csize,
1070 (do_compress ? "deflated" : "stored"),
1071 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1073 return 0;
1076 int create_central_header(int fd){
1077 ub1 header[46];
1078 ub1 end_header[22];
1079 int start_offset;
1080 int dir_size;
1081 int *iheader;
1082 int total_in = 0, total_out = 22;
1084 zipentry *ze;
1086 iheader = (int*)header;
1088 /* magic number */
1089 header[0] = 'P';
1090 header[1] = 'K';
1091 header[2] = 1;
1092 header[3] = 2;
1093 /* version made by */
1094 header[4] = 10;
1095 header[5] = 0;
1096 /* version needed to extract */
1097 header[6] = 10;
1098 header[7] = 0;
1099 /* bit flag */
1100 header[8] = 0;
1101 header[9] = 0;
1102 /* compression method */
1103 header[10] = 0;
1104 header[11] = 0;
1105 /* file mod time */
1106 header[12] = 0;
1107 header[13] = 0;
1108 /* file mod date */
1109 header[14] = 0;
1110 header[15] = 0;
1111 /* crc 32 */
1112 header[16] = 0;
1113 header[17] = 0;
1114 header[18] = 0;
1115 header[19] = 0;
1116 /* compressed size */
1117 header[20] = 0;
1118 header[21] = 0;
1119 header[22] = 0;
1120 header[23] = 0;
1121 /* uncompressed size */
1122 header[24] = 0;
1123 header[25] = 0;
1124 header[26] = 0;
1125 header[27] = 0;
1126 /* filename length */
1127 header[28] = 0;
1128 header[29] = 0;
1129 /* extra field length */
1130 header[30] = 0;
1131 header[31] = 0;
1132 /* file comment length */
1133 header[32] = 0;
1134 header[33] = 0;
1135 /* disk number start */
1136 header[34] = 0;
1137 header[35] = 0;
1138 /* internal file attribs */
1139 header[36] = 0;
1140 header[37] = 0;
1141 /* external file attribs */
1142 header[38] = 0;
1143 header[39] = 0;
1144 header[40] = 0;
1145 header[41] = 0;
1146 /* relative offset of local header */
1147 header[42] = 0;
1148 header[43] = 0;
1149 header[44] = 0;
1150 header[45] = 0;
1152 start_offset = lseek(fd, 0, SEEK_CUR);
1154 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1156 total_in += ze->usize;
1157 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1159 if(ze->compressed){
1160 PACK_UB2(header, CEN_COMP, 8);
1161 } else {
1162 PACK_UB2(header, CEN_COMP, 0);
1165 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1166 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1167 PACK_UB4(header, CEN_CRC, ze->crc);
1168 PACK_UB4(header, CEN_CSIZE, ze->csize);
1169 PACK_UB4(header, CEN_USIZE, ze->usize);
1170 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1171 PACK_UB4(header, CEN_OFFSET, ze->offset);
1173 write(fd, header, 46);
1175 write(fd, ze->filename, strlen(ze->filename));
1178 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1180 /* magic number */
1181 end_header[0] = 0x50;
1182 end_header[1] = 0x4b;
1183 end_header[2] = 0x05;
1184 end_header[3] = 0x06;
1185 /* number of this disk */
1186 end_header[4] = 0;
1187 end_header[5] = 0;
1188 /* number of disk w/ start of central header */
1189 end_header[6] = 0;
1190 end_header[7] = 0;
1191 /* total number of entries in central dir on this disk*/
1192 PACK_UB2(end_header, 8, number_of_entries);
1193 /* total number of entries in central dir*/
1194 PACK_UB2(end_header, 10, number_of_entries);
1195 /* size of central dir. */
1196 PACK_UB4(end_header, 12, dir_size);
1197 /* offset of start of central dir */
1198 PACK_UB4(end_header, 16, start_offset);
1199 /* zipfile comment length */
1200 end_header[20] = 0;
1201 end_header[21] = 0;
1203 write(fd, end_header, 22);
1205 if(verbose)
1206 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1207 total_in,
1208 total_out,
1209 (do_compress ? "deflated" : "stored"),
1210 (int)((1 - (total_out / (float)total_in)) * 100)
1213 return 0;
1216 int extract_jar(int fd, char **files, int file_num){
1217 int rdamt;
1218 int out_a, in_a;
1219 ub4 signature;
1220 ub4 csize;
1221 ub4 crc;
1222 ub2 fnlen;
1223 ub2 eflen;
1224 ub2 flags;
1225 ub2 method;
1226 ub1 *filename = NULL;
1227 int filename_len = 0;
1228 ub4 rd_buff[RDSZ];
1229 pb_file pbf;
1230 ub1 scratch[16];
1231 zipentry ze;
1232 int f_fd;
1233 int dir;
1234 int handle;
1235 int j;
1237 init_inflation();
1239 pb_init(&pbf, fd);
1241 for(;;){
1242 f_fd = 0;
1243 crc = 0;
1244 ze.crc = 0;
1246 dir = FALSE; /* by default, the file isn't a dir */
1247 handle = TRUE; /* by default we'll extract/create the file */
1249 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1250 perror("read");
1251 break;
1254 signature = UNPACK_UB4(scratch, 0);
1256 #ifdef DEBUG
1257 printf("signature is %x\n", signature);
1258 #endif
1259 if(signature == 0x08074b50){
1260 #ifdef DEBUG
1261 printf("skipping data descriptor\n");
1262 #endif
1263 pb_read(&pbf, scratch, 12);
1264 continue;
1265 } else if(signature == 0x02014b50){
1266 #ifdef DEBUG
1267 printf("Central header reached.. we're all done!\n");
1268 #endif
1269 break;
1270 }else if(signature != 0x04034b50){
1271 printf("Ick! %#x\n", signature);
1272 break;
1275 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1276 perror("read");
1277 break;
1280 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1281 #ifdef DEBUG
1282 printf("Compressed size is %u\n", csize);
1283 #endif
1285 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1286 #ifdef DEBUG
1287 printf("Filename length is %hu\n", fnlen);
1288 #endif
1290 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1291 #ifdef DEBUG
1292 printf("Extra field length is %hu\n", eflen);
1293 #endif
1295 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1296 #ifdef DEBUG
1297 printf("Flags are %#hx\n", flags);
1298 #endif
1300 method = UNPACK_UB2(file_header, LOC_COMP);
1301 #ifdef DEBUG
1302 printf("Compression method is %#hx\n", method);
1303 #endif
1305 /* if there isn't a data descriptor */
1306 if(!(flags & 0x0008)){
1307 crc = UNPACK_UB4(file_header, LOC_CRC);
1308 #ifdef DEBUG
1309 printf("CRC is %x\n", crc);
1310 #endif
1313 if(filename_len < fnlen + 1){
1314 if(filename != NULL)
1315 free(filename);
1317 filename = malloc(sizeof(ub1) * (fnlen + 1));
1318 filename_len = fnlen + 1;
1321 pb_read(&pbf, filename, fnlen);
1322 filename[fnlen] = '\0';
1324 #ifdef DEBUG
1325 printf("filename is %s\n", filename);
1326 #endif
1328 if(file_num > 0){
1329 handle = FALSE;
1331 for(j = 0; j < file_num; j++)
1332 if(strcmp(files[j], (const char *)filename) == 0){
1333 handle = TRUE;
1334 break;
1338 if(!handle)
1339 f_fd = -1;
1341 /* OK, there is some directory information in the file. Nothing to do
1342 but ensure the directory(s) exist, and create them if they don't.
1343 What a pain! */
1344 if(strchr((const char *)filename, '/') != NULL && handle){
1345 /* Loop through all the directories in the path, (everything w/ a '/') */
1346 const ub1 *start = filename;
1347 char *tmp_buff;
1348 struct stat sbuf;
1350 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1352 for(;;){
1353 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1355 if(idx == NULL)
1356 break;
1357 else if(idx == start){
1358 start++;
1359 continue;
1361 start = idx + 1;
1363 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1364 tmp_buff[(idx - filename)] = '\0';
1366 #ifdef DEBUG
1367 printf("checking the existance of %s\n", tmp_buff);
1368 #endif
1370 if(stat(tmp_buff, &sbuf) < 0){
1371 if(errno != ENOENT){
1372 perror("stat");
1373 exit(1);
1376 } else if(S_ISDIR(sbuf.st_mode)){
1377 #ifdef DEBUG
1378 printf("Directory exists\n");
1379 #endif
1380 continue;
1381 }else {
1382 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1383 tmp_buff);
1384 exit(1);
1387 #ifdef DEBUG
1388 printf("Making directory..\n");
1389 #endif
1390 if(mkdir(tmp_buff, 0755) < 0){
1391 perror("mkdir");
1392 exit(1);
1394 if(verbose && handle)
1395 printf("%10s: %s/\n", "created", tmp_buff);
1399 /* only a directory */
1400 if(strlen((const char *)start) == 0)
1401 dir = TRUE;
1403 #ifdef DEBUG
1404 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1405 #endif
1407 /* If the entry was just a directory, don't write to file, etc */
1408 if(strlen((const char *)start) == 0)
1409 f_fd = -1;
1411 free(tmp_buff);
1414 if(f_fd != -1 && handle){
1415 f_fd = creat((const char *)filename, 00644);
1417 if(f_fd < 0){
1418 fprintf(stderr, "Error extracting JAR archive!\n");
1419 perror((const char *)filename);
1420 exit(1);
1424 if(method != 8 && flags & 0x0008){
1425 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1426 exit(1);
1429 if(method == 8 || flags & 0x0008){
1430 if(seekable)
1431 lseek(fd, eflen, SEEK_CUR);
1432 else
1433 consume(&pbf, eflen);
1435 inflate_file(&pbf, f_fd, &ze);
1436 } else {
1438 #ifdef DEBUG
1439 printf("writing stored data.. (%d bytes)\n", csize);
1440 #endif
1442 out_a = 0;
1443 in_a = csize;
1445 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1447 while(out_a < csize){
1448 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1449 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1450 perror("read");
1451 exit(1);
1454 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1456 if(f_fd >= 0)
1457 write(f_fd, rd_buff, rdamt);
1459 out_a += rdamt;
1460 in_a -= rdamt;
1462 #ifdef DEBUG
1463 printf("%d bytes written\n", out_a);
1464 #endif
1467 if(seekable)
1468 lseek(fd, eflen, SEEK_CUR);
1469 else
1470 consume(&pbf, eflen);
1473 /* if there is a data descriptor left, compare the CRC */
1474 if(flags & 0x0008){
1476 if(pb_read(&pbf, scratch, 16) != 16){
1477 perror("read");
1478 exit(1);
1481 signature = UNPACK_UB4(scratch, 0);
1483 if(signature != 0x08074b50){
1484 fprintf(stderr, "Error! Missing data descriptor!\n");
1485 exit(1);
1488 crc = UNPACK_UB4(scratch, 4);
1492 if(crc != ze.crc){
1493 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1494 ze.crc, crc);
1495 exit(1);
1498 close(f_fd);
1500 if(verbose && dir == FALSE && handle)
1501 printf("%10s: %s\n",
1502 (method == 8 ? "inflated" : "extracted"),
1503 filename);
1506 return 0;
1509 int list_jar(int fd, char **files, int file_num){
1510 int rdamt;
1511 ub4 signature;
1512 ub4 csize;
1513 ub4 usize;
1514 ub4 mdate;
1515 ub4 tmp;
1516 ub2 fnlen;
1517 ub2 eflen;
1518 ub2 clen;
1519 ub2 flags;
1520 ub2 method;
1521 ub2 cen_size;
1522 ub1 *filename = NULL;
1523 ub1 scratch[16];
1524 ub1 cen_header[46];
1525 int filename_len = 0;
1526 off_t size;
1527 int i, j;
1528 time_t tdate;
1529 struct tm *s_tm;
1530 char ascii_date[30];
1531 zipentry ze;
1533 #ifdef DEBUG
1534 printf("Listing jar file, looking for %d files\n", file_num);
1535 #endif
1537 /* This should be the start of the central-header-end section */
1538 if(seekable){
1539 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1540 perror("lseek");
1541 exit(1);
1544 if(read(fd, &tmp, sizeof(ub4)) != 4){
1545 perror("read");
1546 exit(1);
1549 #ifdef WORDS_BIGENDIAN
1550 tmp = L2BI(tmp);
1551 #endif
1553 if(tmp != 0x06054b50){
1554 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1555 exit(1);
1558 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1559 perror("lseek");
1560 exit(1);
1563 if(read(fd, &cen_size, 2) != 2){
1564 perror("read");
1565 exit(1);
1568 #ifdef WORDS_BIGENDIAN
1569 cen_size = L2BS(cen_size);
1570 #endif
1572 /* printf("%hu entries in central header\n", cen_size); */
1574 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1575 perror("lseek");
1576 exit(1);
1579 if(read(fd, &tmp, 4) != 4){
1580 perror("read");
1581 exit(1);
1584 #ifdef WORDS_BIGENDIAN
1585 tmp = L2BI(tmp);
1586 #endif
1588 /* printf("Central header offset = %d\n", tmp); */
1590 if(lseek(fd, tmp, SEEK_SET) != tmp){
1591 perror("lseek");
1592 exit(1);
1595 /* Loop through the entries in the central header */
1596 for(i = 0; i < cen_size; i++){
1598 if(read(fd, &cen_header, 46) != 46){
1599 perror("read");
1600 exit(1);
1603 signature = UNPACK_UB4(cen_header, 0);
1604 if(signature != 0x02014b50){
1605 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1606 exit(1);
1609 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1610 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1611 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1612 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1614 /* If we're providing verbose output, we need to make an ASCII
1615 * formatted version of the date. */
1616 if(verbose){
1617 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1618 tdate = dos2unixtime(mdate);
1619 s_tm = localtime(&tdate);
1620 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1623 if(filename_len < fnlen){
1624 if(filename != NULL)
1625 free(filename);
1627 filename = malloc(sizeof(ub1) * (fnlen + 1));
1628 filename_len = fnlen + 1;
1631 if(read(fd, filename, fnlen) != fnlen){
1632 perror("read");
1633 exit(1);
1635 filename[fnlen] = '\0';
1637 /* if the user specified a list of files on the command line,
1638 we'll only display those, otherwise we'll display everything */
1639 if(file_num > 0){
1640 for(j = 0; j < file_num; j++)
1641 if(strcmp(files[j], (const char *)filename) == 0){
1642 if(verbose)
1643 printf("%6d %s %s\n", usize, ascii_date, filename);
1644 else
1645 printf("%s\n", filename);
1646 break;
1648 } else {
1649 if(verbose)
1650 printf("%6d %s %s\n", usize, ascii_date, filename);
1651 else
1652 printf("%s\n", filename);
1655 size = eflen + clen;
1656 if(size > 0){
1657 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1658 perror("lseek");
1659 exit(1);
1663 } else {
1664 /* the file isn't seekable.. evil! */
1665 pb_file pbf;
1667 pb_init(&pbf, fd);
1669 init_inflation();
1671 for(;;){
1672 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1673 perror("read");
1674 break;
1677 signature = UNPACK_UB4(scratch, 0);
1679 #ifdef DEBUG
1680 printf("signature is %x\n", signature);
1681 #endif
1683 if(signature == 0x08074b50){
1684 #ifdef DEBUG
1685 printf("skipping data descriptor\n");
1686 #endif
1687 pb_read(&pbf, scratch, 12);
1688 continue;
1689 } else if(signature == 0x02014b50){
1690 #ifdef DEBUG
1691 printf("Central header reached.. we're all done!\n");
1692 #endif
1693 break;
1694 }else if(signature != 0x04034b50){
1695 #ifdef DEBUG
1696 printf("Ick! %#x\n", signature);
1697 #endif
1698 break;
1701 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1702 perror("read");
1703 break;
1706 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1707 #ifdef DEBUG
1708 printf("Compressed size is %u\n", csize);
1709 #endif
1711 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1712 #ifdef DEBUG
1713 printf("Filename length is %hu\n", fnlen);
1714 #endif
1716 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1717 #ifdef DEBUG
1718 printf("Extra field length is %hu\n", eflen);
1719 #endif
1721 method = UNPACK_UB2(file_header, LOC_COMP);
1722 #ifdef DEBUG
1723 printf("Compression method is %#hx\n", method);
1724 #endif
1726 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1727 #ifdef DEBUG
1728 printf("Flags are %#hx\n", flags);
1729 #endif
1731 usize = UNPACK_UB4(file_header, LOC_USIZE);
1733 /* If we're providing verbose output, we need to make an ASCII
1734 * formatted version of the date. */
1735 if(verbose){
1736 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1737 tdate = dos2unixtime(mdate);
1738 s_tm = localtime(&tdate);
1739 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1742 if(filename_len < fnlen){
1743 if(filename != NULL)
1744 free(filename);
1746 filename = malloc(sizeof(ub1) * (fnlen + 1));
1747 filename_len = fnlen + 1;
1750 pb_read(&pbf, filename, fnlen);
1751 filename[fnlen] = '\0';
1753 /* the header is at the end. In a JAR file, this means that the data
1754 happens to be compressed. We have no choice but to inflate the
1755 data */
1756 if(flags & 0x0008){
1758 size = eflen;
1760 if(size > 0)
1761 consume(&pbf, size);
1763 if(method == 8){
1764 #ifdef DEBUG
1765 printf("inflating %s\n", filename);
1766 #endif
1767 inflate_file(&pbf, -1, &ze);
1769 usize = ze.usize;
1770 } else
1771 printf("We're shit outta luck!\n");
1773 } else {
1774 size = csize + (eflen > 0 ? eflen : 0);
1777 #ifdef DEBUG
1778 printf("Skipping %ld bytes\n", (long)size);
1779 #endif
1781 consume(&pbf, size);
1783 /* print out the listing */
1784 if(file_num > 0){
1785 for(j = 0; j < file_num; j++)
1786 if(strcmp(files[j], (const char *)filename) == 0){
1787 if(verbose)
1788 printf("%6d %s %s\n", usize, ascii_date, filename);
1789 else
1790 printf("%s\n", filename);
1791 break;
1793 } else {
1794 if(verbose)
1795 printf("%6d %s %s\n", usize, ascii_date, filename);
1796 else
1797 printf("%s\n", filename);
1801 return 0;
1804 int consume(pb_file *pbf, int amt){
1805 int tc = 0; /* total amount consumed */
1806 ub1 buff[RDSZ];
1807 int rdamt;
1809 #ifdef DEBUG
1810 printf("Consuming %d bytes\n", amt);
1811 #endif
1813 while(tc < amt){
1814 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1815 #ifdef DEBUG
1816 printf("got %d bytes\n", rdamt);
1817 #endif
1818 tc += rdamt;
1821 #ifdef DEBUG
1822 printf("%d bytes consumed\n", tc);
1823 #endif
1825 return 0;
1828 void usage(const char *filename){
1829 fprintf(stderr, "\
1830 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1831 Options\n\
1832 -c create new archive\n\
1833 -t list table of contents for archive\n\
1834 -x extract named (or all) files from archive\n\
1835 ", filename);
1836 fprintf(stderr, "\
1837 -u update existing archive\n\
1838 -V display version information\n\
1839 -v generate verbose output on standard output\n\
1840 -f specify archive file name\n\
1841 -m include manifest information from specified manifest file\n\
1842 -0 store only; use no ZIP compression\n\
1843 -M Do not create a manifest file for the entries\n\
1844 -C change to the specified directory and include the following file\n\
1845 -E don't include the files found in a directory\n\
1846 -@ Read names from stdin\n\
1848 fprintf(stderr, "\
1849 If any file is a directory then it is processed recursively.\n\
1850 The manifest file name and the archive file name needs to be specified\n\
1851 in the same order the 'm' and 'f' flags are specified.\n\
1853 Example 1: to archive two class files into an archive called classes.jar: \n\
1854 jar cvf classes.jar Foo.class Bar.class \n\
1855 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1856 files in the foo/ directory into 'classes.jar': \n\
1857 jar cvfm classes.jar mymanifest -C foo/ .\n\
1860 exit(1);
1863 static char *
1864 jt_strdup(s)
1865 char *s;
1867 char *result = (char*)malloc(strlen(s) + 1);
1868 if (result == (char*)0)
1869 return (char*)0;
1870 strcpy(result, s);
1871 return result;