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