OSDN Git Service

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