OSDN Git Service

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