OSDN Git Service

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