* loop.c (loop_delete_insns): Add prototype.
[official-gcc.git] / fastjar / jartool.c
blobd8bfb2a86f323bddeb8e98942f92cd8f3241d23b
1 /*
2 jartool.c - main functions for fastjar utility
3 Copyright (C) 1999, 2000 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.3 2000/12/14 18:45:35 ghazi Exp $
22 $Log: jartool.c,v $
23 Revision 1.3 2000/12/14 18:45:35 ghazi
24 Warning fixes:
26 * compress.c: Include stdlib.h and compress.h.
27 (rcsid): Delete.
28 (report_str_error): Make static.
29 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
30 (hrd_inflate_str): Likewise.
32 * compress.h (init_compression, end_compression, init_inflation,
33 end_inflation): Prototype void arguments.
35 * dostime.c (rcsid): Delete.
37 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
38 Make functions static. Cast ctype function argument to `unsigned
39 char'. Add parens in if-stmts. Constify.
40 (Usage): Change into a macro.
41 (jargrep): Remove unused parameter.
43 * jartool.c: Constify. Add parens in if-stmts. Align
44 signed/unsigned char pointers in functions calls using casts.
45 (rcsid): Delete.
46 (list_jar): Fix printf format specifier.
47 (usage): Chop long string into bits. Reformat.
49 * pushback.c (rcsid): Delete.
51 Revision 1.2 2000/12/13 18:11:57 tromey
52 * jartool.c (extract_jar): Use strchr, not index.
54 Revision 1.1 2000/12/09 03:08:23 apbianco
55 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
57 * fastjar: Imported.
59 Revision 1.5 2000/08/24 15:01:27 cory
60 Made certain that fastjar opened the jar file before trying to update it
61 with the -u option.
63 Revision 1.4 2000/08/24 13:39:21 cory
64 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
65 when byte swapping. Better safe than sorry.
67 Revision 1.3 2000/08/23 19:42:17 cory
68 Added support for more Unix platforms. The following code has been hacked
69 to work on AIX, Solaris, True 64, and HP-UX.
70 Added bigendian check. Probably works on most big and little endian platforms
71 now.
73 Revision 1.2 1999/12/06 07:38:28 toast
74 fixed recursive archiving bug
76 Revision 1.1.1.1 1999/12/06 03:09:34 toast
77 initial checkin..
81 Revision 1.22 1999/10/12 19:45:13 burnsbr
82 adding patch to fix compat problem
84 Revision 1.21 1999/05/10 09:15:49 burnsbr
85 fixed manifest file version info
87 Revision 1.20 1999/05/10 08:53:16 burnsbr
88 *** empty log message ***
90 Revision 1.19 1999/05/10 08:30:39 burnsbr
91 added extract / listing code
93 Revision 1.18 1999/04/28 04:24:29 burnsbr
94 updated version
96 Revision 1.17 1999/04/28 04:21:23 burnsbr
97 added support for -C dir-changing flag.. Updated total compression display
99 Revision 1.16 1999/04/27 10:28:22 burnsbr
100 updated version string
102 Revision 1.15 1999/04/27 10:04:06 burnsbr
103 configure support
105 Revision 1.14 1999/04/27 08:56:14 burnsbr
106 added -V flag, better error messages
108 Revision 1.13 1999/04/26 02:35:21 burnsbr
109 changed all sorts of stuff.. compression now works 100%
111 Revision 1.12 1999/04/23 12:00:45 burnsbr
112 90% done with compression code
114 Revision 1.11 1999/04/22 04:12:57 burnsbr
115 finished first round of Manifest file support..
116 might need to do more, digest etc..
118 Revision 1.10 1999/04/22 02:35:23 burnsbr
119 added more manifest support, about 75% done now. Replaced all the
120 redundant shifts and bit-logic with a macro or two, making the code
121 easier to read.
123 Revision 1.9 1999/04/21 09:55:16 burnsbr
124 pulled out printfs
126 Revision 1.8 1999/04/21 02:58:01 burnsbr
127 started manifest code
129 Revision 1.7 1999/04/20 23:15:28 burnsbr
130 added patch sent by John Bley <jbb6@acpub.duke.edu>
132 Revision 1.6 1999/04/20 08:56:02 burnsbr
133 added GPL comment
135 Revision 1.5 1999/04/20 08:16:09 burnsbr
136 fixed verbose flag, did some optimization
138 Revision 1.4 1999/04/20 05:09:59 burnsbr
139 added rcsid variable
141 Revision 1.3 1999/04/20 05:08:54 burnsbr
142 fixed Log statement
146 #include "config.h"
148 #include <zlib.h>
150 #ifdef STDC_HEADERS
151 #include <stdlib.h>
152 #endif
154 #ifdef HAVE_UNISTD_H
155 #include <unistd.h>
156 #endif
158 #include <stdio.h>
159 #include <sys/stat.h>
160 #include <sys/types.h>
162 #ifdef HAVE_SYS_PARAM_H
163 #include <sys/param.h>
164 #endif
166 #ifndef MAXPATHLEN
167 #define MAXPATHLEN 1024
168 #endif
170 #ifdef HAVE_DIRENT_H
171 #include <dirent.h>
172 #endif
174 #ifdef HAVE_FCNTL_H
175 #include <fcntl.h>
176 #endif
178 #include <string.h>
179 #include <errno.h>
181 #ifdef TM_IN_SYS_TIME
182 #include <sys/time.h>
183 #else
184 #include <time.h>
185 #endif
187 #include "jartool.h"
188 #include "zipfile.h"
189 #include "dostime.h"
190 #include "pushback.h"
191 #include "compress.h"
193 #ifdef WORDS_BIGENDIAN
195 #define L2BI(l) ((l & 0xff000000) >> 24) | \
196 ((l & 0x00ff0000) >> 8) | \
197 ((l & 0x0000ff00) << 8) | \
198 ((l & 0x000000ff) << 24);
200 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
202 #endif
204 static char version_string[] = VERSION;
206 extern int errno;
208 void usage(const char*);
209 void add_entry(struct zipentry *);
210 void init_headers(void);
212 int consume(pb_file *, int);
213 int list_jar(int, char**, int);
214 int extract_jar(int, char**, int);
215 int add_file_to_jar(int, int, const char*, struct stat*);
216 int add_to_jar(int, const char*, const char*);
217 int create_central_header(int);
218 int make_manifest(int, const char*);
219 static void init_args(char **, int);
220 static char *get_next_arg (void);
222 /* global variables */
223 ub1 file_header[30];
224 ub1 data_descriptor[16];
225 int do_compress;
226 int seekable;
227 int verbose;
228 char jarfile[256];
230 /* If non zero, then don't recurse in directory. Instead, add the
231 directory entry and relie on an explicit list of files to populate
232 the archive. This option isn't supported by the original jar tool. */
233 int use_explicit_list_only;
235 /* If non zero, then read the entry names from stdin. This option
236 isn't supported by the original jar tool. */
237 int read_names_from_stdin;
239 zipentry *ziplist; /* linked list of entries */
240 zipentry *ziptail; /* tail of the linked list */
242 int number_of_entries; /* number of entries in the linked list */
244 int main(int argc, char **argv){
246 char mfile[256];
248 int action = ACTION_NONE;
249 int manifest = TRUE;
250 int manifest_file = FALSE;
251 int file = FALSE;
252 int file_first = FALSE;
254 int i, j;
255 int jarfd = -1;
257 do_compress = TRUE;
258 verbose = FALSE;
260 ziplist = NULL;
262 number_of_entries = 0;
264 if(argc < 2)
265 usage(argv[0]);
267 j = strlen(argv[1]);
269 for(i = 0; i < j; i++){
270 switch(argv[1][i]){
271 case 'c':
272 action = ACTION_CREATE;
273 break;
274 case 't':
275 action = ACTION_LIST;
276 break;
277 case 'x':
278 action = ACTION_EXTRACT;
279 break;
280 case 'u':
281 action = ACTION_UPDATE;
282 break;
283 case 'v':
284 verbose = TRUE;
285 break;
286 case 'V':
287 printf("%s\n", version_string);
288 exit(0);
289 case 'f':
290 file = TRUE;
291 if(!manifest_file)
292 file_first = TRUE;
293 else
294 file_first = FALSE;
295 break;
296 case 'm':
297 manifest_file = TRUE;
298 break;
299 case '0':
300 do_compress = FALSE;
301 break;
302 case 'M':
303 manifest = FALSE;
304 break;
305 case '-':
306 break;
307 /* The following options aren't supported by the original jar tool. */
308 case 'E':
309 use_explicit_list_only = TRUE;
310 break;
311 case '@':
312 read_names_from_stdin = TRUE;
313 break;
314 default:
315 fprintf(stderr, "Illegal option: %c\n", argv[1][i]);
316 usage(argv[0]);
320 if(action == ACTION_NONE){
321 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
322 usage(argv[0]);
325 /* Verify unsupported combinations and warn of the use of non
326 standard features */
327 if(verbose && use_explicit_list_only)
328 fprintf (stderr, "Warning: using non standard '-E' option\n");
329 if(verbose && read_names_from_stdin)
330 fprintf (stderr, "Warning: using non standard '-@' option\n");
331 if(read_names_from_stdin
332 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
333 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
334 usage(argv[0]);
337 i = 2;
339 /* get the jarfile and manifest file (if any) */
340 if(file && file_first){
341 if(i >= argc)
342 usage(argv[0]);
344 strncpy(jarfile, argv[i++], 256);
346 if(manifest_file){
347 if(i >= argc)
348 usage(argv[0]);
350 strncpy(mfile, argv[i++], 256);
353 if(file && !file_first){
354 if(i >= argc)
355 usage(argv[0]);
357 strncpy(jarfile, argv[i++], 256);
360 /* create the jarfile */
361 if(action == ACTION_CREATE){
362 if(file){
363 jarfd = creat(jarfile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
365 if(jarfd < 0){
366 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
367 perror(jarfile);
368 exit(1);
371 /* We assume that the file is seekable */
372 seekable = TRUE;
374 } else {
376 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
378 /* standard out is not seekable */
379 seekable = FALSE;
381 /* don't want our output to be part of the jar file.. figured this one
382 out the hard way.. =P */
383 verbose = FALSE;
385 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
387 if(file){
388 jarfd = open(jarfile, O_RDONLY);
390 if(jarfd < 0){
391 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
392 perror(jarfile);
393 exit(1);
396 seekable = TRUE;
397 } else {
398 jarfd = STDIN_FILENO; /* jarfd is standard in */
400 /* we assume that the stream isn't seekable for safety */
401 seekable = FALSE;
405 if(action == ACTION_CREATE || action == ACTION_UPDATE){
406 const char *arg;
407 init_headers();
409 if((action == ACTION_UPDATE) && file) {
410 if((jarfd = open(jarfile, O_RDWR)) < 0) {
411 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
412 perror(jarfile);
413 exit(1);
417 if(do_compress)
418 init_compression();
421 /* Add the META-INF/ directory and the manifest */
422 if(manifest && manifest_file)
423 make_manifest(jarfd, mfile);
424 else if(manifest)
425 make_manifest(jarfd, NULL);
427 init_args (argv, i);
428 /* now we add the files to the archive */
429 while ((arg = get_next_arg ())){
431 if(!strcmp(arg, "-C")){
432 const char *dir_to_change = get_next_arg ();
433 const char *file_to_add = get_next_arg ();
434 if(!dir_to_change
435 || !file_to_add
436 || add_to_jar(jarfd, dir_to_change, file_to_add)){
437 printf("Error adding %s to jar archive!\n", arg);
438 exit(1);
440 } else {
441 if(add_to_jar(jarfd, NULL, arg)){
442 printf("Error adding %s to jar archive!\n", arg);
443 exit(1);
447 /* de-initialize the compression DS */
448 if(do_compress)
449 end_compression();
451 create_central_header(jarfd);
453 if (close(jarfd) != 0) {
454 fprintf(stderr, "Error closing jar archive!\n");
456 } else if(action == ACTION_LIST){
457 list_jar(jarfd, &argv[i], (argc - i));
458 } else if(action == ACTION_EXTRACT){
459 extract_jar(jarfd, &argv[i], (argc - i));
462 exit(0);
465 static int args_current_g;
466 static char **args_g;
468 static void
469 init_args(args, current)
470 char **args;
471 int current;
473 if(!read_names_from_stdin)
475 args_g = args;
476 args_current_g = current;
480 static char *
481 get_next_arg ()
483 static int reached_end = 0;
485 if (reached_end)
486 return NULL;
488 if (args_g)
490 if (!args_g [args_current_g])
492 reached_end = 1;
493 return NULL;
495 return args_g [args_current_g++];
497 else
499 /* Read the name from stdin. Delimiters are '\n' and
500 '\r'. Reading EOF indicates that we don't have anymore file
501 names characters to read. */
503 char s [MAXPATHLEN];
504 int pos = 0;
506 /* Get rid of '\n' and '\r' first. */
507 while (1)
509 int c = getc (stdin);
510 if (c == '\n' || c == '\r')
511 continue;
512 else
514 if (c == EOF)
515 return NULL;
516 ungetc (c, stdin);
517 break;
521 while (1)
523 int c = getc (stdin);
524 /* Exit when we get a delimiter or don't have any characters
525 to read */
526 if (c == '\n'|| c == '\r'|| c == EOF)
527 break;
528 s [pos++] = (char) c;
531 if (pos)
533 s [pos] = '\0';
534 return strdup (s);
536 else
537 return NULL;
541 void init_headers(){
542 /* packing file header */
543 /* magic number */
544 file_header[0] = 0x50;
545 file_header[1] = 0x4b;
546 file_header[2] = 0x03;
547 file_header[3] = 0x04;
548 /* version number (Unix 1.0)*/
549 file_header[4] = 10;
550 file_header[5] = 0;
551 /* bit flag (normal deflation)*/
552 file_header[6] = 0x00;
554 file_header[7] = 0x00;
555 /* do_compression method (deflation) */
556 file_header[8] = 0;
557 file_header[9] = 0;
559 /* last mod file time (MS-DOS format) */
560 file_header[10] = 0;
561 file_header[11] = 0;
562 /* last mod file date (MS-DOS format) */
563 file_header[12] = 0;
564 file_header[13] = 0;
565 /* CRC 32 */
566 file_header[14] = 0;
567 file_header[15] = 0;
568 file_header[16] = 0;
569 file_header[17] = 0;
570 /* compressed size */
571 file_header[18] = 0;
572 file_header[19] = 0;
573 file_header[20] = 0;
574 file_header[21] = 0;
575 /* uncompressed size */
576 file_header[22] = 0;
577 file_header[23] = 0;
578 file_header[24] = 0;
579 file_header[25] = 0;
580 /* filename length */
581 file_header[26] = 0;
582 file_header[27] = 0;
583 /* extra field length */
584 file_header[28] = 0;
585 file_header[29] = 0;
587 /* Initialize the compression DS */
588 PACK_UB4(data_descriptor, 0, 0x08074b50);
592 void add_entry(struct zipentry *ze){
594 if(ziplist == NULL){
595 ziplist = ze;
596 ziptail = ziplist;
597 } else {
598 ziplist->next_entry = ze;
599 ziplist = ze;
602 number_of_entries++;
605 int make_manifest(int jfd, const char *mf_name){
606 time_t current_time;
607 int nlen; /* length of file name */
608 int mod_time; /* file modification time */
609 struct zipentry *ze;
611 nlen = 9; /* trust me on this one */
613 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
615 current_time = time(NULL);
616 if(current_time == (time_t)-1){
617 perror("time");
618 exit(1);
621 mod_time = unix2dostime(&current_time);
623 PACK_UB2(file_header, LOC_EXTRA, 0);
624 PACK_UB2(file_header, LOC_COMP, 0);
625 PACK_UB2(file_header, LOC_FNLEN, nlen);
626 PACK_UB4(file_header, LOC_MODTIME, mod_time);
628 if(verbose)
629 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
631 ze = (zipentry*)malloc(sizeof(zipentry));
632 if(ze == NULL){
633 perror("malloc");
634 exit(1);
637 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
638 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
639 strcpy(ze->filename, "META-INF/");
640 ze->filename[nlen] = '\0';
642 ze->offset = lseek(jfd, 0, SEEK_CUR);
643 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
644 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
645 ze->compressed = FALSE;
647 add_entry(ze);
649 write(jfd, file_header, 30);
650 write(jfd, "META-INF/", nlen);
652 /* if the user didn't specify an external manifest file... */
653 if(mf_name == NULL){
654 int mf_len = 37 + strlen(VERSION);
655 char *mf;
657 if((mf = (char *) malloc(mf_len + 1))) {
658 uLong crc;
660 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
662 crc = crc32(0L, Z_NULL, 0);
664 crc = crc32(crc, (const unsigned char *)mf, mf_len);
666 nlen = 20; /* once again, trust me */
668 PACK_UB2(file_header, LOC_EXTRA, 0);
669 PACK_UB2(file_header, LOC_COMP, 0);
670 PACK_UB2(file_header, LOC_FNLEN, nlen);
671 PACK_UB4(file_header, LOC_USIZE, mf_len);
673 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
675 PACK_UB4(file_header, LOC_CRC, crc);
677 if(verbose)
678 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
680 ze = (zipentry*)malloc(sizeof(zipentry));
681 if(ze == NULL){
682 perror("malloc");
683 exit(1);
686 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
687 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
688 strcpy(ze->filename, "META-INF/MANIFEST.MF");
689 ze->filename[nlen] = '\0';
691 ze->offset = lseek(jfd, 0, SEEK_CUR);
692 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
693 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
694 ze->crc = crc;
695 ze->csize = mf_len;
696 ze->usize = ze->csize;
697 ze->compressed = FALSE;
699 add_entry(ze);
701 write(jfd, file_header, 30);
702 write(jfd, "META-INF/MANIFEST.MF", nlen);
703 write(jfd, mf, mf_len);
704 free(mf);
706 else {
707 printf("malloc errror\n");
708 exit(-1);
710 } else {
711 int mfd;
712 struct stat statbuf;
714 stat(mf_name, &statbuf);
716 if(!S_ISREG(statbuf.st_mode)){
717 fprintf(stderr, "Invalid manifest file specified.\n");
718 exit(1);
721 mfd = open(mf_name, O_RDONLY);
723 if(mfd < 0){
724 fprintf(stderr, "Error opening %s.\n", mf_name);
725 exit(1);
728 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
729 perror("error writing to jar");
730 exit(1);
735 return 0;
738 int add_to_jar(int fd, const char *new_dir, const char *file){
739 struct stat statbuf;
740 DIR *dir;
741 struct dirent *de;
742 zipentry *ze;
743 int stat_return;
744 char *old_dir = NULL;
746 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
747 * It fixes this:
748 * "normal" jar : org/apache/java/io/LogRecord.class
749 * fastjar : ./org/apache/java/io/LogRecord.class
750 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
751 * with both kaffe-1.0b4 and JDK.
753 while (*file=='.' && *(file+1)=='/')
754 file+=2;
756 /* If new_dir isn't null, we need to change to that directory. However,
757 we also need to return to the old directory when we're done */
758 if(new_dir != NULL){
759 old_dir = getcwd(NULL, 0);
761 if(chdir(new_dir) == -1){
762 perror(new_dir);
763 return 1;
767 if(!strcmp(file, jarfile)){
768 if(verbose)
769 printf("skipping: %s\n", file);
770 return 0; /* we don't want to add ourselves.. */
773 stat_return = stat(file, &statbuf);
775 if(stat_return == -1){
776 perror(file);
777 } else if(S_ISDIR(statbuf.st_mode)){
778 char *fullname;
779 char *t_ptr;
780 int nlen;
781 unsigned long mod_time;
783 dir = opendir(file);
785 if(dir == NULL){
786 perror("opendir");
787 return 1;
790 nlen = strlen(file) + 256;
791 fullname = (char*)malloc(nlen * sizeof(char));
792 memset(fullname, 0, (nlen * sizeof(char)));
794 if(fullname == NULL){
795 fprintf(stderr, "Filename is NULL!\n");
796 return 1;
799 strcpy(fullname, file);
800 nlen = strlen(file);
802 if(fullname[nlen - 1] != '/'){
803 fullname[nlen] = '/';
804 t_ptr = (fullname + nlen + 1);
805 } else
806 t_ptr = (fullname + nlen);
809 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
811 nlen = (t_ptr - fullname);
813 mod_time = unix2dostime(&statbuf.st_mtime);
815 PACK_UB2(file_header, LOC_EXTRA, 0);
816 PACK_UB2(file_header, LOC_COMP, 0);
817 PACK_UB2(file_header, LOC_FNLEN, nlen);
818 PACK_UB4(file_header, LOC_MODTIME, mod_time);
820 if(verbose)
821 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
823 ze = (zipentry*)malloc(sizeof(zipentry));
824 if(ze == NULL){
825 perror("malloc");
826 exit(1);
829 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
830 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
831 strcpy(ze->filename, fullname);
832 ze->filename[nlen] = '\0';
834 ze->offset = lseek(fd, 0, SEEK_CUR);
835 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
836 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
837 ze->compressed = FALSE;
839 add_entry(ze);
841 write(fd, file_header, 30);
842 write(fd, fullname, nlen);
844 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
845 if(de->d_name[0] == '.')
846 continue;
847 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
848 if(verbose)
849 printf("skipping: %s\n", de->d_name);
850 continue;
853 strcpy(t_ptr, de->d_name);
855 if(add_to_jar(fd, NULL, fullname)){
856 fprintf(stderr, "Error adding file to jar!\n");
857 return 1;
861 free(fullname);
862 closedir(dir);
864 } else if(S_ISREG(statbuf.st_mode)){
865 int add_fd;
867 add_fd = open(file, O_RDONLY);
868 if(add_fd < 0){
869 fprintf(stderr, "Error opening %s.\n", file);
870 return 0;
873 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
874 fprintf(stderr, "Error adding file to jar!\n");
875 return 1;
878 } else {
879 fprintf(stderr, "Illegal file specified: %s\n", file);
882 if(old_dir != NULL){
883 if(chdir(old_dir))
884 perror(old_dir);
886 free(old_dir);
889 return 0;
892 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
894 unsigned short file_name_length;
895 unsigned long mod_time;
896 ub1 rd_buff[RDSZ];
897 uLong crc = 0;
898 off_t offset = 0;
899 int rdamt;
900 struct zipentry *ze;
902 mod_time = unix2dostime(&(statbuf->st_mtime));
903 file_name_length = strlen(fname);
905 if(!seekable && !do_compress){
906 crc = crc32(0L, Z_NULL, 0);
908 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
909 crc = crc32(crc, rd_buff, rdamt);
911 lseek(ffd, 0, SEEK_SET);
914 /* data descriptor */
915 if(!seekable && do_compress){
916 PACK_UB2(file_header, LOC_EXTRA, 8);
917 } else {
918 PACK_UB2(file_header, LOC_EXTRA, 0);
921 if(do_compress){
922 PACK_UB2(file_header, LOC_COMP, 8);
923 } else {
924 PACK_UB2(file_header, LOC_COMP, 0);
927 PACK_UB4(file_header, LOC_MODTIME, mod_time);
928 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
930 if(!seekable && !do_compress){
931 PACK_UB4(file_header, LOC_CRC, crc);
932 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
933 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
934 } else
935 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
937 ze = (zipentry*)malloc(sizeof(zipentry));
938 if(ze == NULL){
939 perror("malloc");
940 exit(1);
943 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
944 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
945 strcpy(ze->filename, fname);
947 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
948 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
950 if(!seekable && !do_compress)
951 ze->crc = crc;
953 ze->csize = statbuf->st_size;
954 ze->usize = ze->csize;
955 ze->offset = lseek(jfd, 0, SEEK_CUR);
956 if(do_compress)
957 ze->compressed = TRUE;
958 else
959 ze->compressed = FALSE;
961 add_entry(ze);
963 /* Write the local header */
964 write(jfd, file_header, 30);
966 /* write the file name to the zip file */
967 write(jfd, fname, file_name_length);
970 if(verbose){
971 printf("adding: %s ", fname);
972 fflush(stdout);
975 if(do_compress){
976 /* compress the file */
977 compress_file(ffd, jfd, ze);
978 } else {
979 /* Write the contents of the file (uncompressed) to the zip file */
980 /* calculate the CRC as we go along */
981 ze->crc = crc32(0L, Z_NULL, 0);
983 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
984 ze->crc = crc32(ze->crc, rd_buff, rdamt);
985 if(write(jfd, rd_buff, rdamt) != rdamt){
986 perror("write");
987 return 0;
991 close(ffd);
993 /* write out data descriptor */
994 PACK_UB4(data_descriptor, 4, ze->crc);
995 PACK_UB4(data_descriptor, 8, ze->csize);
996 PACK_UB4(data_descriptor, 12, ze->usize);
998 /* we need to seek back and fill the header */
999 if(seekable){
1000 offset = (ze->csize + strlen(ze->filename) + 16);
1002 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1003 perror("lseek");
1004 exit(1);
1007 if(write(jfd, (data_descriptor + 4), 12) != 12){
1008 perror("write");
1009 return 0;
1012 offset -= 12;
1014 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1015 perror("lseek");
1016 exit(1);
1018 } else if(do_compress){
1019 /* Sun's jar tool will only allow a data descriptor if the entry is
1020 compressed, but we'll save 16 bytes/entry if we only use it when
1021 we can't seek back on the file */
1023 if(write(jfd, data_descriptor, 16) != 16){
1024 perror("write");
1025 return 0;
1029 if(verbose)
1030 printf("(in=%d) (out=%d) (%s %d%%)\n",
1031 (int)ze->usize, (int)ze->csize,
1032 (do_compress ? "deflated" : "stored"),
1033 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1035 return 0;
1038 int create_central_header(int fd){
1039 ub1 header[46];
1040 ub1 end_header[22];
1041 int start_offset;
1042 int dir_size;
1043 int *iheader;
1044 int total_in = 0, total_out = 22;
1046 zipentry *ze;
1048 iheader = (int*)header;
1050 /* magic number */
1051 header[0] = 'P';
1052 header[1] = 'K';
1053 header[2] = 1;
1054 header[3] = 2;
1055 /* version made by */
1056 header[4] = 10;
1057 header[5] = 0;
1058 /* version needed to extract */
1059 header[6] = 10;
1060 header[7] = 0;
1061 /* bit flag */
1062 header[8] = 0;
1063 header[9] = 0;
1064 /* compression method */
1065 header[10] = 0;
1066 header[11] = 0;
1067 /* file mod time */
1068 header[12] = 0;
1069 header[13] = 0;
1070 /* file mod date */
1071 header[14] = 0;
1072 header[15] = 0;
1073 /* crc 32 */
1074 header[16] = 0;
1075 header[17] = 0;
1076 header[18] = 0;
1077 header[19] = 0;
1078 /* compressed size */
1079 header[20] = 0;
1080 header[21] = 0;
1081 header[22] = 0;
1082 header[23] = 0;
1083 /* uncompressed size */
1084 header[24] = 0;
1085 header[25] = 0;
1086 header[26] = 0;
1087 header[27] = 0;
1088 /* filename length */
1089 header[28] = 0;
1090 header[29] = 0;
1091 /* extra field length */
1092 header[30] = 0;
1093 header[31] = 0;
1094 /* file comment length */
1095 header[32] = 0;
1096 header[33] = 0;
1097 /* disk number start */
1098 header[34] = 0;
1099 header[35] = 0;
1100 /* internal file attribs */
1101 header[36] = 0;
1102 header[37] = 0;
1103 /* external file attribs */
1104 header[38] = 0;
1105 header[39] = 0;
1106 header[40] = 0;
1107 header[41] = 0;
1108 /* relative offset of local header */
1109 header[42] = 0;
1110 header[43] = 0;
1111 header[44] = 0;
1112 header[45] = 0;
1114 start_offset = lseek(fd, 0, SEEK_CUR);
1116 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1118 total_in += ze->usize;
1119 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1121 if(ze->compressed){
1122 PACK_UB2(header, CEN_COMP, 8);
1123 } else {
1124 PACK_UB2(header, CEN_COMP, 0);
1127 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1128 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1129 PACK_UB4(header, CEN_CRC, ze->crc);
1130 PACK_UB4(header, CEN_CSIZE, ze->csize);
1131 PACK_UB4(header, CEN_USIZE, ze->usize);
1132 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1133 PACK_UB4(header, CEN_OFFSET, ze->offset);
1135 write(fd, header, 46);
1137 write(fd, ze->filename, strlen(ze->filename));
1140 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1142 /* magic number */
1143 end_header[0] = 0x50;
1144 end_header[1] = 0x4b;
1145 end_header[2] = 0x05;
1146 end_header[3] = 0x06;
1147 /* number of this disk */
1148 end_header[4] = 0;
1149 end_header[5] = 0;
1150 /* number of disk w/ start of central header */
1151 end_header[6] = 0;
1152 end_header[7] = 0;
1153 /* total number of entries in central dir on this disk*/
1154 PACK_UB2(end_header, 8, number_of_entries);
1155 /* total number of entries in central dir*/
1156 PACK_UB2(end_header, 10, number_of_entries);
1157 /* size of central dir. */
1158 PACK_UB4(end_header, 12, dir_size);
1159 /* offset of start of central dir */
1160 PACK_UB4(end_header, 16, start_offset);
1161 /* zipfile comment length */
1162 end_header[20] = 0;
1163 end_header[21] = 0;
1165 write(fd, end_header, 22);
1167 if(verbose)
1168 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1169 total_in,
1170 total_out,
1171 (do_compress ? "deflated" : "stored"),
1172 (int)((1 - (total_out / (float)total_in)) * 100)
1175 return 0;
1178 int extract_jar(int fd, char **files, int file_num){
1179 int rdamt;
1180 int out_a, in_a;
1181 ub4 signature;
1182 ub4 csize;
1183 ub4 crc;
1184 ub2 fnlen;
1185 ub2 eflen;
1186 ub2 flags;
1187 ub2 method;
1188 ub1 *filename = NULL;
1189 int filename_len = 0;
1190 ub4 rd_buff[RDSZ];
1191 pb_file pbf;
1192 ub1 scratch[16];
1193 zipentry ze;
1194 int f_fd;
1195 int dir;
1196 int handle;
1197 int j;
1199 init_inflation();
1201 pb_init(&pbf, fd);
1203 for(;;){
1204 f_fd = 0;
1205 crc = 0;
1206 ze.crc = 0;
1208 dir = FALSE; /* by default, the file isn't a dir */
1209 handle = TRUE; /* by default we'll extract/create the file */
1211 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1212 perror("read");
1213 break;
1216 signature = UNPACK_UB4(scratch, 0);
1218 #ifdef DEBUG
1219 printf("signature is %x\n", signature);
1220 #endif
1221 if(signature == 0x08074b50){
1222 #ifdef DEBUG
1223 printf("skipping data descriptor\n");
1224 #endif
1225 pb_read(&pbf, scratch, 12);
1226 continue;
1227 } else if(signature == 0x02014b50){
1228 #ifdef DEBUG
1229 printf("Central header reached.. we're all done!\n");
1230 #endif
1231 break;
1232 }else if(signature != 0x04034b50){
1233 printf("Ick! %#x\n", signature);
1234 break;
1237 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1238 perror("read");
1239 break;
1242 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1243 #ifdef DEBUG
1244 printf("Compressed size is %u\n", csize);
1245 #endif
1247 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1248 #ifdef DEBUG
1249 printf("Filename length is %hu\n", fnlen);
1250 #endif
1252 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1253 #ifdef DEBUG
1254 printf("Extra field length is %hu\n", eflen);
1255 #endif
1257 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1258 #ifdef DEBUG
1259 printf("Flags are %#hx\n", flags);
1260 #endif
1262 method = UNPACK_UB2(file_header, LOC_COMP);
1263 #ifdef DEBUG
1264 printf("Compression method is %#hx\n", method);
1265 #endif
1267 /* if there isn't a data descriptor */
1268 if(!(flags & 0x0008)){
1269 crc = UNPACK_UB4(file_header, LOC_CRC);
1270 #ifdef DEBUG
1271 printf("CRC is %x\n", crc);
1272 #endif
1275 if(filename_len < fnlen){
1276 if(filename != NULL)
1277 free(filename);
1279 filename = malloc(sizeof(ub1) * (fnlen + 1));
1280 filename_len = fnlen + 1;
1283 pb_read(&pbf, filename, fnlen);
1284 filename[fnlen] = '\0';
1286 #ifdef DEBUG
1287 printf("filename is %s\n", filename);
1288 #endif
1290 if(file_num > 0){
1291 handle = FALSE;
1293 for(j = 0; j < file_num; j++)
1294 if(strcmp(files[j], (const char *)filename) == 0){
1295 handle = TRUE;
1296 break;
1300 if(!handle)
1301 f_fd = -1;
1303 /* OK, there is some directory information in the file. Nothing to do
1304 but ensure the directory(s) exist, and create them if they don't.
1305 What a pain! */
1306 if(strchr((const char *)filename, '/') != NULL && handle){
1307 /* Loop through all the directories in the path, (everything w/ a '/') */
1308 const ub1 *start = filename;
1309 char *tmp_buff;
1310 struct stat sbuf;
1312 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1314 for(;;){
1315 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1317 if(idx == NULL)
1318 break;
1319 else if(idx == start){
1320 start++;
1321 continue;
1323 start = idx + 1;
1325 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1326 tmp_buff[(idx - filename)] = '\0';
1328 #ifdef DEBUG
1329 printf("checking the existance of %s\n", tmp_buff);
1330 #endif
1332 if(stat(tmp_buff, &sbuf) < 0){
1333 if(errno != ENOENT){
1334 perror("stat");
1335 exit(1);
1338 } else if(S_ISDIR(sbuf.st_mode)){
1339 #ifdef DEBUG
1340 printf("Directory exists\n");
1341 #endif
1342 continue;
1343 }else {
1344 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1345 tmp_buff);
1346 exit(1);
1349 #ifdef DEBUG
1350 printf("Making directory..\n");
1351 #endif
1352 if(mkdir(tmp_buff, 0755) < 0){
1353 perror("mkdir");
1354 exit(1);
1356 if(verbose && handle)
1357 printf("%10s: %s/\n", "created", tmp_buff);
1361 /* only a directory */
1362 if(strlen((const char *)start) == 0)
1363 dir = TRUE;
1365 #ifdef DEBUG
1366 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1367 #endif
1369 /* If the entry was just a directory, don't write to file, etc */
1370 if(strlen((const char *)start) == 0)
1371 f_fd = -1;
1373 free(tmp_buff);
1376 if(f_fd != -1 && handle){
1377 f_fd = creat((const char *)filename, 00644);
1379 if(f_fd < 0){
1380 fprintf(stderr, "Error extracting JAR archive!\n");
1381 perror((const char *)filename);
1382 exit(1);
1386 if(method != 8 && flags & 0x0008){
1387 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1388 exit(1);
1391 if(method == 8 || flags & 0x0008){
1392 if(seekable)
1393 lseek(fd, eflen, SEEK_CUR);
1394 else
1395 consume(&pbf, eflen);
1397 inflate_file(&pbf, f_fd, &ze);
1398 } else {
1400 #ifdef DEBUG
1401 printf("writing stored data.. (%d bytes)\n", csize);
1402 #endif
1404 out_a = 0;
1405 in_a = csize;
1407 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1409 while(out_a < csize){
1410 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1411 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1412 perror("read");
1413 exit(1);
1416 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1418 if(f_fd >= 0)
1419 write(f_fd, rd_buff, rdamt);
1421 out_a += rdamt;
1422 in_a -= rdamt;
1424 #ifdef DEBUG
1425 printf("%d bytes written\n", out_a);
1426 #endif
1429 if(seekable)
1430 lseek(fd, eflen, SEEK_CUR);
1431 else
1432 consume(&pbf, eflen);
1435 /* if there is a data descriptor left, compare the CRC */
1436 if(flags & 0x0008){
1438 if(pb_read(&pbf, scratch, 16) != 16){
1439 perror("read");
1440 exit(1);
1443 signature = UNPACK_UB4(scratch, 0);
1445 if(signature != 0x08074b50){
1446 fprintf(stderr, "Error! Missing data descriptor!\n");
1447 exit(1);
1450 crc = UNPACK_UB4(scratch, 4);
1454 if(crc != ze.crc){
1455 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1456 ze.crc, crc);
1457 exit(1);
1460 close(f_fd);
1462 if(verbose && dir == FALSE && handle)
1463 printf("%10s: %s\n",
1464 (method == 8 ? "inflated" : "extracted"),
1465 filename);
1468 return 0;
1471 int list_jar(int fd, char **files, int file_num){
1472 int rdamt;
1473 ub4 signature;
1474 ub4 csize;
1475 ub4 usize;
1476 ub4 mdate;
1477 ub4 tmp;
1478 ub2 fnlen;
1479 ub2 eflen;
1480 ub2 clen;
1481 ub2 flags;
1482 ub2 method;
1483 ub2 cen_size;
1484 ub1 *filename = NULL;
1485 ub1 scratch[16];
1486 ub1 cen_header[46];
1487 int filename_len = 0;
1488 off_t size;
1489 int i, j;
1490 time_t tdate;
1491 struct tm *s_tm;
1492 char ascii_date[30];
1493 zipentry ze;
1495 #ifdef DEBUG
1496 printf("Listing jar file, looking for %d files\n", file_num);
1497 #endif
1499 /* This should be the start of the central-header-end section */
1500 if(seekable){
1501 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1502 perror("lseek");
1503 exit(1);
1506 if(read(fd, &tmp, sizeof(ub4)) != 4){
1507 perror("read");
1508 exit(1);
1511 #ifdef WORDS_BIGENDIAN
1512 tmp = L2BI(tmp);
1513 #endif
1515 if(tmp != 0x06054b50){
1516 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1517 exit(1);
1520 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1521 perror("lseek");
1522 exit(1);
1525 if(read(fd, &cen_size, 2) != 2){
1526 perror("read");
1527 exit(1);
1530 #ifdef WORDS_BIGENDIAN
1531 cen_size = L2BS(cen_size);
1532 #endif
1534 /* printf("%hu entries in central header\n", cen_size); */
1536 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1537 perror("lseek");
1538 exit(1);
1541 if(read(fd, &tmp, 4) != 4){
1542 perror("read");
1543 exit(1);
1546 #ifdef WORDS_BIGENDIAN
1547 tmp = L2BI(tmp);
1548 #endif
1550 /* printf("Central header offset = %d\n", tmp); */
1552 if(lseek(fd, tmp, SEEK_SET) != tmp){
1553 perror("lseek");
1554 exit(1);
1557 /* Loop through the entries in the central header */
1558 for(i = 0; i < cen_size; i++){
1560 if(read(fd, &cen_header, 46) != 46){
1561 perror("read");
1562 exit(1);
1565 signature = UNPACK_UB4(cen_header, 0);
1566 if(signature != 0x02014b50){
1567 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1568 exit(1);
1571 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1572 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1573 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1574 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1576 /* If we're providing verbose output, we need to make an ASCII
1577 * formatted version of the date. */
1578 if(verbose){
1579 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1580 tdate = dos2unixtime(mdate);
1581 s_tm = localtime(&tdate);
1582 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1585 if(filename_len < fnlen){
1586 if(filename != NULL)
1587 free(filename);
1589 filename = malloc(sizeof(ub1) * (fnlen + 1));
1590 filename_len = fnlen + 1;
1593 if(read(fd, filename, fnlen) != fnlen){
1594 perror("read");
1595 exit(1);
1597 filename[fnlen] = '\0';
1599 /* if the user specified a list of files on the command line,
1600 we'll only display those, otherwise we'll display everything */
1601 if(file_num > 0){
1602 for(j = 0; j < file_num; j++)
1603 if(strcmp(files[j], (const char *)filename) == 0){
1604 if(verbose)
1605 printf("%6d %s %s\n", usize, ascii_date, filename);
1606 else
1607 printf("%s\n", filename);
1608 break;
1610 } else {
1611 if(verbose)
1612 printf("%6d %s %s\n", usize, ascii_date, filename);
1613 else
1614 printf("%s\n", filename);
1617 size = eflen + clen;
1618 if(size > 0){
1619 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1620 perror("lseek");
1621 exit(1);
1625 } else {
1626 /* the file isn't seekable.. evil! */
1627 pb_file pbf;
1629 pb_init(&pbf, fd);
1631 init_inflation();
1633 for(;;){
1634 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1635 perror("read");
1636 break;
1639 signature = UNPACK_UB4(scratch, 0);
1641 #ifdef DEBUG
1642 printf("signature is %x\n", signature);
1643 #endif
1645 if(signature == 0x08074b50){
1646 #ifdef DEBUG
1647 printf("skipping data descriptor\n");
1648 #endif
1649 pb_read(&pbf, scratch, 12);
1650 continue;
1651 } else if(signature == 0x02014b50){
1652 #ifdef DEBUG
1653 printf("Central header reached.. we're all done!\n");
1654 #endif
1655 break;
1656 }else if(signature != 0x04034b50){
1657 #ifdef DEBUG
1658 printf("Ick! %#x\n", signature);
1659 #endif
1660 break;
1663 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1664 perror("read");
1665 break;
1668 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1669 #ifdef DEBUG
1670 printf("Compressed size is %u\n", csize);
1671 #endif
1673 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1674 #ifdef DEBUG
1675 printf("Filename length is %hu\n", fnlen);
1676 #endif
1678 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1679 #ifdef DEBUG
1680 printf("Extra field length is %hu\n", eflen);
1681 #endif
1683 method = UNPACK_UB2(file_header, LOC_COMP);
1684 #ifdef DEBUG
1685 printf("Compression method is %#hx\n", method);
1686 #endif
1688 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1689 #ifdef DEBUG
1690 printf("Flags are %#hx\n", flags);
1691 #endif
1693 usize = UNPACK_UB4(file_header, LOC_USIZE);
1695 /* If we're providing verbose output, we need to make an ASCII
1696 * formatted version of the date. */
1697 if(verbose){
1698 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1699 tdate = dos2unixtime(mdate);
1700 s_tm = localtime(&tdate);
1701 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1704 if(filename_len < fnlen){
1705 if(filename != NULL)
1706 free(filename);
1708 filename = malloc(sizeof(ub1) * (fnlen + 1));
1709 filename_len = fnlen + 1;
1712 pb_read(&pbf, filename, fnlen);
1713 filename[fnlen] = '\0';
1715 /* the header is at the end. In a JAR file, this means that the data
1716 happens to be compressed. We have no choice but to inflate the
1717 data */
1718 if(flags & 0x0008){
1720 size = eflen;
1722 if(size > 0)
1723 consume(&pbf, size);
1725 if(method == 8){
1726 #ifdef DEBUG
1727 printf("inflating %s\n", filename);
1728 #endif
1729 inflate_file(&pbf, -1, &ze);
1731 usize = ze.usize;
1732 } else
1733 printf("We're shit outta luck!\n");
1735 } else {
1736 size = csize + (eflen > 0 ? eflen : 0);
1739 #ifdef DEBUG
1740 printf("Skipping %ld bytes\n", (long)size);
1741 #endif
1743 consume(&pbf, size);
1745 /* print out the listing */
1746 if(file_num > 0){
1747 for(j = 0; j < file_num; j++)
1748 if(strcmp(files[j], (const char *)filename) == 0){
1749 if(verbose)
1750 printf("%6d %s %s\n", usize, ascii_date, filename);
1751 else
1752 printf("%s\n", filename);
1753 break;
1755 } else {
1756 if(verbose)
1757 printf("%6d %s %s\n", usize, ascii_date, filename);
1758 else
1759 printf("%s\n", filename);
1763 return 0;
1766 int consume(pb_file *pbf, int amt){
1767 int tc = 0; /* total amount consumed */
1768 ub1 buff[RDSZ];
1769 int rdamt;
1771 #ifdef DEBUG
1772 printf("Consuming %d bytes\n", amt);
1773 #endif
1775 while(tc < amt){
1776 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1777 #ifdef DEBUG
1778 printf("got %d bytes\n", rdamt);
1779 #endif
1780 tc += rdamt;
1783 #ifdef DEBUG
1784 printf("%d bytes consumed\n", tc);
1785 #endif
1787 return 0;
1790 void usage(const char *filename){
1791 fprintf(stderr, "\
1792 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1793 Options\n\
1794 -c create new archive\n\
1795 -t list table of contents for archive\n\
1796 -x extract named (or all) files from archive\n\
1797 ", filename);
1798 fprintf(stderr, "\
1799 -u update existing archive\n\
1800 -V display version information\n\
1801 -v generate verbose output on standard output\n\
1802 -f specify archive file name\n\
1803 -m include manifest information from specified manifest file\n\
1804 -0 store only; use no ZIP compression\n\
1805 -M Do not create a manifest file for the entries\n\
1806 -C change to the specified directory and include the following file\n\
1807 -E don't include the files found in a directory\n\
1808 -@ Read names from stdin\n\
1810 fprintf(stderr, "\
1811 If any file is a directory then it is processed recursively.\n\
1812 The manifest file name and the archive file name needs to be specified\n\
1813 in the same order the 'm' and 'f' flags are specified.\n\
1815 Example 1: to archive two class files into an archive called classes.jar: \n\
1816 jar cvf classes.jar Foo.class Bar.class \n\
1817 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1818 files in the foo/ directory into 'classes.jar': \n\
1819 jar cvfm classes.jar mymanifest -C foo/ .\n\
1822 exit(1);