OSDN Git Service

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