OSDN Git Service

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