OSDN Git Service

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