* config.gcc: Add sh-*-symbianelf target.
[official-gcc.git] / fastjar / jartool.c
blob8d223f6cbf0d5233a54f519f9f0ee919a59e56be
1 /*
2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002, 2004 Free Software Foundation
4 Copyright (C) 1999, 2000, 2001 Bryan Burns
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){
340 char *mfile = NULL;
342 int action = ACTION_NONE;
343 int manifest = TRUE;
344 int opt;
346 int jarfd = -1;
348 /* These are used to collect file names and `-C' options for the
349 second pass through the command line. */
350 int new_argc;
351 char **new_argv;
353 progname = argv[0];
355 do_compress = TRUE;
356 verbose = FALSE;
358 ziplist = NULL;
360 number_of_entries = 0;
362 if(argc < 2)
363 usage(argv[0]);
365 new_argc = 0;
366 new_argv = (char **) malloc (argc * sizeof (char *));
368 expand_options (&argc, &argv);
369 while ((opt = getopt_long (argc, argv, OPTION_STRING,
370 options, NULL)) != -1) {
371 switch(opt){
372 case 'C':
373 new_argv[new_argc++] = (char *) "-C";
374 /* ... fall through ... */
375 case 1:
376 /* File name or unparsed option, due to RETURN_IN_ORDER. */
377 new_argv[new_argc++] = optarg;
378 break;
379 case 'c':
380 action = ACTION_CREATE;
381 break;
382 case 't':
383 action = ACTION_LIST;
384 break;
385 case 'x':
386 action = ACTION_EXTRACT;
387 break;
388 case 'u':
389 action = ACTION_UPDATE;
390 break;
391 case 'v':
392 verbose = TRUE;
393 break;
394 case 'V':
395 version();
396 exit(0);
397 case 'f':
398 jarfile = optarg;
399 break;
400 case 'm':
401 mfile = optarg;
402 break;
403 case '0':
404 do_compress = FALSE;
405 break;
406 case 'M':
407 manifest = FALSE;
408 break;
410 case OPT_HELP:
411 help(argv[0]);
412 break;
414 /* The following options aren't supported by the original jar tool. */
415 case 'E':
416 use_explicit_list_only = TRUE;
417 break;
418 case '@':
419 read_names_from_stdin = TRUE;
420 break;
421 default:
422 usage(argv[0]);
426 /* We might have seen `--'. In this case we want to make sure that
427 all following options are handled as file names. */
428 while (optind < argc)
429 new_argv[new_argc++] = argv[optind++];
430 new_argv[new_argc] = NULL;
432 if(action == ACTION_NONE){
433 fprintf(stderr, "%s: one of options -{ctxu} must be specified.\n",
434 progname);
435 usage(argv[0]);
438 /* Verify unsupported combinations and warn of the use of non
439 standard features */
440 if(verbose && use_explicit_list_only)
441 fprintf (stderr, "Warning: using non standard '-E' option\n");
442 if(verbose && read_names_from_stdin)
443 fprintf (stderr, "Warning: using non standard '-@' option\n");
444 if(read_names_from_stdin
445 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
446 fprintf(stderr, "%s: option '-@' is supported only with '-c' or '-u'.\n",
447 progname);
448 usage(argv[0]);
451 /* create the jarfile */
452 if(action == ACTION_CREATE){
453 if(jarfile){
454 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
456 if(jarfd < 0){
457 fprintf(stderr, "%s: error opening %s for writing: %s\n", progname,
458 jarfile, strerror (errno));
459 exit(1);
462 /* We assume that the file is seekable */
463 seekable = TRUE;
465 } else {
467 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
469 /* standard out is not seekable */
470 seekable = FALSE;
472 /* don't want our output to be part of the jar file.. figured this one
473 out the hard way.. =P */
474 verbose = FALSE;
476 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
478 if(jarfile){
479 jarfd = open(jarfile, O_RDONLY | O_BINARY);
481 if(jarfd < 0){
482 fprintf(stderr, "%s: error opening %s for reading: %s\n", progname,
483 jarfile, strerror (errno));
484 exit(1);
487 seekable = TRUE;
488 } else {
489 jarfd = STDIN_FILENO; /* jarfd is standard in */
491 /* we assume that the stream isn't seekable for safety */
492 seekable = FALSE;
496 if (action == ACTION_UPDATE)
498 if (!jarfile)
500 fprintf (stderr, "%s: `-u' mode requires a file name\n",
501 argv[0]);
502 exit (1);
505 if ((jarfd = open (jarfile, O_RDWR | O_BINARY)) < 0)
507 fprintf (stderr, "Error opening %s for reading!\n", jarfile);
508 perror (jarfile);
509 exit (1);
512 /* Assert that jarfd is seekable. */
513 if (lseek (jarfd, 0, SEEK_CUR) == -1)
515 fprintf (stderr, "%s: %s is not seekable\n", argv[0], jarfile);
516 exit (1);
519 seekable = TRUE;
522 if(action == ACTION_CREATE || action == ACTION_UPDATE){
523 const char *arg;
524 init_headers();
526 if(do_compress)
527 init_compression();
529 if (action == ACTION_UPDATE)
531 if (read_entries (jarfd))
532 exit (1);
535 /* Add the META-INF/ directory and the manifest */
536 if(manifest && mfile)
537 make_manifest(jarfd, mfile, action == ACTION_UPDATE);
538 else if(manifest && action == ACTION_CREATE)
539 make_manifest(jarfd, NULL, FALSE);
541 init_args (new_argv, 0);
542 /* now we add the files to the archive */
543 while ((arg = get_next_arg ())){
545 if(!strcmp(arg, "-C")){
546 const char *dir_to_change = get_next_arg ();
547 const char *file_to_add = get_next_arg ();
548 if (!dir_to_change || !file_to_add) {
549 fprintf(stderr, "%s: error: missing argument for -C.\n", progname);
550 exit(1);
552 if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add,
553 action == ACTION_UPDATE))
555 fprintf(stderr,
556 "Error adding %s (in directory %s) to jar archive!\n",
557 file_to_add, dir_to_change);
558 exit(1);
560 } else {
561 if(add_to_jar(jarfd, arg, action == ACTION_UPDATE)){
562 fprintf(stderr, "Error adding %s to jar archive!\n", arg);
563 exit(1);
567 /* de-initialize the compression DS */
568 if(do_compress)
569 end_compression();
571 if (action == ACTION_UPDATE)
572 lseek (jarfd, end_of_entries, SEEK_SET);
574 create_central_header(jarfd);
576 /* Check if the file shrunk when we updated it. */
577 if (action == ACTION_UPDATE)
578 ftruncate (jarfd, lseek (jarfd, 0, SEEK_CUR));
580 if (jarfd != STDIN_FILENO && close(jarfd) != 0) {
581 fprintf(stderr, "%s: error closing jar archive: %s\n",
582 progname, strerror (errno));
583 exit (1);
585 } else if(action == ACTION_LIST){
586 list_jar(jarfd, &new_argv[0], new_argc);
587 } else if(action == ACTION_EXTRACT){
588 extract_jar(jarfd, &new_argv[0], new_argc);
591 exit(0);
594 static int args_current_g;
595 static char **args_g;
597 static void
598 init_args(args, current)
599 char **args;
600 int current;
602 if(!read_names_from_stdin)
604 args_g = args;
605 args_current_g = current;
609 static char *
610 get_next_arg ()
612 static int reached_end = 0;
614 if (reached_end)
615 return NULL;
617 if (args_g)
619 if (!args_g [args_current_g])
621 reached_end = 1;
622 return NULL;
624 return args_g [args_current_g++];
626 else
628 /* Read the name from stdin. Delimiters are '\n' and
629 '\r'. Reading EOF indicates that we don't have anymore file
630 names characters to read. */
632 char s [MAXPATHLEN];
633 int pos = 0;
635 /* Get rid of '\n' and '\r' first. */
636 while (1)
638 int c = getc (stdin);
639 if (c == '\n' || c == '\r')
640 continue;
641 else
643 if (c == EOF)
644 return NULL;
645 ungetc (c, stdin);
646 break;
650 while (1)
652 int c = getc (stdin);
653 /* Exit when we get a delimiter or don't have any characters
654 to read */
655 if (c == '\n'|| c == '\r'|| c == EOF)
656 break;
657 s [pos++] = (char) c;
660 if (pos)
662 s [pos] = '\0';
663 return jt_strdup (s);
665 else
666 return NULL;
670 void init_headers(){
671 /* packing file header */
672 /* magic number */
673 file_header[0] = 0x50;
674 file_header[1] = 0x4b;
675 file_header[2] = 0x03;
676 file_header[3] = 0x04;
677 /* version number (Unix 1.0)*/
678 file_header[4] = 10;
679 file_header[5] = 0;
680 /* bit flag (normal deflation)*/
681 file_header[6] = 0x00;
683 file_header[7] = 0x00;
684 /* do_compression method (deflation) */
685 file_header[8] = 0;
686 file_header[9] = 0;
688 /* last mod file time (MS-DOS format) */
689 file_header[10] = 0;
690 file_header[11] = 0;
691 /* last mod file date (MS-DOS format) */
692 file_header[12] = 0;
693 file_header[13] = 0;
694 /* CRC 32 */
695 file_header[14] = 0;
696 file_header[15] = 0;
697 file_header[16] = 0;
698 file_header[17] = 0;
699 /* compressed size */
700 file_header[18] = 0;
701 file_header[19] = 0;
702 file_header[20] = 0;
703 file_header[21] = 0;
704 /* uncompressed size */
705 file_header[22] = 0;
706 file_header[23] = 0;
707 file_header[24] = 0;
708 file_header[25] = 0;
709 /* filename length */
710 file_header[26] = 0;
711 file_header[27] = 0;
712 /* extra field length */
713 file_header[28] = 0;
714 file_header[29] = 0;
716 /* Initialize the compression DS */
717 PACK_UB4(data_descriptor, 0, 0x08074b50);
721 void add_entry(struct zipentry *ze){
723 if(ziplist == NULL){
724 ziplist = ze;
725 ziptail = ziplist;
726 } else {
727 ziplist->next_entry = ze;
728 ziplist = ze;
731 number_of_entries++;
734 static struct zipentry *
735 find_entry (const char *fname)
737 struct zipentry *ze;
739 for (ze = ziptail; ze; ze = ze->next_entry)
741 if (!strcmp (ze->filename, fname))
742 return ze;
744 return NULL;
748 static int
749 looks_like_dir (const char *fname)
751 struct zipentry *ze;
752 size_t len = strlen (fname);
754 for (ze = ziptail; ze; ze = ze->next_entry)
756 if (strlen (ze->filename) > len
757 && !strncmp (fname, ze->filename, len)
758 && ze->filename[len] == '/')
759 return 1;
761 return 0;
766 * Read the zip entries of an existing file, building `ziplist' as we go.
768 int read_entries (int fd)
770 struct zipentry *ze;
771 ub1 intbuf[4];
772 ub1 header[46];
773 ub2 len;
774 ub2 count, i;
775 off_t offset;
777 if (lseek (fd, -22, SEEK_END) == -1)
779 fprintf (stderr, "%s: %s: can't seek file\n", progname, jarfile);
780 return 1;
783 if (read (fd, intbuf, 4) < 4)
785 perror (progname);
786 return 1;
788 /* Is there a zipfile comment? */
789 while (UNPACK_UB4(intbuf, 0) != 0x06054b50)
791 if (lseek (fd, -5, SEEK_CUR) == -1 ||
792 read (fd, intbuf, 4) != 4)
794 fprintf (stderr, "%s: can't find end of central directory: %s\n",
795 progname, strerror (errno));
796 return 1;
800 /* Skip disk numbers. */
801 if (lseek (fd, 6, SEEK_CUR) == -1)
803 perror (progname);
804 return 1;
807 /* Number of entries in the central directory. */
808 if (read (fd, intbuf, 2) != 2)
810 perror (progname);
811 return 1;
813 count = UNPACK_UB2(intbuf, 0);
815 if (lseek (fd, 4, SEEK_CUR) == -1)
817 perror (progname);
818 return 1;
821 /* Offset where the central directory begins. */
822 if (read (fd, intbuf, 4) != 4)
824 perror (progname);
825 return 1;
827 offset = UNPACK_UB4(intbuf, 0);
828 end_of_entries = offset;
830 if (lseek (fd, offset, SEEK_SET) != offset)
832 perror (progname);
833 return 1;
836 if (read (fd, header, 46) != 46)
838 fprintf (stderr, "%s: %s: unexpected end of file\n",
839 progname, jarfile);
840 return 1;
843 for (i = 0; i < count; i++)
845 if (UNPACK_UB4(header, 0) != 0x02014b50)
847 fprintf (stderr, "%s: can't find central directory header\n",
848 progname);
849 return 1;
851 ze = (struct zipentry *) malloc (sizeof (struct zipentry));
852 if (!ze)
854 perror (progname);
855 return 1;
857 memset (ze, 0, sizeof (struct zipentry));
858 ze->flags = UNPACK_UB2(header, CEN_FLAGS);
859 ze->mod_time = UNPACK_UB2(header, CEN_MODTIME);
860 ze->mod_date = UNPACK_UB2(header, CEN_MODDATE);
861 ze->crc = UNPACK_UB4(header, CEN_CRC);
862 ze->usize = UNPACK_UB4(header, CEN_USIZE);
863 ze->csize = UNPACK_UB4(header, CEN_CSIZE);
864 ze->offset = UNPACK_UB4(header, CEN_OFFSET);
865 ze->compressed = (header[CEN_COMP] || header[CEN_COMP+1]);
866 len = UNPACK_UB2(header, CEN_FNLEN);
867 ze->filename = (char *) malloc ((len+1) * sizeof (char));
868 if (!ze->filename)
870 perror (progname);
871 return 1;
873 if (read (fd, ze->filename, len) != len)
875 fprintf (stderr, "%s: %s: unexpected end of file\n",
876 progname, jarfile);
877 return 1;
879 len = UNPACK_UB4(header, CEN_EFLEN);
880 len += UNPACK_UB4(header, CEN_COMLEN);
881 if (lseek (fd, len, SEEK_CUR) == -1)
883 perror (progname);
884 return 1;
886 add_entry (ze);
887 if (i < count - 1)
889 if (read (fd, header, 46) != 46)
891 fprintf (stderr, "%s: %s: unexpected end of file\n",
892 progname, jarfile);
893 return 1;
898 lseek (fd, 0, SEEK_SET);
899 return 0;
902 int make_manifest(int jfd, const char *mf_name, int updating){
903 time_t current_time;
904 int nlen; /* length of file name */
905 int mod_time; /* file modification time */
906 struct zipentry *ze;
908 nlen = 9; /* trust me on this one */
910 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
912 current_time = time(NULL);
913 if(current_time == (time_t)-1){
914 perror("time");
915 exit(1);
918 mod_time = unix2dostime(&current_time);
920 PACK_UB2(file_header, LOC_EXTRA, 0);
921 PACK_UB2(file_header, LOC_COMP, 0);
922 PACK_UB2(file_header, LOC_FNLEN, nlen);
923 PACK_UB4(file_header, LOC_MODTIME, mod_time);
925 if(verbose)
926 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
928 ze = (zipentry*)malloc(sizeof(zipentry));
929 if(ze == NULL){
930 perror("malloc");
931 exit(1);
934 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
935 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
936 strcpy(ze->filename, "META-INF/");
937 ze->filename[nlen] = '\0';
939 ze->offset = lseek(jfd, 0, SEEK_CUR);
940 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
941 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
942 ze->compressed = FALSE;
944 add_entry(ze);
946 write(jfd, file_header, 30);
947 write(jfd, "META-INF/", nlen);
949 /* if the user didn't specify an external manifest file... */
950 if(mf_name == NULL){
952 int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
953 char *mf;
955 if((mf = (char *) malloc(mf_len + 1))) {
956 uLong crc;
958 sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
960 crc = crc32(0L, Z_NULL, 0);
962 crc = crc32(crc, (const unsigned char *)mf, mf_len);
964 nlen = 20; /* once again, trust me */
966 PACK_UB2(file_header, LOC_EXTRA, 0);
967 PACK_UB2(file_header, LOC_COMP, 0);
968 PACK_UB2(file_header, LOC_FNLEN, nlen);
969 PACK_UB4(file_header, LOC_USIZE, mf_len);
971 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
973 PACK_UB4(file_header, LOC_CRC, crc);
975 if(verbose)
976 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
978 ze = (zipentry*)malloc(sizeof(zipentry));
979 if(ze == NULL){
980 perror("malloc");
981 exit(1);
984 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
985 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
986 strcpy(ze->filename, "META-INF/MANIFEST.MF");
987 ze->filename[nlen] = '\0';
989 ze->offset = lseek(jfd, 0, SEEK_CUR);
990 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
991 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
992 ze->crc = crc;
993 ze->csize = mf_len;
994 ze->usize = ze->csize;
995 ze->compressed = FALSE;
997 add_entry(ze);
999 write(jfd, file_header, 30);
1000 write(jfd, "META-INF/MANIFEST.MF", nlen);
1001 write(jfd, mf, mf_len);
1002 free(mf);
1004 else {
1005 printf("malloc errror\n");
1006 exit(-1);
1008 } else {
1009 int mfd;
1010 struct stat statbuf;
1012 stat(mf_name, &statbuf);
1014 if(!S_ISREG(statbuf.st_mode)){
1015 fprintf(stderr, "Invalid manifest file specified.\n");
1016 exit(1);
1019 mfd = open(mf_name, O_RDONLY | O_BINARY);
1021 if(mfd < 0){
1022 fprintf(stderr, "Error opening %s.\n", mf_name);
1023 exit(1);
1026 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
1027 perror("error writing to jar");
1028 exit(1);
1033 return 0;
1036 /* Implements -C by wrapping add_to_jar. new_dir is the directory
1037 to switch to.
1039 `updating', if nonzero, will indicate that we are updating an
1040 existing file, and will need to take special care. If set, we will
1041 also expect that the linked list of zip entries will be filled in
1042 with the jar file's current contents.
1044 int
1045 add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
1046 const int updating)
1048 int retval;
1049 char old_dir[MAXPATHLEN];
1050 if (getcwd(old_dir, MAXPATHLEN) == NULL) {
1051 perror("getcwd");
1052 return 1;
1054 if (chdir(new_dir) == -1) {
1055 perror(new_dir);
1056 return 1;
1058 retval=add_to_jar(fd, file, updating);
1059 if (chdir(old_dir) == -1) {
1060 perror(old_dir);
1061 return 1;
1063 return retval;
1066 int
1067 add_to_jar (int fd, const char *file, const int updating)
1069 struct stat statbuf;
1070 DIR *dir;
1071 struct dirent *de;
1072 zipentry *ze;
1073 zipentry *existing = NULL;
1074 int stat_return;
1076 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
1077 * It fixes this:
1078 * "normal" jar : org/apache/java/io/LogRecord.class
1079 * fastjar : ./org/apache/java/io/LogRecord.class
1080 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
1081 * with both kaffe-1.0b4 and JDK.
1083 while (*file=='.' && *(file+1)=='/')
1084 file+=2;
1086 if(jarfile && !strcmp(file, jarfile)){
1087 if(verbose)
1088 printf("skipping: %s\n", file);
1089 return 0; /* we don't want to add ourselves.. */
1092 stat_return = stat(file, &statbuf);
1094 if(stat_return == -1){
1095 perror(file);
1096 return 1;
1097 } else if(S_ISDIR(statbuf.st_mode)){
1098 char *fullname;
1099 char *t_ptr;
1100 int nlen;
1101 unsigned long mod_time;
1103 dir = opendir(file);
1105 if(dir == NULL){
1106 perror("opendir");
1107 return 1;
1110 nlen = strlen(file) + 256;
1111 fullname = (char*)malloc(nlen * sizeof(char));
1112 memset(fullname, 0, (nlen * sizeof(char)));
1114 if(fullname == NULL){
1115 fprintf(stderr, "Filename is NULL!\n");
1116 return 1;
1119 strcpy(fullname, file);
1120 nlen = strlen(file);
1122 if(fullname[nlen - 1] != '/'){
1123 fullname[nlen] = '/';
1124 t_ptr = (fullname + nlen + 1);
1125 } else
1126 t_ptr = (fullname + nlen);
1129 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
1131 nlen = (t_ptr - fullname);
1133 mod_time = unix2dostime(&statbuf.st_mtime);
1135 PACK_UB2(file_header, LOC_EXTRA, 0);
1136 PACK_UB2(file_header, LOC_COMP, 0);
1137 PACK_UB2(file_header, LOC_FNLEN, nlen);
1138 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1140 ze = (zipentry*)malloc(sizeof(zipentry));
1141 if(ze == NULL){
1142 perror("malloc");
1143 exit(1);
1146 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1147 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
1148 strcpy(ze->filename, fullname);
1149 ze->filename[nlen] = '\0';
1151 ze->offset = lseek(fd, 0, SEEK_CUR);
1152 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1153 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1154 ze->compressed = FALSE;
1156 if (updating)
1158 if ((existing = find_entry (ze->filename)) != NULL)
1160 if (existing->usize != 0)
1162 /* XXX overwriting non-directory with directory? */
1163 fprintf (stderr, "%s: %s: can't overwrite non-directory with directory\n",
1164 progname, fullname);
1165 return 1;
1168 if (lseek (fd, end_of_entries, SEEK_SET) == -1)
1170 fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
1171 perror ("lseek");
1172 return 1;
1176 if (!existing)
1178 add_entry (ze);
1179 write (fd, file_header, 30);
1180 write (fd, fullname, nlen);
1181 end_of_entries = lseek (fd, 0, SEEK_CUR);
1183 if (verbose)
1184 printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
1187 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
1188 if(de->d_name[0] == '.')
1189 continue;
1190 if(jarfile && !strcmp(de->d_name, jarfile)){
1191 /* we don't want to add ourselves. Believe me */
1192 if(verbose)
1193 printf("skipping: %s\n", de->d_name);
1194 continue;
1197 strcpy(t_ptr, de->d_name);
1199 if (add_to_jar(fd, fullname, updating)) {
1200 fprintf(stderr, "Error adding file to jar!\n");
1201 return 1;
1205 free(fullname);
1206 closedir(dir);
1208 } else if(S_ISREG(statbuf.st_mode)){
1209 int add_fd;
1211 add_fd = open(file, O_RDONLY | O_BINARY);
1212 if(add_fd < 0){
1213 fprintf(stderr, "Error opening %s.\n", file);
1214 return 1;
1217 if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
1218 fprintf(stderr, "Error adding file to jar!\n");
1219 return 1;
1222 } else {
1223 fprintf(stderr, "Illegal file specified: %s\n", file);
1225 return 0;
1228 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
1229 const int updating)
1231 unsigned short file_name_length;
1232 unsigned long mod_time;
1233 ub1 rd_buff[RDSZ];
1234 uLong crc = 0;
1235 off_t offset = 0;
1236 int rdamt;
1237 struct zipentry *ze;
1238 struct zipentry *existing = NULL;
1240 if (updating)
1242 existing = find_entry (fname);
1243 if (existing && looks_like_dir (fname))
1245 fprintf (stderr, "%s: %s is a directory in the archive\n",
1246 progname, fname);
1247 return 1;
1251 mod_time = unix2dostime(&(statbuf->st_mtime));
1252 file_name_length = strlen(fname);
1254 if(!seekable && !do_compress){
1255 crc = crc32(0L, Z_NULL, 0);
1257 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
1258 crc = crc32(crc, rd_buff, rdamt);
1260 lseek(ffd, 0, SEEK_SET);
1263 /* data descriptor */
1264 if(!seekable && do_compress){
1265 PACK_UB2(file_header, LOC_EXTRA, 8);
1266 } else {
1267 PACK_UB2(file_header, LOC_EXTRA, 0);
1270 if(do_compress){
1271 PACK_UB2(file_header, LOC_COMP, 8);
1272 } else {
1273 PACK_UB2(file_header, LOC_COMP, 0);
1276 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1277 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1279 if(!seekable && !do_compress){
1280 PACK_UB4(file_header, LOC_CRC, crc);
1281 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1282 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1283 } else
1284 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1286 ze = (zipentry*)malloc(sizeof(zipentry));
1287 if(ze == NULL){
1288 perror("malloc");
1289 exit(1);
1292 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1293 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1294 strcpy(ze->filename, fname);
1296 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1297 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1299 if(!seekable && !do_compress)
1300 ze->crc = crc;
1302 ze->csize = statbuf->st_size;
1303 ze->usize = ze->csize;
1305 if (existing)
1306 ze->offset = existing->offset;
1307 else if (updating)
1308 ze->offset = end_of_entries;
1309 else
1310 ze->offset = lseek(jfd, 0, SEEK_CUR);
1312 if(do_compress)
1313 ze->compressed = TRUE;
1314 else
1315 ze->compressed = FALSE;
1317 if (!existing)
1318 add_entry(ze);
1319 if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
1321 perror ("lseek");
1322 return 1;
1325 /* We can safely write the header here, since it will be the same size
1326 as before */
1328 /* Write the local header */
1329 write(jfd, file_header, 30);
1331 /* write the file name to the zip file */
1332 write(jfd, fname, file_name_length);
1335 if(verbose){
1336 if (existing)
1337 printf ("updating: %s ", fname);
1338 else
1339 printf("adding: %s ", fname);
1340 fflush(stdout);
1343 if(do_compress){
1344 /* compress the file */
1345 compress_file(ffd, jfd, ze, existing);
1346 } else {
1347 /* If we are not writing the last entry, make space for it. */
1348 if (existing && existing->next_entry)
1350 if (ze->usize > existing->usize)
1352 if (shift_down (jfd, existing->next_entry->offset,
1353 ze->usize - existing->usize, existing->next_entry))
1355 fprintf (stderr, "%s: %s\n", progname, strerror (errno));
1356 return 1;
1361 /* Write the contents of the file (uncompressed) to the zip file */
1362 /* calculate the CRC as we go along */
1363 ze->crc = crc32(0L, Z_NULL, 0);
1365 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1366 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1367 if(write(jfd, rd_buff, rdamt) != rdamt){
1368 perror("write");
1369 return 0;
1373 close(ffd);
1375 /* write out data descriptor */
1376 PACK_UB4(data_descriptor, 4, ze->crc);
1377 PACK_UB4(data_descriptor, 8, ze->csize);
1378 PACK_UB4(data_descriptor, 12, ze->usize);
1380 /* we need to seek back and fill the header */
1381 if(seekable){
1382 offset = (ze->csize + strlen(ze->filename) + 16);
1384 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1385 perror("lseek");
1386 exit(1);
1389 if(write(jfd, (data_descriptor + 4), 12) != 12){
1390 perror("write");
1391 return 0;
1394 offset -= 12;
1396 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1397 perror("lseek");
1398 exit(1);
1400 } else if(do_compress){
1401 /* Sun's jar tool will only allow a data descriptor if the entry is
1402 compressed, but we'll save 16 bytes/entry if we only use it when
1403 we can't seek back on the file */
1404 /* Technically, you CAN'T have a data descriptor unless the data
1405 part has an obvious end, which DEFLATED does. Otherwise, there
1406 would not be any way to determine where the data descriptor is.
1407 Store an uncompressed file that ends with 0x504b0708, and see.
1408 -- csm */
1410 if(write(jfd, data_descriptor, 16) != 16){
1411 perror("write");
1412 return 0;
1416 if (existing)
1418 int dd = (existing->flags & (1 << 3)) ? 12 : 0;
1419 if (existing->next_entry && ze->csize < existing->csize + dd)
1421 if (shift_up (jfd, existing->next_entry->offset,
1422 existing->csize + dd - ze->csize,
1423 existing->next_entry))
1425 perror (progname);
1426 return 1;
1429 /* Replace the existing entry data with this entry's. */
1430 existing->csize = ze->csize;
1431 existing->usize = ze->usize;
1432 existing->crc = ze->crc;
1433 existing->mod_time = ze->mod_time;
1434 existing->mod_date = ze->mod_date;
1435 free (ze->filename);
1436 free (ze);
1438 else if (updating)
1439 end_of_entries = lseek (jfd, 0, SEEK_CUR);
1441 if(verbose)
1442 printf("(in=%d) (out=%d) (%s %d%%)\n",
1443 (int)ze->usize, (int)ze->csize,
1444 (do_compress ? "deflated" : "stored"),
1445 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1447 return 0;
1450 int create_central_header(int fd){
1451 ub1 header[46];
1452 ub1 end_header[22];
1453 int start_offset;
1454 int dir_size;
1455 int total_in = 0, total_out = 22;
1457 zipentry *ze;
1459 /* magic number */
1460 header[0] = 'P';
1461 header[1] = 'K';
1462 header[2] = 1;
1463 header[3] = 2;
1464 /* version made by */
1465 header[4] = 10;
1466 header[5] = 0;
1467 /* version needed to extract */
1468 header[6] = 10;
1469 header[7] = 0;
1470 /* bit flag */
1471 header[8] = 0;
1472 header[9] = 0;
1473 /* compression method */
1474 header[10] = 0;
1475 header[11] = 0;
1476 /* file mod time */
1477 header[12] = 0;
1478 header[13] = 0;
1479 /* file mod date */
1480 header[14] = 0;
1481 header[15] = 0;
1482 /* crc 32 */
1483 header[16] = 0;
1484 header[17] = 0;
1485 header[18] = 0;
1486 header[19] = 0;
1487 /* compressed size */
1488 header[20] = 0;
1489 header[21] = 0;
1490 header[22] = 0;
1491 header[23] = 0;
1492 /* uncompressed size */
1493 header[24] = 0;
1494 header[25] = 0;
1495 header[26] = 0;
1496 header[27] = 0;
1497 /* filename length */
1498 header[28] = 0;
1499 header[29] = 0;
1500 /* extra field length */
1501 header[30] = 0;
1502 header[31] = 0;
1503 /* file comment length */
1504 header[32] = 0;
1505 header[33] = 0;
1506 /* disk number start */
1507 header[34] = 0;
1508 header[35] = 0;
1509 /* internal file attribs */
1510 header[36] = 0;
1511 header[37] = 0;
1512 /* external file attribs */
1513 header[38] = 0;
1514 header[39] = 0;
1515 header[40] = 0;
1516 header[41] = 0;
1517 /* relative offset of local header */
1518 header[42] = 0;
1519 header[43] = 0;
1520 header[44] = 0;
1521 header[45] = 0;
1523 start_offset = lseek(fd, 0, SEEK_CUR);
1525 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1527 total_in += ze->usize;
1528 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1530 if(ze->compressed){
1531 PACK_UB2(header, CEN_COMP, 8);
1532 } else {
1533 PACK_UB2(header, CEN_COMP, 0);
1536 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1537 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1538 PACK_UB4(header, CEN_CRC, ze->crc);
1539 PACK_UB4(header, CEN_CSIZE, ze->csize);
1540 PACK_UB4(header, CEN_USIZE, ze->usize);
1541 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1542 PACK_UB4(header, CEN_OFFSET, ze->offset);
1544 write(fd, header, 46);
1546 write(fd, ze->filename, strlen(ze->filename));
1549 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1551 /* magic number */
1552 end_header[0] = 0x50;
1553 end_header[1] = 0x4b;
1554 end_header[2] = 0x05;
1555 end_header[3] = 0x06;
1556 /* number of this disk */
1557 end_header[4] = 0;
1558 end_header[5] = 0;
1559 /* number of disk w/ start of central header */
1560 end_header[6] = 0;
1561 end_header[7] = 0;
1562 /* total number of entries in central dir on this disk*/
1563 PACK_UB2(end_header, 8, number_of_entries);
1564 /* total number of entries in central dir*/
1565 PACK_UB2(end_header, 10, number_of_entries);
1566 /* size of central dir. */
1567 PACK_UB4(end_header, 12, dir_size);
1568 /* offset of start of central dir */
1569 PACK_UB4(end_header, 16, start_offset);
1570 /* zipfile comment length */
1571 end_header[20] = 0;
1572 end_header[21] = 0;
1574 write(fd, end_header, 22);
1576 if(verbose)
1577 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1578 total_in,
1579 total_out,
1580 (do_compress ? "deflated" : "stored"),
1581 (int)((1 - (total_out / (float)total_in)) * 100)
1584 return 0;
1587 int extract_jar(int fd, char **files, int file_num){
1588 int rdamt;
1589 int out_a, in_a;
1590 ub4 signature;
1591 ub4 csize;
1592 ub4 crc;
1593 ub2 fnlen;
1594 ub2 eflen;
1595 ub2 flags;
1596 ub2 method;
1597 ub1 *filename = NULL;
1598 int filename_len = 0;
1599 ub4 rd_buff[RDSZ];
1600 pb_file pbf;
1601 ub1 scratch[16];
1602 zipentry ze;
1603 int f_fd;
1604 int dir;
1605 int handle;
1606 int j;
1608 init_inflation();
1610 pb_init(&pbf, fd);
1612 for(;;){
1613 f_fd = 0;
1614 crc = 0;
1615 ze.crc = 0;
1617 dir = FALSE; /* by default, the file isn't a dir */
1618 handle = TRUE; /* by default we'll extract/create the file */
1620 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1621 perror("read");
1622 break;
1625 signature = UNPACK_UB4(scratch, 0);
1627 #ifdef DEBUG
1628 printf("signature is %x\n", signature);
1629 #endif
1630 if(signature == 0x08074b50){
1631 #ifdef DEBUG
1632 printf("skipping data descriptor\n");
1633 #endif
1634 pb_read(&pbf, scratch, 12);
1635 continue;
1636 } else if(signature == 0x02014b50){
1637 #ifdef DEBUG
1638 printf("Central header reached.. we're all done!\n");
1639 #endif
1640 break;
1641 }else if(signature != 0x04034b50){
1642 printf("Ick! %#x\n", signature);
1643 break;
1646 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1647 perror("read");
1648 break;
1651 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1652 #ifdef DEBUG
1653 printf("Compressed size is %u\n", csize);
1654 #endif
1656 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1657 #ifdef DEBUG
1658 printf("Filename length is %hu\n", fnlen);
1659 #endif
1661 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1662 #ifdef DEBUG
1663 printf("Extra field length is %hu\n", eflen);
1664 #endif
1666 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1667 #ifdef DEBUG
1668 printf("Flags are %#hx\n", flags);
1669 #endif
1671 method = UNPACK_UB2(file_header, LOC_COMP);
1672 #ifdef DEBUG
1673 printf("Compression method is %#hx\n", method);
1674 #endif
1676 /* if there isn't a data descriptor */
1677 if(!(flags & 0x0008)){
1678 crc = UNPACK_UB4(file_header, LOC_CRC);
1679 #ifdef DEBUG
1680 printf("CRC is %x\n", crc);
1681 #endif
1684 if(filename_len < fnlen + 1){
1685 if(filename != NULL)
1686 free(filename);
1688 filename = malloc(sizeof(ub1) * (fnlen + 1));
1689 filename_len = fnlen + 1;
1692 pb_read(&pbf, filename, fnlen);
1693 filename[fnlen] = '\0';
1695 #ifdef DEBUG
1696 printf("filename is %s\n", filename);
1697 #endif
1699 if(file_num > 0){
1700 handle = FALSE;
1702 for(j = 0; j < file_num; j++)
1703 if(strcmp(files[j], (const char *)filename) == 0){
1704 handle = TRUE;
1705 break;
1709 if(!handle)
1710 f_fd = -1;
1712 /* OK, there is some directory information in the file. Nothing to do
1713 but ensure the directory(s) exist, and create them if they don't.
1714 What a pain! */
1715 if(strchr((const char *)filename, '/') != NULL && handle){
1716 /* Loop through all the directories in the path, (everything w/ a '/') */
1717 const ub1 *start = filename;
1718 char *tmp_buff;
1719 struct stat sbuf;
1721 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1723 for(;;){
1724 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1726 if(idx == NULL)
1727 break;
1728 else if(idx == start){
1729 start++;
1730 continue;
1732 start = idx + 1;
1734 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1735 tmp_buff[(idx - filename)] = '\0';
1737 #ifdef DEBUG
1738 printf("checking the existance of %s\n", tmp_buff);
1739 #endif
1741 if(stat(tmp_buff, &sbuf) < 0){
1742 if(errno != ENOENT){
1743 perror("stat");
1744 exit(1);
1747 } else if(S_ISDIR(sbuf.st_mode)){
1748 #ifdef DEBUG
1749 printf("Directory exists\n");
1750 #endif
1751 continue;
1752 }else {
1753 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1754 tmp_buff);
1755 exit(1);
1758 #ifdef DEBUG
1759 printf("Making directory..\n");
1760 #endif
1761 if(mkdir(tmp_buff, 0755) < 0){
1762 perror("mkdir");
1763 exit(1);
1765 if(verbose && handle)
1766 printf("%10s: %s/\n", "created", tmp_buff);
1770 /* only a directory */
1771 if(strlen((const char *)start) == 0)
1772 dir = TRUE;
1774 #ifdef DEBUG
1775 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1776 #endif
1778 /* If the entry was just a directory, don't write to file, etc */
1779 if(strlen((const char *)start) == 0)
1780 f_fd = -1;
1782 free(tmp_buff);
1785 if(f_fd != -1 && handle){
1786 f_fd = open((const char *)filename,
1787 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1789 if(f_fd < 0){
1790 fprintf(stderr, "Error extracting JAR archive!\n");
1791 perror((const char *)filename);
1792 exit(1);
1796 if(method != 8 && flags & 0x0008){
1797 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1798 exit(1);
1801 if(method == 8 || flags & 0x0008){
1802 consume(&pbf, eflen);
1804 inflate_file(&pbf, f_fd, &ze);
1805 } else {
1807 #ifdef DEBUG
1808 printf("writing stored data.. (%d bytes)\n", csize);
1809 #endif
1811 out_a = 0;
1812 in_a = csize;
1814 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1816 while(out_a < (int)csize){
1817 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1818 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1819 perror("read");
1820 exit(1);
1823 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1825 if(f_fd >= 0)
1826 write(f_fd, rd_buff, rdamt);
1828 out_a += rdamt;
1829 in_a -= rdamt;
1831 #ifdef DEBUG
1832 printf("%d bytes written\n", out_a);
1833 #endif
1836 consume(&pbf, eflen);
1839 /* if there is a data descriptor left, compare the CRC */
1840 if(flags & 0x0008){
1842 if(pb_read(&pbf, scratch, 16) != 16){
1843 perror("read");
1844 exit(1);
1847 signature = UNPACK_UB4(scratch, 0);
1849 if(signature != 0x08074b50){
1850 fprintf(stderr, "Error! Missing data descriptor!\n");
1851 exit(1);
1854 crc = UNPACK_UB4(scratch, 4);
1858 if(crc != ze.crc){
1859 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1860 ze.crc, crc);
1861 exit(1);
1864 close(f_fd);
1866 if(verbose && dir == FALSE && handle)
1867 printf("%10s: %s\n",
1868 (method == 8 ? "inflated" : "extracted"),
1869 filename);
1872 return 0;
1875 int list_jar(int fd, char **files, int file_num){
1876 ub4 signature;
1877 ub4 csize;
1878 ub4 usize;
1879 ub4 mdate;
1880 ub4 tmp;
1881 ub2 fnlen;
1882 ub2 eflen;
1883 ub2 clen;
1884 ub2 flags;
1885 ub2 method;
1886 ub2 cen_size;
1887 ub1 *filename = NULL;
1888 ub1 scratch[16];
1889 ub1 cen_header[46];
1890 int filename_len = 0;
1891 off_t size;
1892 int i, j;
1893 time_t tdate;
1894 struct tm *s_tm;
1895 char ascii_date[31];
1896 zipentry ze;
1898 #ifdef DEBUG
1899 printf("Listing jar file, looking for %d files\n", file_num);
1900 #endif
1902 /* This should be the start of the central-header-end section */
1903 if(seekable){
1904 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1905 perror("lseek");
1906 exit(1);
1909 if(read(fd, &tmp, sizeof(ub4)) != 4){
1910 perror("read");
1911 exit(1);
1914 #ifdef WORDS_BIGENDIAN
1915 tmp = L2BI(tmp);
1916 #endif
1918 if(tmp != 0x06054b50){
1919 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1920 exit(1);
1923 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1924 perror("lseek");
1925 exit(1);
1928 if(read(fd, &cen_size, 2) != 2){
1929 perror("read");
1930 exit(1);
1933 #ifdef WORDS_BIGENDIAN
1934 cen_size = L2BS(cen_size);
1935 #endif
1937 /* printf("%hu entries in central header\n", cen_size); */
1939 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1940 perror("lseek");
1941 exit(1);
1944 if(read(fd, &tmp, 4) != 4){
1945 perror("read");
1946 exit(1);
1949 #ifdef WORDS_BIGENDIAN
1950 tmp = L2BI(tmp);
1951 #endif
1953 /* printf("Central header offset = %d\n", tmp); */
1955 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1956 perror("lseek");
1957 exit(1);
1960 /* Loop through the entries in the central header */
1961 for(i = 0; i < cen_size; i++){
1963 if(read(fd, &cen_header, 46) != 46){
1964 perror("read");
1965 exit(1);
1968 signature = UNPACK_UB4(cen_header, 0);
1969 if(signature != 0x02014b50){
1970 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1971 exit(1);
1974 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1975 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1976 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1977 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1979 /* If we're providing verbose output, we need to make an ASCII
1980 * formatted version of the date. */
1981 if(verbose){
1982 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1983 tdate = dos2unixtime(mdate);
1984 s_tm = localtime(&tdate);
1985 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1986 ascii_date[30] = '\0';
1989 if(filename_len < fnlen + 1){
1990 if(filename != NULL)
1991 free(filename);
1993 filename = malloc(sizeof(ub1) * (fnlen + 1));
1994 filename_len = fnlen + 1;
1997 if(read(fd, filename, fnlen) != fnlen){
1998 perror("read");
1999 exit(1);
2001 filename[fnlen] = '\0';
2003 /* if the user specified a list of files on the command line,
2004 we'll only display those, otherwise we'll display everything */
2005 if(file_num > 0){
2006 for(j = 0; j < file_num; j++)
2007 if(strcmp(files[j], (const char *)filename) == 0){
2008 if(verbose)
2009 printf("%6d %s %s\n", usize, ascii_date, filename);
2010 else
2011 printf("%s\n", filename);
2012 break;
2014 } else {
2015 if(verbose)
2016 printf("%6d %s %s\n", usize, ascii_date, filename);
2017 else
2018 printf("%s\n", filename);
2021 size = eflen + clen;
2022 if(size > 0){
2023 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
2024 perror("lseek");
2025 exit(1);
2029 } else {
2030 /* the file isn't seekable.. evil! */
2031 pb_file pbf;
2033 pb_init(&pbf, fd);
2035 init_inflation();
2037 for(;;){
2038 if(pb_read(&pbf, scratch, 4) != 4){
2039 perror("read");
2040 break;
2043 signature = UNPACK_UB4(scratch, 0);
2045 #ifdef DEBUG
2046 printf("signature is %x\n", signature);
2047 #endif
2049 if(signature == 0x08074b50){
2050 #ifdef DEBUG
2051 printf("skipping data descriptor\n");
2052 #endif
2053 pb_read(&pbf, scratch, 12);
2054 continue;
2055 } else if(signature == 0x02014b50){
2056 #ifdef DEBUG
2057 printf("Central header reached.. we're all done!\n");
2058 #endif
2059 break;
2060 }else if(signature != 0x04034b50){
2061 #ifdef DEBUG
2062 printf("Ick! %#x\n", signature);
2063 #endif
2064 break;
2067 if(pb_read(&pbf, (file_header + 4), 26) != 26){
2068 perror("read");
2069 break;
2072 csize = UNPACK_UB4(file_header, LOC_CSIZE);
2073 #ifdef DEBUG
2074 printf("Compressed size is %u\n", csize);
2075 #endif
2077 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
2078 #ifdef DEBUG
2079 printf("Filename length is %hu\n", fnlen);
2080 #endif
2082 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
2083 #ifdef DEBUG
2084 printf("Extra field length is %hu\n", eflen);
2085 #endif
2087 method = UNPACK_UB2(file_header, LOC_COMP);
2088 #ifdef DEBUG
2089 printf("Compression method is %#hx\n", method);
2090 #endif
2092 flags = UNPACK_UB2(file_header, LOC_EXTRA);
2093 #ifdef DEBUG
2094 printf("Flags are %#hx\n", flags);
2095 #endif
2097 usize = UNPACK_UB4(file_header, LOC_USIZE);
2099 /* If we're providing verbose output, we need to make an ASCII
2100 * formatted version of the date. */
2101 if(verbose){
2102 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
2103 tdate = dos2unixtime(mdate);
2104 s_tm = localtime(&tdate);
2105 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
2108 if(filename_len < fnlen + 1){
2109 if(filename != NULL)
2110 free(filename);
2112 filename = malloc(sizeof(ub1) * (fnlen + 1));
2113 ascii_date[30] = '\0';
2114 filename_len = fnlen + 1;
2117 pb_read(&pbf, filename, fnlen);
2118 filename[fnlen] = '\0';
2120 /* the header is at the end. In a JAR file, this means that the data
2121 happens to be compressed. We have no choice but to inflate the
2122 data */
2123 if(flags & 0x0008){
2125 size = eflen;
2127 if(size > 0)
2128 consume(&pbf, size);
2130 if(method == 8){
2131 #ifdef DEBUG
2132 printf("inflating %s\n", filename);
2133 #endif
2134 inflate_file(&pbf, -1, &ze);
2136 usize = ze.usize;
2137 } else
2138 printf("We're shit outta luck!\n");
2140 } else {
2141 size = csize + (eflen > 0 ? eflen : 0);
2144 #ifdef DEBUG
2145 printf("Skipping %ld bytes\n", (long)size);
2146 #endif
2148 consume(&pbf, size);
2150 /* print out the listing */
2151 if(file_num > 0){
2152 for(j = 0; j < file_num; j++)
2153 if(strcmp(files[j], (const char *)filename) == 0){
2154 if(verbose)
2155 printf("%6d %s %s\n", usize, ascii_date, filename);
2156 else
2157 printf("%s\n", filename);
2158 break;
2160 } else {
2161 if(verbose)
2162 printf("%6d %s %s\n", usize, ascii_date, filename);
2163 else
2164 printf("%s\n", filename);
2168 return 0;
2171 int consume(pb_file *pbf, int amt){
2172 int tc = 0; /* total amount consumed */
2173 ub1 buff[RDSZ];
2174 int rdamt;
2176 #ifdef DEBUG
2177 printf("Consuming %d bytes\n", amt);
2178 #endif
2180 if (seekable){
2181 if (amt <= (int)pbf->buff_amt)
2182 pb_read(pbf, buff, amt);
2183 else {
2184 lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
2185 pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
2187 } else
2188 while(tc < amt){
2189 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
2190 #ifdef DEBUG
2191 printf("got %d bytes\n", rdamt);
2192 #endif
2193 tc += rdamt;
2196 #ifdef DEBUG
2197 printf("%d bytes consumed\n", amt);
2198 #endif
2200 return 0;
2203 void usage(const char *filename){
2204 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
2205 exit (1);
2208 void version ()
2210 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
2211 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
2212 printf("Copyright 2002, 2004 Free Software Foundation\n");
2213 printf("\
2214 This is free software; see the source for copying conditions. There is NO\n\
2215 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2216 exit (0);
2219 void help(const char *filename)
2221 printf("\
2222 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2224 Store many files together in a single `jar' file.\n\
2226 -c create new archive\n\
2227 -t list table of contents for archive\n\
2228 -x extract named (or all) files from archive\n\
2229 -u update existing archive\n\
2230 ", filename);
2231 printf("\n\
2232 -@ read names from stdin\n\
2233 -0 store only; use no ZIP compression\n\
2234 -C DIR FILE change to the specified directory and include\n\
2235 the following file\n\
2236 -E don't include the files found in a directory\n\
2237 -f FILE specify archive file name\n\
2238 --help print this help, then exit\n\
2239 -m FILE include manifest information from specified manifest file\n\
2240 -M Do not create a manifest file for the entries\n\
2241 -v generate verbose output on standard output\n\
2242 -V, --version display version information\n\
2244 printf("\n\
2245 If any file is a directory then it is processed recursively.\n\
2246 The manifest file name and the archive file name needs to be specified\n\
2247 in the same order the 'm' and 'f' flags are specified.\n\
2249 Example 1: to archive two class files into an archive called classes.jar: \n\
2250 jar cvf classes.jar Foo.class Bar.class \n\
2251 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2252 files in the foo/ directory into 'classes.jar': \n\
2253 jar cvfm classes.jar mymanifest -C foo/ .\n\
2256 exit(0);
2259 static char *
2260 jt_strdup(s)
2261 char *s;
2263 char *result = (char*)malloc(strlen(s) + 1);
2264 if (result == (char*)0)
2265 return (char*)0;
2266 strcpy(result, s);
2267 return result;
2270 /* Convert "tar-style" first argument to a form expected by getopt.
2271 This idea and the code comes from GNU tar. This can allocate a new
2272 argument vector. This might leak some memory, but we don't care. */
2273 static void
2274 expand_options (int *argcp, char ***argvp)
2276 int argc = *argcp;
2277 char **argv = *argvp;
2279 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
2280 if a long argument (like "--help") is detected. */
2281 if (argc > 1 && argv[1][1] != '-')
2283 char buf[3];
2284 char **new_argv;
2285 int new_argc;
2286 int args_to_expand;
2287 char *p;
2288 char **in, **out;
2290 buf[0] = '-';
2291 buf[2] = '\0';
2293 args_to_expand = strlen (argv[1]);
2294 if (argv[1][0] == '-')
2295 --args_to_expand;
2297 new_argc = argc - 1 + args_to_expand;
2298 new_argv = (char **) malloc (new_argc * sizeof (char *));
2299 in = argv;
2300 out = new_argv;
2302 *out++ = *in++;
2303 p = *in++;
2304 if (*p == '-')
2305 p++;
2306 while (*p != '\0')
2308 char *opt;
2309 buf[1] = *p;
2310 *out++ = jt_strdup (buf);
2311 /* If the option takes an argument, move the next argument
2312 to just after this option. */
2313 opt = strchr (OPTION_STRING, *p);
2314 if (opt && opt[1] == ':')
2316 if (in < argv + argc)
2317 *out++ = *in++;
2318 else
2320 fprintf(stderr, "%s: option `%s' requires an argument.\n",
2321 argv[0], buf);
2322 usage(argv[0]);
2325 ++p;
2328 /* Copy remaining options. */
2329 while (in < argv + argc)
2330 *out++ = *in++;
2332 *argcp = new_argc;
2333 *argvp = new_argv;