OSDN Git Service

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