OSDN Git Service

PR doc/7484
[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,
443                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
444
445       if(jarfd < 0){
446         fprintf(stderr, "Error opening %s for writing!\n", jarfile);
447         perror(jarfile);
448         exit(1);
449       }
450       
451       /* We assume that the file is seekable */
452       seekable = TRUE;
453       
454     } else {
455       
456       jarfd = STDOUT_FILENO;  /* jarfd is stdout otherwise */
457       
458       /* standard out is not seekable */
459       seekable = FALSE;
460       
461       /* don't want our output to be part of the jar file.. figured this one
462          out the hard way.. =P */
463       verbose = FALSE;
464     }
465   } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
466
467     if(jarfile){
468       jarfd = open(jarfile, O_RDONLY | O_BINARY);
469
470       if(jarfd < 0){
471         fprintf(stderr, "Error opening %s for reading!\n", jarfile);
472         perror(jarfile);
473         exit(1);
474       }
475
476       seekable = TRUE;
477     } else {
478       jarfd = STDIN_FILENO; /* jarfd is standard in */
479
480       /* we assume that the stream isn't seekable for safety */
481       seekable = FALSE;
482     }
483   }
484
485   if(action == ACTION_CREATE || action == ACTION_UPDATE){
486     const char *arg;
487     init_headers();
488
489    if((action == ACTION_UPDATE) && jarfile) {
490       if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
491         fprintf(stderr, "Error opening %s for reading!\n", jarfile);
492         perror(jarfile);
493         exit(1);
494       }
495     }
496
497     if(do_compress)
498       init_compression();
499   
500
501     /* Add the META-INF/ directory and the manifest */
502     if(manifest && mfile)
503       make_manifest(jarfd, mfile);
504     else if(manifest)
505       make_manifest(jarfd, NULL);
506     
507     init_args (new_argv, 0);
508     /* now we add the files to the archive */
509     while ((arg = get_next_arg ())){
510
511       if(!strcmp(arg, "-C")){
512         const char *dir_to_change = get_next_arg ();
513         const char *file_to_add = get_next_arg ();
514         if(!dir_to_change 
515            || !file_to_add
516            || add_to_jar(jarfd, dir_to_change, file_to_add)){
517           printf("Error adding %s to jar archive!\n", arg);
518           exit(1);
519         }
520       } else {
521         if(add_to_jar(jarfd, NULL, arg)){
522           printf("Error adding %s to jar archive!\n", arg);
523           exit(1);
524         }
525       }
526     }
527     /* de-initialize the compression DS */
528     if(do_compress)
529       end_compression();
530     
531     create_central_header(jarfd);
532     
533     if (close(jarfd) != 0) {
534       fprintf(stderr, "Error closing jar archive!\n");
535     }
536   } else if(action == ACTION_LIST){
537     list_jar(jarfd, &new_argv[0], new_argc);
538   } else if(action == ACTION_EXTRACT){
539     extract_jar(jarfd, &new_argv[0], new_argc);
540   }
541   
542   exit(0);
543 }
544
545 static int args_current_g;
546 static char **args_g;
547
548 static void 
549 init_args(args, current)
550      char **args;
551      int current;
552 {
553   if(!read_names_from_stdin)
554     {
555       args_g = args;
556       args_current_g = current;
557     }
558 }
559
560 static char *
561 get_next_arg ()
562 {
563   static int reached_end = 0;
564
565   if (reached_end)
566     return NULL;
567
568   if (args_g)
569     {
570       if (!args_g [args_current_g])
571         {
572           reached_end = 1;
573           return NULL;
574         }
575       return args_g [args_current_g++];
576     }
577   else
578     {
579       /* Read the name from stdin. Delimiters are '\n' and
580          '\r'. Reading EOF indicates that we don't have anymore file
581          names characters to read. */
582
583       char s [MAXPATHLEN];
584       int  pos = 0;
585
586       /* Get rid of '\n' and '\r' first. */
587       while (1)
588         {
589           int c = getc (stdin);
590           if (c == '\n' || c == '\r')
591             continue;
592           else
593             {
594               if (c == EOF)
595                 return NULL;
596               ungetc (c, stdin);
597               break;
598             }
599         }
600
601       while (1)
602         {
603           int c = getc (stdin);
604           /* Exit when we get a delimiter or don't have any characters
605              to read */
606           if (c == '\n'|| c == '\r'|| c == EOF)
607             break;
608           s [pos++] = (char) c;
609         }
610
611       if (pos)
612         {
613           s [pos] = '\0';
614           return jt_strdup (s);
615         }
616       else
617         return NULL;
618     }
619 }
620
621 void init_headers(){
622   /* packing file header */
623   /* magic number */
624   file_header[0] = 0x50;
625   file_header[1] = 0x4b;
626   file_header[2] = 0x03;
627   file_header[3] = 0x04;
628   /* version number (Unix 1.0)*/
629   file_header[4] = 10;
630   file_header[5] = 0;
631   /* bit flag (normal deflation)*/
632   file_header[6] = 0x00;
633
634   file_header[7] = 0x00;
635   /* do_compression method (deflation) */
636   file_header[8] = 0;
637   file_header[9] = 0;
638
639   /* last mod file time (MS-DOS format) */
640   file_header[10] = 0;
641   file_header[11] = 0;
642   /* last mod file date (MS-DOS format) */
643   file_header[12] = 0;
644   file_header[13] = 0;
645   /* CRC 32 */
646   file_header[14] = 0;
647   file_header[15] = 0;
648   file_header[16] = 0;
649   file_header[17] = 0;
650   /* compressed size */
651   file_header[18] = 0;
652   file_header[19] = 0;
653   file_header[20] = 0;
654   file_header[21] = 0;
655   /* uncompressed size */
656   file_header[22] = 0;
657   file_header[23] = 0;
658   file_header[24] = 0;
659   file_header[25] = 0;
660   /* filename length */
661   file_header[26] = 0;
662   file_header[27] = 0;
663   /* extra field length */
664   file_header[28] = 0;
665   file_header[29] = 0;
666
667   /* Initialize the compression DS */
668   PACK_UB4(data_descriptor, 0, 0x08074b50);
669   
670 }
671
672 void add_entry(struct zipentry *ze){
673
674   if(ziplist == NULL){
675     ziplist = ze;
676     ziptail = ziplist;
677   } else {
678     ziplist->next_entry = ze;
679     ziplist = ze;
680   }
681   
682   number_of_entries++;
683 }
684
685 int make_manifest(int jfd, const char *mf_name){
686   time_t current_time;
687   int nlen;   /* length of file name */
688   int mod_time; /* file modification time */
689   struct zipentry *ze;
690   
691   nlen = 9;  /* trust me on this one */
692
693   memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
694   
695   current_time = time(NULL);
696   if(current_time == (time_t)-1){
697     perror("time");
698     exit(1);
699   }
700
701   mod_time = unix2dostime(&current_time);
702   
703   PACK_UB2(file_header, LOC_EXTRA, 0);
704   PACK_UB2(file_header, LOC_COMP, 0);
705   PACK_UB2(file_header, LOC_FNLEN, nlen);
706   PACK_UB4(file_header, LOC_MODTIME, mod_time);
707   
708   if(verbose)
709     printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
710   
711   ze = (zipentry*)malloc(sizeof(zipentry));
712   if(ze == NULL){
713     perror("malloc");
714     exit(1);
715   }
716   
717   memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
718   ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
719   strcpy(ze->filename, "META-INF/");
720   ze->filename[nlen] = '\0';
721     
722   ze->offset = lseek(jfd, 0, SEEK_CUR);
723   ze->mod_time = (ub2)(mod_time & 0x0000ffff);
724   ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
725   ze->compressed = FALSE;
726
727   add_entry(ze);
728   
729   write(jfd, file_header, 30);
730   write(jfd, "META-INF/", nlen);
731
732   /* if the user didn't specify an external manifest file... */
733   if(mf_name == NULL){
734     int mf_len = 37 + strlen(VERSION);
735     char *mf;
736
737     if((mf = (char *) malloc(mf_len + 1))) {
738     uLong crc;
739
740     sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
741
742     crc = crc32(0L, Z_NULL, 0);
743     
744     crc = crc32(crc, (const unsigned char *)mf, mf_len);
745
746     nlen = 20;  /* once again, trust me */
747
748     PACK_UB2(file_header, LOC_EXTRA, 0);
749     PACK_UB2(file_header, LOC_COMP, 0);
750     PACK_UB2(file_header, LOC_FNLEN, nlen);
751     PACK_UB4(file_header, LOC_USIZE, mf_len);
752     
753     memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
754     
755     PACK_UB4(file_header, LOC_CRC, crc);
756
757     if(verbose)
758       printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
759     
760     ze = (zipentry*)malloc(sizeof(zipentry));
761     if(ze == NULL){
762       perror("malloc");
763       exit(1);
764     }
765     
766     memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
767     ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
768     strcpy(ze->filename, "META-INF/MANIFEST.MF");
769     ze->filename[nlen] = '\0';
770     
771     ze->offset = lseek(jfd, 0, SEEK_CUR);
772     ze->mod_time = (ub2)(mod_time & 0x0000ffff);
773     ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
774     ze->crc = crc;
775     ze->csize = mf_len;
776     ze->usize = ze->csize;
777     ze->compressed = FALSE;
778     
779     add_entry(ze);
780     
781     write(jfd, file_header, 30);
782     write(jfd, "META-INF/MANIFEST.MF", nlen);
783     write(jfd, mf, mf_len);
784     free(mf);
785     }
786     else {
787         printf("malloc errror\n");
788         exit(-1);
789     }
790   } else {
791     int mfd;
792     struct stat statbuf;
793
794     stat(mf_name, &statbuf);
795
796     if(!S_ISREG(statbuf.st_mode)){
797       fprintf(stderr, "Invalid manifest file specified.\n");
798       exit(1);
799     }
800   
801     mfd = open(mf_name, O_RDONLY | O_BINARY);
802
803     if(mfd < 0){
804       fprintf(stderr, "Error opening %s.\n", mf_name);
805       exit(1);
806     }
807
808     if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
809       perror("error writing to jar");
810       exit(1);
811     }
812
813   }
814
815   return 0;
816 }
817
818 int add_to_jar(int fd, const char *new_dir, const char *file){
819   struct stat statbuf;
820   DIR *dir;
821   struct dirent *de;
822   zipentry *ze;
823   int stat_return;
824   char *old_dir = NULL;
825   
826   /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com> 
827    * It fixes this:
828    *   "normal" jar : org/apache/java/io/LogRecord.class
829    *   fastjar      : ./org/apache/java/io/LogRecord.class
830    * Fastjar's preservation of the ./'s makes the jarfile unusuable for use 
831    * with both kaffe-1.0b4 and JDK.
832    */
833   while (*file=='.' && *(file+1)=='/')
834     file+=2;
835   
836   /* If new_dir isn't null, we need to change to that directory.  However,
837      we also need to return to the old directory when we're done */
838   if(new_dir != NULL){
839     old_dir = getcwd(NULL, 0);
840
841     if(chdir(new_dir) == -1){
842       perror(new_dir);
843       return 1;
844     }
845   }
846
847   if(!strcmp(file, jarfile)){
848     if(verbose)
849       printf("skipping: %s\n", file);
850     return 0;  /* we don't want to add ourselves.. */
851   }
852
853   stat_return = stat(file, &statbuf);
854   
855   if(stat_return == -1){
856     perror(file);
857     return 1;
858   } else if(S_ISDIR(statbuf.st_mode)){
859     char *fullname;
860     char *t_ptr;
861     int nlen;
862     unsigned long mod_time;
863
864     dir = opendir(file);
865     
866     if(dir == NULL){
867       perror("opendir");
868       return 1;
869     }
870     
871     nlen = strlen(file) + 256;
872     fullname = (char*)malloc(nlen * sizeof(char));
873     memset(fullname, 0, (nlen * sizeof(char)));
874     
875     if(fullname == NULL){
876       fprintf(stderr, "Filename is NULL!\n");
877       return 1;
878     }
879
880     strcpy(fullname, file);
881     nlen = strlen(file);
882
883     if(fullname[nlen - 1] != '/'){
884       fullname[nlen] = '/';
885       t_ptr = (fullname + nlen + 1);
886     } else
887       t_ptr = (fullname + nlen);
888
889
890     memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
891     
892     nlen = (t_ptr - fullname);
893
894     mod_time = unix2dostime(&statbuf.st_mtime);
895
896     PACK_UB2(file_header, LOC_EXTRA, 0);
897     PACK_UB2(file_header, LOC_COMP, 0);
898     PACK_UB2(file_header, LOC_FNLEN, nlen);
899     PACK_UB4(file_header, LOC_MODTIME, mod_time);
900
901     if(verbose)
902       printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
903
904     ze = (zipentry*)malloc(sizeof(zipentry));
905     if(ze == NULL){
906       perror("malloc");
907       exit(1);
908     }
909
910     memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
911     ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
912     strcpy(ze->filename, fullname);
913     ze->filename[nlen] = '\0';
914     
915     ze->offset = lseek(fd, 0, SEEK_CUR);
916     ze->mod_time = (ub2)(mod_time & 0x0000ffff);
917     ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
918     ze->compressed = FALSE;
919
920     add_entry(ze);
921
922     write(fd, file_header, 30);
923     write(fd, fullname, nlen);
924
925     while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
926       if(de->d_name[0] == '.')
927         continue;
928       if(!strcmp(de->d_name, jarfile)){ /* 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 = creat((const char *)filename, 00644);
1459
1460       if(f_fd < 0){
1461         fprintf(stderr, "Error extracting JAR archive!\n");
1462         perror((const char *)filename);
1463         exit(1);
1464       }
1465     }
1466
1467     if(method != 8 && flags & 0x0008){
1468       fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1469       exit(1);
1470     }
1471
1472     if(method == 8 || flags & 0x0008){
1473       if(seekable)
1474         lseek(fd, eflen, SEEK_CUR);
1475       else
1476         consume(&pbf, eflen);
1477       
1478       inflate_file(&pbf, f_fd, &ze);
1479     } else {
1480
1481 #ifdef DEBUG    
1482       printf("writing stored data.. (%d bytes)\n", csize);
1483 #endif
1484
1485       out_a = 0;
1486       in_a = csize;
1487
1488       ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1489
1490       while(out_a < (int)csize){
1491         rdamt = (in_a > RDSZ ? RDSZ : in_a);
1492         if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1493           perror("read");
1494           exit(1);
1495         }
1496         
1497         ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1498
1499         if(f_fd >= 0)
1500           write(f_fd, rd_buff, rdamt);
1501
1502         out_a += rdamt;
1503         in_a -= rdamt;
1504
1505 #ifdef DEBUG    
1506         printf("%d bytes written\n", out_a);
1507 #endif
1508       }
1509
1510       if(seekable)
1511         lseek(fd, eflen, SEEK_CUR);
1512       else
1513         consume(&pbf, eflen);
1514     }
1515
1516     /* if there is a data descriptor left, compare the CRC */
1517     if(flags & 0x0008){
1518
1519       if(pb_read(&pbf, scratch, 16) != 16){
1520         perror("read");
1521         exit(1);
1522       }
1523
1524       signature = UNPACK_UB4(scratch, 0);
1525
1526       if(signature != 0x08074b50){
1527         fprintf(stderr, "Error! Missing data descriptor!\n");
1528         exit(1);
1529       }
1530
1531       crc = UNPACK_UB4(scratch, 4);
1532
1533     }
1534
1535     if(crc != ze.crc){
1536       fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1537               ze.crc, crc);
1538       exit(1);
1539     }
1540
1541     close(f_fd);
1542
1543     if(verbose && dir == FALSE && handle)
1544       printf("%10s: %s\n",
1545              (method == 8 ? "inflated" : "extracted"),
1546              filename);
1547   }
1548
1549   return 0;
1550 }
1551
1552 int list_jar(int fd, char **files, int file_num){
1553   int rdamt;
1554   ub4 signature;
1555   ub4 csize;
1556   ub4 usize;
1557   ub4 mdate;
1558   ub4 tmp;
1559   ub2 fnlen;
1560   ub2 eflen;
1561   ub2 clen;
1562   ub2 flags;
1563   ub2 method;
1564   ub2 cen_size;
1565   ub1 *filename = NULL;
1566   ub1 scratch[16];
1567   ub1 cen_header[46];
1568   int filename_len = 0;
1569   off_t size;
1570   int i, j;
1571   time_t tdate;
1572   struct tm *s_tm;
1573   char ascii_date[30];
1574   zipentry ze;
1575
1576 #ifdef DEBUG
1577   printf("Listing jar file, looking for %d files\n", file_num);
1578 #endif
1579
1580   /* This should be the start of the central-header-end section */
1581   if(seekable){
1582     if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1583       perror("lseek");
1584       exit(1);
1585     }
1586     
1587     if(read(fd, &tmp, sizeof(ub4)) != 4){
1588       perror("read");
1589       exit(1);
1590     }
1591
1592 #ifdef WORDS_BIGENDIAN
1593     tmp = L2BI(tmp);
1594 #endif
1595
1596     if(tmp != 0x06054b50){
1597       fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1598       exit(1);
1599     }
1600
1601     if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1602       perror("lseek");
1603       exit(1);
1604     }
1605   
1606     if(read(fd, &cen_size, 2) != 2){
1607       perror("read");
1608       exit(1);
1609     }
1610
1611 #ifdef WORDS_BIGENDIAN
1612     cen_size = L2BS(cen_size);
1613 #endif
1614
1615     /*   printf("%hu entries in central header\n", cen_size); */
1616
1617     if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1618       perror("lseek");
1619       exit(1);
1620     }
1621
1622     if(read(fd, &tmp, 4) != 4){
1623       perror("read");
1624       exit(1);
1625     }
1626
1627 #ifdef WORDS_BIGENDIAN
1628     tmp = L2BI(tmp);
1629 #endif
1630
1631     /*   printf("Central header offset = %d\n", tmp); */
1632
1633     if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1634       perror("lseek");
1635       exit(1);
1636     }
1637
1638     /* Loop through the entries in the central header */
1639     for(i = 0; i < cen_size; i++){
1640     
1641       if(read(fd, &cen_header, 46) != 46){
1642         perror("read");
1643         exit(1);
1644       }
1645
1646       signature = UNPACK_UB4(cen_header, 0);
1647       if(signature != 0x02014b50){
1648         fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1649         exit(1);
1650       }
1651
1652       usize = UNPACK_UB4(cen_header, CEN_USIZE);
1653       fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1654       eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1655       clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1656
1657       /* If we're providing verbose output, we need to make an ASCII
1658        * formatted version of the date. */
1659       if(verbose){
1660         mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1661         tdate = dos2unixtime(mdate);
1662         s_tm = localtime(&tdate);
1663         strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1664       }
1665
1666       if(filename_len < fnlen + 1){
1667         if(filename != NULL)
1668           free(filename);
1669       
1670         filename = malloc(sizeof(ub1) * (fnlen + 1));
1671         filename_len = fnlen + 1;
1672       }
1673     
1674       if(read(fd, filename, fnlen) != fnlen){
1675         perror("read");
1676         exit(1);
1677       }
1678       filename[fnlen] = '\0';
1679     
1680       /* if the user specified a list of files on the command line,
1681          we'll only display those, otherwise we'll display everything */
1682       if(file_num > 0){
1683         for(j = 0; j < file_num; j++)
1684           if(strcmp(files[j], (const char *)filename) == 0){
1685             if(verbose)
1686               printf("%6d %s %s\n", usize, ascii_date, filename);
1687             else
1688               printf("%s\n", filename);
1689             break;
1690           }
1691       } else {
1692         if(verbose)
1693           printf("%6d %s %s\n", usize, ascii_date, filename);
1694         else
1695           printf("%s\n", filename);
1696       }            
1697       
1698       size = eflen + clen;
1699       if(size > 0){
1700         if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1701           perror("lseek");
1702           exit(1);
1703         }
1704       }
1705     }
1706   } else {
1707     /* the file isn't seekable.. evil! */
1708     pb_file pbf;
1709
1710     pb_init(&pbf, fd);
1711
1712     init_inflation();
1713
1714     for(;;){
1715       if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1716         perror("read");
1717         break;
1718       }
1719       
1720       signature = UNPACK_UB4(scratch, 0);
1721       
1722 #ifdef DEBUG
1723       printf("signature is %x\n", signature);
1724 #endif
1725       
1726       if(signature == 0x08074b50){
1727 #ifdef DEBUG
1728         printf("skipping data descriptor\n");
1729 #endif
1730         pb_read(&pbf, scratch, 12);
1731         continue;
1732       } else if(signature == 0x02014b50){
1733 #ifdef DEBUG
1734         printf("Central header reached.. we're all done!\n");
1735 #endif
1736         break;
1737       }else if(signature != 0x04034b50){
1738 #ifdef DEBUG
1739         printf("Ick! %#x\n", signature);
1740 #endif
1741         break;
1742       }
1743       
1744       if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1745         perror("read");
1746         break;
1747       }
1748       
1749       csize = UNPACK_UB4(file_header, LOC_CSIZE);
1750 #ifdef DEBUG
1751       printf("Compressed size is %u\n", csize);
1752 #endif
1753       
1754       fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1755 #ifdef DEBUG
1756       printf("Filename length is %hu\n", fnlen);
1757 #endif
1758       
1759       eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1760 #ifdef DEBUG
1761       printf("Extra field length is %hu\n", eflen);
1762 #endif
1763       
1764       method = UNPACK_UB2(file_header, LOC_COMP);
1765 #ifdef DEBUG
1766       printf("Compression method is %#hx\n", method);
1767 #endif
1768
1769       flags = UNPACK_UB2(file_header, LOC_EXTRA);
1770 #ifdef DEBUG
1771       printf("Flags are %#hx\n", flags);
1772 #endif
1773       
1774       usize = UNPACK_UB4(file_header, LOC_USIZE);
1775
1776       /* If we're providing verbose output, we need to make an ASCII
1777        * formatted version of the date. */
1778       if(verbose){
1779         mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1780         tdate = dos2unixtime(mdate);
1781         s_tm = localtime(&tdate);
1782         strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1783       }
1784
1785       if(filename_len < fnlen + 1){
1786         if(filename != NULL)
1787           free(filename);
1788         
1789         filename = malloc(sizeof(ub1) * (fnlen + 1));
1790         filename_len = fnlen + 1;
1791       }
1792       
1793       pb_read(&pbf, filename, fnlen);
1794       filename[fnlen] = '\0';
1795       
1796       /* the header is at the end.  In a JAR file, this means that the data
1797          happens to be compressed.  We have no choice but to inflate the
1798          data */
1799       if(flags & 0x0008){
1800
1801         size = eflen;
1802
1803         if(size > 0)
1804           consume(&pbf, size);
1805         
1806         if(method == 8){
1807 #ifdef DEBUG
1808           printf("inflating %s\n", filename);
1809 #endif
1810           inflate_file(&pbf, -1, &ze);
1811
1812           usize = ze.usize;
1813         } else 
1814           printf("We're shit outta luck!\n");
1815           
1816       } else {
1817         size = csize + (eflen > 0 ? eflen : 0);
1818         
1819
1820 #ifdef DEBUG
1821         printf("Skipping %ld bytes\n", (long)size);
1822 #endif
1823
1824         consume(&pbf, size);
1825       }
1826       /* print out the listing */
1827       if(file_num > 0){
1828         for(j = 0; j < file_num; j++)
1829           if(strcmp(files[j], (const char *)filename) == 0){
1830             if(verbose)
1831               printf("%6d %s %s\n", usize, ascii_date, filename);
1832             else
1833               printf("%s\n", filename);
1834             break;
1835           }
1836       } else {
1837         if(verbose)
1838           printf("%6d %s %s\n", usize, ascii_date, filename);
1839         else
1840           printf("%s\n", filename);
1841       }        
1842     }
1843   }
1844   return 0;
1845 }
1846
1847 int consume(pb_file *pbf, int amt){
1848   int tc = 0; /* total amount consumed */
1849   ub1 buff[RDSZ];
1850   int rdamt;
1851
1852 #ifdef DEBUG
1853   printf("Consuming %d bytes\n", amt);
1854 #endif
1855
1856   while(tc < amt){
1857     rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1858 #ifdef DEBUG
1859     printf("got %d bytes\n", rdamt);
1860 #endif
1861     tc += rdamt;
1862   }
1863
1864 #ifdef DEBUG
1865   printf("%d bytes consumed\n", tc);
1866 #endif
1867
1868   return 0;
1869 }
1870
1871 void usage(const char *filename){
1872   fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1873   exit (1);
1874 }
1875
1876 void version ()
1877 {
1878   printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1879   printf("Copyright 1999, 2000, 2001  Bryan Burns\n");
1880   printf("Copyright 2002 Free Software Foundation\n");
1881   printf("\
1882 This is free software; see the source for copying conditions.  There is NO\n\
1883 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1884   exit (0);
1885 }
1886
1887 void help(const char *filename)
1888 {
1889   printf("\
1890 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1891 \n\
1892 Store many files together in a single `jar' file.\n\
1893 \n\
1894   -c              create new archive\n\
1895   -t              list table of contents for archive\n\
1896   -x              extract named (or all) files from archive\n\
1897   -u              update existing archive\n\
1898 ", filename);
1899   printf("\n\
1900   -@              read names from stdin\n\
1901   -0              store only; use no ZIP compression\n\
1902   -C DIR FILE     change to the specified directory and include\n\
1903                   the following file\n\
1904   -E              don't include the files found in a directory\n\
1905   -f FILE         specify archive file name\n\
1906   --help          print this help, then exit\n\
1907   -m FILE         include manifest information from specified manifest file\n\
1908   -M              Do not create a manifest file for the entries\n\
1909   -v              generate verbose output on standard output\n\
1910   -V, --version   display version information\n\
1911 ");
1912   printf("\n\
1913 If any file is a directory then it is processed recursively.\n\
1914 The manifest file name and the archive file name needs to be specified\n\
1915 in the same order the 'm' and 'f' flags are specified.\n\
1916 \n\
1917 Example 1: to archive two class files into an archive called classes.jar: \n\
1918      jar cvf classes.jar Foo.class Bar.class \n\
1919 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1920      files in the foo/ directory into 'classes.jar': \n\
1921      jar cvfm classes.jar mymanifest -C foo/ .\n\
1922 ");
1923
1924   exit(0);
1925 }
1926
1927 static char *
1928 jt_strdup(s)
1929      char *s;
1930 {
1931   char *result = (char*)malloc(strlen(s) + 1);
1932   if (result == (char*)0)
1933     return (char*)0;
1934   strcpy(result, s);
1935   return result;
1936 }
1937
1938 /* Convert "tar-style" first argument to a form expected by getopt.
1939    This idea and the code comes from GNU tar.  This can allocate a new
1940    argument vector.  This might leak some memory, but we don't care.  */
1941 static void
1942 expand_options (int *argcp, char ***argvp)
1943 {
1944   int argc = *argcp;
1945   char **argv = *argvp;
1946
1947   /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion 
1948      if a long argument (like "--help") is detected. */
1949   if (argc > 1 && argv[1][1] != '-')
1950     {
1951       char buf[3];
1952       char **new_argv;
1953       int new_argc;
1954       int args_to_expand;
1955       char *p;
1956       char **in, **out;
1957
1958       buf[0] = '-';
1959       buf[2] = '\0';
1960
1961       args_to_expand = strlen (argv[1]);
1962       if (argv[1][0] == '-')
1963         --args_to_expand;
1964         
1965       new_argc = argc - 1 + args_to_expand;
1966       new_argv = (char **) malloc (new_argc * sizeof (char *));
1967       in = argv;
1968       out = new_argv;
1969
1970       *out++ = *in++;
1971       p = *in++;
1972       if (*p == '-')
1973         p++;
1974       while (*p != '\0')
1975         {
1976           char *opt;
1977           buf[1] = *p;
1978           *out++ = jt_strdup (buf);
1979           /* If the option takes an argument, move the next argument
1980              to just after this option.  */
1981           opt = strchr (OPTION_STRING, *p);
1982           if (opt && opt[1] == ':')
1983             {
1984               if (in < argv + argc)
1985                 *out++ = *in++;
1986               else
1987                 {
1988                   fprintf(stderr, "%s: option `%s' requires an argument.\n",
1989                           argv[0], buf);
1990                   usage(argv[0]);
1991                 }
1992             }
1993           ++p;
1994         }
1995
1996       /* Copy remaining options.  */
1997       while (in < argv + argc)
1998         *out++ = *in++;
1999
2000       *argcp = new_argc;
2001       *argvp = new_argv;
2002     }
2003 }