OSDN Git Service

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