OSDN Git Service

2000-12-28 Robert Lipe <robertl@sco.com>
[pf3gnuchains/gcc-fork.git] / fastjar / jartool.c
1 /*
2   jartool.c - main functions for fastjar utility
3   Copyright (C) 1999, 2000  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.3 2000/12/14 18:45:35 ghazi Exp $
21
22    $Log: jartool.c,v $
23    Revision 1.3  2000/12/14 18:45:35  ghazi
24    Warning fixes:
25
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.
31
32         * compress.h (init_compression, end_compression, init_inflation,
33         end_inflation): Prototype void arguments.
34
35         * dostime.c (rcsid): Delete.
36
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.
42
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.
48
49         * pushback.c (rcsid): Delete.
50
51    Revision 1.2  2000/12/13 18:11:57  tromey
52         * jartool.c (extract_jar): Use strchr, not index.
53
54    Revision 1.1  2000/12/09 03:08:23  apbianco
55    2000-12-08  Alexandre Petit-Bianco  <apbianco@cygnus.com>
56
57            * fastjar: Imported.
58
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.
62
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.
66
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.
72
73    Revision 1.2  1999/12/06 07:38:28  toast
74    fixed recursive archiving bug
75
76    Revision 1.1.1.1  1999/12/06 03:09:34  toast
77    initial checkin..
78
79
80
81    Revision 1.22  1999/10/12 19:45:13  burnsbr
82    adding patch to fix compat problem
83
84    Revision 1.21  1999/05/10 09:15:49  burnsbr
85    fixed manifest file version info
86
87    Revision 1.20  1999/05/10 08:53:16  burnsbr
88    *** empty log message ***
89
90    Revision 1.19  1999/05/10 08:30:39  burnsbr
91    added extract / listing code
92
93    Revision 1.18  1999/04/28 04:24:29  burnsbr
94    updated version
95
96    Revision 1.17  1999/04/28 04:21:23  burnsbr
97    added support for -C dir-changing flag.. Updated total compression display
98
99    Revision 1.16  1999/04/27 10:28:22  burnsbr
100    updated version string
101
102    Revision 1.15  1999/04/27 10:04:06  burnsbr
103    configure support
104
105    Revision 1.14  1999/04/27 08:56:14  burnsbr
106    added -V flag, better error messages
107
108    Revision 1.13  1999/04/26 02:35:21  burnsbr
109    changed all sorts of stuff.. compression now works 100%
110
111    Revision 1.12  1999/04/23 12:00:45  burnsbr
112    90% done with compression code
113
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..
117
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.
122
123    Revision 1.9  1999/04/21 09:55:16  burnsbr
124    pulled out printfs
125
126    Revision 1.8  1999/04/21 02:58:01  burnsbr
127    started manifest code
128
129    Revision 1.7  1999/04/20 23:15:28  burnsbr
130    added patch sent by John Bley <jbb6@acpub.duke.edu>
131
132    Revision 1.6  1999/04/20 08:56:02  burnsbr
133    added GPL comment
134
135    Revision 1.5  1999/04/20 08:16:09  burnsbr
136    fixed verbose flag, did some optimization
137
138    Revision 1.4  1999/04/20 05:09:59  burnsbr
139    added rcsid variable
140
141    Revision 1.3  1999/04/20 05:08:54  burnsbr
142    fixed Log statement
143
144 */
145
146 #include "config.h"
147
148 #include <zlib.h>
149
150 #ifdef STDC_HEADERS
151 #include <stdlib.h>
152 #endif
153
154 #ifdef HAVE_UNISTD_H
155 #include <unistd.h>
156 #endif
157
158 #include <stdio.h>
159 #include <sys/stat.h>
160 #include <sys/types.h>
161
162 #ifdef HAVE_SYS_PARAM_H
163 #include <sys/param.h>
164 #endif
165
166 #ifndef MAXPATHLEN
167 #define MAXPATHLEN 1024
168 #endif
169
170 #ifdef HAVE_DIRENT_H
171 #include <dirent.h>
172 #endif
173
174 #ifdef HAVE_FCNTL_H
175 #include <fcntl.h>
176 #endif
177
178 #include <string.h>
179 #include <errno.h>
180
181 #ifdef TM_IN_SYS_TIME
182 #include <sys/time.h>
183 #else
184 #include <time.h>
185 #endif
186
187 #include "jartool.h"
188 #include "zipfile.h"
189 #include "dostime.h"
190 #include "pushback.h"
191 #include "compress.h"
192
193 #ifdef WORDS_BIGENDIAN
194
195 #define L2BI(l) ((l & 0xff000000) >> 24) | \
196                 ((l & 0x00ff0000) >> 8)  | \
197                 ((l & 0x0000ff00) << 8)  | \
198                 ((l & 0x000000ff) << 24);
199
200 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
201
202 #endif
203
204 static char version_string[] = VERSION;
205
206 extern int errno;
207
208 void usage(const char*);
209 void add_entry(struct zipentry *);
210 void init_headers(void);
211
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);
221
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];
229
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;
234
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;
238
239 zipentry *ziplist; /* linked list of entries */
240 zipentry *ziptail; /* tail of the linked list */
241
242 int number_of_entries; /* number of entries in the linked list */
243
244 int main(int argc, char **argv){
245
246   char mfile[256];
247   
248   int action = ACTION_NONE;
249   int manifest = TRUE;
250   int manifest_file = FALSE;
251   int file = FALSE;
252   int file_first = FALSE;
253   
254   int i, j;
255   int jarfd = -1;
256   
257   do_compress = TRUE;
258   verbose = FALSE;
259   
260   ziplist = NULL;
261   
262   number_of_entries = 0;
263   
264   if(argc < 2)
265     usage(argv[0]);
266   
267   j = strlen(argv[1]);
268   
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]);
317     }
318   }
319
320   if(action == ACTION_NONE){
321     fprintf(stderr, "One of options -{ctxu} must be specified.\n");
322     usage(argv[0]);
323   }
324
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]);
335   }
336
337   i = 2;
338
339   /* get the jarfile and manifest file (if any) */
340   if(file && file_first){
341     if(i >= argc)
342       usage(argv[0]);
343
344     strncpy(jarfile, argv[i++], 256);
345   }
346   if(manifest_file){
347     if(i >= argc)
348       usage(argv[0]);
349
350     strncpy(mfile, argv[i++], 256);
351   }
352
353   if(file && !file_first){
354     if(i >= argc)
355       usage(argv[0]);
356
357     strncpy(jarfile, argv[i++], 256);
358   }
359
360   /* create the jarfile */
361   if(action == ACTION_CREATE){
362     if(file){
363       jarfd = creat(jarfile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
364       
365       if(jarfd < 0){
366         fprintf(stderr, "Error opening %s for writing!\n", jarfile);
367         perror(jarfile);
368         exit(1);
369       }
370       
371       /* We assume that the file is seekable */
372       seekable = TRUE;
373       
374     } else {
375       
376       jarfd = STDOUT_FILENO;  /* jarfd is stdout otherwise */
377       
378       /* standard out is not seekable */
379       seekable = FALSE;
380       
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;
384     }
385   } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
386
387     if(file){
388       jarfd = open(jarfile, O_RDONLY);
389
390       if(jarfd < 0){
391         fprintf(stderr, "Error opening %s for reading!\n", jarfile);
392         perror(jarfile);
393         exit(1);
394       }
395
396       seekable = TRUE;
397     } else {
398       jarfd = STDIN_FILENO; /* jarfd is standard in */
399
400       /* we assume that the stream isn't seekable for safety */
401       seekable = FALSE;
402     }
403   }
404
405   if(action == ACTION_CREATE || action == ACTION_UPDATE){
406     const char *arg;
407     init_headers();
408
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);
414       }
415     }
416
417     if(do_compress)
418       init_compression();
419   
420
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);
426     
427     init_args (argv, i);
428     /* now we add the files to the archive */
429     while ((arg = get_next_arg ())){
430
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);
439         }
440       } else {
441         if(add_to_jar(jarfd, NULL, arg)){
442           printf("Error adding %s to jar archive!\n", arg);
443           exit(1);
444         }
445       }
446     }
447     /* de-initialize the compression DS */
448     if(do_compress)
449       end_compression();
450     
451     create_central_header(jarfd);
452     
453     if (close(jarfd) != 0) {
454       fprintf(stderr, "Error closing jar archive!\n");
455     }
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));
460   }
461   
462   exit(0);
463 }
464
465 static int args_current_g;
466 static char **args_g;
467
468 static void 
469 init_args(args, current)
470      char **args;
471      int current;
472 {
473   if(!read_names_from_stdin)
474     {
475       args_g = args;
476       args_current_g = current;
477     }
478 }
479
480 static char *
481 get_next_arg ()
482 {
483   static int reached_end = 0;
484
485   if (reached_end)
486     return NULL;
487
488   if (args_g)
489     {
490       if (!args_g [args_current_g])
491         {
492           reached_end = 1;
493           return NULL;
494         }
495       return args_g [args_current_g++];
496     }
497   else
498     {
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. */
502
503       char s [MAXPATHLEN];
504       int  pos = 0;
505
506       /* Get rid of '\n' and '\r' first. */
507       while (1)
508         {
509           int c = getc (stdin);
510           if (c == '\n' || c == '\r')
511             continue;
512           else
513             {
514               if (c == EOF)
515                 return NULL;
516               ungetc (c, stdin);
517               break;
518             }
519         }
520
521       while (1)
522         {
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;
529         }
530
531       if (pos)
532         {
533           s [pos] = '\0';
534           return strdup (s);
535         }
536       else
537         return NULL;
538     }
539 }
540
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;
553
554   file_header[7] = 0x00;
555   /* do_compression method (deflation) */
556   file_header[8] = 0;
557   file_header[9] = 0;
558
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;
586
587   /* Initialize the compression DS */
588   PACK_UB4(data_descriptor, 0, 0x08074b50);
589   
590 }
591
592 void add_entry(struct zipentry *ze){
593
594   if(ziplist == NULL){
595     ziplist = ze;
596     ziptail = ziplist;
597   } else {
598     ziplist->next_entry = ze;
599     ziplist = ze;
600   }
601   
602   number_of_entries++;
603 }
604
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;
610   
611   nlen = 9;  /* trust me on this one */
612
613   memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
614   
615   current_time = time(NULL);
616   if(current_time == (time_t)-1){
617     perror("time");
618     exit(1);
619   }
620
621   mod_time = unix2dostime(&current_time);
622   
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);
627   
628   if(verbose)
629     printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
630   
631   ze = (zipentry*)malloc(sizeof(zipentry));
632   if(ze == NULL){
633     perror("malloc");
634     exit(1);
635   }
636   
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';
641     
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;
646
647   add_entry(ze);
648   
649   write(jfd, file_header, 30);
650   write(jfd, "META-INF/", nlen);
651
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;
656
657     if((mf = (char *) malloc(mf_len + 1))) {
658     uLong crc;
659
660     sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
661
662     crc = crc32(0L, Z_NULL, 0);
663     
664     crc = crc32(crc, (const unsigned char *)mf, mf_len);
665
666     nlen = 20;  /* once again, trust me */
667
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);
672     
673     memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
674     
675     PACK_UB4(file_header, LOC_CRC, crc);
676
677     if(verbose)
678       printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
679     
680     ze = (zipentry*)malloc(sizeof(zipentry));
681     if(ze == NULL){
682       perror("malloc");
683       exit(1);
684     }
685     
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';
690     
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;
698     
699     add_entry(ze);
700     
701     write(jfd, file_header, 30);
702     write(jfd, "META-INF/MANIFEST.MF", nlen);
703     write(jfd, mf, mf_len);
704     free(mf);
705     }
706     else {
707         printf("malloc errror\n");
708         exit(-1);
709     }
710   } else {
711     int mfd;
712     struct stat statbuf;
713
714     stat(mf_name, &statbuf);
715
716     if(!S_ISREG(statbuf.st_mode)){
717       fprintf(stderr, "Invalid manifest file specified.\n");
718       exit(1);
719     }
720   
721     mfd = open(mf_name, O_RDONLY);
722
723     if(mfd < 0){
724       fprintf(stderr, "Error opening %s.\n", mf_name);
725       exit(1);
726     }
727
728     if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
729       perror("error writing to jar");
730       exit(1);
731     }
732
733   }
734
735   return 0;
736 }
737
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;
745   
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.
752    */
753   while (*file=='.' && *(file+1)=='/')
754     file+=2;
755   
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);
760
761     if(chdir(new_dir) == -1){
762       perror(new_dir);
763       return 1;
764     }
765   }
766
767   if(!strcmp(file, jarfile)){
768     if(verbose)
769       printf("skipping: %s\n", file);
770     return 0;  /* we don't want to add ourselves.. */
771   }
772
773   stat_return = stat(file, &statbuf);
774   
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;
782
783     dir = opendir(file);
784     
785     if(dir == NULL){
786       perror("opendir");
787       return 1;
788     }
789     
790     nlen = strlen(file) + 256;
791     fullname = (char*)malloc(nlen * sizeof(char));
792     memset(fullname, 0, (nlen * sizeof(char)));
793     
794     if(fullname == NULL){
795       fprintf(stderr, "Filename is NULL!\n");
796       return 1;
797     }
798
799     strcpy(fullname, file);
800     nlen = strlen(file);
801
802     if(fullname[nlen - 1] != '/'){
803       fullname[nlen] = '/';
804       t_ptr = (fullname + nlen + 1);
805     } else
806       t_ptr = (fullname + nlen);
807
808
809     memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
810     
811     nlen = (t_ptr - fullname);
812
813     mod_time = unix2dostime(&statbuf.st_mtime);
814
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);
819
820     if(verbose)
821       printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
822
823     ze = (zipentry*)malloc(sizeof(zipentry));
824     if(ze == NULL){
825       perror("malloc");
826       exit(1);
827     }
828
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';
833     
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;
838
839     add_entry(ze);
840
841     write(fd, file_header, 30);
842     write(fd, fullname, nlen);
843
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;
851       }
852
853       strcpy(t_ptr, de->d_name);
854
855       if(add_to_jar(fd, NULL, fullname)){
856         fprintf(stderr, "Error adding file to jar!\n");
857         return 1;
858       }
859     }
860
861     free(fullname);
862     closedir(dir);
863       
864   } else if(S_ISREG(statbuf.st_mode)){
865     int add_fd;
866
867     add_fd = open(file, O_RDONLY);
868     if(add_fd < 0){
869       fprintf(stderr, "Error opening %s.\n", file);
870       return 0;
871     }
872     
873     if(add_file_to_jar(fd, add_fd, file, &statbuf)){
874       fprintf(stderr, "Error adding file to jar!\n");
875       return 1;
876     }
877     
878   } else {
879     fprintf(stderr, "Illegal file specified: %s\n", file);
880   }
881   
882   if(old_dir != NULL){
883     if(chdir(old_dir))
884       perror(old_dir);
885     
886     free(old_dir);
887   }
888
889   return 0;
890 }
891
892 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
893
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;
901
902   mod_time = unix2dostime(&(statbuf->st_mtime));
903   file_name_length = strlen(fname);
904
905   if(!seekable && !do_compress){
906     crc = crc32(0L, Z_NULL, 0); 
907     
908     while((rdamt = read(ffd, rd_buff, RDSZ)) != 0) 
909       crc = crc32(crc, rd_buff, rdamt); 
910     
911     lseek(ffd, 0, SEEK_SET);
912   }
913   
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);
919   }
920   
921   if(do_compress){
922     PACK_UB2(file_header, LOC_COMP, 8);
923   } else {
924     PACK_UB2(file_header, LOC_COMP, 0);
925   }
926     
927   PACK_UB4(file_header, LOC_MODTIME, mod_time);
928   PACK_UB2(file_header, LOC_FNLEN, file_name_length);
929   
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 */
936   
937   ze = (zipentry*)malloc(sizeof(zipentry));
938   if(ze == NULL){
939     perror("malloc");
940     exit(1);
941   }
942   
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);
946
947   ze->mod_time = (ub2)(mod_time & 0x0000ffff);
948   ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
949
950   if(!seekable && !do_compress)
951     ze->crc = crc;
952
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;
960   
961   add_entry(ze);
962   
963   /* Write the local header */
964   write(jfd, file_header, 30);
965     
966   /* write the file name to the zip file */
967   write(jfd, fname, file_name_length);
968
969
970   if(verbose){
971     printf("adding: %s ", fname);
972     fflush(stdout);
973   }
974  
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); 
982       
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;
988       }
989     }
990   }
991   close(ffd);
992   
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);
997
998   /* we need to seek back and fill the header */
999   if(seekable){
1000     offset = (ze->csize + strlen(ze->filename) + 16);
1001     
1002     if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1003       perror("lseek");
1004       exit(1);
1005     }
1006
1007     if(write(jfd, (data_descriptor + 4), 12) != 12){
1008       perror("write");
1009       return 0;
1010     }
1011     
1012     offset -= 12;
1013
1014     if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1015       perror("lseek");
1016       exit(1);
1017     }
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 */
1022     
1023     if(write(jfd, data_descriptor, 16) != 16){
1024       perror("write");
1025       return 0;
1026     }
1027   }
1028   
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));
1034
1035   return 0;
1036 }
1037
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;
1045
1046   zipentry *ze;
1047
1048   iheader = (int*)header;
1049
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;
1113
1114   start_offset = lseek(fd, 0, SEEK_CUR);
1115
1116   for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1117
1118     total_in += ze->usize;
1119     total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1120
1121     if(ze->compressed){
1122       PACK_UB2(header, CEN_COMP, 8);
1123     } else {
1124       PACK_UB2(header, CEN_COMP, 0);
1125     }
1126         
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);
1134
1135     write(fd, header, 46);
1136
1137     write(fd, ze->filename, strlen(ze->filename));
1138   }
1139
1140   dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1141
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;
1164
1165   write(fd, end_header, 22);
1166   
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)
1173            );
1174
1175   return 0;
1176 }
1177
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;
1198
1199   init_inflation();
1200
1201   pb_init(&pbf, fd);
1202
1203   for(;;){
1204     f_fd = 0;
1205     crc = 0;
1206     ze.crc = 0;
1207     
1208     dir = FALSE; /* by default, the file isn't a dir */
1209     handle = TRUE; /* by default we'll extract/create the file */
1210
1211     if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1212       perror("read");
1213       break;
1214     }
1215     
1216     signature = UNPACK_UB4(scratch, 0);
1217
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;
1235     }
1236     
1237     if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1238       perror("read");
1239       break;
1240     }
1241     
1242     csize = UNPACK_UB4(file_header, LOC_CSIZE);
1243 #ifdef DEBUG    
1244     printf("Compressed size is %u\n", csize);
1245 #endif
1246
1247     fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1248 #ifdef DEBUG    
1249     printf("Filename length is %hu\n", fnlen);
1250 #endif
1251
1252     eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1253 #ifdef DEBUG    
1254     printf("Extra field length is %hu\n", eflen);
1255 #endif
1256
1257     flags = UNPACK_UB2(file_header, LOC_EXTRA);
1258 #ifdef DEBUG    
1259     printf("Flags are %#hx\n", flags);
1260 #endif
1261
1262     method = UNPACK_UB2(file_header, LOC_COMP);
1263 #ifdef DEBUG
1264     printf("Compression method is %#hx\n", method);
1265 #endif
1266
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
1273     }
1274
1275     if(filename_len < fnlen){
1276       if(filename != NULL)
1277         free(filename);
1278       
1279       filename = malloc(sizeof(ub1) * (fnlen + 1));
1280       filename_len = fnlen + 1;
1281     }
1282
1283     pb_read(&pbf, filename, fnlen);
1284     filename[fnlen] = '\0';
1285
1286 #ifdef DEBUG    
1287     printf("filename is %s\n", filename);
1288 #endif
1289
1290     if(file_num > 0){
1291       handle = FALSE;
1292       
1293       for(j = 0; j < file_num; j++)
1294         if(strcmp(files[j], (const char *)filename) == 0){
1295           handle = TRUE;
1296           break;
1297         }
1298     }
1299
1300     if(!handle)
1301       f_fd = -1;
1302
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;
1311
1312       tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1313
1314       for(;;){
1315         const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1316
1317         if(idx == NULL)
1318           break;
1319         else if(idx == start){
1320           start++;
1321           continue;
1322         }
1323         start = idx + 1;
1324
1325         strncpy(tmp_buff, (const char *)filename, (idx - filename));
1326         tmp_buff[(idx - filename)] = '\0';
1327
1328 #ifdef DEBUG    
1329         printf("checking the existance of %s\n", tmp_buff);
1330 #endif
1331
1332         if(stat(tmp_buff, &sbuf) < 0){
1333           if(errno != ENOENT){
1334             perror("stat");
1335             exit(1);
1336           }
1337
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);
1347         }
1348         
1349 #ifdef DEBUG    
1350         printf("Making directory..\n");
1351 #endif
1352         if(mkdir(tmp_buff, 0755) < 0){
1353           perror("mkdir");
1354           exit(1);
1355         }
1356         if(verbose && handle)
1357           printf("%10s: %s/\n", "created", tmp_buff);
1358
1359       }
1360
1361       /* only a directory */
1362       if(strlen((const char *)start) == 0)
1363         dir = TRUE;
1364
1365 #ifdef DEBUG    
1366       printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1367 #endif
1368
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;
1372
1373       free(tmp_buff);
1374     }
1375
1376     if(f_fd != -1 && handle){
1377       f_fd = creat((const char *)filename, 00644);
1378
1379       if(f_fd < 0){
1380         fprintf(stderr, "Error extracting JAR archive!\n");
1381         perror((const char *)filename);
1382         exit(1);
1383       }
1384     }
1385
1386     if(method != 8 && flags & 0x0008){
1387       fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1388       exit(1);
1389     }
1390
1391     if(method == 8 || flags & 0x0008){
1392       if(seekable)
1393         lseek(fd, eflen, SEEK_CUR);
1394       else
1395         consume(&pbf, eflen);
1396       
1397       inflate_file(&pbf, f_fd, &ze);
1398     } else {
1399
1400 #ifdef DEBUG    
1401       printf("writing stored data.. (%d bytes)\n", csize);
1402 #endif
1403
1404       out_a = 0;
1405       in_a = csize;
1406
1407       ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1408
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);
1414         }
1415         
1416         ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1417
1418         if(f_fd >= 0)
1419           write(f_fd, rd_buff, rdamt);
1420
1421         out_a += rdamt;
1422         in_a -= rdamt;
1423
1424 #ifdef DEBUG    
1425         printf("%d bytes written\n", out_a);
1426 #endif
1427       }
1428
1429       if(seekable)
1430         lseek(fd, eflen, SEEK_CUR);
1431       else
1432         consume(&pbf, eflen);
1433     }
1434
1435     /* if there is a data descriptor left, compare the CRC */
1436     if(flags & 0x0008){
1437
1438       if(pb_read(&pbf, scratch, 16) != 16){
1439         perror("read");
1440         exit(1);
1441       }
1442
1443       signature = UNPACK_UB4(scratch, 0);
1444
1445       if(signature != 0x08074b50){
1446         fprintf(stderr, "Error! Missing data descriptor!\n");
1447         exit(1);
1448       }
1449
1450       crc = UNPACK_UB4(scratch, 4);
1451
1452     }
1453
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);
1458     }
1459
1460     close(f_fd);
1461
1462     if(verbose && dir == FALSE && handle)
1463       printf("%10s: %s\n",
1464              (method == 8 ? "inflated" : "extracted"),
1465              filename);
1466   }
1467
1468   return 0;
1469 }
1470
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;
1494
1495 #ifdef DEBUG
1496   printf("Listing jar file, looking for %d files\n", file_num);
1497 #endif
1498
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);
1504     }
1505     
1506     if(read(fd, &tmp, sizeof(ub4)) != 4){
1507       perror("read");
1508       exit(1);
1509     }
1510
1511 #ifdef WORDS_BIGENDIAN
1512     tmp = L2BI(tmp);
1513 #endif
1514
1515     if(tmp != 0x06054b50){
1516       fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1517       exit(1);
1518     }
1519
1520     if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1521       perror("lseek");
1522       exit(1);
1523     }
1524   
1525     if(read(fd, &cen_size, 2) != 2){
1526       perror("read");
1527       exit(1);
1528     }
1529
1530 #ifdef WORDS_BIGENDIAN
1531     cen_size = L2BS(cen_size);
1532 #endif
1533
1534     /*   printf("%hu entries in central header\n", cen_size); */
1535
1536     if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1537       perror("lseek");
1538       exit(1);
1539     }
1540
1541     if(read(fd, &tmp, 4) != 4){
1542       perror("read");
1543       exit(1);
1544     }
1545
1546 #ifdef WORDS_BIGENDIAN
1547     tmp = L2BI(tmp);
1548 #endif
1549
1550     /*   printf("Central header offset = %d\n", tmp); */
1551
1552     if(lseek(fd, tmp, SEEK_SET) != tmp){
1553       perror("lseek");
1554       exit(1);
1555     }
1556
1557     /* Loop through the entries in the central header */
1558     for(i = 0; i < cen_size; i++){
1559     
1560       if(read(fd, &cen_header, 46) != 46){
1561         perror("read");
1562         exit(1);
1563       }
1564
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);
1569       }
1570
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);
1575
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);
1583       }
1584
1585       if(filename_len < fnlen){
1586         if(filename != NULL)
1587           free(filename);
1588       
1589         filename = malloc(sizeof(ub1) * (fnlen + 1));
1590         filename_len = fnlen + 1;
1591       }
1592     
1593       if(read(fd, filename, fnlen) != fnlen){
1594         perror("read");
1595         exit(1);
1596       }
1597       filename[fnlen] = '\0';
1598     
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;
1609           }
1610       } else {
1611         if(verbose)
1612           printf("%6d %s %s\n", usize, ascii_date, filename);
1613         else
1614           printf("%s\n", filename);
1615       }            
1616       
1617       size = eflen + clen;
1618       if(size > 0){
1619         if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1620           perror("lseek");
1621           exit(1);
1622         }
1623       }
1624     }
1625   } else {
1626     /* the file isn't seekable.. evil! */
1627     pb_file pbf;
1628
1629     pb_init(&pbf, fd);
1630
1631     init_inflation();
1632
1633     for(;;){
1634       if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1635         perror("read");
1636         break;
1637       }
1638       
1639       signature = UNPACK_UB4(scratch, 0);
1640       
1641 #ifdef DEBUG
1642       printf("signature is %x\n", signature);
1643 #endif
1644       
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;
1661       }
1662       
1663       if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1664         perror("read");
1665         break;
1666       }
1667       
1668       csize = UNPACK_UB4(file_header, LOC_CSIZE);
1669 #ifdef DEBUG
1670       printf("Compressed size is %u\n", csize);
1671 #endif
1672       
1673       fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1674 #ifdef DEBUG
1675       printf("Filename length is %hu\n", fnlen);
1676 #endif
1677       
1678       eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1679 #ifdef DEBUG
1680       printf("Extra field length is %hu\n", eflen);
1681 #endif
1682       
1683       method = UNPACK_UB2(file_header, LOC_COMP);
1684 #ifdef DEBUG
1685       printf("Compression method is %#hx\n", method);
1686 #endif
1687
1688       flags = UNPACK_UB2(file_header, LOC_EXTRA);
1689 #ifdef DEBUG
1690       printf("Flags are %#hx\n", flags);
1691 #endif
1692       
1693       usize = UNPACK_UB4(file_header, LOC_USIZE);
1694
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);
1702       }
1703
1704       if(filename_len < fnlen){
1705         if(filename != NULL)
1706           free(filename);
1707         
1708         filename = malloc(sizeof(ub1) * (fnlen + 1));
1709         filename_len = fnlen + 1;
1710       }
1711       
1712       pb_read(&pbf, filename, fnlen);
1713       filename[fnlen] = '\0';
1714       
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){
1719
1720         size = eflen;
1721
1722         if(size > 0)
1723           consume(&pbf, size);
1724         
1725         if(method == 8){
1726 #ifdef DEBUG
1727           printf("inflating %s\n", filename);
1728 #endif
1729           inflate_file(&pbf, -1, &ze);
1730
1731           usize = ze.usize;
1732         } else 
1733           printf("We're shit outta luck!\n");
1734           
1735       } else {
1736         size = csize + (eflen > 0 ? eflen : 0);
1737         
1738
1739 #ifdef DEBUG
1740         printf("Skipping %ld bytes\n", (long)size);
1741 #endif
1742
1743         consume(&pbf, size);
1744       }
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;
1754           }
1755       } else {
1756         if(verbose)
1757           printf("%6d %s %s\n", usize, ascii_date, filename);
1758         else
1759           printf("%s\n", filename);
1760       }        
1761     }
1762   }
1763   return 0;
1764 }
1765
1766 int consume(pb_file *pbf, int amt){
1767   int tc = 0; /* total amount consumed */
1768   ub1 buff[RDSZ];
1769   int rdamt;
1770
1771 #ifdef DEBUG
1772   printf("Consuming %d bytes\n", amt);
1773 #endif
1774
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;
1781   }
1782
1783 #ifdef DEBUG
1784   printf("%d bytes consumed\n", tc);
1785 #endif
1786
1787   return 0;
1788 }
1789
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\
1809 ");
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\
1814 \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\
1820 ");
1821
1822   exit(1);
1823 }