2005-05-18 Thomas Koenig <Thomas.Koenig@online.de>
[official-gcc.git] / fastjar / jartool.c
blob09f5dbcb78733c8083e33c6f34fca2e97daabe9e
1 /*
2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002, 2004, 2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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:0ME@"
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;
411 case OPT_HELP:
412 help(argv[0]);
413 break;
415 /* The following options aren't supported by the original jar tool. */
416 case 'E':
417 use_explicit_list_only = TRUE;
418 break;
419 case '@':
420 read_names_from_stdin = TRUE;
421 break;
422 default:
423 usage(argv[0]);
427 /* We might have seen `--'. In this case we want to make sure that
428 all following options are handled as file names. */
429 while (optind < argc)
430 new_argv[new_argc++] = argv[optind++];
431 new_argv[new_argc] = NULL;
433 if(action == ACTION_NONE){
434 fprintf(stderr, "%s: one of options -{ctxu} must be specified.\n",
435 progname);
436 usage(argv[0]);
439 /* Verify unsupported combinations and warn of the use of non
440 standard features */
441 if(verbose && use_explicit_list_only)
442 fprintf (stderr, "Warning: using non standard '-E' option\n");
443 if(verbose && read_names_from_stdin)
444 fprintf (stderr, "Warning: using non standard '-@' option\n");
445 if(read_names_from_stdin
446 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
447 fprintf(stderr, "%s: option '-@' is supported only with '-c' or '-u'.\n",
448 progname);
449 usage(argv[0]);
452 /* create the jarfile */
453 if(action == ACTION_CREATE){
454 if(jarfile){
455 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
457 if(jarfd < 0){
458 fprintf(stderr, "%s: error opening %s for writing: %s\n", progname,
459 jarfile, strerror (errno));
460 exit(1);
463 /* We assume that the file is seekable */
464 seekable = TRUE;
466 } else {
468 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
470 /* standard out is not seekable */
471 seekable = FALSE;
473 /* don't want our output to be part of the jar file.. figured this one
474 out the hard way.. =P */
475 verbose = FALSE;
477 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
479 if(jarfile){
480 jarfd = open(jarfile, O_RDONLY | O_BINARY);
482 if(jarfd < 0){
483 fprintf(stderr, "%s: error opening %s for reading: %s\n", progname,
484 jarfile, strerror (errno));
485 exit(1);
488 seekable = TRUE;
489 } else {
490 jarfd = STDIN_FILENO; /* jarfd is standard in */
492 /* we assume that the stream isn't seekable for safety */
493 seekable = FALSE;
497 if (action == ACTION_UPDATE)
499 if (!jarfile)
501 fprintf (stderr, "%s: `-u' mode requires a file name\n",
502 argv[0]);
503 exit (1);
506 if ((jarfd = open (jarfile, O_RDWR | O_BINARY)) < 0)
508 fprintf (stderr, "Error opening %s for reading!\n", jarfile);
509 perror (jarfile);
510 exit (1);
513 /* Assert that jarfd is seekable. */
514 if (lseek (jarfd, 0, SEEK_CUR) == -1)
516 fprintf (stderr, "%s: %s is not seekable\n", argv[0], jarfile);
517 exit (1);
520 seekable = TRUE;
523 if(action == ACTION_CREATE || action == ACTION_UPDATE){
524 const char *arg;
525 init_headers();
527 if(do_compress)
528 init_compression();
530 if (action == ACTION_UPDATE)
532 if (read_entries (jarfd))
533 exit (1);
536 /* Add the META-INF/ directory and the manifest */
537 if(manifest && mfile)
538 make_manifest(jarfd, mfile, action == ACTION_UPDATE);
539 else if(manifest && action == ACTION_CREATE)
540 make_manifest(jarfd, NULL, FALSE);
542 init_args (new_argv, 0);
543 /* now we add the files to the archive */
544 while ((arg = get_next_arg ())){
546 if(!strcmp(arg, "-C")){
547 const char *dir_to_change = get_next_arg ();
548 const char *file_to_add = get_next_arg ();
549 if (!dir_to_change || !file_to_add) {
550 fprintf(stderr, "%s: error: missing argument for -C.\n", progname);
551 exit(1);
553 if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add,
554 action == ACTION_UPDATE))
556 fprintf(stderr,
557 "Error adding %s (in directory %s) to jar archive!\n",
558 file_to_add, dir_to_change);
559 exit(1);
561 } else {
562 if(add_to_jar(jarfd, arg, action == ACTION_UPDATE)){
563 fprintf(stderr, "Error adding %s to jar archive!\n", arg);
564 exit(1);
568 /* de-initialize the compression DS */
569 if(do_compress)
570 end_compression();
572 if (action == ACTION_UPDATE)
573 lseek (jarfd, end_of_entries, SEEK_SET);
575 create_central_header(jarfd);
577 #if ! (HAVE_FTRUNCATE || HAVE__CHSIZE)
578 #error neither ftruncate() or _chsize() available
579 #endif
580 /* Check if the file shrunk when we updated it. */
581 if (action == ACTION_UPDATE)
582 #if HAVE_FTRUNCATE
583 ftruncate (jarfd, lseek (jarfd, 0, SEEK_CUR));
584 #else
585 _chsize (jarfd, lseek (jarfd, 0, SEEK_CUR));
586 #endif
588 if (jarfd != STDIN_FILENO && close(jarfd) != 0) {
589 fprintf(stderr, "%s: error closing jar archive: %s\n",
590 progname, strerror (errno));
591 exit (1);
593 } else if(action == ACTION_LIST){
594 list_jar(jarfd, &new_argv[0], new_argc);
595 } else if(action == ACTION_EXTRACT){
596 extract_jar(jarfd, &new_argv[0], new_argc);
599 exit(0);
602 static int args_current_g;
603 static char **args_g;
605 static void
606 init_args(char **args, int current)
608 if(!read_names_from_stdin)
610 args_g = args;
611 args_current_g = current;
615 static char *
616 get_next_arg (void)
618 static int reached_end = 0;
620 if (reached_end)
621 return NULL;
623 if (args_g)
625 if (!args_g [args_current_g])
627 reached_end = 1;
628 return NULL;
630 return args_g [args_current_g++];
632 else
634 /* Read the name from stdin. Delimiters are '\n' and
635 '\r'. Reading EOF indicates that we don't have anymore file
636 names characters to read. */
638 char s [MAXPATHLEN];
639 int pos = 0;
641 /* Get rid of '\n' and '\r' first. */
642 while (1)
644 int c = getc (stdin);
645 if (c == '\n' || c == '\r')
646 continue;
647 else
649 if (c == EOF)
650 return NULL;
651 ungetc (c, stdin);
652 break;
656 while (1)
658 int c = getc (stdin);
659 /* Exit when we get a delimiter or don't have any characters
660 to read */
661 if (c == '\n'|| c == '\r'|| c == EOF)
662 break;
663 s [pos++] = (char) c;
666 if (pos)
668 s [pos] = '\0';
669 return jt_strdup (s);
671 else
672 return NULL;
676 void init_headers(void)
678 /* packing file header */
679 /* magic number */
680 file_header[0] = 0x50;
681 file_header[1] = 0x4b;
682 file_header[2] = 0x03;
683 file_header[3] = 0x04;
684 /* version number (Unix 1.0)*/
685 file_header[4] = 10;
686 file_header[5] = 0;
687 /* bit flag (normal deflation)*/
688 file_header[6] = 0x00;
690 file_header[7] = 0x00;
691 /* do_compression method (deflation) */
692 file_header[8] = 0;
693 file_header[9] = 0;
695 /* last mod file time (MS-DOS format) */
696 file_header[10] = 0;
697 file_header[11] = 0;
698 /* last mod file date (MS-DOS format) */
699 file_header[12] = 0;
700 file_header[13] = 0;
701 /* CRC 32 */
702 file_header[14] = 0;
703 file_header[15] = 0;
704 file_header[16] = 0;
705 file_header[17] = 0;
706 /* compressed size */
707 file_header[18] = 0;
708 file_header[19] = 0;
709 file_header[20] = 0;
710 file_header[21] = 0;
711 /* uncompressed size */
712 file_header[22] = 0;
713 file_header[23] = 0;
714 file_header[24] = 0;
715 file_header[25] = 0;
716 /* filename length */
717 file_header[26] = 0;
718 file_header[27] = 0;
719 /* extra field length */
720 file_header[28] = 0;
721 file_header[29] = 0;
723 /* Initialize the compression DS */
724 PACK_UB4(data_descriptor, 0, 0x08074b50);
728 void add_entry(struct zipentry *ze)
731 if(ziplist == NULL){
732 ziplist = ze;
733 ziptail = ziplist;
734 } else {
735 ziplist->next_entry = ze;
736 ziplist = ze;
739 number_of_entries++;
742 static struct zipentry *
743 find_entry (const char *fname)
745 struct zipentry *ze;
747 for (ze = ziptail; ze; ze = ze->next_entry)
749 if (!strcmp (ze->filename, fname))
750 return ze;
752 return NULL;
756 static int
757 looks_like_dir (const char *fname)
759 struct zipentry *ze;
760 size_t len = strlen (fname);
762 for (ze = ziptail; ze; ze = ze->next_entry)
764 if (strlen (ze->filename) > len
765 && !strncmp (fname, ze->filename, len)
766 && ze->filename[len] == '/')
767 return 1;
769 return 0;
774 * Read the zip entries of an existing file, building `ziplist' as we go.
776 int read_entries (int fd)
778 struct zipentry *ze;
779 ub1 intbuf[4];
780 ub1 header[46];
781 ub2 len;
782 ub2 count, i;
783 off_t offset;
785 if (lseek (fd, -22, SEEK_END) == -1)
787 fprintf (stderr, "%s: %s: can't seek file\n", progname, jarfile);
788 return 1;
791 if (read (fd, intbuf, 4) < 4)
793 perror (progname);
794 return 1;
796 /* Is there a zipfile comment? */
797 while (UNPACK_UB4(intbuf, 0) != 0x06054b50)
799 if (lseek (fd, -5, SEEK_CUR) == -1 ||
800 read (fd, intbuf, 4) != 4)
802 fprintf (stderr, "%s: can't find end of central directory: %s\n",
803 progname, strerror (errno));
804 return 1;
808 /* Skip disk numbers. */
809 if (lseek (fd, 6, SEEK_CUR) == -1)
811 perror (progname);
812 return 1;
815 /* Number of entries in the central directory. */
816 if (read (fd, intbuf, 2) != 2)
818 perror (progname);
819 return 1;
821 count = UNPACK_UB2(intbuf, 0);
823 if (lseek (fd, 4, SEEK_CUR) == -1)
825 perror (progname);
826 return 1;
829 /* Offset where the central directory begins. */
830 if (read (fd, intbuf, 4) != 4)
832 perror (progname);
833 return 1;
835 offset = UNPACK_UB4(intbuf, 0);
836 end_of_entries = offset;
838 if (lseek (fd, offset, SEEK_SET) != offset)
840 perror (progname);
841 return 1;
844 if (read (fd, header, 46) != 46)
846 fprintf (stderr, "%s: %s: unexpected end of file\n",
847 progname, jarfile);
848 return 1;
851 for (i = 0; i < count; i++)
853 if (UNPACK_UB4(header, 0) != 0x02014b50)
855 fprintf (stderr, "%s: can't find central directory header\n",
856 progname);
857 return 1;
859 ze = (struct zipentry *) malloc (sizeof (struct zipentry));
860 if (!ze)
862 perror (progname);
863 return 1;
865 memset (ze, 0, sizeof (struct zipentry));
866 ze->flags = UNPACK_UB2(header, CEN_FLAGS);
867 ze->mod_time = UNPACK_UB2(header, CEN_MODTIME);
868 ze->mod_date = UNPACK_UB2(header, CEN_MODDATE);
869 ze->crc = UNPACK_UB4(header, CEN_CRC);
870 ze->usize = UNPACK_UB4(header, CEN_USIZE);
871 ze->csize = UNPACK_UB4(header, CEN_CSIZE);
872 ze->offset = UNPACK_UB4(header, CEN_OFFSET);
873 ze->compressed = (header[CEN_COMP] || header[CEN_COMP+1]);
874 len = UNPACK_UB2(header, CEN_FNLEN);
875 ze->filename = (char *) malloc ((len+1) * sizeof (char));
876 if (!ze->filename)
878 perror (progname);
879 return 1;
881 if (read (fd, ze->filename, len) != len)
883 fprintf (stderr, "%s: %s: unexpected end of file\n",
884 progname, jarfile);
885 return 1;
887 len = UNPACK_UB4(header, CEN_EFLEN);
888 len += UNPACK_UB4(header, CEN_COMLEN);
889 if (lseek (fd, len, SEEK_CUR) == -1)
891 perror (progname);
892 return 1;
894 add_entry (ze);
895 if (i < count - 1)
897 if (read (fd, header, 46) != 46)
899 fprintf (stderr, "%s: %s: unexpected end of file\n",
900 progname, jarfile);
901 return 1;
906 lseek (fd, 0, SEEK_SET);
907 return 0;
910 int make_manifest(int jfd, const char *mf_name, int updating)
912 time_t current_time;
913 int nlen; /* length of file name */
914 int mod_time; /* file modification time */
915 struct zipentry *ze;
917 nlen = 9; /* trust me on this one */
919 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
921 current_time = time(NULL);
922 if(current_time == (time_t)-1){
923 perror("time");
924 exit(1);
927 mod_time = unix2dostime(&current_time);
929 PACK_UB2(file_header, LOC_EXTRA, 0);
930 PACK_UB2(file_header, LOC_COMP, 0);
931 PACK_UB2(file_header, LOC_FNLEN, nlen);
932 PACK_UB4(file_header, LOC_MODTIME, mod_time);
934 if(verbose)
935 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
937 ze = (zipentry*)malloc(sizeof(zipentry));
938 if(ze == NULL){
939 perror("malloc");
940 exit(1);
943 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
944 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
945 strcpy(ze->filename, "META-INF/");
946 ze->filename[nlen] = '\0';
948 ze->offset = lseek(jfd, 0, SEEK_CUR);
949 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
950 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
951 ze->compressed = FALSE;
953 add_entry(ze);
955 write(jfd, file_header, 30);
956 write(jfd, "META-INF/", nlen);
958 /* if the user didn't specify an external manifest file... */
959 if(mf_name == NULL){
961 int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
962 char *mf;
964 if((mf = (char *) malloc(mf_len + 1))) {
965 uLong crc;
967 sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
969 crc = crc32(0L, Z_NULL, 0);
971 crc = crc32(crc, (const unsigned char *)mf, mf_len);
973 nlen = 20; /* once again, trust me */
975 PACK_UB2(file_header, LOC_EXTRA, 0);
976 PACK_UB2(file_header, LOC_COMP, 0);
977 PACK_UB2(file_header, LOC_FNLEN, nlen);
978 PACK_UB4(file_header, LOC_USIZE, mf_len);
980 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
982 PACK_UB4(file_header, LOC_CRC, crc);
984 if(verbose)
985 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
987 ze = (zipentry*)malloc(sizeof(zipentry));
988 if(ze == NULL){
989 perror("malloc");
990 exit(1);
993 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
994 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
995 strcpy(ze->filename, "META-INF/MANIFEST.MF");
996 ze->filename[nlen] = '\0';
998 ze->offset = lseek(jfd, 0, SEEK_CUR);
999 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1000 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1001 ze->crc = crc;
1002 ze->csize = mf_len;
1003 ze->usize = ze->csize;
1004 ze->compressed = FALSE;
1006 add_entry(ze);
1008 write(jfd, file_header, 30);
1009 write(jfd, "META-INF/MANIFEST.MF", nlen);
1010 write(jfd, mf, mf_len);
1011 free(mf);
1013 else {
1014 printf("malloc errror\n");
1015 exit(-1);
1017 } else {
1018 int mfd;
1019 struct stat statbuf;
1021 stat(mf_name, &statbuf);
1023 if(!S_ISREG(statbuf.st_mode)){
1024 fprintf(stderr, "Invalid manifest file specified.\n");
1025 exit(1);
1028 mfd = open(mf_name, O_RDONLY | O_BINARY);
1030 if(mfd < 0){
1031 fprintf(stderr, "Error opening %s.\n", mf_name);
1032 exit(1);
1035 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
1036 perror("error writing to jar");
1037 exit(1);
1042 return 0;
1045 /* Implements -C by wrapping add_to_jar. new_dir is the directory
1046 to switch to.
1048 `updating', if nonzero, will indicate that we are updating an
1049 existing file, and will need to take special care. If set, we will
1050 also expect that the linked list of zip entries will be filled in
1051 with the jar file's current contents.
1053 int
1054 add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
1055 const int updating)
1057 int retval;
1058 char old_dir[MAXPATHLEN];
1059 if (getcwd(old_dir, MAXPATHLEN) == NULL) {
1060 perror("getcwd");
1061 return 1;
1063 if (chdir(new_dir) == -1) {
1064 perror(new_dir);
1065 return 1;
1067 retval=add_to_jar(fd, file, updating);
1068 if (chdir(old_dir) == -1) {
1069 perror(old_dir);
1070 return 1;
1072 return retval;
1075 int
1076 add_to_jar (int fd, const char *file, const int updating)
1078 struct stat statbuf;
1079 DIR *dir;
1080 struct dirent *de;
1081 zipentry *ze;
1082 zipentry *existing = NULL;
1083 int stat_return;
1085 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
1086 * It fixes this:
1087 * "normal" jar : org/apache/java/io/LogRecord.class
1088 * fastjar : ./org/apache/java/io/LogRecord.class
1089 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
1090 * with both kaffe-1.0b4 and JDK.
1092 while (*file=='.' && *(file+1)=='/')
1093 file+=2;
1095 if(jarfile && !strcmp(file, jarfile)){
1096 if(verbose)
1097 printf("skipping: %s\n", file);
1098 return 0; /* we don't want to add ourselves.. */
1101 stat_return = stat(file, &statbuf);
1103 if(stat_return == -1){
1104 perror(file);
1105 return 1;
1106 } else if(S_ISDIR(statbuf.st_mode)){
1107 char *fullname;
1108 char *t_ptr;
1109 int nlen;
1110 unsigned long mod_time;
1112 dir = opendir(file);
1114 if(dir == NULL){
1115 perror("opendir");
1116 return 1;
1119 nlen = strlen(file) + 256;
1120 fullname = (char*)malloc(nlen * sizeof(char));
1121 memset(fullname, 0, (nlen * sizeof(char)));
1123 if(fullname == NULL){
1124 fprintf(stderr, "Filename is NULL!\n");
1125 return 1;
1128 strcpy(fullname, file);
1129 nlen = strlen(file);
1131 if(fullname[nlen - 1] != '/'){
1132 fullname[nlen] = '/';
1133 t_ptr = (fullname + nlen + 1);
1134 } else
1135 t_ptr = (fullname + nlen);
1138 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
1140 nlen = (t_ptr - fullname);
1142 mod_time = unix2dostime(&statbuf.st_mtime);
1144 PACK_UB2(file_header, LOC_EXTRA, 0);
1145 PACK_UB2(file_header, LOC_COMP, 0);
1146 PACK_UB2(file_header, LOC_FNLEN, nlen);
1147 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1149 ze = (zipentry*)malloc(sizeof(zipentry));
1150 if(ze == NULL){
1151 perror("malloc");
1152 exit(1);
1155 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1156 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
1157 strcpy(ze->filename, fullname);
1158 ze->filename[nlen] = '\0';
1160 ze->offset = lseek(fd, 0, SEEK_CUR);
1161 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1162 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1163 ze->compressed = FALSE;
1165 if (updating)
1167 if ((existing = find_entry (ze->filename)) != NULL)
1169 if (existing->usize != 0)
1171 /* XXX overwriting non-directory with directory? */
1172 fprintf (stderr, "%s: %s: can't overwrite non-directory with directory\n",
1173 progname, fullname);
1174 return 1;
1177 if (lseek (fd, end_of_entries, SEEK_SET) == -1)
1179 fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
1180 perror ("lseek");
1181 return 1;
1185 if (!existing)
1187 add_entry (ze);
1188 write (fd, file_header, 30);
1189 write (fd, fullname, nlen);
1190 end_of_entries = lseek (fd, 0, SEEK_CUR);
1192 if (verbose)
1193 printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
1196 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
1197 if(de->d_name[0] == '.')
1198 continue;
1199 if(jarfile && !strcmp(de->d_name, jarfile)){
1200 /* we don't want to add ourselves. Believe me */
1201 if(verbose)
1202 printf("skipping: %s\n", de->d_name);
1203 continue;
1206 strcpy(t_ptr, de->d_name);
1208 if (add_to_jar(fd, fullname, updating)) {
1209 fprintf(stderr, "Error adding file to jar!\n");
1210 return 1;
1214 free(fullname);
1215 closedir(dir);
1217 } else if(S_ISREG(statbuf.st_mode)){
1218 int add_fd;
1220 add_fd = open(file, O_RDONLY | O_BINARY);
1221 if(add_fd < 0){
1222 fprintf(stderr, "Error opening %s.\n", file);
1223 return 1;
1226 if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
1227 fprintf(stderr, "Error adding file to jar!\n");
1228 return 1;
1231 } else {
1232 fprintf(stderr, "Illegal file specified: %s\n", file);
1234 return 0;
1237 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
1238 const int updating)
1240 unsigned short file_name_length;
1241 unsigned long mod_time;
1242 ub1 rd_buff[RDSZ];
1243 uLong crc = 0;
1244 off_t offset = 0;
1245 int rdamt;
1246 struct zipentry *ze;
1247 struct zipentry *existing = NULL;
1249 if (updating)
1251 existing = find_entry (fname);
1252 if (existing && looks_like_dir (fname))
1254 fprintf (stderr, "%s: %s is a directory in the archive\n",
1255 progname, fname);
1256 return 1;
1260 mod_time = unix2dostime(&(statbuf->st_mtime));
1261 file_name_length = strlen(fname);
1263 if(!seekable && !do_compress){
1264 crc = crc32(0L, Z_NULL, 0);
1266 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
1267 crc = crc32(crc, rd_buff, rdamt);
1269 lseek(ffd, 0, SEEK_SET);
1272 /* data descriptor */
1273 if(!seekable && do_compress){
1274 PACK_UB2(file_header, LOC_EXTRA, 8);
1275 } else {
1276 PACK_UB2(file_header, LOC_EXTRA, 0);
1279 if(do_compress){
1280 PACK_UB2(file_header, LOC_COMP, 8);
1281 } else {
1282 PACK_UB2(file_header, LOC_COMP, 0);
1285 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1286 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1288 if(!seekable && !do_compress){
1289 PACK_UB4(file_header, LOC_CRC, crc);
1290 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1291 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1292 } else
1293 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1295 ze = (zipentry*)malloc(sizeof(zipentry));
1296 if(ze == NULL){
1297 perror("malloc");
1298 exit(1);
1301 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1302 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1303 strcpy(ze->filename, fname);
1305 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1306 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1308 if(!seekable && !do_compress)
1309 ze->crc = crc;
1311 ze->csize = statbuf->st_size;
1312 ze->usize = ze->csize;
1314 if (existing)
1315 ze->offset = existing->offset;
1316 else if (updating)
1317 ze->offset = end_of_entries;
1318 else
1319 ze->offset = lseek(jfd, 0, SEEK_CUR);
1321 if(do_compress)
1322 ze->compressed = TRUE;
1323 else
1324 ze->compressed = FALSE;
1326 if (!existing)
1327 add_entry(ze);
1328 if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
1330 perror ("lseek");
1331 return 1;
1334 /* We can safely write the header here, since it will be the same size
1335 as before */
1337 /* Write the local header */
1338 write(jfd, file_header, 30);
1340 /* write the file name to the zip file */
1341 write(jfd, fname, file_name_length);
1344 if(verbose){
1345 if (existing)
1346 printf ("updating: %s ", fname);
1347 else
1348 printf("adding: %s ", fname);
1349 fflush(stdout);
1352 if(do_compress){
1353 /* compress the file */
1354 compress_file(ffd, jfd, ze, existing);
1355 } else {
1356 /* If we are not writing the last entry, make space for it. */
1357 if (existing && existing->next_entry)
1359 if (ze->usize > existing->usize)
1361 if (shift_down (jfd, existing->next_entry->offset,
1362 ze->usize - existing->usize, existing->next_entry))
1364 fprintf (stderr, "%s: %s\n", progname, strerror (errno));
1365 return 1;
1370 /* Write the contents of the file (uncompressed) to the zip file */
1371 /* calculate the CRC as we go along */
1372 ze->crc = crc32(0L, Z_NULL, 0);
1374 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1375 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1376 if(write(jfd, rd_buff, rdamt) != rdamt){
1377 perror("write");
1378 return 0;
1382 close(ffd);
1384 /* write out data descriptor */
1385 PACK_UB4(data_descriptor, 4, ze->crc);
1386 PACK_UB4(data_descriptor, 8, ze->csize);
1387 PACK_UB4(data_descriptor, 12, ze->usize);
1389 /* we need to seek back and fill the header */
1390 if(seekable){
1391 offset = (ze->csize + strlen(ze->filename) + 16);
1393 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1394 perror("lseek");
1395 exit(1);
1398 if(write(jfd, (data_descriptor + 4), 12) != 12){
1399 perror("write");
1400 return 0;
1403 offset -= 12;
1405 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1406 perror("lseek");
1407 exit(1);
1409 } else if(do_compress){
1410 /* Sun's jar tool will only allow a data descriptor if the entry is
1411 compressed, but we'll save 16 bytes/entry if we only use it when
1412 we can't seek back on the file */
1413 /* Technically, you CAN'T have a data descriptor unless the data
1414 part has an obvious end, which DEFLATED does. Otherwise, there
1415 would not be any way to determine where the data descriptor is.
1416 Store an uncompressed file that ends with 0x504b0708, and see.
1417 -- csm */
1419 if(write(jfd, data_descriptor, 16) != 16){
1420 perror("write");
1421 return 0;
1425 if (existing)
1427 int dd = (existing->flags & (1 << 3)) ? 12 : 0;
1428 if (existing->next_entry && ze->csize < existing->csize + dd)
1430 if (shift_up (jfd, existing->next_entry->offset,
1431 existing->csize + dd - ze->csize,
1432 existing->next_entry))
1434 perror (progname);
1435 return 1;
1438 /* Replace the existing entry data with this entry's. */
1439 existing->csize = ze->csize;
1440 existing->usize = ze->usize;
1441 existing->crc = ze->crc;
1442 existing->mod_time = ze->mod_time;
1443 existing->mod_date = ze->mod_date;
1444 free (ze->filename);
1445 free (ze);
1447 else if (updating)
1448 end_of_entries = lseek (jfd, 0, SEEK_CUR);
1450 if(verbose)
1451 printf("(in=%d) (out=%d) (%s %d%%)\n",
1452 (int)ze->usize, (int)ze->csize,
1453 (do_compress ? "deflated" : "stored"),
1454 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1456 return 0;
1459 int create_central_header(int fd){
1460 ub1 header[46];
1461 ub1 end_header[22];
1462 int start_offset;
1463 int dir_size;
1464 int total_in = 0, total_out = 22;
1466 zipentry *ze;
1468 /* magic number */
1469 header[0] = 'P';
1470 header[1] = 'K';
1471 header[2] = 1;
1472 header[3] = 2;
1473 /* version made by */
1474 header[4] = 10;
1475 header[5] = 0;
1476 /* version needed to extract */
1477 header[6] = 10;
1478 header[7] = 0;
1479 /* bit flag */
1480 header[8] = 0;
1481 header[9] = 0;
1482 /* compression method */
1483 header[10] = 0;
1484 header[11] = 0;
1485 /* file mod time */
1486 header[12] = 0;
1487 header[13] = 0;
1488 /* file mod date */
1489 header[14] = 0;
1490 header[15] = 0;
1491 /* crc 32 */
1492 header[16] = 0;
1493 header[17] = 0;
1494 header[18] = 0;
1495 header[19] = 0;
1496 /* compressed size */
1497 header[20] = 0;
1498 header[21] = 0;
1499 header[22] = 0;
1500 header[23] = 0;
1501 /* uncompressed size */
1502 header[24] = 0;
1503 header[25] = 0;
1504 header[26] = 0;
1505 header[27] = 0;
1506 /* filename length */
1507 header[28] = 0;
1508 header[29] = 0;
1509 /* extra field length */
1510 header[30] = 0;
1511 header[31] = 0;
1512 /* file comment length */
1513 header[32] = 0;
1514 header[33] = 0;
1515 /* disk number start */
1516 header[34] = 0;
1517 header[35] = 0;
1518 /* internal file attribs */
1519 header[36] = 0;
1520 header[37] = 0;
1521 /* external file attribs */
1522 header[38] = 0;
1523 header[39] = 0;
1524 header[40] = 0;
1525 header[41] = 0;
1526 /* relative offset of local header */
1527 header[42] = 0;
1528 header[43] = 0;
1529 header[44] = 0;
1530 header[45] = 0;
1532 start_offset = lseek(fd, 0, SEEK_CUR);
1534 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1536 total_in += ze->usize;
1537 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1539 if(ze->compressed){
1540 PACK_UB2(header, CEN_COMP, 8);
1541 } else {
1542 PACK_UB2(header, CEN_COMP, 0);
1545 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1546 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1547 PACK_UB4(header, CEN_CRC, ze->crc);
1548 PACK_UB4(header, CEN_CSIZE, ze->csize);
1549 PACK_UB4(header, CEN_USIZE, ze->usize);
1550 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1551 PACK_UB4(header, CEN_OFFSET, ze->offset);
1553 write(fd, header, 46);
1555 write(fd, ze->filename, strlen(ze->filename));
1558 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1560 /* magic number */
1561 end_header[0] = 0x50;
1562 end_header[1] = 0x4b;
1563 end_header[2] = 0x05;
1564 end_header[3] = 0x06;
1565 /* number of this disk */
1566 end_header[4] = 0;
1567 end_header[5] = 0;
1568 /* number of disk w/ start of central header */
1569 end_header[6] = 0;
1570 end_header[7] = 0;
1571 /* total number of entries in central dir on this disk*/
1572 PACK_UB2(end_header, 8, number_of_entries);
1573 /* total number of entries in central dir*/
1574 PACK_UB2(end_header, 10, number_of_entries);
1575 /* size of central dir. */
1576 PACK_UB4(end_header, 12, dir_size);
1577 /* offset of start of central dir */
1578 PACK_UB4(end_header, 16, start_offset);
1579 /* zipfile comment length */
1580 end_header[20] = 0;
1581 end_header[21] = 0;
1583 write(fd, end_header, 22);
1585 if(verbose)
1586 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1587 total_in,
1588 total_out,
1589 (do_compress ? "deflated" : "stored"),
1590 (int)((1 - (total_out / (float)total_in)) * 100)
1593 return 0;
1596 int extract_jar(int fd, char **files, int file_num){
1597 int rdamt;
1598 int out_a, in_a;
1599 ub4 signature;
1600 ub4 csize;
1601 ub4 crc;
1602 ub2 fnlen;
1603 ub2 eflen;
1604 ub2 flags;
1605 ub2 method;
1606 ub1 *filename = NULL;
1607 int filename_len = 0;
1608 ub4 rd_buff[RDSZ];
1609 pb_file pbf;
1610 ub1 scratch[16];
1611 zipentry ze;
1612 int f_fd;
1613 int dir;
1614 int handle;
1615 int j;
1617 init_inflation();
1619 pb_init(&pbf, fd);
1621 for(;;){
1622 f_fd = 0;
1623 crc = 0;
1624 ze.crc = 0;
1626 dir = FALSE; /* by default, the file isn't a dir */
1627 handle = TRUE; /* by default we'll extract/create the file */
1629 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1630 perror("read");
1631 break;
1634 signature = UNPACK_UB4(scratch, 0);
1636 #ifdef DEBUG
1637 printf("signature is %x\n", signature);
1638 #endif
1639 if(signature == 0x08074b50){
1640 #ifdef DEBUG
1641 printf("skipping data descriptor\n");
1642 #endif
1643 pb_read(&pbf, scratch, 12);
1644 continue;
1645 } else if(signature == 0x02014b50){
1646 #ifdef DEBUG
1647 printf("Central header reached.. we're all done!\n");
1648 #endif
1649 break;
1650 }else if(signature != 0x04034b50){
1651 printf("Ick! %#x\n", signature);
1652 break;
1655 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1656 perror("read");
1657 break;
1660 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1661 #ifdef DEBUG
1662 printf("Compressed size is %u\n", csize);
1663 #endif
1665 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1666 #ifdef DEBUG
1667 printf("Filename length is %hu\n", fnlen);
1668 #endif
1670 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1671 #ifdef DEBUG
1672 printf("Extra field length is %hu\n", eflen);
1673 #endif
1675 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1676 #ifdef DEBUG
1677 printf("Flags are %#hx\n", flags);
1678 #endif
1680 method = UNPACK_UB2(file_header, LOC_COMP);
1681 #ifdef DEBUG
1682 printf("Compression method is %#hx\n", method);
1683 #endif
1685 /* if there isn't a data descriptor */
1686 if(!(flags & 0x0008)){
1687 crc = UNPACK_UB4(file_header, LOC_CRC);
1688 #ifdef DEBUG
1689 printf("CRC is %x\n", crc);
1690 #endif
1693 if(filename_len < fnlen + 1){
1694 if(filename != NULL)
1695 free(filename);
1697 filename = malloc(sizeof(ub1) * (fnlen + 1));
1698 filename_len = fnlen + 1;
1701 pb_read(&pbf, filename, fnlen);
1702 filename[fnlen] = '\0';
1704 #ifdef DEBUG
1705 printf("filename is %s\n", filename);
1706 #endif
1708 if(file_num > 0){
1709 handle = FALSE;
1711 for(j = 0; j < file_num; j++)
1712 if(strcmp(files[j], (const char *)filename) == 0){
1713 handle = TRUE;
1714 break;
1718 if(!handle)
1719 f_fd = -1;
1721 /* OK, there is some directory information in the file. Nothing to do
1722 but ensure the directory(s) exist, and create them if they don't.
1723 What a pain! */
1724 if(strchr((const char *)filename, '/') != NULL && handle){
1725 /* Loop through all the directories in the path, (everything w/ a '/') */
1726 const ub1 *start = filename;
1727 char *tmp_buff;
1728 struct stat sbuf;
1730 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1732 for(;;){
1733 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1735 if(idx == NULL)
1736 break;
1737 else if(idx == start){
1738 start++;
1739 continue;
1741 start = idx + 1;
1743 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1744 tmp_buff[(idx - filename)] = '\0';
1746 #ifdef DEBUG
1747 printf("checking the existance of %s\n", tmp_buff);
1748 #endif
1750 if(stat(tmp_buff, &sbuf) < 0){
1751 if(errno != ENOENT){
1752 perror("stat");
1753 exit(1);
1756 } else if(S_ISDIR(sbuf.st_mode)){
1757 #ifdef DEBUG
1758 printf("Directory exists\n");
1759 #endif
1760 continue;
1761 }else {
1762 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1763 tmp_buff);
1764 exit(1);
1767 #ifdef DEBUG
1768 printf("Making directory..\n");
1769 #endif
1770 if(mkdir(tmp_buff, 0755) < 0){
1771 perror("mkdir");
1772 exit(1);
1774 if(verbose && handle)
1775 printf("%10s: %s/\n", "created", tmp_buff);
1779 /* only a directory */
1780 if(strlen((const char *)start) == 0)
1781 dir = TRUE;
1783 #ifdef DEBUG
1784 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1785 #endif
1787 /* If the entry was just a directory, don't write to file, etc */
1788 if(strlen((const char *)start) == 0)
1789 f_fd = -1;
1791 free(tmp_buff);
1794 if(f_fd != -1 && handle){
1795 f_fd = open((const char *)filename,
1796 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1798 if(f_fd < 0){
1799 fprintf(stderr, "Error extracting JAR archive!\n");
1800 perror((const char *)filename);
1801 exit(1);
1805 if(method != 8 && flags & 0x0008){
1806 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1807 exit(1);
1810 if (eflen > 0)
1811 consume(&pbf, eflen);
1813 if(method == 8 || flags & 0x0008){
1815 inflate_file(&pbf, f_fd, &ze);
1816 } else {
1818 #ifdef DEBUG
1819 printf("writing stored data.. (%d bytes)\n", csize);
1820 #endif
1822 out_a = 0;
1823 in_a = csize;
1825 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1827 while(out_a < (int)csize){
1828 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1829 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1830 perror("read");
1831 exit(1);
1834 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1836 if(f_fd >= 0)
1837 write(f_fd, rd_buff, rdamt);
1839 out_a += rdamt;
1840 in_a -= rdamt;
1842 #ifdef DEBUG
1843 printf("%d bytes written\n", out_a);
1844 #endif
1848 /* if there is a data descriptor left, compare the CRC */
1849 if(flags & 0x0008){
1851 if(pb_read(&pbf, scratch, 16) != 16){
1852 perror("read");
1853 exit(1);
1856 signature = UNPACK_UB4(scratch, 0);
1858 if(signature != 0x08074b50){
1859 fprintf(stderr, "Error! Missing data descriptor!\n");
1860 exit(1);
1863 crc = UNPACK_UB4(scratch, 4);
1867 if(crc != ze.crc){
1868 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1869 ze.crc, crc);
1870 exit(1);
1873 close(f_fd);
1875 if(verbose && dir == FALSE && handle)
1876 printf("%10s: %s\n",
1877 (method == 8 ? "inflated" : "extracted"),
1878 filename);
1881 return 0;
1884 int list_jar(int fd, char **files, int file_num){
1885 ub4 signature;
1886 ub4 csize;
1887 ub4 usize;
1888 ub4 mdate;
1889 ub4 tmp;
1890 ub2 fnlen;
1891 ub2 eflen;
1892 ub2 clen;
1893 ub2 flags;
1894 ub2 method;
1895 ub2 cen_size;
1896 ub1 *filename = NULL;
1897 ub1 scratch[16];
1898 ub1 cen_header[46];
1899 int filename_len = 0;
1900 off_t size;
1901 int i, j;
1902 time_t tdate;
1903 struct tm *s_tm;
1904 char ascii_date[31];
1905 zipentry ze;
1907 #ifdef DEBUG
1908 printf("Listing jar file, looking for %d files\n", file_num);
1909 #endif
1911 /* This should be the start of the central-header-end section */
1912 if(seekable){
1913 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1914 perror("lseek");
1915 exit(1);
1918 if(read(fd, &tmp, sizeof(ub4)) != 4){
1919 perror("read");
1920 exit(1);
1923 #ifdef WORDS_BIGENDIAN
1924 tmp = L2BI(tmp);
1925 #endif
1927 if(tmp != 0x06054b50){
1928 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1929 exit(1);
1932 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1933 perror("lseek");
1934 exit(1);
1937 if(read(fd, &cen_size, 2) != 2){
1938 perror("read");
1939 exit(1);
1942 #ifdef WORDS_BIGENDIAN
1943 cen_size = L2BS(cen_size);
1944 #endif
1946 /* printf("%hu entries in central header\n", cen_size); */
1948 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1949 perror("lseek");
1950 exit(1);
1953 if(read(fd, &tmp, 4) != 4){
1954 perror("read");
1955 exit(1);
1958 #ifdef WORDS_BIGENDIAN
1959 tmp = L2BI(tmp);
1960 #endif
1962 /* printf("Central header offset = %d\n", tmp); */
1964 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1965 perror("lseek");
1966 exit(1);
1969 /* Loop through the entries in the central header */
1970 for(i = 0; i < cen_size; i++){
1972 if(read(fd, &cen_header, 46) != 46){
1973 perror("read");
1974 exit(1);
1977 signature = UNPACK_UB4(cen_header, 0);
1978 if(signature != 0x02014b50){
1979 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1980 exit(1);
1983 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1984 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1985 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1986 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1988 /* If we're providing verbose output, we need to make an ASCII
1989 * formatted version of the date. */
1990 if(verbose){
1991 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1992 tdate = dos2unixtime(mdate);
1993 s_tm = localtime(&tdate);
1994 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1995 ascii_date[30] = '\0';
1998 if(filename_len < fnlen + 1){
1999 if(filename != NULL)
2000 free(filename);
2002 filename = malloc(sizeof(ub1) * (fnlen + 1));
2003 filename_len = fnlen + 1;
2006 if(read(fd, filename, fnlen) != fnlen){
2007 perror("read");
2008 exit(1);
2010 filename[fnlen] = '\0';
2012 /* if the user specified a list of files on the command line,
2013 we'll only display those, otherwise we'll display everything */
2014 if(file_num > 0){
2015 for(j = 0; j < file_num; j++)
2016 if(strcmp(files[j], (const char *)filename) == 0){
2017 if(verbose)
2018 printf("%6d %s %s\n", usize, ascii_date, filename);
2019 else
2020 printf("%s\n", filename);
2021 break;
2023 } else {
2024 if(verbose)
2025 printf("%6d %s %s\n", usize, ascii_date, filename);
2026 else
2027 printf("%s\n", filename);
2030 size = eflen + clen;
2031 if(size > 0){
2032 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
2033 perror("lseek");
2034 exit(1);
2038 } else {
2039 /* the file isn't seekable.. evil! */
2040 pb_file pbf;
2042 pb_init(&pbf, fd);
2044 init_inflation();
2046 for(;;){
2047 if(pb_read(&pbf, scratch, 4) != 4){
2048 perror("read");
2049 break;
2052 signature = UNPACK_UB4(scratch, 0);
2054 #ifdef DEBUG
2055 printf("signature is %x\n", signature);
2056 #endif
2058 if(signature == 0x08074b50){
2059 #ifdef DEBUG
2060 printf("skipping data descriptor\n");
2061 #endif
2062 pb_read(&pbf, scratch, 12);
2063 continue;
2064 } else if(signature == 0x02014b50){
2065 #ifdef DEBUG
2066 printf("Central header reached.. we're all done!\n");
2067 #endif
2068 break;
2069 }else if(signature != 0x04034b50){
2070 #ifdef DEBUG
2071 printf("Ick! %#x\n", signature);
2072 #endif
2073 break;
2076 if(pb_read(&pbf, (file_header + 4), 26) != 26){
2077 perror("read");
2078 break;
2081 csize = UNPACK_UB4(file_header, LOC_CSIZE);
2082 #ifdef DEBUG
2083 printf("Compressed size is %u\n", csize);
2084 #endif
2086 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
2087 #ifdef DEBUG
2088 printf("Filename length is %hu\n", fnlen);
2089 #endif
2091 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
2092 #ifdef DEBUG
2093 printf("Extra field length is %hu\n", eflen);
2094 #endif
2096 method = UNPACK_UB2(file_header, LOC_COMP);
2097 #ifdef DEBUG
2098 printf("Compression method is %#hx\n", method);
2099 #endif
2101 flags = UNPACK_UB2(file_header, LOC_EXTRA);
2102 #ifdef DEBUG
2103 printf("Flags are %#hx\n", flags);
2104 #endif
2106 usize = UNPACK_UB4(file_header, LOC_USIZE);
2108 /* If we're providing verbose output, we need to make an ASCII
2109 * formatted version of the date. */
2110 if(verbose){
2111 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
2112 tdate = dos2unixtime(mdate);
2113 s_tm = localtime(&tdate);
2114 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
2117 if(filename_len < fnlen + 1){
2118 if(filename != NULL)
2119 free(filename);
2121 filename = malloc(sizeof(ub1) * (fnlen + 1));
2122 ascii_date[30] = '\0';
2123 filename_len = fnlen + 1;
2126 pb_read(&pbf, filename, fnlen);
2127 filename[fnlen] = '\0';
2129 /* the header is at the end. In a JAR file, this means that the data
2130 happens to be compressed. We have no choice but to inflate the
2131 data */
2132 if(flags & 0x0008){
2134 size = eflen;
2136 if(size > 0)
2137 consume(&pbf, size);
2139 if(method == 8){
2140 #ifdef DEBUG
2141 printf("inflating %s\n", filename);
2142 #endif
2143 inflate_file(&pbf, -1, &ze);
2145 usize = ze.usize;
2146 } else
2147 printf("We're shit outta luck!\n");
2149 } else {
2150 size = csize + (eflen > 0 ? eflen : 0);
2153 #ifdef DEBUG
2154 printf("Skipping %ld bytes\n", (long)size);
2155 #endif
2157 consume(&pbf, size);
2159 /* print out the listing */
2160 if(file_num > 0){
2161 for(j = 0; j < file_num; j++)
2162 if(strcmp(files[j], (const char *)filename) == 0){
2163 if(verbose)
2164 printf("%6d %s %s\n", usize, ascii_date, filename);
2165 else
2166 printf("%s\n", filename);
2167 break;
2169 } else {
2170 if(verbose)
2171 printf("%6d %s %s\n", usize, ascii_date, filename);
2172 else
2173 printf("%s\n", filename);
2177 return 0;
2180 int consume(pb_file *pbf, int amt){
2181 int tc = 0; /* total amount consumed */
2182 ub1 buff[RDSZ];
2183 int rdamt;
2185 #ifdef DEBUG
2186 printf("Consuming %d bytes\n", amt);
2187 #endif
2189 if (seekable){
2190 if (amt <= (int)pbf->buff_amt)
2191 pb_read(pbf, buff, amt);
2192 else {
2193 lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
2194 pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
2196 } else
2197 while(tc < amt){
2198 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
2199 #ifdef DEBUG
2200 printf("got %d bytes\n", rdamt);
2201 #endif
2202 tc += rdamt;
2205 #ifdef DEBUG
2206 printf("%d bytes consumed\n", amt);
2207 #endif
2209 return 0;
2212 void usage(const char *filename){
2213 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
2214 exit (1);
2217 void version (void)
2219 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
2220 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
2221 printf("Copyright 2002, 2004 Free Software Foundation\n");
2222 printf("\
2223 This is free software; see the source for copying conditions. There is NO\n\
2224 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2225 exit (0);
2228 void help(const char *filename)
2230 printf("\
2231 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2233 Store many files together in a single `jar' file.\n\
2235 -c create new archive\n\
2236 -t list table of contents for archive\n\
2237 -x extract named (or all) files from archive\n\
2238 -u update existing archive\n\
2239 ", filename);
2240 printf("\n\
2241 -@ read names from stdin\n\
2242 -0 store only; use no ZIP compression\n\
2243 -C DIR FILE change to the specified directory and include\n\
2244 the following file\n\
2245 -E don't include the files found in a directory\n\
2246 -f FILE specify archive file name\n\
2247 --help print this help, then exit\n\
2248 -m FILE include manifest information from specified manifest file\n\
2249 -M Do not create a manifest file for the entries\n\
2250 -v generate verbose output on standard output\n\
2251 -V, --version display version information\n\
2253 printf("\n\
2254 If any file is a directory then it is processed recursively.\n\
2255 The manifest file name and the archive file name needs to be specified\n\
2256 in the same order the 'm' and 'f' flags are specified.\n\
2258 Example 1: to archive two class files into an archive called classes.jar: \n\
2259 jar cvf classes.jar Foo.class Bar.class \n\
2260 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2261 files in the foo/ directory into 'classes.jar': \n\
2262 jar cvfm classes.jar mymanifest -C foo/ .\n\
2265 exit(0);
2268 static char *
2269 jt_strdup(char *s)
2271 char *result = (char*)malloc(strlen(s) + 1);
2272 if (result == (char*)0)
2273 return (char*)0;
2274 strcpy(result, s);
2275 return result;
2278 /* Convert "tar-style" first argument to a form expected by getopt.
2279 This idea and the code comes from GNU tar. This can allocate a new
2280 argument vector. This might leak some memory, but we don't care. */
2281 static void
2282 expand_options (int *argcp, char ***argvp)
2284 int argc = *argcp;
2285 char **argv = *argvp;
2287 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
2288 if a long argument (like "--help") is detected. */
2289 if (argc > 1 && argv[1][1] != '-')
2291 char buf[3];
2292 char **new_argv;
2293 int new_argc;
2294 int args_to_expand;
2295 char *p;
2296 char **in, **out;
2298 buf[0] = '-';
2299 buf[2] = '\0';
2301 args_to_expand = strlen (argv[1]);
2302 if (argv[1][0] == '-')
2303 --args_to_expand;
2305 new_argc = argc - 1 + args_to_expand;
2306 new_argv = (char **) malloc (new_argc * sizeof (char *));
2307 in = argv;
2308 out = new_argv;
2310 *out++ = *in++;
2311 p = *in++;
2312 if (*p == '-')
2313 p++;
2314 while (*p != '\0')
2316 char *opt;
2317 buf[1] = *p;
2318 *out++ = jt_strdup (buf);
2319 /* If the option takes an argument, move the next argument
2320 to just after this option. */
2321 opt = strchr (OPTION_STRING, *p);
2322 if (opt && opt[1] == ':')
2324 if (in < argv + argc)
2325 *out++ = *in++;
2326 else
2328 fprintf(stderr, "%s: option `%s' requires an argument.\n",
2329 argv[0], buf);
2330 usage(argv[0]);
2333 ++p;
2336 /* Copy remaining options. */
2337 while (in < argv + argc)
2338 *out++ = *in++;
2340 *argcp = new_argc;
2341 *argvp = new_argv;