Dead
[official-gcc.git] / gomp-20050608-branch / fastjar / jartool.c
blob7faa518a2e196cde0a2ed545387bfc4289f27150
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
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.
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.
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.
22 Revision 1.10 2002/01/03 04:57:56 rodrigc
23 2001-01-02 Craig Rodrigues <rodrigc@gcc.gnu.org>
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.
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".
41 Revision 1.8 2001/08/29 01:35:31 apbianco
42 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
44 * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
45 Fixes PR java/3949.
47 (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
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.
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.
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.
65 Revision 1.4 2000/12/28 21:47:37 robertl
66 2000-12-28 Robert Lipe <robertl@sco.com>
68 * jartool.c (MAXPATHLEN): Provide if not defined.
70 Revision 1.3 2000/12/14 18:45:35 ghazi
71 Warning fixes:
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.
79 * compress.h (init_compression, end_compression, init_inflation,
80 end_inflation): Prototype void arguments.
82 * dostime.c (rcsid): Delete.
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.
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.
96 * pushback.c (rcsid): Delete.
98 Revision 1.2 2000/12/13 18:11:57 tromey
99 * jartool.c (extract_jar): Use strchr, not index.
101 Revision 1.1 2000/12/09 03:08:23 apbianco
102 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
104 * fastjar: Imported.
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.
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.
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.
120 Revision 1.2 1999/12/06 07:38:28 toast
121 fixed recursive archiving bug
123 Revision 1.1.1.1 1999/12/06 03:09:34 toast
124 initial checkin..
128 Revision 1.22 1999/10/12 19:45:13 burnsbr
129 adding patch to fix compat problem
131 Revision 1.21 1999/05/10 09:15:49 burnsbr
132 fixed manifest file version info
134 Revision 1.20 1999/05/10 08:53:16 burnsbr
135 *** empty log message ***
137 Revision 1.19 1999/05/10 08:30:39 burnsbr
138 added extract / listing code
140 Revision 1.18 1999/04/28 04:24:29 burnsbr
141 updated version
143 Revision 1.17 1999/04/28 04:21:23 burnsbr
144 added support for -C dir-changing flag.. Updated total compression display
146 Revision 1.16 1999/04/27 10:28:22 burnsbr
147 updated version string
149 Revision 1.15 1999/04/27 10:04:06 burnsbr
150 configure support
152 Revision 1.14 1999/04/27 08:56:14 burnsbr
153 added -V flag, better error messages
155 Revision 1.13 1999/04/26 02:35:21 burnsbr
156 changed all sorts of stuff.. compression now works 100%
158 Revision 1.12 1999/04/23 12:00:45 burnsbr
159 90% done with compression code
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..
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.
170 Revision 1.9 1999/04/21 09:55:16 burnsbr
171 pulled out printfs
173 Revision 1.8 1999/04/21 02:58:01 burnsbr
174 started manifest code
176 Revision 1.7 1999/04/20 23:15:28 burnsbr
177 added patch sent by John Bley <jbb6@acpub.duke.edu>
179 Revision 1.6 1999/04/20 08:56:02 burnsbr
180 added GPL comment
182 Revision 1.5 1999/04/20 08:16:09 burnsbr
183 fixed verbose flag, did some optimization
185 Revision 1.4 1999/04/20 05:09:59 burnsbr
186 added rcsid variable
188 Revision 1.3 1999/04/20 05:08:54 burnsbr
189 fixed Log statement
193 #include "config.h"
195 #include <zlib.h>
197 #ifdef HAVE_STDLIB_H
198 #include <stdlib.h>
199 #endif
201 #ifdef HAVE_UNISTD_H
202 #include <unistd.h>
203 #endif
205 #include <stdio.h>
206 #include <sys/stat.h>
207 #include <sys/types.h>
209 #ifdef HAVE_SYS_PARAM_H
210 #include <sys/param.h>
211 #endif
213 #ifndef MAXPATHLEN
214 #define MAXPATHLEN 1024
215 #endif
217 #ifdef HAVE_DIRENT_H
218 #include <dirent.h>
219 #endif
221 #ifdef HAVE_FCNTL_H
222 #include <fcntl.h>
223 #endif
225 #include <string.h>
226 #include <errno.h>
228 #ifdef TM_IN_SYS_TIME
229 #include <sys/time.h>
230 #else
231 #include <time.h>
232 #endif
234 #include <getopt.h>
236 #include "jartool.h"
237 #include "zipfile.h"
238 #include "dostime.h"
239 #include "pushback.h"
240 #include "compress.h"
241 #include "shift.h"
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
249 #ifdef WORDS_BIGENDIAN
251 #define L2BI(l) ((l & 0xff000000) >> 24) | \
252 ((l & 0x00ff0000) >> 8) | \
253 ((l & 0x0000ff00) << 8) | \
254 ((l & 0x000000ff) << 24);
256 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
258 #endif
260 #ifndef errno
261 extern int errno;
262 #endif
264 #ifndef O_BINARY
265 #define O_BINARY 0
266 #endif
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);
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 *);
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;
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;
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;
307 zipentry *ziplist; /* linked list of entries */
308 zipentry *ziptail; /* tail of the linked list */
310 int number_of_entries; /* number of entries in the linked list */
312 /* What we go by. */
313 const char *progname;
315 /* The offset of the end of the last zip entry. */
316 ub4 end_of_entries;
318 /* This is used to mark options with no short value. */
319 #define LONG_OPT(Num) ((Num) + 128)
321 #define OPT_HELP LONG_OPT (0)
323 /* This holds all options. */
324 #define OPTION_STRING "-ctxuvVf:m:C:0MiE@"
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"
331 static const struct option options[] =
333 { "help", no_argument, NULL, OPT_HELP },
334 { "version", no_argument, NULL, 'V' },
335 { NULL, no_argument, NULL, 0 }
338 int main(int argc, char **argv)
341 char *mfile = NULL;
343 int action = ACTION_NONE;
344 int manifest = TRUE;
345 int opt;
347 int jarfd = -1;
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;
354 progname = argv[0];
356 do_compress = TRUE;
357 verbose = FALSE;
359 ziplist = NULL;
361 number_of_entries = 0;
363 if(argc < 2)
364 usage(argv[0]);
366 new_argc = 0;
367 new_argv = (char **) malloc (argc * sizeof (char *));
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;
414 case OPT_HELP:
415 help(argv[0]);
416 break;
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]);
430 if(verbose && action == ACTION_INDEX)
431 fprintf(stderr, "Warning: '-i' option is currently a no-op\n");
433 /* FIXME: implement -i option. */
434 if(action == ACTION_INDEX)
435 exit(0);
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;
443 if(action == ACTION_NONE){
444 fprintf(stderr, "%s: one of options -{ctxu} must be specified.\n",
445 progname);
446 usage(argv[0]);
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]);
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);
467 if(jarfd < 0){
468 fprintf(stderr, "%s: error opening %s for writing: %s\n", progname,
469 jarfile, strerror (errno));
470 exit(1);
473 /* We assume that the file is seekable */
474 seekable = TRUE;
476 } else {
478 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
480 /* standard out is not seekable */
481 seekable = FALSE;
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;
487 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
489 if(jarfile){
490 jarfd = open(jarfile, O_RDONLY | O_BINARY);
492 if(jarfd < 0){
493 fprintf(stderr, "%s: error opening %s for reading: %s\n", progname,
494 jarfile, strerror (errno));
495 exit(1);
498 seekable = TRUE;
499 } else {
500 jarfd = STDIN_FILENO; /* jarfd is standard in */
502 /* we assume that the stream isn't seekable for safety */
503 seekable = FALSE;
507 if (action == ACTION_UPDATE)
509 if (!jarfile)
511 fprintf (stderr, "%s: `-u' mode requires a file name\n",
512 argv[0]);
513 exit (1);
516 if ((jarfd = open (jarfile, O_RDWR | O_BINARY)) < 0)
518 fprintf (stderr, "Error opening %s for reading!\n", jarfile);
519 perror (jarfile);
520 exit (1);
523 /* Assert that jarfd is seekable. */
524 if (lseek (jarfd, 0, SEEK_CUR) == -1)
526 fprintf (stderr, "%s: %s is not seekable\n", argv[0], jarfile);
527 exit (1);
530 seekable = TRUE;
533 if(action == ACTION_CREATE || action == ACTION_UPDATE){
534 const char *arg;
535 init_headers();
537 if(do_compress)
538 init_compression();
540 if (action == ACTION_UPDATE)
542 if (read_entries (jarfd))
543 exit (1);
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);
552 init_args (new_argv, 0);
553 /* now we add the files to the archive */
554 while ((arg = get_next_arg ())){
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);
563 if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add,
564 action == ACTION_UPDATE))
566 fprintf(stderr,
567 "Error adding %s (in directory %s) to jar archive!\n",
568 file_to_add, dir_to_change);
569 exit(1);
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);
578 /* de-initialize the compression DS */
579 if(do_compress)
580 end_compression();
582 if (action == ACTION_UPDATE)
583 lseek (jarfd, end_of_entries, SEEK_SET);
585 create_central_header(jarfd);
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
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);
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);
609 exit(0);
612 static int args_current_g;
613 static char **args_g;
615 static void
616 init_args(char **args, int current)
618 if(!read_names_from_stdin)
620 args_g = args;
621 args_current_g = current;
625 static char *
626 get_next_arg (void)
628 static int reached_end = 0;
630 if (reached_end)
631 return NULL;
633 if (args_g)
635 if (!args_g [args_current_g])
637 reached_end = 1;
638 return NULL;
640 return args_g [args_current_g++];
642 else
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. */
648 char s [MAXPATHLEN];
649 int pos = 0;
651 /* Get rid of '\n' and '\r' first. */
652 while (1)
654 int c = getc (stdin);
655 if (c == '\n' || c == '\r')
656 continue;
657 else
659 if (c == EOF)
660 return NULL;
661 ungetc (c, stdin);
662 break;
666 while (1)
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;
676 if (pos)
678 s [pos] = '\0';
679 return jt_strdup (s);
681 else
682 return NULL;
686 void init_headers(void)
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;
700 file_header[7] = 0x00;
701 /* do_compression method (deflation) */
702 file_header[8] = 0;
703 file_header[9] = 0;
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;
733 /* Initialize the compression DS */
734 PACK_UB4(data_descriptor, 0, 0x08074b50);
738 void add_entry(struct zipentry *ze)
741 if(ziplist == NULL){
742 ziplist = ze;
743 ziptail = ziplist;
744 } else {
745 ziplist->next_entry = ze;
746 ziplist = ze;
749 number_of_entries++;
752 static struct zipentry *
753 find_entry (const char *fname)
755 struct zipentry *ze;
757 for (ze = ziptail; ze; ze = ze->next_entry)
759 if (!strcmp (ze->filename, fname))
760 return ze;
762 return NULL;
766 static int
767 looks_like_dir (const char *fname)
769 struct zipentry *ze;
770 size_t len = strlen (fname);
772 for (ze = ziptail; ze; ze = ze->next_entry)
774 if (strlen (ze->filename) > len
775 && !strncmp (fname, ze->filename, len)
776 && ze->filename[len] == '/')
777 return 1;
779 return 0;
784 * Read the zip entries of an existing file, building `ziplist' as we go.
786 int read_entries (int fd)
788 struct zipentry *ze;
789 ub1 intbuf[4];
790 ub1 header[46];
791 ub2 len;
792 ub2 count, i;
793 off_t offset;
795 if (lseek (fd, -22, SEEK_END) == -1)
797 fprintf (stderr, "%s: %s: can't seek file\n", progname, jarfile);
798 return 1;
801 if (read (fd, intbuf, 4) < 4)
803 perror (progname);
804 return 1;
806 /* Is there a zipfile comment? */
807 while (UNPACK_UB4(intbuf, 0) != 0x06054b50)
809 if (lseek (fd, -5, SEEK_CUR) == -1 ||
810 read (fd, intbuf, 4) != 4)
812 fprintf (stderr, "%s: can't find end of central directory: %s\n",
813 progname, strerror (errno));
814 return 1;
818 /* Skip disk numbers. */
819 if (lseek (fd, 6, SEEK_CUR) == -1)
821 perror (progname);
822 return 1;
825 /* Number of entries in the central directory. */
826 if (read (fd, intbuf, 2) != 2)
828 perror (progname);
829 return 1;
831 count = UNPACK_UB2(intbuf, 0);
833 if (lseek (fd, 4, SEEK_CUR) == -1)
835 perror (progname);
836 return 1;
839 /* Offset where the central directory begins. */
840 if (read (fd, intbuf, 4) != 4)
842 perror (progname);
843 return 1;
845 offset = UNPACK_UB4(intbuf, 0);
846 end_of_entries = offset;
848 if (lseek (fd, offset, SEEK_SET) != offset)
850 perror (progname);
851 return 1;
854 if (read (fd, header, 46) != 46)
856 fprintf (stderr, "%s: %s: unexpected end of file\n",
857 progname, jarfile);
858 return 1;
861 for (i = 0; i < count; i++)
863 if (UNPACK_UB4(header, 0) != 0x02014b50)
865 fprintf (stderr, "%s: can't find central directory header\n",
866 progname);
867 return 1;
869 ze = (struct zipentry *) malloc (sizeof (struct zipentry));
870 if (!ze)
872 perror (progname);
873 return 1;
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)
888 perror (progname);
889 return 1;
891 if (read (fd, ze->filename, len) != len)
893 fprintf (stderr, "%s: %s: unexpected end of file\n",
894 progname, jarfile);
895 return 1;
897 len = UNPACK_UB4(header, CEN_EFLEN);
898 len += UNPACK_UB4(header, CEN_COMLEN);
899 if (lseek (fd, len, SEEK_CUR) == -1)
901 perror (progname);
902 return 1;
904 add_entry (ze);
905 if (i < count - 1)
907 if (read (fd, header, 46) != 46)
909 fprintf (stderr, "%s: %s: unexpected end of file\n",
910 progname, jarfile);
911 return 1;
916 lseek (fd, 0, SEEK_SET);
917 return 0;
920 int make_manifest(int jfd, const char *mf_name, int updating)
922 time_t current_time;
923 int nlen; /* length of file name */
924 int mod_time; /* file modification time */
925 struct zipentry *ze;
927 nlen = 9; /* trust me on this one */
929 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
931 current_time = time(NULL);
932 if(current_time == (time_t)-1){
933 perror("time");
934 exit(1);
937 mod_time = unix2dostime(&current_time);
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);
944 if(verbose)
945 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
947 ze = (zipentry*)malloc(sizeof(zipentry));
948 if(ze == NULL){
949 perror("malloc");
950 exit(1);
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';
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;
963 add_entry(ze);
965 write(jfd, file_header, 30);
966 write(jfd, "META-INF/", nlen);
968 /* if the user didn't specify an external manifest file... */
969 if(mf_name == NULL){
971 int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
972 char *mf;
974 if((mf = (char *) malloc(mf_len + 1))) {
975 uLong crc;
977 sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
979 crc = crc32(0L, Z_NULL, 0);
981 crc = crc32(crc, (const unsigned char *)mf, mf_len);
983 nlen = 20; /* once again, trust me */
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);
990 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
992 PACK_UB4(file_header, LOC_CRC, crc);
994 if(verbose)
995 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
997 ze = (zipentry*)malloc(sizeof(zipentry));
998 if(ze == NULL){
999 perror("malloc");
1000 exit(1);
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';
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;
1016 add_entry(ze);
1018 write(jfd, file_header, 30);
1019 write(jfd, "META-INF/MANIFEST.MF", nlen);
1020 write(jfd, mf, mf_len);
1021 free(mf);
1023 else {
1024 printf("malloc errror\n");
1025 exit(-1);
1027 } else {
1028 int mfd;
1029 struct stat statbuf;
1031 stat(mf_name, &statbuf);
1033 if(!S_ISREG(statbuf.st_mode)){
1034 fprintf(stderr, "Invalid manifest file specified.\n");
1035 exit(1);
1038 mfd = open(mf_name, O_RDONLY | O_BINARY);
1040 if(mfd < 0){
1041 fprintf(stderr, "Error opening %s.\n", mf_name);
1042 exit(1);
1045 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
1046 perror("error writing to jar");
1047 exit(1);
1052 return 0;
1055 /* Implements -C by wrapping add_to_jar. new_dir is the directory
1056 to switch to.
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.
1063 int
1064 add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
1065 const int updating)
1067 int retval;
1068 char old_dir[MAXPATHLEN];
1069 if (getcwd(old_dir, MAXPATHLEN) == NULL) {
1070 perror("getcwd");
1071 return 1;
1073 if (chdir(new_dir) == -1) {
1074 perror(new_dir);
1075 return 1;
1077 retval=add_to_jar(fd, file, updating);
1078 if (chdir(old_dir) == -1) {
1079 perror(old_dir);
1080 return 1;
1082 return retval;
1085 int
1086 add_to_jar (int fd, const char *file, const int updating)
1088 struct stat statbuf;
1089 DIR *dir;
1090 struct dirent *de;
1091 zipentry *ze;
1092 zipentry *existing = NULL;
1093 int stat_return;
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.
1102 while (*file=='.' && *(file+1)=='/')
1103 file+=2;
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.. */
1111 stat_return = stat(file, &statbuf);
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;
1122 dir = opendir(file);
1124 if(dir == NULL){
1125 perror("opendir");
1126 return 1;
1129 nlen = strlen(file) + 256;
1130 fullname = (char*)malloc(nlen * sizeof(char));
1131 memset(fullname, 0, (nlen * sizeof(char)));
1133 if(fullname == NULL){
1134 fprintf(stderr, "Filename is NULL!\n");
1135 return 1;
1138 strcpy(fullname, file);
1139 nlen = strlen(file);
1141 if(fullname[nlen - 1] != '/'){
1142 fullname[nlen] = '/';
1143 t_ptr = (fullname + nlen + 1);
1144 } else
1145 t_ptr = (fullname + nlen);
1148 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
1150 nlen = (t_ptr - fullname);
1152 mod_time = unix2dostime(&statbuf.st_mtime);
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);
1159 ze = (zipentry*)malloc(sizeof(zipentry));
1160 if(ze == NULL){
1161 perror("malloc");
1162 exit(1);
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';
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;
1175 if (updating)
1177 if ((existing = find_entry (ze->filename)) != NULL)
1179 if (existing->usize != 0)
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;
1187 if (lseek (fd, end_of_entries, SEEK_SET) == -1)
1189 fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
1190 perror ("lseek");
1191 return 1;
1195 if (!existing)
1197 add_entry (ze);
1198 write (fd, file_header, 30);
1199 write (fd, fullname, nlen);
1200 end_of_entries = lseek (fd, 0, SEEK_CUR);
1202 if (verbose)
1203 printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
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;
1216 strcpy(t_ptr, de->d_name);
1218 if (add_to_jar(fd, fullname, updating)) {
1219 fprintf(stderr, "Error adding file to jar!\n");
1220 return 1;
1224 free(fullname);
1225 closedir(dir);
1227 } else if(S_ISREG(statbuf.st_mode)){
1228 int add_fd;
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;
1236 if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
1237 fprintf(stderr, "Error adding file to jar!\n");
1238 return 1;
1241 } else {
1242 fprintf(stderr, "Illegal file specified: %s\n", file);
1244 return 0;
1247 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
1248 const int updating)
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;
1259 if (updating)
1261 existing = find_entry (fname);
1262 if (existing && looks_like_dir (fname))
1264 fprintf (stderr, "%s: %s is a directory in the archive\n",
1265 progname, fname);
1266 return 1;
1270 mod_time = unix2dostime(&(statbuf->st_mtime));
1271 file_name_length = strlen(fname);
1273 if(!seekable && !do_compress){
1274 crc = crc32(0L, Z_NULL, 0);
1276 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
1277 crc = crc32(crc, rd_buff, rdamt);
1279 lseek(ffd, 0, SEEK_SET);
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);
1289 if(do_compress){
1290 PACK_UB2(file_header, LOC_COMP, 8);
1291 } else {
1292 PACK_UB2(file_header, LOC_COMP, 0);
1295 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1296 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
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 */
1305 ze = (zipentry*)malloc(sizeof(zipentry));
1306 if(ze == NULL){
1307 perror("malloc");
1308 exit(1);
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);
1315 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1316 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1318 if(!seekable && !do_compress)
1319 ze->crc = crc;
1321 ze->csize = statbuf->st_size;
1322 ze->usize = ze->csize;
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);
1331 if(do_compress)
1332 ze->compressed = TRUE;
1333 else
1334 ze->compressed = FALSE;
1336 if (!existing)
1337 add_entry(ze);
1338 if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
1340 perror ("lseek");
1341 return 1;
1344 /* We can safely write the header here, since it will be the same size
1345 as before */
1347 /* Write the local header */
1348 write(jfd, file_header, 30);
1350 /* write the file name to the zip file */
1351 write(jfd, fname, file_name_length);
1354 if(verbose){
1355 if (existing)
1356 printf ("updating: %s ", fname);
1357 else
1358 printf("adding: %s ", fname);
1359 fflush(stdout);
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)
1369 if (ze->usize > existing->usize)
1371 if (shift_down (jfd, existing->next_entry->offset,
1372 ze->usize - existing->usize, existing->next_entry))
1374 fprintf (stderr, "%s: %s\n", progname, strerror (errno));
1375 return 1;
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);
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;
1392 close(ffd);
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);
1399 /* we need to seek back and fill the header */
1400 if(seekable){
1401 offset = (ze->csize + strlen(ze->filename) + 16);
1403 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1404 perror("lseek");
1405 exit(1);
1408 if(write(jfd, (data_descriptor + 4), 12) != 12){
1409 perror("write");
1410 return 0;
1413 offset -= 12;
1415 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1416 perror("lseek");
1417 exit(1);
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 */
1429 if(write(jfd, data_descriptor, 16) != 16){
1430 perror("write");
1431 return 0;
1435 if (existing)
1437 int dd = (existing->flags & (1 << 3)) ? 12 : 0;
1438 if (existing->next_entry && ze->csize < existing->csize + dd)
1440 if (shift_up (jfd, existing->next_entry->offset,
1441 existing->csize + dd - ze->csize,
1442 existing->next_entry))
1444 perror (progname);
1445 return 1;
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);
1457 else if (updating)
1458 end_of_entries = lseek (jfd, 0, SEEK_CUR);
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));
1466 return 0;
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;
1476 zipentry *ze;
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;
1542 start_offset = lseek(fd, 0, SEEK_CUR);
1544 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1546 total_in += ze->usize;
1547 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1549 if(ze->compressed){
1550 PACK_UB2(header, CEN_COMP, 8);
1551 } else {
1552 PACK_UB2(header, CEN_COMP, 0);
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);
1563 write(fd, header, 46);
1565 write(fd, ze->filename, strlen(ze->filename));
1568 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
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;
1593 write(fd, end_header, 22);
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)
1603 return 0;
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;
1627 init_inflation();
1629 pb_init(&pbf, fd);
1631 for(;;){
1632 f_fd = 0;
1633 crc = 0;
1634 ze.crc = 0;
1636 dir = FALSE; /* by default, the file isn't a dir */
1637 handle = TRUE; /* by default we'll extract/create the file */
1639 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1640 perror("read");
1641 break;
1644 signature = UNPACK_UB4(scratch, 0);
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;
1665 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1666 perror("read");
1667 break;
1670 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1671 #ifdef DEBUG
1672 printf("Compressed size is %u\n", csize);
1673 #endif
1675 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1676 #ifdef DEBUG
1677 printf("Filename length is %hu\n", fnlen);
1678 #endif
1680 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1681 #ifdef DEBUG
1682 printf("Extra field length is %hu\n", eflen);
1683 #endif
1685 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1686 #ifdef DEBUG
1687 printf("Flags are %#hx\n", flags);
1688 #endif
1690 method = UNPACK_UB2(file_header, LOC_COMP);
1691 #ifdef DEBUG
1692 printf("Compression method is %#hx\n", method);
1693 #endif
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
1703 if(filename_len < fnlen + 1){
1704 if(filename != NULL)
1705 free(filename);
1707 filename = malloc(sizeof(ub1) * (fnlen + 1));
1708 filename_len = fnlen + 1;
1711 pb_read(&pbf, filename, fnlen);
1712 filename[fnlen] = '\0';
1714 #ifdef DEBUG
1715 printf("filename is %s\n", filename);
1716 #endif
1718 if(file_num > 0){
1719 handle = FALSE;
1721 for(j = 0; j < file_num; j++)
1722 if(strcmp(files[j], (const char *)filename) == 0){
1723 handle = TRUE;
1724 break;
1728 if(!handle)
1729 f_fd = -1;
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;
1740 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1742 for(;;){
1743 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1745 if(idx == NULL)
1746 break;
1747 else if(idx == start){
1748 start++;
1749 continue;
1751 start = idx + 1;
1753 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1754 tmp_buff[(idx - filename)] = '\0';
1756 #ifdef DEBUG
1757 printf("checking the existance of %s\n", tmp_buff);
1758 #endif
1760 if(stat(tmp_buff, &sbuf) < 0){
1761 if(errno != ENOENT){
1762 perror("stat");
1763 exit(1);
1766 } else if(S_ISDIR(sbuf.st_mode)){
1767 #ifdef DEBUG
1768 printf("Directory exists\n");
1769 #endif
1770 continue;
1771 }else {
1772 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1773 tmp_buff);
1774 exit(1);
1777 #ifdef DEBUG
1778 printf("Making directory..\n");
1779 #endif
1780 if(mkdir(tmp_buff, 0755) < 0){
1781 perror("mkdir");
1782 exit(1);
1784 if(verbose && handle)
1785 printf("%10s: %s/\n", "created", tmp_buff);
1789 /* only a directory */
1790 if(strlen((const char *)start) == 0)
1791 dir = TRUE;
1793 #ifdef DEBUG
1794 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1795 #endif
1797 /* If the entry was just a directory, don't write to file, etc */
1798 if(strlen((const char *)start) == 0)
1799 f_fd = -1;
1801 free(tmp_buff);
1804 if(f_fd != -1 && handle){
1805 f_fd = open((const char *)filename,
1806 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1808 if(f_fd < 0){
1809 fprintf(stderr, "Error extracting JAR archive!\n");
1810 perror((const char *)filename);
1811 exit(1);
1815 if(method != 8 && flags & 0x0008){
1816 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1817 exit(1);
1820 if (eflen > 0)
1821 consume(&pbf, eflen);
1823 if(method == 8 || flags & 0x0008){
1825 inflate_file(&pbf, f_fd, &ze);
1826 } else {
1828 #ifdef DEBUG
1829 printf("writing stored data.. (%d bytes)\n", csize);
1830 #endif
1832 out_a = 0;
1833 in_a = csize;
1835 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1837 while(out_a < (int)csize){
1838 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1839 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1840 perror("read");
1841 exit(1);
1844 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1846 if(f_fd >= 0)
1847 write(f_fd, rd_buff, rdamt);
1849 out_a += rdamt;
1850 in_a -= rdamt;
1852 #ifdef DEBUG
1853 printf("%d bytes written\n", out_a);
1854 #endif
1858 /* if there is a data descriptor left, compare the CRC */
1859 if(flags & 0x0008){
1861 if(pb_read(&pbf, scratch, 16) != 16){
1862 perror("read");
1863 exit(1);
1866 signature = UNPACK_UB4(scratch, 0);
1868 if(signature != 0x08074b50){
1869 fprintf(stderr, "Error! Missing data descriptor!\n");
1870 exit(1);
1873 crc = UNPACK_UB4(scratch, 4);
1877 if(crc != ze.crc){
1878 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1879 ze.crc, crc);
1880 exit(1);
1883 close(f_fd);
1885 if(verbose && dir == FALSE && handle)
1886 printf("%10s: %s\n",
1887 (method == 8 ? "inflated" : "extracted"),
1888 filename);
1891 return 0;
1894 int list_jar(int fd, char **files, int file_num){
1895 ub4 signature;
1896 ub4 csize;
1897 ub4 usize;
1898 ub4 mdate;
1899 ub4 tmp;
1900 ub2 fnlen;
1901 ub2 eflen;
1902 ub2 clen;
1903 ub2 flags;
1904 ub2 method;
1905 ub2 cen_size;
1906 ub1 *filename = NULL;
1907 ub1 scratch[16];
1908 ub1 cen_header[46];
1909 int filename_len = 0;
1910 off_t size;
1911 int i, j;
1912 time_t tdate;
1913 struct tm *s_tm;
1914 char ascii_date[31];
1915 zipentry ze;
1917 #ifdef DEBUG
1918 printf("Listing jar file, looking for %d files\n", file_num);
1919 #endif
1921 /* This should be the start of the central-header-end section */
1922 if(seekable){
1923 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1924 perror("lseek");
1925 exit(1);
1928 if(read(fd, &tmp, sizeof(ub4)) != 4){
1929 perror("read");
1930 exit(1);
1933 #ifdef WORDS_BIGENDIAN
1934 tmp = L2BI(tmp);
1935 #endif
1937 if(tmp != 0x06054b50){
1938 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1939 exit(1);
1942 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1943 perror("lseek");
1944 exit(1);
1947 if(read(fd, &cen_size, 2) != 2){
1948 perror("read");
1949 exit(1);
1952 #ifdef WORDS_BIGENDIAN
1953 cen_size = L2BS(cen_size);
1954 #endif
1956 /* printf("%hu entries in central header\n", cen_size); */
1958 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1959 perror("lseek");
1960 exit(1);
1963 if(read(fd, &tmp, 4) != 4){
1964 perror("read");
1965 exit(1);
1968 #ifdef WORDS_BIGENDIAN
1969 tmp = L2BI(tmp);
1970 #endif
1972 /* printf("Central header offset = %d\n", tmp); */
1974 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1975 perror("lseek");
1976 exit(1);
1979 /* Loop through the entries in the central header */
1980 for(i = 0; i < cen_size; i++){
1982 if(read(fd, &cen_header, 46) != 46){
1983 perror("read");
1984 exit(1);
1987 signature = UNPACK_UB4(cen_header, 0);
1988 if(signature != 0x02014b50){
1989 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1990 exit(1);
1993 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1994 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1995 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1996 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1998 /* If we're providing verbose output, we need to make an ASCII
1999 * formatted version of the date. */
2000 if(verbose){
2001 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
2002 tdate = dos2unixtime(mdate);
2003 s_tm = localtime(&tdate);
2004 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
2005 ascii_date[30] = '\0';
2008 if(filename_len < fnlen + 1){
2009 if(filename != NULL)
2010 free(filename);
2012 filename = malloc(sizeof(ub1) * (fnlen + 1));
2013 filename_len = fnlen + 1;
2016 if(read(fd, filename, fnlen) != fnlen){
2017 perror("read");
2018 exit(1);
2020 filename[fnlen] = '\0';
2022 /* if the user specified a list of files on the command line,
2023 we'll only display those, otherwise we'll display everything */
2024 if(file_num > 0){
2025 for(j = 0; j < file_num; j++)
2026 if(strcmp(files[j], (const char *)filename) == 0){
2027 if(verbose)
2028 printf("%6d %s %s\n", usize, ascii_date, filename);
2029 else
2030 printf("%s\n", filename);
2031 break;
2033 } else {
2034 if(verbose)
2035 printf("%6d %s %s\n", usize, ascii_date, filename);
2036 else
2037 printf("%s\n", filename);
2040 size = eflen + clen;
2041 if(size > 0){
2042 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
2043 perror("lseek");
2044 exit(1);
2048 } else {
2049 /* the file isn't seekable.. evil! */
2050 pb_file pbf;
2052 pb_init(&pbf, fd);
2054 init_inflation();
2056 for(;;){
2057 if(pb_read(&pbf, scratch, 4) != 4){
2058 perror("read");
2059 break;
2062 signature = UNPACK_UB4(scratch, 0);
2064 #ifdef DEBUG
2065 printf("signature is %x\n", signature);
2066 #endif
2068 if(signature == 0x08074b50){
2069 #ifdef DEBUG
2070 printf("skipping data descriptor\n");
2071 #endif
2072 pb_read(&pbf, scratch, 12);
2073 continue;
2074 } else if(signature == 0x02014b50){
2075 #ifdef DEBUG
2076 printf("Central header reached.. we're all done!\n");
2077 #endif
2078 break;
2079 }else if(signature != 0x04034b50){
2080 #ifdef DEBUG
2081 printf("Ick! %#x\n", signature);
2082 #endif
2083 break;
2086 if(pb_read(&pbf, (file_header + 4), 26) != 26){
2087 perror("read");
2088 break;
2091 csize = UNPACK_UB4(file_header, LOC_CSIZE);
2092 #ifdef DEBUG
2093 printf("Compressed size is %u\n", csize);
2094 #endif
2096 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
2097 #ifdef DEBUG
2098 printf("Filename length is %hu\n", fnlen);
2099 #endif
2101 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
2102 #ifdef DEBUG
2103 printf("Extra field length is %hu\n", eflen);
2104 #endif
2106 method = UNPACK_UB2(file_header, LOC_COMP);
2107 #ifdef DEBUG
2108 printf("Compression method is %#hx\n", method);
2109 #endif
2111 flags = UNPACK_UB2(file_header, LOC_EXTRA);
2112 #ifdef DEBUG
2113 printf("Flags are %#hx\n", flags);
2114 #endif
2116 usize = UNPACK_UB4(file_header, LOC_USIZE);
2118 /* If we're providing verbose output, we need to make an ASCII
2119 * formatted version of the date. */
2120 if(verbose){
2121 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
2122 tdate = dos2unixtime(mdate);
2123 s_tm = localtime(&tdate);
2124 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
2127 if(filename_len < fnlen + 1){
2128 if(filename != NULL)
2129 free(filename);
2131 filename = malloc(sizeof(ub1) * (fnlen + 1));
2132 ascii_date[30] = '\0';
2133 filename_len = fnlen + 1;
2136 pb_read(&pbf, filename, fnlen);
2137 filename[fnlen] = '\0';
2139 /* the header is at the end. In a JAR file, this means that the data
2140 happens to be compressed. We have no choice but to inflate the
2141 data */
2142 if(flags & 0x0008){
2144 size = eflen;
2146 if(size > 0)
2147 consume(&pbf, size);
2149 if(method == 8){
2150 #ifdef DEBUG
2151 printf("inflating %s\n", filename);
2152 #endif
2153 inflate_file(&pbf, -1, &ze);
2155 usize = ze.usize;
2156 } else
2157 printf("We're shit outta luck!\n");
2159 } else {
2160 size = csize + (eflen > 0 ? eflen : 0);
2163 #ifdef DEBUG
2164 printf("Skipping %ld bytes\n", (long)size);
2165 #endif
2167 consume(&pbf, size);
2169 /* print out the listing */
2170 if(file_num > 0){
2171 for(j = 0; j < file_num; j++)
2172 if(strcmp(files[j], (const char *)filename) == 0){
2173 if(verbose)
2174 printf("%6d %s %s\n", usize, ascii_date, filename);
2175 else
2176 printf("%s\n", filename);
2177 break;
2179 } else {
2180 if(verbose)
2181 printf("%6d %s %s\n", usize, ascii_date, filename);
2182 else
2183 printf("%s\n", filename);
2187 return 0;
2190 int consume(pb_file *pbf, int amt){
2191 int tc = 0; /* total amount consumed */
2192 ub1 buff[RDSZ];
2193 int rdamt;
2195 #ifdef DEBUG
2196 printf("Consuming %d bytes\n", amt);
2197 #endif
2199 if (seekable){
2200 if (amt <= (int)pbf->buff_amt)
2201 pb_read(pbf, buff, amt);
2202 else {
2203 lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
2204 pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
2206 } else
2207 while(tc < amt){
2208 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
2209 #ifdef DEBUG
2210 printf("got %d bytes\n", rdamt);
2211 #endif
2212 tc += rdamt;
2215 #ifdef DEBUG
2216 printf("%d bytes consumed\n", amt);
2217 #endif
2219 return 0;
2222 void usage(const char *filename){
2223 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
2224 exit (1);
2227 void version (void)
2229 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
2230 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
2231 printf("Copyright 2006 Free Software Foundation\n");
2232 printf("\
2233 This is free software; see the source for copying conditions. There is NO\n\
2234 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2235 exit (0);
2238 void help(const char *filename)
2240 printf("\
2241 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2243 Store many files together in a single `jar' file.\n\
2245 -c create new archive\n\
2246 -t list table of contents for archive\n\
2247 -x extract named (or all) files from archive\n\
2248 -u update existing archive\n\
2249 ", filename);
2250 printf("\n\
2251 -@ read names from stdin\n\
2252 -0 store only; use no ZIP compression\n\
2253 -C DIR FILE change to the specified directory and include\n\
2254 the following file\n\
2255 -E don't include the files found in a directory\n\
2256 -f FILE specify archive file name\n\
2257 --help print this help, then exit\n\
2259 printf("\
2260 -m FILE include manifest information from specified manifest file\n\
2261 -M Do not create a manifest file for the entries\n\
2262 -i generate an index of the packages in this jar\n\
2263 and its Class-Path (currently unimplemented)\n\
2264 -v generate verbose output on standard output\n\
2265 -V, --version display version information\n\
2267 printf("\n\
2268 If any file is a directory then it is processed recursively.\n\
2269 The manifest file name and the archive file name needs to be specified\n\
2270 in the same order the 'm' and 'f' flags are specified.\n\
2272 Example 1: to archive two class files into an archive called classes.jar: \n\
2273 jar cvf classes.jar Foo.class Bar.class \n\
2274 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2275 files in the foo/ directory into 'classes.jar': \n\
2276 jar cvfm classes.jar mymanifest -C foo/ .\n\
2279 exit(0);
2282 static char *
2283 jt_strdup(char *s)
2285 char *result = (char*)malloc(strlen(s) + 1);
2286 if (result == (char*)0)
2287 return (char*)0;
2288 strcpy(result, s);
2289 return result;
2292 /* Convert "tar-style" first argument to a form expected by getopt.
2293 This idea and the code comes from GNU tar. This can allocate a new
2294 argument vector. This might leak some memory, but we don't care. */
2295 static void
2296 expand_options (int *argcp, char ***argvp)
2298 int argc = *argcp;
2299 char **argv = *argvp;
2301 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
2302 if a long argument (like "--help") is detected. */
2303 if (argc > 1 && argv[1][1] != '-')
2305 char buf[3];
2306 char **new_argv;
2307 int new_argc;
2308 int args_to_expand;
2309 char *p;
2310 char **in, **out;
2312 buf[0] = '-';
2313 buf[2] = '\0';
2315 args_to_expand = strlen (argv[1]);
2316 if (argv[1][0] == '-')
2317 --args_to_expand;
2319 new_argc = argc - 1 + args_to_expand;
2320 new_argv = (char **) malloc (new_argc * sizeof (char *));
2321 in = argv;
2322 out = new_argv;
2324 *out++ = *in++;
2325 p = *in++;
2326 if (*p == '-')
2327 p++;
2328 while (*p != '\0')
2330 char *opt;
2331 buf[1] = *p;
2332 *out++ = jt_strdup (buf);
2333 /* If the option takes an argument, move the next argument
2334 to just after this option. */
2335 opt = strchr (OPTION_STRING, *p);
2336 if (opt && opt[1] == ':')
2338 if (in < argv + argc)
2339 *out++ = *in++;
2340 else
2342 fprintf(stderr, "%s: option `%s' requires an argument.\n",
2343 argv[0], buf);
2344 usage(argv[0]);
2347 ++p;
2350 /* Copy remaining options. */
2351 while (in < argv + argc)
2352 *out++ = *in++;
2354 *argcp = new_argc;
2355 *argvp = new_argv;