OSDN Git Service

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