OSDN Git Service

2004-07-11 Bryce McKinlay <mckinlay@redhat.com>
[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 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   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 #if ! (HAVE_FTRUNCATE || HAVE__CHSIZE)
577   #error neither ftruncate() or _chsize() available
578 #endif
579     /* Check if the file shrunk when we updated it. */
580     if (action == ACTION_UPDATE)
581 #if HAVE_FTRUNCATE
582       ftruncate (jarfd, lseek (jarfd, 0, SEEK_CUR));
583 #endif
584 #if HAVE__CHSIZE
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(args, current)
607      char **args;
608      int current;
609 {
610   if(!read_names_from_stdin)
611     {
612       args_g = args;
613       args_current_g = current;
614     }
615 }
616
617 static char *
618 get_next_arg ()
619 {
620   static int reached_end = 0;
621
622   if (reached_end)
623     return NULL;
624
625   if (args_g)
626     {
627       if (!args_g [args_current_g])
628         {
629           reached_end = 1;
630           return NULL;
631         }
632       return args_g [args_current_g++];
633     }
634   else
635     {
636       /* Read the name from stdin. Delimiters are '\n' and
637          '\r'. Reading EOF indicates that we don't have anymore file
638          names characters to read. */
639
640       char s [MAXPATHLEN];
641       int  pos = 0;
642
643       /* Get rid of '\n' and '\r' first. */
644       while (1)
645         {
646           int c = getc (stdin);
647           if (c == '\n' || c == '\r')
648             continue;
649           else
650             {
651               if (c == EOF)
652                 return NULL;
653               ungetc (c, stdin);
654               break;
655             }
656         }
657
658       while (1)
659         {
660           int c = getc (stdin);
661           /* Exit when we get a delimiter or don't have any characters
662              to read */
663           if (c == '\n'|| c == '\r'|| c == EOF)
664             break;
665           s [pos++] = (char) c;
666         }
667
668       if (pos)
669         {
670           s [pos] = '\0';
671           return jt_strdup (s);
672         }
673       else
674         return NULL;
675     }
676 }
677
678 void init_headers(){
679   /* packing file header */
680   /* magic number */
681   file_header[0] = 0x50;
682   file_header[1] = 0x4b;
683   file_header[2] = 0x03;
684   file_header[3] = 0x04;
685   /* version number (Unix 1.0)*/
686   file_header[4] = 10;
687   file_header[5] = 0;
688   /* bit flag (normal deflation)*/
689   file_header[6] = 0x00;
690
691   file_header[7] = 0x00;
692   /* do_compression method (deflation) */
693   file_header[8] = 0;
694   file_header[9] = 0;
695
696   /* last mod file time (MS-DOS format) */
697   file_header[10] = 0;
698   file_header[11] = 0;
699   /* last mod file date (MS-DOS format) */
700   file_header[12] = 0;
701   file_header[13] = 0;
702   /* CRC 32 */
703   file_header[14] = 0;
704   file_header[15] = 0;
705   file_header[16] = 0;
706   file_header[17] = 0;
707   /* compressed size */
708   file_header[18] = 0;
709   file_header[19] = 0;
710   file_header[20] = 0;
711   file_header[21] = 0;
712   /* uncompressed size */
713   file_header[22] = 0;
714   file_header[23] = 0;
715   file_header[24] = 0;
716   file_header[25] = 0;
717   /* filename length */
718   file_header[26] = 0;
719   file_header[27] = 0;
720   /* extra field length */
721   file_header[28] = 0;
722   file_header[29] = 0;
723
724   /* Initialize the compression DS */
725   PACK_UB4(data_descriptor, 0, 0x08074b50);
726   
727 }
728
729 void add_entry(struct zipentry *ze){
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   time_t current_time;
912   int nlen;   /* length of file name */
913   int mod_time; /* file modification time */
914   struct zipentry *ze;
915   
916   nlen = 9;  /* trust me on this one */
917
918   memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
919   
920   current_time = time(NULL);
921   if(current_time == (time_t)-1){
922     perror("time");
923     exit(1);
924   }
925
926   mod_time = unix2dostime(&current_time);
927   
928   PACK_UB2(file_header, LOC_EXTRA, 0);
929   PACK_UB2(file_header, LOC_COMP, 0);
930   PACK_UB2(file_header, LOC_FNLEN, nlen);
931   PACK_UB4(file_header, LOC_MODTIME, mod_time);
932   
933   if(verbose)
934     printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
935   
936   ze = (zipentry*)malloc(sizeof(zipentry));
937   if(ze == NULL){
938     perror("malloc");
939     exit(1);
940   }
941   
942   memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
943   ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
944   strcpy(ze->filename, "META-INF/");
945   ze->filename[nlen] = '\0';
946     
947   ze->offset = lseek(jfd, 0, SEEK_CUR);
948   ze->mod_time = (ub2)(mod_time & 0x0000ffff);
949   ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
950   ze->compressed = FALSE;
951
952   add_entry(ze);
953   
954   write(jfd, file_header, 30);
955   write(jfd, "META-INF/", nlen);
956
957   /* if the user didn't specify an external manifest file... */
958   if(mf_name == NULL){
959     
960     int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
961     char *mf;
962
963     if((mf = (char *) malloc(mf_len + 1))) {
964     uLong crc;
965
966     sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
967
968     crc = crc32(0L, Z_NULL, 0);
969     
970     crc = crc32(crc, (const unsigned char *)mf, mf_len);
971
972     nlen = 20;  /* once again, trust me */
973
974     PACK_UB2(file_header, LOC_EXTRA, 0);
975     PACK_UB2(file_header, LOC_COMP, 0);
976     PACK_UB2(file_header, LOC_FNLEN, nlen);
977     PACK_UB4(file_header, LOC_USIZE, mf_len);
978     
979     memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
980     
981     PACK_UB4(file_header, LOC_CRC, crc);
982
983     if(verbose)
984       printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
985     
986     ze = (zipentry*)malloc(sizeof(zipentry));
987     if(ze == NULL){
988       perror("malloc");
989       exit(1);
990     }
991     
992     memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
993     ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
994     strcpy(ze->filename, "META-INF/MANIFEST.MF");
995     ze->filename[nlen] = '\0';
996     
997     ze->offset = lseek(jfd, 0, SEEK_CUR);
998     ze->mod_time = (ub2)(mod_time & 0x0000ffff);
999     ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1000     ze->crc = crc;
1001     ze->csize = mf_len;
1002     ze->usize = ze->csize;
1003     ze->compressed = FALSE;
1004     
1005     add_entry(ze);
1006     
1007     write(jfd, file_header, 30);
1008     write(jfd, "META-INF/MANIFEST.MF", nlen);
1009     write(jfd, mf, mf_len);
1010     free(mf);
1011     }
1012     else {
1013         printf("malloc errror\n");
1014         exit(-1);
1015     }
1016   } else {
1017     int mfd;
1018     struct stat statbuf;
1019
1020     stat(mf_name, &statbuf);
1021
1022     if(!S_ISREG(statbuf.st_mode)){
1023       fprintf(stderr, "Invalid manifest file specified.\n");
1024       exit(1);
1025     }
1026   
1027     mfd = open(mf_name, O_RDONLY | O_BINARY);
1028
1029     if(mfd < 0){
1030       fprintf(stderr, "Error opening %s.\n", mf_name);
1031       exit(1);
1032     }
1033
1034     if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
1035       perror("error writing to jar");
1036       exit(1);
1037     }
1038
1039   }
1040
1041   return 0;
1042 }
1043
1044 /* Implements -C by wrapping add_to_jar.  new_dir is the directory 
1045    to switch to.
1046
1047    `updating', if nonzero, will indicate that we are updating an
1048    existing file, and will need to take special care. If set, we will
1049    also expect that the linked list of zip entries will be filled in
1050    with the jar file's current contents.
1051  */
1052 int 
1053 add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
1054                      const int updating)
1055 {
1056   int retval;
1057   char old_dir[MAXPATHLEN]; 
1058   if (getcwd(old_dir, MAXPATHLEN) == NULL) {
1059     perror("getcwd");
1060     return 1;
1061   }
1062   if (chdir(new_dir) == -1) {
1063     perror(new_dir);
1064     return 1;
1065   }
1066   retval=add_to_jar(fd, file, updating);
1067   if (chdir(old_dir) == -1) {
1068     perror(old_dir);
1069     return 1;
1070   }
1071   return retval;
1072 }
1073
1074 int 
1075 add_to_jar (int fd, const char *file, const int updating)
1076 {
1077   struct stat statbuf;
1078   DIR *dir;
1079   struct dirent *de;
1080   zipentry *ze;
1081   zipentry *existing = NULL;
1082   int stat_return;
1083
1084   /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com> 
1085    * It fixes this:
1086    *   "normal" jar : org/apache/java/io/LogRecord.class
1087    *   fastjar      : ./org/apache/java/io/LogRecord.class
1088    * Fastjar's preservation of the ./'s makes the jarfile unusuable for use 
1089    * with both kaffe-1.0b4 and JDK.
1090    */
1091   while (*file=='.' && *(file+1)=='/')
1092     file+=2;
1093   
1094   if(jarfile && !strcmp(file, jarfile)){
1095     if(verbose)
1096       printf("skipping: %s\n", file);
1097     return 0;  /* we don't want to add ourselves.. */
1098   }
1099
1100   stat_return = stat(file, &statbuf);
1101   
1102   if(stat_return == -1){
1103     perror(file);
1104     return 1;
1105   } else if(S_ISDIR(statbuf.st_mode)){
1106     char *fullname;
1107     char *t_ptr;
1108     int nlen;
1109     unsigned long mod_time;
1110
1111     dir = opendir(file);
1112     
1113     if(dir == NULL){
1114       perror("opendir");
1115       return 1;
1116     }
1117     
1118     nlen = strlen(file) + 256;
1119     fullname = (char*)malloc(nlen * sizeof(char));
1120     memset(fullname, 0, (nlen * sizeof(char)));
1121     
1122     if(fullname == NULL){
1123       fprintf(stderr, "Filename is NULL!\n");
1124       return 1;
1125     }
1126
1127     strcpy(fullname, file);
1128     nlen = strlen(file);
1129
1130     if(fullname[nlen - 1] != '/'){
1131       fullname[nlen] = '/';
1132       t_ptr = (fullname + nlen + 1);
1133     } else
1134       t_ptr = (fullname + nlen);
1135
1136
1137     memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
1138     
1139     nlen = (t_ptr - fullname);
1140
1141     mod_time = unix2dostime(&statbuf.st_mtime);
1142
1143     PACK_UB2(file_header, LOC_EXTRA, 0);
1144     PACK_UB2(file_header, LOC_COMP, 0);
1145     PACK_UB2(file_header, LOC_FNLEN, nlen);
1146     PACK_UB4(file_header, LOC_MODTIME, mod_time);
1147
1148     ze = (zipentry*)malloc(sizeof(zipentry));
1149     if(ze == NULL){
1150       perror("malloc");
1151       exit(1);
1152     }
1153
1154     memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1155     ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
1156     strcpy(ze->filename, fullname);
1157     ze->filename[nlen] = '\0';
1158     
1159     ze->offset = lseek(fd, 0, SEEK_CUR);
1160     ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1161     ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1162     ze->compressed = FALSE;
1163
1164     if (updating)
1165       {
1166         if ((existing = find_entry (ze->filename)) != NULL)
1167           {
1168             if (existing->usize != 0)
1169               {
1170                 /* XXX overwriting non-directory with directory? */
1171                 fprintf (stderr, "%s: %s: can't overwrite non-directory with directory\n",
1172                          progname, fullname);
1173                 return 1;
1174               }
1175           }
1176         if (lseek (fd, end_of_entries, SEEK_SET) == -1)
1177           {
1178             fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
1179             perror ("lseek");
1180             return 1;
1181           }
1182       }
1183
1184     if (!existing)
1185       {
1186         add_entry (ze);
1187         write (fd, file_header, 30);
1188         write (fd, fullname, nlen);
1189         end_of_entries = lseek (fd, 0, SEEK_CUR);
1190
1191         if (verbose)
1192           printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
1193       }
1194
1195     while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
1196       if(de->d_name[0] == '.')
1197         continue;
1198       if(jarfile && !strcmp(de->d_name, jarfile)){
1199         /* we don't want to add ourselves.  Believe me */
1200         if(verbose)
1201           printf("skipping: %s\n", de->d_name);
1202         continue;
1203       }
1204
1205       strcpy(t_ptr, de->d_name);
1206
1207       if (add_to_jar(fd, fullname, updating)) {
1208         fprintf(stderr, "Error adding file to jar!\n");
1209         return 1;
1210       }
1211     }
1212
1213     free(fullname);
1214     closedir(dir);
1215       
1216   } else if(S_ISREG(statbuf.st_mode)){
1217     int add_fd;
1218
1219     add_fd = open(file, O_RDONLY | O_BINARY);
1220     if(add_fd < 0){
1221       fprintf(stderr, "Error opening %s.\n", file);
1222       return 1;
1223     }
1224     
1225     if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
1226       fprintf(stderr, "Error adding file to jar!\n");
1227       return 1;
1228     }
1229     
1230   } else {
1231     fprintf(stderr, "Illegal file specified: %s\n", file);
1232   }
1233   return 0;
1234 }
1235
1236 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
1237                     const int updating)
1238 {
1239   unsigned short file_name_length;
1240   unsigned long mod_time;
1241   ub1 rd_buff[RDSZ];
1242   uLong crc = 0;
1243   off_t offset = 0;
1244   int rdamt;
1245   struct zipentry *ze;
1246   struct zipentry *existing = NULL;
1247
1248   if (updating)
1249     {
1250       existing = find_entry (fname);
1251       if (existing && looks_like_dir (fname))
1252         {
1253           fprintf (stderr, "%s: %s is a directory in the archive\n",
1254                    progname, fname);
1255           return 1;
1256         }
1257     }
1258
1259   mod_time = unix2dostime(&(statbuf->st_mtime));
1260   file_name_length = strlen(fname);
1261
1262   if(!seekable && !do_compress){
1263     crc = crc32(0L, Z_NULL, 0); 
1264     
1265     while((rdamt = read(ffd, rd_buff, RDSZ)) != 0) 
1266       crc = crc32(crc, rd_buff, rdamt); 
1267     
1268     lseek(ffd, 0, SEEK_SET);
1269   }
1270   
1271   /* data descriptor */
1272   if(!seekable && do_compress){
1273     PACK_UB2(file_header, LOC_EXTRA, 8);
1274   } else {
1275     PACK_UB2(file_header, LOC_EXTRA, 0);
1276   }
1277   
1278   if(do_compress){
1279     PACK_UB2(file_header, LOC_COMP, 8);
1280   } else {
1281     PACK_UB2(file_header, LOC_COMP, 0);
1282   }
1283     
1284   PACK_UB4(file_header, LOC_MODTIME, mod_time);
1285   PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1286   
1287   if(!seekable && !do_compress){
1288     PACK_UB4(file_header, LOC_CRC, crc);
1289     PACK_UB4(file_header, LOC_USIZE, statbuf->st_size); 
1290     PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1291   } else 
1292     memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1293   
1294   ze = (zipentry*)malloc(sizeof(zipentry));
1295   if(ze == NULL){
1296     perror("malloc");
1297     exit(1);
1298   }
1299   
1300   memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1301   ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1302   strcpy(ze->filename, fname);
1303
1304   ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1305   ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1306
1307   if(!seekable && !do_compress)
1308     ze->crc = crc;
1309
1310   ze->csize = statbuf->st_size;
1311   ze->usize = ze->csize;
1312
1313   if (existing)
1314     ze->offset = existing->offset;
1315   else if (updating)
1316     ze->offset = end_of_entries;
1317   else
1318     ze->offset = lseek(jfd, 0, SEEK_CUR);
1319
1320   if(do_compress)
1321     ze->compressed = TRUE;
1322   else
1323     ze->compressed = FALSE;
1324
1325   if (!existing)
1326     add_entry(ze);
1327   if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
1328     {
1329       perror ("lseek");
1330       return 1;
1331     }
1332
1333   /* We can safely write the header here, since it will be the same size
1334      as before */
1335   
1336   /* Write the local header */
1337   write(jfd, file_header, 30);
1338     
1339   /* write the file name to the zip file */
1340   write(jfd, fname, file_name_length);
1341
1342
1343   if(verbose){
1344     if (existing)
1345       printf ("updating: %s ", fname);
1346     else
1347       printf("adding: %s ", fname);
1348     fflush(stdout);
1349   }
1350  
1351   if(do_compress){
1352     /* compress the file */
1353     compress_file(ffd, jfd, ze, existing);
1354   } else {
1355     /* If we are not writing the last entry, make space for it. */
1356     if (existing && existing->next_entry)
1357       {
1358         if (ze->usize > existing->usize)
1359           {
1360             if (shift_down (jfd, existing->next_entry->offset,
1361                             ze->usize - existing->usize, existing->next_entry))
1362               {
1363                 fprintf (stderr, "%s: %s\n", progname, strerror (errno));
1364                 return 1;
1365               }
1366           }
1367       }
1368
1369     /* Write the contents of the file (uncompressed) to the zip file */
1370     /* calculate the CRC as we go along */
1371     ze->crc = crc32(0L, Z_NULL, 0); 
1372       
1373     while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1374       ze->crc = crc32(ze->crc, rd_buff, rdamt);
1375       if(write(jfd, rd_buff, rdamt) != rdamt){
1376         perror("write");
1377         return 0;
1378       }
1379     }
1380   }
1381   close(ffd);
1382   
1383   /* write out data descriptor */
1384   PACK_UB4(data_descriptor, 4, ze->crc);
1385   PACK_UB4(data_descriptor, 8, ze->csize);
1386   PACK_UB4(data_descriptor, 12, ze->usize);
1387
1388   /* we need to seek back and fill the header */
1389   if(seekable){
1390     offset = (ze->csize + strlen(ze->filename) + 16);
1391     
1392     if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1393       perror("lseek");
1394       exit(1);
1395     }
1396
1397     if(write(jfd, (data_descriptor + 4), 12) != 12){
1398       perror("write");
1399       return 0;
1400     }
1401     
1402     offset -= 12;
1403
1404     if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1405       perror("lseek");
1406       exit(1);
1407     }
1408   } else if(do_compress){
1409     /* Sun's jar tool will only allow a data descriptor if the entry is
1410        compressed, but we'll save 16 bytes/entry if we only use it when
1411        we can't seek back on the file */
1412     /* Technically, you CAN'T have a data descriptor unless the data
1413        part has an obvious end, which DEFLATED does. Otherwise, there
1414        would not be any way to determine where the data descriptor is.
1415        Store an uncompressed file that ends with 0x504b0708, and see.
1416        -- csm */
1417     
1418     if(write(jfd, data_descriptor, 16) != 16){
1419       perror("write");
1420       return 0;
1421     }
1422   }
1423
1424   if (existing)
1425     {
1426       int dd = (existing->flags & (1 << 3)) ? 12 : 0;
1427       if (existing->next_entry && ze->csize < existing->csize + dd)
1428         {
1429           if (shift_up (jfd, existing->next_entry->offset,
1430                         existing->csize + dd - ze->csize,
1431                         existing->next_entry))
1432             {
1433               perror (progname);
1434               return 1;
1435             }
1436         }
1437       /* Replace the existing entry data with this entry's. */
1438       existing->csize = ze->csize;
1439       existing->usize = ze->usize;
1440       existing->crc = ze->crc;
1441       existing->mod_time = ze->mod_time;
1442       existing->mod_date = ze->mod_date;
1443       free (ze->filename);
1444       free (ze);
1445     }
1446   else if (updating)
1447     end_of_entries = lseek (jfd, 0, SEEK_CUR);
1448   
1449   if(verbose)
1450     printf("(in=%d) (out=%d) (%s %d%%)\n", 
1451            (int)ze->usize, (int)ze->csize,
1452            (do_compress ? "deflated" : "stored"),
1453            (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1454
1455   return 0;
1456 }
1457
1458 int create_central_header(int fd){
1459   ub1 header[46];
1460   ub1 end_header[22];
1461   int start_offset;
1462   int dir_size;
1463   int total_in = 0, total_out = 22;
1464
1465   zipentry *ze;
1466
1467   /* magic number */
1468   header[0] = 'P';
1469   header[1] = 'K';
1470   header[2] = 1;
1471   header[3] = 2;
1472   /* version made by */
1473   header[4] = 10;
1474   header[5] = 0;
1475   /* version needed to extract */
1476   header[6] = 10;
1477   header[7] = 0;
1478   /* bit flag */
1479   header[8] = 0;
1480   header[9] = 0;
1481   /* compression method */
1482   header[10] = 0;
1483   header[11] = 0;
1484   /* file mod time */
1485   header[12] = 0;
1486   header[13] = 0;
1487   /* file mod date */
1488   header[14] = 0;
1489   header[15] = 0;
1490   /* crc 32 */
1491   header[16] = 0;
1492   header[17] = 0;
1493   header[18] = 0;
1494   header[19] = 0;
1495   /* compressed size */
1496   header[20] = 0;
1497   header[21] = 0;
1498   header[22] = 0;
1499   header[23] = 0;
1500   /* uncompressed size */
1501   header[24] = 0;
1502   header[25] = 0;
1503   header[26] = 0;
1504   header[27] = 0;
1505   /* filename length */
1506   header[28] = 0;
1507   header[29] = 0;
1508   /* extra field length */
1509   header[30] = 0;
1510   header[31] = 0;
1511   /* file comment length */
1512   header[32] = 0;
1513   header[33] = 0;
1514   /* disk number start */
1515   header[34] = 0;
1516   header[35] = 0;
1517   /* internal file attribs */
1518   header[36] = 0;
1519   header[37] = 0;
1520   /* external file attribs */
1521   header[38] = 0;
1522   header[39] = 0;
1523   header[40] = 0;
1524   header[41] = 0;
1525   /* relative offset of local header */
1526   header[42] = 0;
1527   header[43] = 0;
1528   header[44] = 0;
1529   header[45] = 0;
1530
1531   start_offset = lseek(fd, 0, SEEK_CUR);
1532
1533   for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1534
1535     total_in += ze->usize;
1536     total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1537
1538     if(ze->compressed){
1539       PACK_UB2(header, CEN_COMP, 8);
1540     } else {
1541       PACK_UB2(header, CEN_COMP, 0);
1542     }
1543         
1544     PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1545     PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1546     PACK_UB4(header, CEN_CRC, ze->crc);
1547     PACK_UB4(header, CEN_CSIZE, ze->csize);
1548     PACK_UB4(header, CEN_USIZE, ze->usize);
1549     PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1550     PACK_UB4(header, CEN_OFFSET, ze->offset);
1551
1552     write(fd, header, 46);
1553
1554     write(fd, ze->filename, strlen(ze->filename));
1555   }
1556
1557   dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1558
1559   /* magic number */
1560   end_header[0] = 0x50;
1561   end_header[1] = 0x4b;
1562   end_header[2] = 0x05;
1563   end_header[3] = 0x06;
1564   /* number of this disk */
1565   end_header[4] = 0;
1566   end_header[5] = 0;
1567   /* number of disk w/ start of central header */
1568   end_header[6] = 0;
1569   end_header[7] = 0;
1570   /* total number of entries in central dir on this disk*/
1571   PACK_UB2(end_header, 8, number_of_entries);
1572   /* total number of entries in central dir*/
1573   PACK_UB2(end_header, 10, number_of_entries);
1574   /* size of central dir. */
1575   PACK_UB4(end_header, 12, dir_size);
1576   /* offset of start of central dir */
1577   PACK_UB4(end_header, 16, start_offset);
1578   /* zipfile comment length */
1579   end_header[20] = 0;
1580   end_header[21] = 0;
1581
1582   write(fd, end_header, 22);
1583   
1584   if(verbose)
1585     printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n", 
1586            total_in, 
1587            total_out,
1588            (do_compress ? "deflated" : "stored"),
1589            (int)((1 - (total_out / (float)total_in)) * 100)
1590            );
1591
1592   return 0;
1593 }
1594
1595 int extract_jar(int fd, char **files, int file_num){
1596   int rdamt;
1597   int out_a, in_a;
1598   ub4 signature;
1599   ub4 csize;
1600   ub4 crc;
1601   ub2 fnlen;
1602   ub2 eflen;
1603   ub2 flags;
1604   ub2 method;
1605   ub1 *filename = NULL;
1606   int filename_len = 0;
1607   ub4 rd_buff[RDSZ];
1608   pb_file pbf;
1609   ub1 scratch[16];
1610   zipentry ze;
1611   int f_fd;
1612   int dir;
1613   int handle;
1614   int j;
1615
1616   init_inflation();
1617
1618   pb_init(&pbf, fd);
1619
1620   for(;;){
1621     f_fd = 0;
1622     crc = 0;
1623     ze.crc = 0;
1624     
1625     dir = FALSE; /* by default, the file isn't a dir */
1626     handle = TRUE; /* by default we'll extract/create the file */
1627
1628     if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1629       perror("read");
1630       break;
1631     }
1632     
1633     signature = UNPACK_UB4(scratch, 0);
1634
1635 #ifdef DEBUG    
1636     printf("signature is %x\n", signature);
1637 #endif
1638     if(signature == 0x08074b50){
1639 #ifdef DEBUG    
1640       printf("skipping data descriptor\n");
1641 #endif
1642       pb_read(&pbf, scratch, 12);
1643       continue;
1644     } else if(signature == 0x02014b50){
1645 #ifdef DEBUG    
1646       printf("Central header reached.. we're all done!\n");
1647 #endif
1648       break;
1649     }else if(signature != 0x04034b50){
1650       printf("Ick! %#x\n", signature);
1651       break;
1652     }
1653     
1654     if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1655       perror("read");
1656       break;
1657     }
1658     
1659     csize = UNPACK_UB4(file_header, LOC_CSIZE);
1660 #ifdef DEBUG    
1661     printf("Compressed size is %u\n", csize);
1662 #endif
1663
1664     fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1665 #ifdef DEBUG    
1666     printf("Filename length is %hu\n", fnlen);
1667 #endif
1668
1669     eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1670 #ifdef DEBUG    
1671     printf("Extra field length is %hu\n", eflen);
1672 #endif
1673
1674     flags = UNPACK_UB2(file_header, LOC_EXTRA);
1675 #ifdef DEBUG    
1676     printf("Flags are %#hx\n", flags);
1677 #endif
1678
1679     method = UNPACK_UB2(file_header, LOC_COMP);
1680 #ifdef DEBUG
1681     printf("Compression method is %#hx\n", method);
1682 #endif
1683
1684     /* if there isn't a data descriptor */
1685     if(!(flags & 0x0008)){
1686       crc = UNPACK_UB4(file_header, LOC_CRC);
1687 #ifdef DEBUG    
1688       printf("CRC is %x\n", crc);
1689 #endif
1690     }
1691
1692     if(filename_len < fnlen + 1){
1693       if(filename != NULL)
1694         free(filename);
1695       
1696       filename = malloc(sizeof(ub1) * (fnlen + 1));
1697       filename_len = fnlen + 1;
1698     }
1699
1700     pb_read(&pbf, filename, fnlen);
1701     filename[fnlen] = '\0';
1702
1703 #ifdef DEBUG    
1704     printf("filename is %s\n", filename);
1705 #endif
1706
1707     if(file_num > 0){
1708       handle = FALSE;
1709       
1710       for(j = 0; j < file_num; j++)
1711         if(strcmp(files[j], (const char *)filename) == 0){
1712           handle = TRUE;
1713           break;
1714         }
1715     }
1716
1717     if(!handle)
1718       f_fd = -1;
1719
1720     /* OK, there is some directory information in the file.  Nothing to do
1721        but ensure the directory(s) exist, and create them if they don't.
1722        What a pain! */
1723     if(strchr((const char *)filename, '/') != NULL && handle){
1724       /* Loop through all the directories in the path, (everything w/ a '/') */
1725       const ub1 *start = filename;
1726       char *tmp_buff;
1727       struct stat sbuf;
1728
1729       tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1730
1731       for(;;){
1732         const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1733
1734         if(idx == NULL)
1735           break;
1736         else if(idx == start){
1737           start++;
1738           continue;
1739         }
1740         start = idx + 1;
1741
1742         strncpy(tmp_buff, (const char *)filename, (idx - filename));
1743         tmp_buff[(idx - filename)] = '\0';
1744
1745 #ifdef DEBUG    
1746         printf("checking the existance of %s\n", tmp_buff);
1747 #endif
1748
1749         if(stat(tmp_buff, &sbuf) < 0){
1750           if(errno != ENOENT){
1751             perror("stat");
1752             exit(1);
1753           }
1754
1755         } else if(S_ISDIR(sbuf.st_mode)){
1756 #ifdef DEBUG    
1757           printf("Directory exists\n");
1758 #endif
1759           continue;
1760         }else {
1761           fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1762                   tmp_buff);
1763           exit(1);
1764         }
1765         
1766 #ifdef DEBUG    
1767         printf("Making directory..\n");
1768 #endif
1769         if(mkdir(tmp_buff, 0755) < 0){
1770           perror("mkdir");
1771           exit(1);
1772         }
1773         if(verbose && handle)
1774           printf("%10s: %s/\n", "created", tmp_buff);
1775
1776       }
1777
1778       /* only a directory */
1779       if(strlen((const char *)start) == 0)
1780         dir = TRUE;
1781
1782 #ifdef DEBUG    
1783       printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1784 #endif
1785
1786       /* If the entry was just a directory, don't write to file, etc */
1787       if(strlen((const char *)start) == 0)
1788         f_fd = -1;
1789
1790       free(tmp_buff);
1791     }
1792
1793     if(f_fd != -1 && handle){
1794       f_fd = open((const char *)filename,
1795                   O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1796
1797       if(f_fd < 0){
1798         fprintf(stderr, "Error extracting JAR archive!\n");
1799         perror((const char *)filename);
1800         exit(1);
1801       }
1802     }
1803
1804     if(method != 8 && flags & 0x0008){
1805       fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1806       exit(1);
1807     }
1808
1809     if(method == 8 || flags & 0x0008){
1810         consume(&pbf, eflen);
1811       
1812       inflate_file(&pbf, f_fd, &ze);
1813     } else {
1814
1815 #ifdef DEBUG    
1816       printf("writing stored data.. (%d bytes)\n", csize);
1817 #endif
1818
1819       out_a = 0;
1820       in_a = csize;
1821
1822       ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1823
1824       while(out_a < (int)csize){
1825         rdamt = (in_a > RDSZ ? RDSZ : in_a);
1826         if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1827           perror("read");
1828           exit(1);
1829         }
1830         
1831         ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1832
1833         if(f_fd >= 0)
1834           write(f_fd, rd_buff, rdamt);
1835
1836         out_a += rdamt;
1837         in_a -= rdamt;
1838
1839 #ifdef DEBUG    
1840         printf("%d bytes written\n", out_a);
1841 #endif
1842       }
1843
1844         consume(&pbf, eflen);
1845     }
1846
1847     /* if there is a data descriptor left, compare the CRC */
1848     if(flags & 0x0008){
1849
1850       if(pb_read(&pbf, scratch, 16) != 16){
1851         perror("read");
1852         exit(1);
1853       }
1854
1855       signature = UNPACK_UB4(scratch, 0);
1856
1857       if(signature != 0x08074b50){
1858         fprintf(stderr, "Error! Missing data descriptor!\n");
1859         exit(1);
1860       }
1861
1862       crc = UNPACK_UB4(scratch, 4);
1863
1864     }
1865
1866     if(crc != ze.crc){
1867       fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1868               ze.crc, crc);
1869       exit(1);
1870     }
1871
1872     close(f_fd);
1873
1874     if(verbose && dir == FALSE && handle)
1875       printf("%10s: %s\n",
1876              (method == 8 ? "inflated" : "extracted"),
1877              filename);
1878   }
1879
1880   return 0;
1881 }
1882
1883 int list_jar(int fd, char **files, int file_num){
1884   ub4 signature;
1885   ub4 csize;
1886   ub4 usize;
1887   ub4 mdate;
1888   ub4 tmp;
1889   ub2 fnlen;
1890   ub2 eflen;
1891   ub2 clen;
1892   ub2 flags;
1893   ub2 method;
1894   ub2 cen_size;
1895   ub1 *filename = NULL;
1896   ub1 scratch[16];
1897   ub1 cen_header[46];
1898   int filename_len = 0;
1899   off_t size;
1900   int i, j;
1901   time_t tdate;
1902   struct tm *s_tm;
1903   char ascii_date[31];
1904   zipentry ze;
1905
1906 #ifdef DEBUG
1907   printf("Listing jar file, looking for %d files\n", file_num);
1908 #endif
1909
1910   /* This should be the start of the central-header-end section */
1911   if(seekable){
1912     if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1913       perror("lseek");
1914       exit(1);
1915     }
1916     
1917     if(read(fd, &tmp, sizeof(ub4)) != 4){
1918       perror("read");
1919       exit(1);
1920     }
1921
1922 #ifdef WORDS_BIGENDIAN
1923     tmp = L2BI(tmp);
1924 #endif
1925
1926     if(tmp != 0x06054b50){
1927       fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1928       exit(1);
1929     }
1930
1931     if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1932       perror("lseek");
1933       exit(1);
1934     }
1935   
1936     if(read(fd, &cen_size, 2) != 2){
1937       perror("read");
1938       exit(1);
1939     }
1940
1941 #ifdef WORDS_BIGENDIAN
1942     cen_size = L2BS(cen_size);
1943 #endif
1944
1945     /*   printf("%hu entries in central header\n", cen_size); */
1946
1947     if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1948       perror("lseek");
1949       exit(1);
1950     }
1951
1952     if(read(fd, &tmp, 4) != 4){
1953       perror("read");
1954       exit(1);
1955     }
1956
1957 #ifdef WORDS_BIGENDIAN
1958     tmp = L2BI(tmp);
1959 #endif
1960
1961     /*   printf("Central header offset = %d\n", tmp); */
1962
1963     if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1964       perror("lseek");
1965       exit(1);
1966     }
1967
1968     /* Loop through the entries in the central header */
1969     for(i = 0; i < cen_size; i++){
1970     
1971       if(read(fd, &cen_header, 46) != 46){
1972         perror("read");
1973         exit(1);
1974       }
1975
1976       signature = UNPACK_UB4(cen_header, 0);
1977       if(signature != 0x02014b50){
1978         fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1979         exit(1);
1980       }
1981
1982       usize = UNPACK_UB4(cen_header, CEN_USIZE);
1983       fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1984       eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1985       clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1986
1987       /* If we're providing verbose output, we need to make an ASCII
1988        * formatted version of the date. */
1989       if(verbose){
1990         mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1991         tdate = dos2unixtime(mdate);
1992         s_tm = localtime(&tdate);
1993         strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1994         ascii_date[30] = '\0';
1995       }
1996
1997       if(filename_len < fnlen + 1){
1998         if(filename != NULL)
1999           free(filename);
2000       
2001         filename = malloc(sizeof(ub1) * (fnlen + 1));
2002         filename_len = fnlen + 1;
2003       }
2004     
2005       if(read(fd, filename, fnlen) != fnlen){
2006         perror("read");
2007         exit(1);
2008       }
2009       filename[fnlen] = '\0';
2010     
2011       /* if the user specified a list of files on the command line,
2012          we'll only display those, otherwise we'll display everything */
2013       if(file_num > 0){
2014         for(j = 0; j < file_num; j++)
2015           if(strcmp(files[j], (const char *)filename) == 0){
2016             if(verbose)
2017               printf("%6d %s %s\n", usize, ascii_date, filename);
2018             else
2019               printf("%s\n", filename);
2020             break;
2021           }
2022       } else {
2023         if(verbose)
2024           printf("%6d %s %s\n", usize, ascii_date, filename);
2025         else
2026           printf("%s\n", filename);
2027       }            
2028       
2029       size = eflen + clen;
2030       if(size > 0){
2031         if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
2032           perror("lseek");
2033           exit(1);
2034         }
2035       }
2036     }
2037   } else {
2038     /* the file isn't seekable.. evil! */
2039     pb_file pbf;
2040
2041     pb_init(&pbf, fd);
2042
2043     init_inflation();
2044
2045     for(;;){
2046       if(pb_read(&pbf, scratch, 4) != 4){
2047         perror("read");
2048         break;
2049       }
2050       
2051       signature = UNPACK_UB4(scratch, 0);
2052       
2053 #ifdef DEBUG
2054       printf("signature is %x\n", signature);
2055 #endif
2056       
2057       if(signature == 0x08074b50){
2058 #ifdef DEBUG
2059         printf("skipping data descriptor\n");
2060 #endif
2061         pb_read(&pbf, scratch, 12);
2062         continue;
2063       } else if(signature == 0x02014b50){
2064 #ifdef DEBUG
2065         printf("Central header reached.. we're all done!\n");
2066 #endif
2067         break;
2068       }else if(signature != 0x04034b50){
2069 #ifdef DEBUG
2070         printf("Ick! %#x\n", signature);
2071 #endif
2072         break;
2073       }
2074       
2075       if(pb_read(&pbf, (file_header + 4), 26) != 26){
2076         perror("read");
2077         break;
2078       }
2079       
2080       csize = UNPACK_UB4(file_header, LOC_CSIZE);
2081 #ifdef DEBUG
2082       printf("Compressed size is %u\n", csize);
2083 #endif
2084       
2085       fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
2086 #ifdef DEBUG
2087       printf("Filename length is %hu\n", fnlen);
2088 #endif
2089       
2090       eflen = UNPACK_UB2(file_header, LOC_EFLEN);
2091 #ifdef DEBUG
2092       printf("Extra field length is %hu\n", eflen);
2093 #endif
2094       
2095       method = UNPACK_UB2(file_header, LOC_COMP);
2096 #ifdef DEBUG
2097       printf("Compression method is %#hx\n", method);
2098 #endif
2099
2100       flags = UNPACK_UB2(file_header, LOC_EXTRA);
2101 #ifdef DEBUG
2102       printf("Flags are %#hx\n", flags);
2103 #endif
2104       
2105       usize = UNPACK_UB4(file_header, LOC_USIZE);
2106
2107       /* If we're providing verbose output, we need to make an ASCII
2108        * formatted version of the date. */
2109       if(verbose){
2110         mdate = UNPACK_UB4(file_header, LOC_MODTIME);
2111         tdate = dos2unixtime(mdate);
2112         s_tm = localtime(&tdate);
2113         strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
2114       }
2115
2116       if(filename_len < fnlen + 1){
2117         if(filename != NULL)
2118           free(filename);
2119         
2120         filename = malloc(sizeof(ub1) * (fnlen + 1));
2121         ascii_date[30] = '\0';
2122         filename_len = fnlen + 1;
2123       }
2124       
2125       pb_read(&pbf, filename, fnlen);
2126       filename[fnlen] = '\0';
2127       
2128       /* the header is at the end.  In a JAR file, this means that the data
2129          happens to be compressed.  We have no choice but to inflate the
2130          data */
2131       if(flags & 0x0008){
2132
2133         size = eflen;
2134
2135         if(size > 0)
2136           consume(&pbf, size);
2137         
2138         if(method == 8){
2139 #ifdef DEBUG
2140           printf("inflating %s\n", filename);
2141 #endif
2142           inflate_file(&pbf, -1, &ze);
2143
2144           usize = ze.usize;
2145         } else 
2146           printf("We're shit outta luck!\n");
2147           
2148       } else {
2149         size = csize + (eflen > 0 ? eflen : 0);
2150         
2151
2152 #ifdef DEBUG
2153         printf("Skipping %ld bytes\n", (long)size);
2154 #endif
2155
2156         consume(&pbf, size);
2157       }
2158       /* print out the listing */
2159       if(file_num > 0){
2160         for(j = 0; j < file_num; j++)
2161           if(strcmp(files[j], (const char *)filename) == 0){
2162             if(verbose)
2163               printf("%6d %s %s\n", usize, ascii_date, filename);
2164             else
2165               printf("%s\n", filename);
2166             break;
2167           }
2168       } else {
2169         if(verbose)
2170           printf("%6d %s %s\n", usize, ascii_date, filename);
2171         else
2172           printf("%s\n", filename);
2173       }        
2174     }
2175   }
2176   return 0;
2177 }
2178
2179 int consume(pb_file *pbf, int amt){
2180   int tc = 0; /* total amount consumed */
2181   ub1 buff[RDSZ];
2182   int rdamt;
2183
2184 #ifdef DEBUG
2185   printf("Consuming %d bytes\n", amt);
2186 #endif
2187
2188   if (seekable){
2189     if (amt <= (int)pbf->buff_amt)
2190       pb_read(pbf, buff, amt);
2191     else {
2192       lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
2193       pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
2194     }
2195   } else
2196   while(tc < amt){
2197     rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
2198 #ifdef DEBUG
2199     printf("got %d bytes\n", rdamt);
2200 #endif
2201     tc += rdamt;
2202   }
2203
2204 #ifdef DEBUG
2205   printf("%d bytes consumed\n", amt);
2206 #endif
2207
2208   return 0;
2209 }
2210
2211 void usage(const char *filename){
2212   fprintf(stderr, "Try `%s --help' for more information.\n", filename);
2213   exit (1);
2214 }
2215
2216 void version ()
2217 {
2218   printf("jar (%s) %s\n\n", PACKAGE, VERSION);
2219   printf("Copyright 1999, 2000, 2001  Bryan Burns\n");
2220   printf("Copyright 2002, 2004 Free Software Foundation\n");
2221   printf("\
2222 This is free software; see the source for copying conditions.  There is NO\n\
2223 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2224   exit (0);
2225 }
2226
2227 void help(const char *filename)
2228 {
2229   printf("\
2230 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2231 \n\
2232 Store many files together in a single `jar' file.\n\
2233 \n\
2234   -c              create new archive\n\
2235   -t              list table of contents for archive\n\
2236   -x              extract named (or all) files from archive\n\
2237   -u              update existing archive\n\
2238 ", filename);
2239   printf("\n\
2240   -@              read names from stdin\n\
2241   -0              store only; use no ZIP compression\n\
2242   -C DIR FILE     change to the specified directory and include\n\
2243                   the following file\n\
2244   -E              don't include the files found in a directory\n\
2245   -f FILE         specify archive file name\n\
2246   --help          print this help, then exit\n\
2247   -m FILE         include manifest information from specified manifest file\n\
2248   -M              Do not create a manifest file for the entries\n\
2249   -v              generate verbose output on standard output\n\
2250   -V, --version   display version information\n\
2251 ");
2252   printf("\n\
2253 If any file is a directory then it is processed recursively.\n\
2254 The manifest file name and the archive file name needs to be specified\n\
2255 in the same order the 'm' and 'f' flags are specified.\n\
2256 \n\
2257 Example 1: to archive two class files into an archive called classes.jar: \n\
2258      jar cvf classes.jar Foo.class Bar.class \n\
2259 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2260      files in the foo/ directory into 'classes.jar': \n\
2261      jar cvfm classes.jar mymanifest -C foo/ .\n\
2262 ");
2263
2264   exit(0);
2265 }
2266
2267 static char *
2268 jt_strdup(s)
2269      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 }