OSDN Git Service

* jartool.c: Use mode 0666 when opening new file.
[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(!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(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves.  Believe me */
928         if(verbose)
929           printf("skipping: %s\n", de->d_name);
930         continue;
931       }
932
933       strcpy(t_ptr, de->d_name);
934
935       if(add_to_jar(fd, NULL, fullname)){
936         fprintf(stderr, "Error adding file to jar!\n");
937         return 1;
938       }
939     }
940
941     free(fullname);
942     closedir(dir);
943       
944   } else if(S_ISREG(statbuf.st_mode)){
945     int add_fd;
946
947     add_fd = open(file, O_RDONLY | O_BINARY);
948     if(add_fd < 0){
949       fprintf(stderr, "Error opening %s.\n", file);
950       return 0;
951     }
952     
953     if(add_file_to_jar(fd, add_fd, file, &statbuf)){
954       fprintf(stderr, "Error adding file to jar!\n");
955       return 1;
956     }
957     
958   } else {
959     fprintf(stderr, "Illegal file specified: %s\n", file);
960   }
961   
962   if(old_dir != NULL){
963     if(chdir(old_dir))
964       perror(old_dir);
965     
966     free(old_dir);
967   }
968
969   return 0;
970 }
971
972 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
973
974   unsigned short file_name_length;
975   unsigned long mod_time;
976   ub1 rd_buff[RDSZ];
977   uLong crc = 0;
978   off_t offset = 0;
979   int rdamt;
980   struct zipentry *ze;
981
982   mod_time = unix2dostime(&(statbuf->st_mtime));
983   file_name_length = strlen(fname);
984
985   if(!seekable && !do_compress){
986     crc = crc32(0L, Z_NULL, 0); 
987     
988     while((rdamt = read(ffd, rd_buff, RDSZ)) != 0) 
989       crc = crc32(crc, rd_buff, rdamt); 
990     
991     lseek(ffd, 0, SEEK_SET);
992   }
993   
994   /* data descriptor */
995   if(!seekable && do_compress){
996     PACK_UB2(file_header, LOC_EXTRA, 8);
997   } else {
998     PACK_UB2(file_header, LOC_EXTRA, 0);
999   }
1000   
1001   if(do_compress){
1002     PACK_UB2(file_header, LOC_COMP, 8);
1003   } else {
1004     PACK_UB2(file_header, LOC_COMP, 0);
1005   }
1006     
1007   PACK_UB4(file_header, LOC_MODTIME, mod_time);
1008   PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1009   
1010   if(!seekable && !do_compress){
1011     PACK_UB4(file_header, LOC_CRC, crc);
1012     PACK_UB4(file_header, LOC_USIZE, statbuf->st_size); 
1013     PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1014   } else 
1015     memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1016   
1017   ze = (zipentry*)malloc(sizeof(zipentry));
1018   if(ze == NULL){
1019     perror("malloc");
1020     exit(1);
1021   }
1022   
1023   memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1024   ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1025   strcpy(ze->filename, fname);
1026
1027   ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1028   ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1029
1030   if(!seekable && !do_compress)
1031     ze->crc = crc;
1032
1033   ze->csize = statbuf->st_size;
1034   ze->usize = ze->csize;
1035   ze->offset = lseek(jfd, 0, SEEK_CUR);
1036   if(do_compress)
1037     ze->compressed = TRUE;
1038   else
1039     ze->compressed = FALSE;
1040   
1041   add_entry(ze);
1042   
1043   /* Write the local header */
1044   write(jfd, file_header, 30);
1045     
1046   /* write the file name to the zip file */
1047   write(jfd, fname, file_name_length);
1048
1049
1050   if(verbose){
1051     printf("adding: %s ", fname);
1052     fflush(stdout);
1053   }
1054  
1055   if(do_compress){
1056     /* compress the file */
1057     compress_file(ffd, jfd, ze);
1058   } else {
1059     /* Write the contents of the file (uncompressed) to the zip file */
1060     /* calculate the CRC as we go along */
1061     ze->crc = crc32(0L, Z_NULL, 0); 
1062       
1063     while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1064       ze->crc = crc32(ze->crc, rd_buff, rdamt);
1065       if(write(jfd, rd_buff, rdamt) != rdamt){
1066         perror("write");
1067         return 0;
1068       }
1069     }
1070   }
1071   close(ffd);
1072   
1073   /* write out data descriptor */
1074   PACK_UB4(data_descriptor, 4, ze->crc);
1075   PACK_UB4(data_descriptor, 8, ze->csize);
1076   PACK_UB4(data_descriptor, 12, ze->usize);
1077
1078   /* we need to seek back and fill the header */
1079   if(seekable){
1080     offset = (ze->csize + strlen(ze->filename) + 16);
1081     
1082     if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1083       perror("lseek");
1084       exit(1);
1085     }
1086
1087     if(write(jfd, (data_descriptor + 4), 12) != 12){
1088       perror("write");
1089       return 0;
1090     }
1091     
1092     offset -= 12;
1093
1094     if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1095       perror("lseek");
1096       exit(1);
1097     }
1098   } else if(do_compress){
1099     /* Sun's jar tool will only allow a data descriptor if the entry is
1100        compressed, but we'll save 16 bytes/entry if we only use it when
1101        we can't seek back on the file */
1102     
1103     if(write(jfd, data_descriptor, 16) != 16){
1104       perror("write");
1105       return 0;
1106     }
1107   }
1108   
1109   if(verbose)
1110     printf("(in=%d) (out=%d) (%s %d%%)\n", 
1111            (int)ze->usize, (int)ze->csize,
1112            (do_compress ? "deflated" : "stored"),
1113            (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1114
1115   return 0;
1116 }
1117
1118 int create_central_header(int fd){
1119   ub1 header[46];
1120   ub1 end_header[22];
1121   int start_offset;
1122   int dir_size;
1123   int *iheader;
1124   int total_in = 0, total_out = 22;
1125
1126   zipentry *ze;
1127
1128   iheader = (int*)header;
1129
1130   /* magic number */
1131   header[0] = 'P';
1132   header[1] = 'K';
1133   header[2] = 1;
1134   header[3] = 2;
1135   /* version made by */
1136   header[4] = 10;
1137   header[5] = 0;
1138   /* version needed to extract */
1139   header[6] = 10;
1140   header[7] = 0;
1141   /* bit flag */
1142   header[8] = 0;
1143   header[9] = 0;
1144   /* compression method */
1145   header[10] = 0;
1146   header[11] = 0;
1147   /* file mod time */
1148   header[12] = 0;
1149   header[13] = 0;
1150   /* file mod date */
1151   header[14] = 0;
1152   header[15] = 0;
1153   /* crc 32 */
1154   header[16] = 0;
1155   header[17] = 0;
1156   header[18] = 0;
1157   header[19] = 0;
1158   /* compressed size */
1159   header[20] = 0;
1160   header[21] = 0;
1161   header[22] = 0;
1162   header[23] = 0;
1163   /* uncompressed size */
1164   header[24] = 0;
1165   header[25] = 0;
1166   header[26] = 0;
1167   header[27] = 0;
1168   /* filename length */
1169   header[28] = 0;
1170   header[29] = 0;
1171   /* extra field length */
1172   header[30] = 0;
1173   header[31] = 0;
1174   /* file comment length */
1175   header[32] = 0;
1176   header[33] = 0;
1177   /* disk number start */
1178   header[34] = 0;
1179   header[35] = 0;
1180   /* internal file attribs */
1181   header[36] = 0;
1182   header[37] = 0;
1183   /* external file attribs */
1184   header[38] = 0;
1185   header[39] = 0;
1186   header[40] = 0;
1187   header[41] = 0;
1188   /* relative offset of local header */
1189   header[42] = 0;
1190   header[43] = 0;
1191   header[44] = 0;
1192   header[45] = 0;
1193
1194   start_offset = lseek(fd, 0, SEEK_CUR);
1195
1196   for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1197
1198     total_in += ze->usize;
1199     total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1200
1201     if(ze->compressed){
1202       PACK_UB2(header, CEN_COMP, 8);
1203     } else {
1204       PACK_UB2(header, CEN_COMP, 0);
1205     }
1206         
1207     PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1208     PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1209     PACK_UB4(header, CEN_CRC, ze->crc);
1210     PACK_UB4(header, CEN_CSIZE, ze->csize);
1211     PACK_UB4(header, CEN_USIZE, ze->usize);
1212     PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1213     PACK_UB4(header, CEN_OFFSET, ze->offset);
1214
1215     write(fd, header, 46);
1216
1217     write(fd, ze->filename, strlen(ze->filename));
1218   }
1219
1220   dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1221
1222   /* magic number */
1223   end_header[0] = 0x50;
1224   end_header[1] = 0x4b;
1225   end_header[2] = 0x05;
1226   end_header[3] = 0x06;
1227   /* number of this disk */
1228   end_header[4] = 0;
1229   end_header[5] = 0;
1230   /* number of disk w/ start of central header */
1231   end_header[6] = 0;
1232   end_header[7] = 0;
1233   /* total number of entries in central dir on this disk*/
1234   PACK_UB2(end_header, 8, number_of_entries);
1235   /* total number of entries in central dir*/
1236   PACK_UB2(end_header, 10, number_of_entries);
1237   /* size of central dir. */
1238   PACK_UB4(end_header, 12, dir_size);
1239   /* offset of start of central dir */
1240   PACK_UB4(end_header, 16, start_offset);
1241   /* zipfile comment length */
1242   end_header[20] = 0;
1243   end_header[21] = 0;
1244
1245   write(fd, end_header, 22);
1246   
1247   if(verbose)
1248     printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n", 
1249            total_in, 
1250            total_out,
1251            (do_compress ? "deflated" : "stored"),
1252            (int)((1 - (total_out / (float)total_in)) * 100)
1253            );
1254
1255   return 0;
1256 }
1257
1258 int extract_jar(int fd, char **files, int file_num){
1259   int rdamt;
1260   int out_a, in_a;
1261   ub4 signature;
1262   ub4 csize;
1263   ub4 crc;
1264   ub2 fnlen;
1265   ub2 eflen;
1266   ub2 flags;
1267   ub2 method;
1268   ub1 *filename = NULL;
1269   int filename_len = 0;
1270   ub4 rd_buff[RDSZ];
1271   pb_file pbf;
1272   ub1 scratch[16];
1273   zipentry ze;
1274   int f_fd;
1275   int dir;
1276   int handle;
1277   int j;
1278
1279   init_inflation();
1280
1281   pb_init(&pbf, fd);
1282
1283   for(;;){
1284     f_fd = 0;
1285     crc = 0;
1286     ze.crc = 0;
1287     
1288     dir = FALSE; /* by default, the file isn't a dir */
1289     handle = TRUE; /* by default we'll extract/create the file */
1290
1291     if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1292       perror("read");
1293       break;
1294     }
1295     
1296     signature = UNPACK_UB4(scratch, 0);
1297
1298 #ifdef DEBUG    
1299     printf("signature is %x\n", signature);
1300 #endif
1301     if(signature == 0x08074b50){
1302 #ifdef DEBUG    
1303       printf("skipping data descriptor\n");
1304 #endif
1305       pb_read(&pbf, scratch, 12);
1306       continue;
1307     } else if(signature == 0x02014b50){
1308 #ifdef DEBUG    
1309       printf("Central header reached.. we're all done!\n");
1310 #endif
1311       break;
1312     }else if(signature != 0x04034b50){
1313       printf("Ick! %#x\n", signature);
1314       break;
1315     }
1316     
1317     if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1318       perror("read");
1319       break;
1320     }
1321     
1322     csize = UNPACK_UB4(file_header, LOC_CSIZE);
1323 #ifdef DEBUG    
1324     printf("Compressed size is %u\n", csize);
1325 #endif
1326
1327     fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1328 #ifdef DEBUG    
1329     printf("Filename length is %hu\n", fnlen);
1330 #endif
1331
1332     eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1333 #ifdef DEBUG    
1334     printf("Extra field length is %hu\n", eflen);
1335 #endif
1336
1337     flags = UNPACK_UB2(file_header, LOC_EXTRA);
1338 #ifdef DEBUG    
1339     printf("Flags are %#hx\n", flags);
1340 #endif
1341
1342     method = UNPACK_UB2(file_header, LOC_COMP);
1343 #ifdef DEBUG
1344     printf("Compression method is %#hx\n", method);
1345 #endif
1346
1347     /* if there isn't a data descriptor */
1348     if(!(flags & 0x0008)){
1349       crc = UNPACK_UB4(file_header, LOC_CRC);
1350 #ifdef DEBUG    
1351       printf("CRC is %x\n", crc);
1352 #endif
1353     }
1354
1355     if(filename_len < fnlen + 1){
1356       if(filename != NULL)
1357         free(filename);
1358       
1359       filename = malloc(sizeof(ub1) * (fnlen + 1));
1360       filename_len = fnlen + 1;
1361     }
1362
1363     pb_read(&pbf, filename, fnlen);
1364     filename[fnlen] = '\0';
1365
1366 #ifdef DEBUG    
1367     printf("filename is %s\n", filename);
1368 #endif
1369
1370     if(file_num > 0){
1371       handle = FALSE;
1372       
1373       for(j = 0; j < file_num; j++)
1374         if(strcmp(files[j], (const char *)filename) == 0){
1375           handle = TRUE;
1376           break;
1377         }
1378     }
1379
1380     if(!handle)
1381       f_fd = -1;
1382
1383     /* OK, there is some directory information in the file.  Nothing to do
1384        but ensure the directory(s) exist, and create them if they don't.
1385        What a pain! */
1386     if(strchr((const char *)filename, '/') != NULL && handle){
1387       /* Loop through all the directories in the path, (everything w/ a '/') */
1388       const ub1 *start = filename;
1389       char *tmp_buff;
1390       struct stat sbuf;
1391
1392       tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1393
1394       for(;;){
1395         const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1396
1397         if(idx == NULL)
1398           break;
1399         else if(idx == start){
1400           start++;
1401           continue;
1402         }
1403         start = idx + 1;
1404
1405         strncpy(tmp_buff, (const char *)filename, (idx - filename));
1406         tmp_buff[(idx - filename)] = '\0';
1407
1408 #ifdef DEBUG    
1409         printf("checking the existance of %s\n", tmp_buff);
1410 #endif
1411
1412         if(stat(tmp_buff, &sbuf) < 0){
1413           if(errno != ENOENT){
1414             perror("stat");
1415             exit(1);
1416           }
1417
1418         } else if(S_ISDIR(sbuf.st_mode)){
1419 #ifdef DEBUG    
1420           printf("Directory exists\n");
1421 #endif
1422           continue;
1423         }else {
1424           fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1425                   tmp_buff);
1426           exit(1);
1427         }
1428         
1429 #ifdef DEBUG    
1430         printf("Making directory..\n");
1431 #endif
1432         if(mkdir(tmp_buff, 0755) < 0){
1433           perror("mkdir");
1434           exit(1);
1435         }
1436         if(verbose && handle)
1437           printf("%10s: %s/\n", "created", tmp_buff);
1438
1439       }
1440
1441       /* only a directory */
1442       if(strlen((const char *)start) == 0)
1443         dir = TRUE;
1444
1445 #ifdef DEBUG    
1446       printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1447 #endif
1448
1449       /* If the entry was just a directory, don't write to file, etc */
1450       if(strlen((const char *)start) == 0)
1451         f_fd = -1;
1452
1453       free(tmp_buff);
1454     }
1455
1456     if(f_fd != -1 && handle){
1457       f_fd = creat((const char *)filename, 00644);
1458
1459       if(f_fd < 0){
1460         fprintf(stderr, "Error extracting JAR archive!\n");
1461         perror((const char *)filename);
1462         exit(1);
1463       }
1464     }
1465
1466     if(method != 8 && flags & 0x0008){
1467       fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1468       exit(1);
1469     }
1470
1471     if(method == 8 || flags & 0x0008){
1472       if(seekable)
1473         lseek(fd, eflen, SEEK_CUR);
1474       else
1475         consume(&pbf, eflen);
1476       
1477       inflate_file(&pbf, f_fd, &ze);
1478     } else {
1479
1480 #ifdef DEBUG    
1481       printf("writing stored data.. (%d bytes)\n", csize);
1482 #endif
1483
1484       out_a = 0;
1485       in_a = csize;
1486
1487       ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1488
1489       while(out_a < (int)csize){
1490         rdamt = (in_a > RDSZ ? RDSZ : in_a);
1491         if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1492           perror("read");
1493           exit(1);
1494         }
1495         
1496         ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1497
1498         if(f_fd >= 0)
1499           write(f_fd, rd_buff, rdamt);
1500
1501         out_a += rdamt;
1502         in_a -= rdamt;
1503
1504 #ifdef DEBUG    
1505         printf("%d bytes written\n", out_a);
1506 #endif
1507       }
1508
1509       if(seekable)
1510         lseek(fd, eflen, SEEK_CUR);
1511       else
1512         consume(&pbf, eflen);
1513     }
1514
1515     /* if there is a data descriptor left, compare the CRC */
1516     if(flags & 0x0008){
1517
1518       if(pb_read(&pbf, scratch, 16) != 16){
1519         perror("read");
1520         exit(1);
1521       }
1522
1523       signature = UNPACK_UB4(scratch, 0);
1524
1525       if(signature != 0x08074b50){
1526         fprintf(stderr, "Error! Missing data descriptor!\n");
1527         exit(1);
1528       }
1529
1530       crc = UNPACK_UB4(scratch, 4);
1531
1532     }
1533
1534     if(crc != ze.crc){
1535       fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1536               ze.crc, crc);
1537       exit(1);
1538     }
1539
1540     close(f_fd);
1541
1542     if(verbose && dir == FALSE && handle)
1543       printf("%10s: %s\n",
1544              (method == 8 ? "inflated" : "extracted"),
1545              filename);
1546   }
1547
1548   return 0;
1549 }
1550
1551 int list_jar(int fd, char **files, int file_num){
1552   int rdamt;
1553   ub4 signature;
1554   ub4 csize;
1555   ub4 usize;
1556   ub4 mdate;
1557   ub4 tmp;
1558   ub2 fnlen;
1559   ub2 eflen;
1560   ub2 clen;
1561   ub2 flags;
1562   ub2 method;
1563   ub2 cen_size;
1564   ub1 *filename = NULL;
1565   ub1 scratch[16];
1566   ub1 cen_header[46];
1567   int filename_len = 0;
1568   off_t size;
1569   int i, j;
1570   time_t tdate;
1571   struct tm *s_tm;
1572   char ascii_date[30];
1573   zipentry ze;
1574
1575 #ifdef DEBUG
1576   printf("Listing jar file, looking for %d files\n", file_num);
1577 #endif
1578
1579   /* This should be the start of the central-header-end section */
1580   if(seekable){
1581     if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1582       perror("lseek");
1583       exit(1);
1584     }
1585     
1586     if(read(fd, &tmp, sizeof(ub4)) != 4){
1587       perror("read");
1588       exit(1);
1589     }
1590
1591 #ifdef WORDS_BIGENDIAN
1592     tmp = L2BI(tmp);
1593 #endif
1594
1595     if(tmp != 0x06054b50){
1596       fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1597       exit(1);
1598     }
1599
1600     if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1601       perror("lseek");
1602       exit(1);
1603     }
1604   
1605     if(read(fd, &cen_size, 2) != 2){
1606       perror("read");
1607       exit(1);
1608     }
1609
1610 #ifdef WORDS_BIGENDIAN
1611     cen_size = L2BS(cen_size);
1612 #endif
1613
1614     /*   printf("%hu entries in central header\n", cen_size); */
1615
1616     if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1617       perror("lseek");
1618       exit(1);
1619     }
1620
1621     if(read(fd, &tmp, 4) != 4){
1622       perror("read");
1623       exit(1);
1624     }
1625
1626 #ifdef WORDS_BIGENDIAN
1627     tmp = L2BI(tmp);
1628 #endif
1629
1630     /*   printf("Central header offset = %d\n", tmp); */
1631
1632     if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1633       perror("lseek");
1634       exit(1);
1635     }
1636
1637     /* Loop through the entries in the central header */
1638     for(i = 0; i < cen_size; i++){
1639     
1640       if(read(fd, &cen_header, 46) != 46){
1641         perror("read");
1642         exit(1);
1643       }
1644
1645       signature = UNPACK_UB4(cen_header, 0);
1646       if(signature != 0x02014b50){
1647         fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1648         exit(1);
1649       }
1650
1651       usize = UNPACK_UB4(cen_header, CEN_USIZE);
1652       fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1653       eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1654       clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1655
1656       /* If we're providing verbose output, we need to make an ASCII
1657        * formatted version of the date. */
1658       if(verbose){
1659         mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1660         tdate = dos2unixtime(mdate);
1661         s_tm = localtime(&tdate);
1662         strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1663       }
1664
1665       if(filename_len < fnlen + 1){
1666         if(filename != NULL)
1667           free(filename);
1668       
1669         filename = malloc(sizeof(ub1) * (fnlen + 1));
1670         filename_len = fnlen + 1;
1671       }
1672     
1673       if(read(fd, filename, fnlen) != fnlen){
1674         perror("read");
1675         exit(1);
1676       }
1677       filename[fnlen] = '\0';
1678     
1679       /* if the user specified a list of files on the command line,
1680          we'll only display those, otherwise we'll display everything */
1681       if(file_num > 0){
1682         for(j = 0; j < file_num; j++)
1683           if(strcmp(files[j], (const char *)filename) == 0){
1684             if(verbose)
1685               printf("%6d %s %s\n", usize, ascii_date, filename);
1686             else
1687               printf("%s\n", filename);
1688             break;
1689           }
1690       } else {
1691         if(verbose)
1692           printf("%6d %s %s\n", usize, ascii_date, filename);
1693         else
1694           printf("%s\n", filename);
1695       }            
1696       
1697       size = eflen + clen;
1698       if(size > 0){
1699         if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1700           perror("lseek");
1701           exit(1);
1702         }
1703       }
1704     }
1705   } else {
1706     /* the file isn't seekable.. evil! */
1707     pb_file pbf;
1708
1709     pb_init(&pbf, fd);
1710
1711     init_inflation();
1712
1713     for(;;){
1714       if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1715         perror("read");
1716         break;
1717       }
1718       
1719       signature = UNPACK_UB4(scratch, 0);
1720       
1721 #ifdef DEBUG
1722       printf("signature is %x\n", signature);
1723 #endif
1724       
1725       if(signature == 0x08074b50){
1726 #ifdef DEBUG
1727         printf("skipping data descriptor\n");
1728 #endif
1729         pb_read(&pbf, scratch, 12);
1730         continue;
1731       } else if(signature == 0x02014b50){
1732 #ifdef DEBUG
1733         printf("Central header reached.. we're all done!\n");
1734 #endif
1735         break;
1736       }else if(signature != 0x04034b50){
1737 #ifdef DEBUG
1738         printf("Ick! %#x\n", signature);
1739 #endif
1740         break;
1741       }
1742       
1743       if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1744         perror("read");
1745         break;
1746       }
1747       
1748       csize = UNPACK_UB4(file_header, LOC_CSIZE);
1749 #ifdef DEBUG
1750       printf("Compressed size is %u\n", csize);
1751 #endif
1752       
1753       fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1754 #ifdef DEBUG
1755       printf("Filename length is %hu\n", fnlen);
1756 #endif
1757       
1758       eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1759 #ifdef DEBUG
1760       printf("Extra field length is %hu\n", eflen);
1761 #endif
1762       
1763       method = UNPACK_UB2(file_header, LOC_COMP);
1764 #ifdef DEBUG
1765       printf("Compression method is %#hx\n", method);
1766 #endif
1767
1768       flags = UNPACK_UB2(file_header, LOC_EXTRA);
1769 #ifdef DEBUG
1770       printf("Flags are %#hx\n", flags);
1771 #endif
1772       
1773       usize = UNPACK_UB4(file_header, LOC_USIZE);
1774
1775       /* If we're providing verbose output, we need to make an ASCII
1776        * formatted version of the date. */
1777       if(verbose){
1778         mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1779         tdate = dos2unixtime(mdate);
1780         s_tm = localtime(&tdate);
1781         strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1782       }
1783
1784       if(filename_len < fnlen + 1){
1785         if(filename != NULL)
1786           free(filename);
1787         
1788         filename = malloc(sizeof(ub1) * (fnlen + 1));
1789         filename_len = fnlen + 1;
1790       }
1791       
1792       pb_read(&pbf, filename, fnlen);
1793       filename[fnlen] = '\0';
1794       
1795       /* the header is at the end.  In a JAR file, this means that the data
1796          happens to be compressed.  We have no choice but to inflate the
1797          data */
1798       if(flags & 0x0008){
1799
1800         size = eflen;
1801
1802         if(size > 0)
1803           consume(&pbf, size);
1804         
1805         if(method == 8){
1806 #ifdef DEBUG
1807           printf("inflating %s\n", filename);
1808 #endif
1809           inflate_file(&pbf, -1, &ze);
1810
1811           usize = ze.usize;
1812         } else 
1813           printf("We're shit outta luck!\n");
1814           
1815       } else {
1816         size = csize + (eflen > 0 ? eflen : 0);
1817         
1818
1819 #ifdef DEBUG
1820         printf("Skipping %ld bytes\n", (long)size);
1821 #endif
1822
1823         consume(&pbf, size);
1824       }
1825       /* print out the listing */
1826       if(file_num > 0){
1827         for(j = 0; j < file_num; j++)
1828           if(strcmp(files[j], (const char *)filename) == 0){
1829             if(verbose)
1830               printf("%6d %s %s\n", usize, ascii_date, filename);
1831             else
1832               printf("%s\n", filename);
1833             break;
1834           }
1835       } else {
1836         if(verbose)
1837           printf("%6d %s %s\n", usize, ascii_date, filename);
1838         else
1839           printf("%s\n", filename);
1840       }        
1841     }
1842   }
1843   return 0;
1844 }
1845
1846 int consume(pb_file *pbf, int amt){
1847   int tc = 0; /* total amount consumed */
1848   ub1 buff[RDSZ];
1849   int rdamt;
1850
1851 #ifdef DEBUG
1852   printf("Consuming %d bytes\n", amt);
1853 #endif
1854
1855   while(tc < amt){
1856     rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1857 #ifdef DEBUG
1858     printf("got %d bytes\n", rdamt);
1859 #endif
1860     tc += rdamt;
1861   }
1862
1863 #ifdef DEBUG
1864   printf("%d bytes consumed\n", tc);
1865 #endif
1866
1867   return 0;
1868 }
1869
1870 void usage(const char *filename){
1871   fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1872   exit (1);
1873 }
1874
1875 void version ()
1876 {
1877   printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1878   printf("Copyright 1999, 2000, 2001  Bryan Burns\n");
1879   printf("Copyright 2002 Free Software Foundation\n");
1880   printf("\
1881 This is free software; see the source for copying conditions.  There is NO\n\
1882 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1883   exit (0);
1884 }
1885
1886 void help(const char *filename)
1887 {
1888   printf("\
1889 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1890 \n\
1891 Store many files together in a single `jar' file.\n\
1892 \n\
1893   -c              create new archive\n\
1894   -t              list table of contents for archive\n\
1895   -x              extract named (or all) files from archive\n\
1896   -u              update existing archive\n\
1897 ", filename);
1898   printf("\n\
1899   -@              read names from stdin\n\
1900   -0              store only; use no ZIP compression\n\
1901   -C DIR FILE     change to the specified directory and include\n\
1902                   the following file\n\
1903   -E              don't include the files found in a directory\n\
1904   -f FILE         specify archive file name\n\
1905   --help          print this help, then exit\n\
1906   -m FILE         include manifest information from specified manifest file\n\
1907   -M              Do not create a manifest file for the entries\n\
1908   -v              generate verbose output on standard output\n\
1909   -V, --version   display version information\n\
1910 ");
1911   printf("\n\
1912 If any file is a directory then it is processed recursively.\n\
1913 The manifest file name and the archive file name needs to be specified\n\
1914 in the same order the 'm' and 'f' flags are specified.\n\
1915 \n\
1916 Example 1: to archive two class files into an archive called classes.jar: \n\
1917      jar cvf classes.jar Foo.class Bar.class \n\
1918 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1919      files in the foo/ directory into 'classes.jar': \n\
1920      jar cvfm classes.jar mymanifest -C foo/ .\n\
1921 ");
1922
1923   exit(0);
1924 }
1925
1926 static char *
1927 jt_strdup(s)
1928      char *s;
1929 {
1930   char *result = (char*)malloc(strlen(s) + 1);
1931   if (result == (char*)0)
1932     return (char*)0;
1933   strcpy(result, s);
1934   return result;
1935 }
1936
1937 /* Convert "tar-style" first argument to a form expected by getopt.
1938    This idea and the code comes from GNU tar.  This can allocate a new
1939    argument vector.  This might leak some memory, but we don't care.  */
1940 static void
1941 expand_options (int *argcp, char ***argvp)
1942 {
1943   int argc = *argcp;
1944   char **argv = *argvp;
1945
1946   /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion 
1947      if a long argument (like "--help") is detected. */
1948   if (argc > 1 && argv[1][1] != '-')
1949     {
1950       char buf[3];
1951       char **new_argv;
1952       int new_argc;
1953       int args_to_expand;
1954       char *p;
1955       char **in, **out;
1956
1957       buf[0] = '-';
1958       buf[2] = '\0';
1959
1960       args_to_expand = strlen (argv[1]);
1961       if (argv[1][0] == '-')
1962         --args_to_expand;
1963         
1964       new_argc = argc - 1 + args_to_expand;
1965       new_argv = (char **) malloc (new_argc * sizeof (char *));
1966       in = argv;
1967       out = new_argv;
1968
1969       *out++ = *in++;
1970       p = *in++;
1971       if (*p == '-')
1972         p++;
1973       while (*p != '\0')
1974         {
1975           char *opt;
1976           buf[1] = *p;
1977           *out++ = jt_strdup (buf);
1978           /* If the option takes an argument, move the next argument
1979              to just after this option.  */
1980           opt = strchr (OPTION_STRING, *p);
1981           if (opt && opt[1] == ':')
1982             {
1983               if (in < argv + argc)
1984                 *out++ = *in++;
1985               else
1986                 {
1987                   fprintf(stderr, "%s: option `%s' requires an argument.\n",
1988                           argv[0], buf);
1989                   usage(argv[0]);
1990                 }
1991             }
1992           ++p;
1993         }
1994
1995       /* Copy remaining options.  */
1996       while (in < argv + argc)
1997         *out++ = *in++;
1998
1999       *argcp = new_argc;
2000       *argvp = new_argv;
2001     }
2002 }