OSDN Git Service

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