OSDN Git Service

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