oops - omitted from previouys delta
[official-gcc.git] / fastjar / jartool.c
blob150ffdc33dbabdbc74ff43c83f8e9bb068f2806d
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.7 2001/08/27 23:09:37 tromey Exp $
22 $Log: jartool.c,v $
23 Revision 1.7 2001/08/27 23:09:37 tromey
24 * jartool.c (jarfile): Remove length limitation.
25 (main): Use jt_strdup when initializing jarfile.
27 Revision 1.6 2001/07/04 18:33:53 tromey
28 Modified from patch by Julian Hall <jules@acris.co.uk>:
29 * jartool.c (errno): Conditionally declare.
30 (O_BINARY): Conditionally define.
31 (main): Use open, not creat. Use O_BINARY everywhere.
32 (make_manifest): Use O_BINARY.
33 (add_to_jar): Likewise.
35 Revision 1.5 2001/05/03 21:40:47 danglin
36 * jartool.c (jt_strdup): New function.
37 (get_next_arg): Use jt_strdup instead of strdup.
39 Revision 1.4 2000/12/28 21:47:37 robertl
40 2000-12-28 Robert Lipe <robertl@sco.com>
42 * jartool.c (MAXPATHLEN): Provide if not defined.
44 Revision 1.3 2000/12/14 18:45:35 ghazi
45 Warning fixes:
47 * compress.c: Include stdlib.h and compress.h.
48 (rcsid): Delete.
49 (report_str_error): Make static.
50 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
51 (hrd_inflate_str): Likewise.
53 * compress.h (init_compression, end_compression, init_inflation,
54 end_inflation): Prototype void arguments.
56 * dostime.c (rcsid): Delete.
58 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
59 Make functions static. Cast ctype function argument to `unsigned
60 char'. Add parens in if-stmts. Constify.
61 (Usage): Change into a macro.
62 (jargrep): Remove unused parameter.
64 * jartool.c: Constify. Add parens in if-stmts. Align
65 signed/unsigned char pointers in functions calls using casts.
66 (rcsid): Delete.
67 (list_jar): Fix printf format specifier.
68 (usage): Chop long string into bits. Reformat.
70 * pushback.c (rcsid): Delete.
72 Revision 1.2 2000/12/13 18:11:57 tromey
73 * jartool.c (extract_jar): Use strchr, not index.
75 Revision 1.1 2000/12/09 03:08:23 apbianco
76 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
78 * fastjar: Imported.
80 Revision 1.5 2000/08/24 15:01:27 cory
81 Made certain that fastjar opened the jar file before trying to update it
82 with the -u option.
84 Revision 1.4 2000/08/24 13:39:21 cory
85 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
86 when byte swapping. Better safe than sorry.
88 Revision 1.3 2000/08/23 19:42:17 cory
89 Added support for more Unix platforms. The following code has been hacked
90 to work on AIX, Solaris, True 64, and HP-UX.
91 Added bigendian check. Probably works on most big and little endian platforms
92 now.
94 Revision 1.2 1999/12/06 07:38:28 toast
95 fixed recursive archiving bug
97 Revision 1.1.1.1 1999/12/06 03:09:34 toast
98 initial checkin..
102 Revision 1.22 1999/10/12 19:45:13 burnsbr
103 adding patch to fix compat problem
105 Revision 1.21 1999/05/10 09:15:49 burnsbr
106 fixed manifest file version info
108 Revision 1.20 1999/05/10 08:53:16 burnsbr
109 *** empty log message ***
111 Revision 1.19 1999/05/10 08:30:39 burnsbr
112 added extract / listing code
114 Revision 1.18 1999/04/28 04:24:29 burnsbr
115 updated version
117 Revision 1.17 1999/04/28 04:21:23 burnsbr
118 added support for -C dir-changing flag.. Updated total compression display
120 Revision 1.16 1999/04/27 10:28:22 burnsbr
121 updated version string
123 Revision 1.15 1999/04/27 10:04:06 burnsbr
124 configure support
126 Revision 1.14 1999/04/27 08:56:14 burnsbr
127 added -V flag, better error messages
129 Revision 1.13 1999/04/26 02:35:21 burnsbr
130 changed all sorts of stuff.. compression now works 100%
132 Revision 1.12 1999/04/23 12:00:45 burnsbr
133 90% done with compression code
135 Revision 1.11 1999/04/22 04:12:57 burnsbr
136 finished first round of Manifest file support..
137 might need to do more, digest etc..
139 Revision 1.10 1999/04/22 02:35:23 burnsbr
140 added more manifest support, about 75% done now. Replaced all the
141 redundant shifts and bit-logic with a macro or two, making the code
142 easier to read.
144 Revision 1.9 1999/04/21 09:55:16 burnsbr
145 pulled out printfs
147 Revision 1.8 1999/04/21 02:58:01 burnsbr
148 started manifest code
150 Revision 1.7 1999/04/20 23:15:28 burnsbr
151 added patch sent by John Bley <jbb6@acpub.duke.edu>
153 Revision 1.6 1999/04/20 08:56:02 burnsbr
154 added GPL comment
156 Revision 1.5 1999/04/20 08:16:09 burnsbr
157 fixed verbose flag, did some optimization
159 Revision 1.4 1999/04/20 05:09:59 burnsbr
160 added rcsid variable
162 Revision 1.3 1999/04/20 05:08:54 burnsbr
163 fixed Log statement
167 #include "config.h"
169 #include <zlib.h>
171 #ifdef STDC_HEADERS
172 #include <stdlib.h>
173 #endif
175 #ifdef HAVE_UNISTD_H
176 #include <unistd.h>
177 #endif
179 #include <stdio.h>
180 #include <sys/stat.h>
181 #include <sys/types.h>
183 #ifdef HAVE_SYS_PARAM_H
184 #include <sys/param.h>
185 #endif
187 #ifndef MAXPATHLEN
188 #define MAXPATHLEN 1024
189 #endif
191 #ifdef HAVE_DIRENT_H
192 #include <dirent.h>
193 #endif
195 #ifdef HAVE_FCNTL_H
196 #include <fcntl.h>
197 #endif
199 #include <string.h>
200 #include <errno.h>
202 #ifdef TM_IN_SYS_TIME
203 #include <sys/time.h>
204 #else
205 #include <time.h>
206 #endif
208 #include "jartool.h"
209 #include "zipfile.h"
210 #include "dostime.h"
211 #include "pushback.h"
212 #include "compress.h"
214 #ifdef WORDS_BIGENDIAN
216 #define L2BI(l) ((l & 0xff000000) >> 24) | \
217 ((l & 0x00ff0000) >> 8) | \
218 ((l & 0x0000ff00) << 8) | \
219 ((l & 0x000000ff) << 24);
221 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
223 #endif
225 static char version_string[] = VERSION;
227 #ifndef errno
228 extern int errno;
229 #endif
231 #ifndef O_BINARY
232 #define O_BINARY 0
233 #endif
235 void usage(const char*);
236 void add_entry(struct zipentry *);
237 void init_headers(void);
239 int consume(pb_file *, int);
240 int list_jar(int, char**, int);
241 int extract_jar(int, char**, int);
242 int add_file_to_jar(int, int, const char*, struct stat*);
243 int add_to_jar(int, const char*, const char*);
244 int create_central_header(int);
245 int make_manifest(int, const char*);
246 static void init_args(char **, int);
247 static char *get_next_arg (void);
248 static char *jt_strdup (char*);
250 /* global variables */
251 ub1 file_header[30];
252 ub1 data_descriptor[16];
253 int do_compress;
254 int seekable;
255 int verbose;
256 char *jarfile;
258 /* If non zero, then don't recurse in directory. Instead, add the
259 directory entry and relie on an explicit list of files to populate
260 the archive. This option isn't supported by the original jar tool. */
261 int use_explicit_list_only;
263 /* If non zero, then read the entry names from stdin. This option
264 isn't supported by the original jar tool. */
265 int read_names_from_stdin;
267 zipentry *ziplist; /* linked list of entries */
268 zipentry *ziptail; /* tail of the linked list */
270 int number_of_entries; /* number of entries in the linked list */
272 int main(int argc, char **argv){
274 char mfile[256];
276 int action = ACTION_NONE;
277 int manifest = TRUE;
278 int manifest_file = FALSE;
279 int file = FALSE;
280 int file_first = FALSE;
282 int i, j;
283 int jarfd = -1;
285 do_compress = TRUE;
286 verbose = FALSE;
288 ziplist = NULL;
290 number_of_entries = 0;
292 if(argc < 2)
293 usage(argv[0]);
295 j = strlen(argv[1]);
297 for(i = 0; i < j; i++){
298 switch(argv[1][i]){
299 case 'c':
300 action = ACTION_CREATE;
301 break;
302 case 't':
303 action = ACTION_LIST;
304 break;
305 case 'x':
306 action = ACTION_EXTRACT;
307 break;
308 case 'u':
309 action = ACTION_UPDATE;
310 break;
311 case 'v':
312 verbose = TRUE;
313 break;
314 case 'V':
315 printf("%s\n", version_string);
316 exit(0);
317 case 'f':
318 file = TRUE;
319 if(!manifest_file)
320 file_first = TRUE;
321 else
322 file_first = FALSE;
323 break;
324 case 'm':
325 manifest_file = TRUE;
326 break;
327 case '0':
328 do_compress = FALSE;
329 break;
330 case 'M':
331 manifest = FALSE;
332 break;
333 case '-':
334 break;
335 /* The following options aren't supported by the original jar tool. */
336 case 'E':
337 use_explicit_list_only = TRUE;
338 break;
339 case '@':
340 read_names_from_stdin = TRUE;
341 break;
342 default:
343 fprintf(stderr, "Illegal option: %c\n", argv[1][i]);
344 usage(argv[0]);
348 if(action == ACTION_NONE){
349 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
350 usage(argv[0]);
353 /* Verify unsupported combinations and warn of the use of non
354 standard features */
355 if(verbose && use_explicit_list_only)
356 fprintf (stderr, "Warning: using non standard '-E' option\n");
357 if(verbose && read_names_from_stdin)
358 fprintf (stderr, "Warning: using non standard '-@' option\n");
359 if(read_names_from_stdin
360 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
361 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
362 usage(argv[0]);
365 i = 2;
367 /* get the jarfile and manifest file (if any) */
368 if(file && file_first){
369 if(i >= argc)
370 usage(argv[0]);
372 jarfile = jt_strdup (argv[i++]);
374 if(manifest_file){
375 if(i >= argc)
376 usage(argv[0]);
378 strncpy(mfile, argv[i++], 256);
381 if(file && !file_first){
382 if(i >= argc)
383 usage(argv[0]);
385 jarfile = jt_strdup (argv[i++]);
388 /* create the jarfile */
389 if(action == ACTION_CREATE){
390 if(file){
391 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
392 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
394 if(jarfd < 0){
395 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
396 perror(jarfile);
397 exit(1);
400 /* We assume that the file is seekable */
401 seekable = TRUE;
403 } else {
405 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
407 /* standard out is not seekable */
408 seekable = FALSE;
410 /* don't want our output to be part of the jar file.. figured this one
411 out the hard way.. =P */
412 verbose = FALSE;
414 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
416 if(file){
417 jarfd = open(jarfile, O_RDONLY | O_BINARY);
419 if(jarfd < 0){
420 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
421 perror(jarfile);
422 exit(1);
425 seekable = TRUE;
426 } else {
427 jarfd = STDIN_FILENO; /* jarfd is standard in */
429 /* we assume that the stream isn't seekable for safety */
430 seekable = FALSE;
434 if(action == ACTION_CREATE || action == ACTION_UPDATE){
435 const char *arg;
436 init_headers();
438 if((action == ACTION_UPDATE) && file) {
439 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
440 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
441 perror(jarfile);
442 exit(1);
446 if(do_compress)
447 init_compression();
450 /* Add the META-INF/ directory and the manifest */
451 if(manifest && manifest_file)
452 make_manifest(jarfd, mfile);
453 else if(manifest)
454 make_manifest(jarfd, NULL);
456 init_args (argv, i);
457 /* now we add the files to the archive */
458 while ((arg = get_next_arg ())){
460 if(!strcmp(arg, "-C")){
461 const char *dir_to_change = get_next_arg ();
462 const char *file_to_add = get_next_arg ();
463 if(!dir_to_change
464 || !file_to_add
465 || add_to_jar(jarfd, dir_to_change, file_to_add)){
466 printf("Error adding %s to jar archive!\n", arg);
467 exit(1);
469 } else {
470 if(add_to_jar(jarfd, NULL, arg)){
471 printf("Error adding %s to jar archive!\n", arg);
472 exit(1);
476 /* de-initialize the compression DS */
477 if(do_compress)
478 end_compression();
480 create_central_header(jarfd);
482 if (close(jarfd) != 0) {
483 fprintf(stderr, "Error closing jar archive!\n");
485 } else if(action == ACTION_LIST){
486 list_jar(jarfd, &argv[i], (argc - i));
487 } else if(action == ACTION_EXTRACT){
488 extract_jar(jarfd, &argv[i], (argc - i));
491 exit(0);
494 static int args_current_g;
495 static char **args_g;
497 static void
498 init_args(args, current)
499 char **args;
500 int current;
502 if(!read_names_from_stdin)
504 args_g = args;
505 args_current_g = current;
509 static char *
510 get_next_arg ()
512 static int reached_end = 0;
514 if (reached_end)
515 return NULL;
517 if (args_g)
519 if (!args_g [args_current_g])
521 reached_end = 1;
522 return NULL;
524 return args_g [args_current_g++];
526 else
528 /* Read the name from stdin. Delimiters are '\n' and
529 '\r'. Reading EOF indicates that we don't have anymore file
530 names characters to read. */
532 char s [MAXPATHLEN];
533 int pos = 0;
535 /* Get rid of '\n' and '\r' first. */
536 while (1)
538 int c = getc (stdin);
539 if (c == '\n' || c == '\r')
540 continue;
541 else
543 if (c == EOF)
544 return NULL;
545 ungetc (c, stdin);
546 break;
550 while (1)
552 int c = getc (stdin);
553 /* Exit when we get a delimiter or don't have any characters
554 to read */
555 if (c == '\n'|| c == '\r'|| c == EOF)
556 break;
557 s [pos++] = (char) c;
560 if (pos)
562 s [pos] = '\0';
563 return jt_strdup (s);
565 else
566 return NULL;
570 void init_headers(){
571 /* packing file header */
572 /* magic number */
573 file_header[0] = 0x50;
574 file_header[1] = 0x4b;
575 file_header[2] = 0x03;
576 file_header[3] = 0x04;
577 /* version number (Unix 1.0)*/
578 file_header[4] = 10;
579 file_header[5] = 0;
580 /* bit flag (normal deflation)*/
581 file_header[6] = 0x00;
583 file_header[7] = 0x00;
584 /* do_compression method (deflation) */
585 file_header[8] = 0;
586 file_header[9] = 0;
588 /* last mod file time (MS-DOS format) */
589 file_header[10] = 0;
590 file_header[11] = 0;
591 /* last mod file date (MS-DOS format) */
592 file_header[12] = 0;
593 file_header[13] = 0;
594 /* CRC 32 */
595 file_header[14] = 0;
596 file_header[15] = 0;
597 file_header[16] = 0;
598 file_header[17] = 0;
599 /* compressed size */
600 file_header[18] = 0;
601 file_header[19] = 0;
602 file_header[20] = 0;
603 file_header[21] = 0;
604 /* uncompressed size */
605 file_header[22] = 0;
606 file_header[23] = 0;
607 file_header[24] = 0;
608 file_header[25] = 0;
609 /* filename length */
610 file_header[26] = 0;
611 file_header[27] = 0;
612 /* extra field length */
613 file_header[28] = 0;
614 file_header[29] = 0;
616 /* Initialize the compression DS */
617 PACK_UB4(data_descriptor, 0, 0x08074b50);
621 void add_entry(struct zipentry *ze){
623 if(ziplist == NULL){
624 ziplist = ze;
625 ziptail = ziplist;
626 } else {
627 ziplist->next_entry = ze;
628 ziplist = ze;
631 number_of_entries++;
634 int make_manifest(int jfd, const char *mf_name){
635 time_t current_time;
636 int nlen; /* length of file name */
637 int mod_time; /* file modification time */
638 struct zipentry *ze;
640 nlen = 9; /* trust me on this one */
642 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
644 current_time = time(NULL);
645 if(current_time == (time_t)-1){
646 perror("time");
647 exit(1);
650 mod_time = unix2dostime(&current_time);
652 PACK_UB2(file_header, LOC_EXTRA, 0);
653 PACK_UB2(file_header, LOC_COMP, 0);
654 PACK_UB2(file_header, LOC_FNLEN, nlen);
655 PACK_UB4(file_header, LOC_MODTIME, mod_time);
657 if(verbose)
658 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
660 ze = (zipentry*)malloc(sizeof(zipentry));
661 if(ze == NULL){
662 perror("malloc");
663 exit(1);
666 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
667 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
668 strcpy(ze->filename, "META-INF/");
669 ze->filename[nlen] = '\0';
671 ze->offset = lseek(jfd, 0, SEEK_CUR);
672 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
673 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
674 ze->compressed = FALSE;
676 add_entry(ze);
678 write(jfd, file_header, 30);
679 write(jfd, "META-INF/", nlen);
681 /* if the user didn't specify an external manifest file... */
682 if(mf_name == NULL){
683 int mf_len = 37 + strlen(VERSION);
684 char *mf;
686 if((mf = (char *) malloc(mf_len + 1))) {
687 uLong crc;
689 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
691 crc = crc32(0L, Z_NULL, 0);
693 crc = crc32(crc, (const unsigned char *)mf, mf_len);
695 nlen = 20; /* once again, trust me */
697 PACK_UB2(file_header, LOC_EXTRA, 0);
698 PACK_UB2(file_header, LOC_COMP, 0);
699 PACK_UB2(file_header, LOC_FNLEN, nlen);
700 PACK_UB4(file_header, LOC_USIZE, mf_len);
702 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
704 PACK_UB4(file_header, LOC_CRC, crc);
706 if(verbose)
707 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
709 ze = (zipentry*)malloc(sizeof(zipentry));
710 if(ze == NULL){
711 perror("malloc");
712 exit(1);
715 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
716 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
717 strcpy(ze->filename, "META-INF/MANIFEST.MF");
718 ze->filename[nlen] = '\0';
720 ze->offset = lseek(jfd, 0, SEEK_CUR);
721 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
722 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
723 ze->crc = crc;
724 ze->csize = mf_len;
725 ze->usize = ze->csize;
726 ze->compressed = FALSE;
728 add_entry(ze);
730 write(jfd, file_header, 30);
731 write(jfd, "META-INF/MANIFEST.MF", nlen);
732 write(jfd, mf, mf_len);
733 free(mf);
735 else {
736 printf("malloc errror\n");
737 exit(-1);
739 } else {
740 int mfd;
741 struct stat statbuf;
743 stat(mf_name, &statbuf);
745 if(!S_ISREG(statbuf.st_mode)){
746 fprintf(stderr, "Invalid manifest file specified.\n");
747 exit(1);
750 mfd = open(mf_name, O_RDONLY | O_BINARY);
752 if(mfd < 0){
753 fprintf(stderr, "Error opening %s.\n", mf_name);
754 exit(1);
757 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
758 perror("error writing to jar");
759 exit(1);
764 return 0;
767 int add_to_jar(int fd, const char *new_dir, const char *file){
768 struct stat statbuf;
769 DIR *dir;
770 struct dirent *de;
771 zipentry *ze;
772 int stat_return;
773 char *old_dir = NULL;
775 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
776 * It fixes this:
777 * "normal" jar : org/apache/java/io/LogRecord.class
778 * fastjar : ./org/apache/java/io/LogRecord.class
779 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
780 * with both kaffe-1.0b4 and JDK.
782 while (*file=='.' && *(file+1)=='/')
783 file+=2;
785 /* If new_dir isn't null, we need to change to that directory. However,
786 we also need to return to the old directory when we're done */
787 if(new_dir != NULL){
788 old_dir = getcwd(NULL, 0);
790 if(chdir(new_dir) == -1){
791 perror(new_dir);
792 return 1;
796 if(!strcmp(file, jarfile)){
797 if(verbose)
798 printf("skipping: %s\n", file);
799 return 0; /* we don't want to add ourselves.. */
802 stat_return = stat(file, &statbuf);
804 if(stat_return == -1){
805 perror(file);
806 return 1;
807 } else if(S_ISDIR(statbuf.st_mode)){
808 char *fullname;
809 char *t_ptr;
810 int nlen;
811 unsigned long mod_time;
813 dir = opendir(file);
815 if(dir == NULL){
816 perror("opendir");
817 return 1;
820 nlen = strlen(file) + 256;
821 fullname = (char*)malloc(nlen * sizeof(char));
822 memset(fullname, 0, (nlen * sizeof(char)));
824 if(fullname == NULL){
825 fprintf(stderr, "Filename is NULL!\n");
826 return 1;
829 strcpy(fullname, file);
830 nlen = strlen(file);
832 if(fullname[nlen - 1] != '/'){
833 fullname[nlen] = '/';
834 t_ptr = (fullname + nlen + 1);
835 } else
836 t_ptr = (fullname + nlen);
839 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
841 nlen = (t_ptr - fullname);
843 mod_time = unix2dostime(&statbuf.st_mtime);
845 PACK_UB2(file_header, LOC_EXTRA, 0);
846 PACK_UB2(file_header, LOC_COMP, 0);
847 PACK_UB2(file_header, LOC_FNLEN, nlen);
848 PACK_UB4(file_header, LOC_MODTIME, mod_time);
850 if(verbose)
851 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
853 ze = (zipentry*)malloc(sizeof(zipentry));
854 if(ze == NULL){
855 perror("malloc");
856 exit(1);
859 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
860 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
861 strcpy(ze->filename, fullname);
862 ze->filename[nlen] = '\0';
864 ze->offset = lseek(fd, 0, SEEK_CUR);
865 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
866 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
867 ze->compressed = FALSE;
869 add_entry(ze);
871 write(fd, file_header, 30);
872 write(fd, fullname, nlen);
874 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
875 if(de->d_name[0] == '.')
876 continue;
877 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
878 if(verbose)
879 printf("skipping: %s\n", de->d_name);
880 continue;
883 strcpy(t_ptr, de->d_name);
885 if(add_to_jar(fd, NULL, fullname)){
886 fprintf(stderr, "Error adding file to jar!\n");
887 return 1;
891 free(fullname);
892 closedir(dir);
894 } else if(S_ISREG(statbuf.st_mode)){
895 int add_fd;
897 add_fd = open(file, O_RDONLY | O_BINARY);
898 if(add_fd < 0){
899 fprintf(stderr, "Error opening %s.\n", file);
900 return 0;
903 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
904 fprintf(stderr, "Error adding file to jar!\n");
905 return 1;
908 } else {
909 fprintf(stderr, "Illegal file specified: %s\n", file);
912 if(old_dir != NULL){
913 if(chdir(old_dir))
914 perror(old_dir);
916 free(old_dir);
919 return 0;
922 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
924 unsigned short file_name_length;
925 unsigned long mod_time;
926 ub1 rd_buff[RDSZ];
927 uLong crc = 0;
928 off_t offset = 0;
929 int rdamt;
930 struct zipentry *ze;
932 mod_time = unix2dostime(&(statbuf->st_mtime));
933 file_name_length = strlen(fname);
935 if(!seekable && !do_compress){
936 crc = crc32(0L, Z_NULL, 0);
938 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
939 crc = crc32(crc, rd_buff, rdamt);
941 lseek(ffd, 0, SEEK_SET);
944 /* data descriptor */
945 if(!seekable && do_compress){
946 PACK_UB2(file_header, LOC_EXTRA, 8);
947 } else {
948 PACK_UB2(file_header, LOC_EXTRA, 0);
951 if(do_compress){
952 PACK_UB2(file_header, LOC_COMP, 8);
953 } else {
954 PACK_UB2(file_header, LOC_COMP, 0);
957 PACK_UB4(file_header, LOC_MODTIME, mod_time);
958 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
960 if(!seekable && !do_compress){
961 PACK_UB4(file_header, LOC_CRC, crc);
962 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
963 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
964 } else
965 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
967 ze = (zipentry*)malloc(sizeof(zipentry));
968 if(ze == NULL){
969 perror("malloc");
970 exit(1);
973 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
974 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
975 strcpy(ze->filename, fname);
977 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
978 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
980 if(!seekable && !do_compress)
981 ze->crc = crc;
983 ze->csize = statbuf->st_size;
984 ze->usize = ze->csize;
985 ze->offset = lseek(jfd, 0, SEEK_CUR);
986 if(do_compress)
987 ze->compressed = TRUE;
988 else
989 ze->compressed = FALSE;
991 add_entry(ze);
993 /* Write the local header */
994 write(jfd, file_header, 30);
996 /* write the file name to the zip file */
997 write(jfd, fname, file_name_length);
1000 if(verbose){
1001 printf("adding: %s ", fname);
1002 fflush(stdout);
1005 if(do_compress){
1006 /* compress the file */
1007 compress_file(ffd, jfd, ze);
1008 } else {
1009 /* Write the contents of the file (uncompressed) to the zip file */
1010 /* calculate the CRC as we go along */
1011 ze->crc = crc32(0L, Z_NULL, 0);
1013 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1014 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1015 if(write(jfd, rd_buff, rdamt) != rdamt){
1016 perror("write");
1017 return 0;
1021 close(ffd);
1023 /* write out data descriptor */
1024 PACK_UB4(data_descriptor, 4, ze->crc);
1025 PACK_UB4(data_descriptor, 8, ze->csize);
1026 PACK_UB4(data_descriptor, 12, ze->usize);
1028 /* we need to seek back and fill the header */
1029 if(seekable){
1030 offset = (ze->csize + strlen(ze->filename) + 16);
1032 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1033 perror("lseek");
1034 exit(1);
1037 if(write(jfd, (data_descriptor + 4), 12) != 12){
1038 perror("write");
1039 return 0;
1042 offset -= 12;
1044 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1045 perror("lseek");
1046 exit(1);
1048 } else if(do_compress){
1049 /* Sun's jar tool will only allow a data descriptor if the entry is
1050 compressed, but we'll save 16 bytes/entry if we only use it when
1051 we can't seek back on the file */
1053 if(write(jfd, data_descriptor, 16) != 16){
1054 perror("write");
1055 return 0;
1059 if(verbose)
1060 printf("(in=%d) (out=%d) (%s %d%%)\n",
1061 (int)ze->usize, (int)ze->csize,
1062 (do_compress ? "deflated" : "stored"),
1063 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1065 return 0;
1068 int create_central_header(int fd){
1069 ub1 header[46];
1070 ub1 end_header[22];
1071 int start_offset;
1072 int dir_size;
1073 int *iheader;
1074 int total_in = 0, total_out = 22;
1076 zipentry *ze;
1078 iheader = (int*)header;
1080 /* magic number */
1081 header[0] = 'P';
1082 header[1] = 'K';
1083 header[2] = 1;
1084 header[3] = 2;
1085 /* version made by */
1086 header[4] = 10;
1087 header[5] = 0;
1088 /* version needed to extract */
1089 header[6] = 10;
1090 header[7] = 0;
1091 /* bit flag */
1092 header[8] = 0;
1093 header[9] = 0;
1094 /* compression method */
1095 header[10] = 0;
1096 header[11] = 0;
1097 /* file mod time */
1098 header[12] = 0;
1099 header[13] = 0;
1100 /* file mod date */
1101 header[14] = 0;
1102 header[15] = 0;
1103 /* crc 32 */
1104 header[16] = 0;
1105 header[17] = 0;
1106 header[18] = 0;
1107 header[19] = 0;
1108 /* compressed size */
1109 header[20] = 0;
1110 header[21] = 0;
1111 header[22] = 0;
1112 header[23] = 0;
1113 /* uncompressed size */
1114 header[24] = 0;
1115 header[25] = 0;
1116 header[26] = 0;
1117 header[27] = 0;
1118 /* filename length */
1119 header[28] = 0;
1120 header[29] = 0;
1121 /* extra field length */
1122 header[30] = 0;
1123 header[31] = 0;
1124 /* file comment length */
1125 header[32] = 0;
1126 header[33] = 0;
1127 /* disk number start */
1128 header[34] = 0;
1129 header[35] = 0;
1130 /* internal file attribs */
1131 header[36] = 0;
1132 header[37] = 0;
1133 /* external file attribs */
1134 header[38] = 0;
1135 header[39] = 0;
1136 header[40] = 0;
1137 header[41] = 0;
1138 /* relative offset of local header */
1139 header[42] = 0;
1140 header[43] = 0;
1141 header[44] = 0;
1142 header[45] = 0;
1144 start_offset = lseek(fd, 0, SEEK_CUR);
1146 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1148 total_in += ze->usize;
1149 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1151 if(ze->compressed){
1152 PACK_UB2(header, CEN_COMP, 8);
1153 } else {
1154 PACK_UB2(header, CEN_COMP, 0);
1157 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1158 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1159 PACK_UB4(header, CEN_CRC, ze->crc);
1160 PACK_UB4(header, CEN_CSIZE, ze->csize);
1161 PACK_UB4(header, CEN_USIZE, ze->usize);
1162 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1163 PACK_UB4(header, CEN_OFFSET, ze->offset);
1165 write(fd, header, 46);
1167 write(fd, ze->filename, strlen(ze->filename));
1170 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1172 /* magic number */
1173 end_header[0] = 0x50;
1174 end_header[1] = 0x4b;
1175 end_header[2] = 0x05;
1176 end_header[3] = 0x06;
1177 /* number of this disk */
1178 end_header[4] = 0;
1179 end_header[5] = 0;
1180 /* number of disk w/ start of central header */
1181 end_header[6] = 0;
1182 end_header[7] = 0;
1183 /* total number of entries in central dir on this disk*/
1184 PACK_UB2(end_header, 8, number_of_entries);
1185 /* total number of entries in central dir*/
1186 PACK_UB2(end_header, 10, number_of_entries);
1187 /* size of central dir. */
1188 PACK_UB4(end_header, 12, dir_size);
1189 /* offset of start of central dir */
1190 PACK_UB4(end_header, 16, start_offset);
1191 /* zipfile comment length */
1192 end_header[20] = 0;
1193 end_header[21] = 0;
1195 write(fd, end_header, 22);
1197 if(verbose)
1198 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1199 total_in,
1200 total_out,
1201 (do_compress ? "deflated" : "stored"),
1202 (int)((1 - (total_out / (float)total_in)) * 100)
1205 return 0;
1208 int extract_jar(int fd, char **files, int file_num){
1209 int rdamt;
1210 int out_a, in_a;
1211 ub4 signature;
1212 ub4 csize;
1213 ub4 crc;
1214 ub2 fnlen;
1215 ub2 eflen;
1216 ub2 flags;
1217 ub2 method;
1218 ub1 *filename = NULL;
1219 int filename_len = 0;
1220 ub4 rd_buff[RDSZ];
1221 pb_file pbf;
1222 ub1 scratch[16];
1223 zipentry ze;
1224 int f_fd;
1225 int dir;
1226 int handle;
1227 int j;
1229 init_inflation();
1231 pb_init(&pbf, fd);
1233 for(;;){
1234 f_fd = 0;
1235 crc = 0;
1236 ze.crc = 0;
1238 dir = FALSE; /* by default, the file isn't a dir */
1239 handle = TRUE; /* by default we'll extract/create the file */
1241 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1242 perror("read");
1243 break;
1246 signature = UNPACK_UB4(scratch, 0);
1248 #ifdef DEBUG
1249 printf("signature is %x\n", signature);
1250 #endif
1251 if(signature == 0x08074b50){
1252 #ifdef DEBUG
1253 printf("skipping data descriptor\n");
1254 #endif
1255 pb_read(&pbf, scratch, 12);
1256 continue;
1257 } else if(signature == 0x02014b50){
1258 #ifdef DEBUG
1259 printf("Central header reached.. we're all done!\n");
1260 #endif
1261 break;
1262 }else if(signature != 0x04034b50){
1263 printf("Ick! %#x\n", signature);
1264 break;
1267 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1268 perror("read");
1269 break;
1272 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1273 #ifdef DEBUG
1274 printf("Compressed size is %u\n", csize);
1275 #endif
1277 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1278 #ifdef DEBUG
1279 printf("Filename length is %hu\n", fnlen);
1280 #endif
1282 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1283 #ifdef DEBUG
1284 printf("Extra field length is %hu\n", eflen);
1285 #endif
1287 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1288 #ifdef DEBUG
1289 printf("Flags are %#hx\n", flags);
1290 #endif
1292 method = UNPACK_UB2(file_header, LOC_COMP);
1293 #ifdef DEBUG
1294 printf("Compression method is %#hx\n", method);
1295 #endif
1297 /* if there isn't a data descriptor */
1298 if(!(flags & 0x0008)){
1299 crc = UNPACK_UB4(file_header, LOC_CRC);
1300 #ifdef DEBUG
1301 printf("CRC is %x\n", crc);
1302 #endif
1305 if(filename_len < fnlen){
1306 if(filename != NULL)
1307 free(filename);
1309 filename = malloc(sizeof(ub1) * (fnlen + 1));
1310 filename_len = fnlen + 1;
1313 pb_read(&pbf, filename, fnlen);
1314 filename[fnlen] = '\0';
1316 #ifdef DEBUG
1317 printf("filename is %s\n", filename);
1318 #endif
1320 if(file_num > 0){
1321 handle = FALSE;
1323 for(j = 0; j < file_num; j++)
1324 if(strcmp(files[j], (const char *)filename) == 0){
1325 handle = TRUE;
1326 break;
1330 if(!handle)
1331 f_fd = -1;
1333 /* OK, there is some directory information in the file. Nothing to do
1334 but ensure the directory(s) exist, and create them if they don't.
1335 What a pain! */
1336 if(strchr((const char *)filename, '/') != NULL && handle){
1337 /* Loop through all the directories in the path, (everything w/ a '/') */
1338 const ub1 *start = filename;
1339 char *tmp_buff;
1340 struct stat sbuf;
1342 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1344 for(;;){
1345 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1347 if(idx == NULL)
1348 break;
1349 else if(idx == start){
1350 start++;
1351 continue;
1353 start = idx + 1;
1355 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1356 tmp_buff[(idx - filename)] = '\0';
1358 #ifdef DEBUG
1359 printf("checking the existance of %s\n", tmp_buff);
1360 #endif
1362 if(stat(tmp_buff, &sbuf) < 0){
1363 if(errno != ENOENT){
1364 perror("stat");
1365 exit(1);
1368 } else if(S_ISDIR(sbuf.st_mode)){
1369 #ifdef DEBUG
1370 printf("Directory exists\n");
1371 #endif
1372 continue;
1373 }else {
1374 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1375 tmp_buff);
1376 exit(1);
1379 #ifdef DEBUG
1380 printf("Making directory..\n");
1381 #endif
1382 if(mkdir(tmp_buff, 0755) < 0){
1383 perror("mkdir");
1384 exit(1);
1386 if(verbose && handle)
1387 printf("%10s: %s/\n", "created", tmp_buff);
1391 /* only a directory */
1392 if(strlen((const char *)start) == 0)
1393 dir = TRUE;
1395 #ifdef DEBUG
1396 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1397 #endif
1399 /* If the entry was just a directory, don't write to file, etc */
1400 if(strlen((const char *)start) == 0)
1401 f_fd = -1;
1403 free(tmp_buff);
1406 if(f_fd != -1 && handle){
1407 f_fd = creat((const char *)filename, 00644);
1409 if(f_fd < 0){
1410 fprintf(stderr, "Error extracting JAR archive!\n");
1411 perror((const char *)filename);
1412 exit(1);
1416 if(method != 8 && flags & 0x0008){
1417 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1418 exit(1);
1421 if(method == 8 || flags & 0x0008){
1422 if(seekable)
1423 lseek(fd, eflen, SEEK_CUR);
1424 else
1425 consume(&pbf, eflen);
1427 inflate_file(&pbf, f_fd, &ze);
1428 } else {
1430 #ifdef DEBUG
1431 printf("writing stored data.. (%d bytes)\n", csize);
1432 #endif
1434 out_a = 0;
1435 in_a = csize;
1437 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1439 while(out_a < csize){
1440 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1441 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1442 perror("read");
1443 exit(1);
1446 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1448 if(f_fd >= 0)
1449 write(f_fd, rd_buff, rdamt);
1451 out_a += rdamt;
1452 in_a -= rdamt;
1454 #ifdef DEBUG
1455 printf("%d bytes written\n", out_a);
1456 #endif
1459 if(seekable)
1460 lseek(fd, eflen, SEEK_CUR);
1461 else
1462 consume(&pbf, eflen);
1465 /* if there is a data descriptor left, compare the CRC */
1466 if(flags & 0x0008){
1468 if(pb_read(&pbf, scratch, 16) != 16){
1469 perror("read");
1470 exit(1);
1473 signature = UNPACK_UB4(scratch, 0);
1475 if(signature != 0x08074b50){
1476 fprintf(stderr, "Error! Missing data descriptor!\n");
1477 exit(1);
1480 crc = UNPACK_UB4(scratch, 4);
1484 if(crc != ze.crc){
1485 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1486 ze.crc, crc);
1487 exit(1);
1490 close(f_fd);
1492 if(verbose && dir == FALSE && handle)
1493 printf("%10s: %s\n",
1494 (method == 8 ? "inflated" : "extracted"),
1495 filename);
1498 return 0;
1501 int list_jar(int fd, char **files, int file_num){
1502 int rdamt;
1503 ub4 signature;
1504 ub4 csize;
1505 ub4 usize;
1506 ub4 mdate;
1507 ub4 tmp;
1508 ub2 fnlen;
1509 ub2 eflen;
1510 ub2 clen;
1511 ub2 flags;
1512 ub2 method;
1513 ub2 cen_size;
1514 ub1 *filename = NULL;
1515 ub1 scratch[16];
1516 ub1 cen_header[46];
1517 int filename_len = 0;
1518 off_t size;
1519 int i, j;
1520 time_t tdate;
1521 struct tm *s_tm;
1522 char ascii_date[30];
1523 zipentry ze;
1525 #ifdef DEBUG
1526 printf("Listing jar file, looking for %d files\n", file_num);
1527 #endif
1529 /* This should be the start of the central-header-end section */
1530 if(seekable){
1531 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1532 perror("lseek");
1533 exit(1);
1536 if(read(fd, &tmp, sizeof(ub4)) != 4){
1537 perror("read");
1538 exit(1);
1541 #ifdef WORDS_BIGENDIAN
1542 tmp = L2BI(tmp);
1543 #endif
1545 if(tmp != 0x06054b50){
1546 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1547 exit(1);
1550 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1551 perror("lseek");
1552 exit(1);
1555 if(read(fd, &cen_size, 2) != 2){
1556 perror("read");
1557 exit(1);
1560 #ifdef WORDS_BIGENDIAN
1561 cen_size = L2BS(cen_size);
1562 #endif
1564 /* printf("%hu entries in central header\n", cen_size); */
1566 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1567 perror("lseek");
1568 exit(1);
1571 if(read(fd, &tmp, 4) != 4){
1572 perror("read");
1573 exit(1);
1576 #ifdef WORDS_BIGENDIAN
1577 tmp = L2BI(tmp);
1578 #endif
1580 /* printf("Central header offset = %d\n", tmp); */
1582 if(lseek(fd, tmp, SEEK_SET) != tmp){
1583 perror("lseek");
1584 exit(1);
1587 /* Loop through the entries in the central header */
1588 for(i = 0; i < cen_size; i++){
1590 if(read(fd, &cen_header, 46) != 46){
1591 perror("read");
1592 exit(1);
1595 signature = UNPACK_UB4(cen_header, 0);
1596 if(signature != 0x02014b50){
1597 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1598 exit(1);
1601 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1602 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1603 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1604 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1606 /* If we're providing verbose output, we need to make an ASCII
1607 * formatted version of the date. */
1608 if(verbose){
1609 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1610 tdate = dos2unixtime(mdate);
1611 s_tm = localtime(&tdate);
1612 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1615 if(filename_len < fnlen){
1616 if(filename != NULL)
1617 free(filename);
1619 filename = malloc(sizeof(ub1) * (fnlen + 1));
1620 filename_len = fnlen + 1;
1623 if(read(fd, filename, fnlen) != fnlen){
1624 perror("read");
1625 exit(1);
1627 filename[fnlen] = '\0';
1629 /* if the user specified a list of files on the command line,
1630 we'll only display those, otherwise we'll display everything */
1631 if(file_num > 0){
1632 for(j = 0; j < file_num; j++)
1633 if(strcmp(files[j], (const char *)filename) == 0){
1634 if(verbose)
1635 printf("%6d %s %s\n", usize, ascii_date, filename);
1636 else
1637 printf("%s\n", filename);
1638 break;
1640 } else {
1641 if(verbose)
1642 printf("%6d %s %s\n", usize, ascii_date, filename);
1643 else
1644 printf("%s\n", filename);
1647 size = eflen + clen;
1648 if(size > 0){
1649 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1650 perror("lseek");
1651 exit(1);
1655 } else {
1656 /* the file isn't seekable.. evil! */
1657 pb_file pbf;
1659 pb_init(&pbf, fd);
1661 init_inflation();
1663 for(;;){
1664 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1665 perror("read");
1666 break;
1669 signature = UNPACK_UB4(scratch, 0);
1671 #ifdef DEBUG
1672 printf("signature is %x\n", signature);
1673 #endif
1675 if(signature == 0x08074b50){
1676 #ifdef DEBUG
1677 printf("skipping data descriptor\n");
1678 #endif
1679 pb_read(&pbf, scratch, 12);
1680 continue;
1681 } else if(signature == 0x02014b50){
1682 #ifdef DEBUG
1683 printf("Central header reached.. we're all done!\n");
1684 #endif
1685 break;
1686 }else if(signature != 0x04034b50){
1687 #ifdef DEBUG
1688 printf("Ick! %#x\n", signature);
1689 #endif
1690 break;
1693 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1694 perror("read");
1695 break;
1698 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1699 #ifdef DEBUG
1700 printf("Compressed size is %u\n", csize);
1701 #endif
1703 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1704 #ifdef DEBUG
1705 printf("Filename length is %hu\n", fnlen);
1706 #endif
1708 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1709 #ifdef DEBUG
1710 printf("Extra field length is %hu\n", eflen);
1711 #endif
1713 method = UNPACK_UB2(file_header, LOC_COMP);
1714 #ifdef DEBUG
1715 printf("Compression method is %#hx\n", method);
1716 #endif
1718 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1719 #ifdef DEBUG
1720 printf("Flags are %#hx\n", flags);
1721 #endif
1723 usize = UNPACK_UB4(file_header, LOC_USIZE);
1725 /* If we're providing verbose output, we need to make an ASCII
1726 * formatted version of the date. */
1727 if(verbose){
1728 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1729 tdate = dos2unixtime(mdate);
1730 s_tm = localtime(&tdate);
1731 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1734 if(filename_len < fnlen){
1735 if(filename != NULL)
1736 free(filename);
1738 filename = malloc(sizeof(ub1) * (fnlen + 1));
1739 filename_len = fnlen + 1;
1742 pb_read(&pbf, filename, fnlen);
1743 filename[fnlen] = '\0';
1745 /* the header is at the end. In a JAR file, this means that the data
1746 happens to be compressed. We have no choice but to inflate the
1747 data */
1748 if(flags & 0x0008){
1750 size = eflen;
1752 if(size > 0)
1753 consume(&pbf, size);
1755 if(method == 8){
1756 #ifdef DEBUG
1757 printf("inflating %s\n", filename);
1758 #endif
1759 inflate_file(&pbf, -1, &ze);
1761 usize = ze.usize;
1762 } else
1763 printf("We're shit outta luck!\n");
1765 } else {
1766 size = csize + (eflen > 0 ? eflen : 0);
1769 #ifdef DEBUG
1770 printf("Skipping %ld bytes\n", (long)size);
1771 #endif
1773 consume(&pbf, size);
1775 /* print out the listing */
1776 if(file_num > 0){
1777 for(j = 0; j < file_num; j++)
1778 if(strcmp(files[j], (const char *)filename) == 0){
1779 if(verbose)
1780 printf("%6d %s %s\n", usize, ascii_date, filename);
1781 else
1782 printf("%s\n", filename);
1783 break;
1785 } else {
1786 if(verbose)
1787 printf("%6d %s %s\n", usize, ascii_date, filename);
1788 else
1789 printf("%s\n", filename);
1793 return 0;
1796 int consume(pb_file *pbf, int amt){
1797 int tc = 0; /* total amount consumed */
1798 ub1 buff[RDSZ];
1799 int rdamt;
1801 #ifdef DEBUG
1802 printf("Consuming %d bytes\n", amt);
1803 #endif
1805 while(tc < amt){
1806 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1807 #ifdef DEBUG
1808 printf("got %d bytes\n", rdamt);
1809 #endif
1810 tc += rdamt;
1813 #ifdef DEBUG
1814 printf("%d bytes consumed\n", tc);
1815 #endif
1817 return 0;
1820 void usage(const char *filename){
1821 fprintf(stderr, "\
1822 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1823 Options\n\
1824 -c create new archive\n\
1825 -t list table of contents for archive\n\
1826 -x extract named (or all) files from archive\n\
1827 ", filename);
1828 fprintf(stderr, "\
1829 -u update existing archive\n\
1830 -V display version information\n\
1831 -v generate verbose output on standard output\n\
1832 -f specify archive file name\n\
1833 -m include manifest information from specified manifest file\n\
1834 -0 store only; use no ZIP compression\n\
1835 -M Do not create a manifest file for the entries\n\
1836 -C change to the specified directory and include the following file\n\
1837 -E don't include the files found in a directory\n\
1838 -@ Read names from stdin\n\
1840 fprintf(stderr, "\
1841 If any file is a directory then it is processed recursively.\n\
1842 The manifest file name and the archive file name needs to be specified\n\
1843 in the same order the 'm' and 'f' flags are specified.\n\
1845 Example 1: to archive two class files into an archive called classes.jar: \n\
1846 jar cvf classes.jar Foo.class Bar.class \n\
1847 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1848 files in the foo/ directory into 'classes.jar': \n\
1849 jar cvfm classes.jar mymanifest -C foo/ .\n\
1852 exit(1);
1855 static char *
1856 jt_strdup(s)
1857 char *s;
1859 char *result = (char*)malloc(strlen(s) + 1);
1860 if (result == (char*)0)
1861 return (char*)0;
1862 strcpy(result, s);
1863 return result;