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(¤t_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