OSDN Git Service

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