OSDN Git Service

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