* java/lang/natClass.cc (_Jv_LayoutVTableMethods): Cast pointers
[official-gcc.git] / fastjar / jartool.c
blob9431bb5e0a243607ab0881416c6d55a384843cfc
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 #if ! (HAVE_FTRUNCATE || HAVE__CHSIZE)
577 #error neither ftruncate() or _chsize() available
578 #endif
579 /* Check if the file shrunk when we updated it. */
580 if (action == ACTION_UPDATE)
581 #if HAVE_FTRUNCATE
582 ftruncate (jarfd, lseek (jarfd, 0, SEEK_CUR));
583 #else
584 _chsize (jarfd, lseek (jarfd, 0, SEEK_CUR));
585 #endif
587 if (jarfd != STDIN_FILENO && close(jarfd) != 0) {
588 fprintf(stderr, "%s: error closing jar archive: %s\n",
589 progname, strerror (errno));
590 exit (1);
592 } else if(action == ACTION_LIST){
593 list_jar(jarfd, &new_argv[0], new_argc);
594 } else if(action == ACTION_EXTRACT){
595 extract_jar(jarfd, &new_argv[0], new_argc);
598 exit(0);
601 static int args_current_g;
602 static char **args_g;
604 static void
605 init_args(args, current)
606 char **args;
607 int current;
609 if(!read_names_from_stdin)
611 args_g = args;
612 args_current_g = current;
616 static char *
617 get_next_arg ()
619 static int reached_end = 0;
621 if (reached_end)
622 return NULL;
624 if (args_g)
626 if (!args_g [args_current_g])
628 reached_end = 1;
629 return NULL;
631 return args_g [args_current_g++];
633 else
635 /* Read the name from stdin. Delimiters are '\n' and
636 '\r'. Reading EOF indicates that we don't have anymore file
637 names characters to read. */
639 char s [MAXPATHLEN];
640 int pos = 0;
642 /* Get rid of '\n' and '\r' first. */
643 while (1)
645 int c = getc (stdin);
646 if (c == '\n' || c == '\r')
647 continue;
648 else
650 if (c == EOF)
651 return NULL;
652 ungetc (c, stdin);
653 break;
657 while (1)
659 int c = getc (stdin);
660 /* Exit when we get a delimiter or don't have any characters
661 to read */
662 if (c == '\n'|| c == '\r'|| c == EOF)
663 break;
664 s [pos++] = (char) c;
667 if (pos)
669 s [pos] = '\0';
670 return jt_strdup (s);
672 else
673 return NULL;
677 void init_headers(){
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){
730 if(ziplist == NULL){
731 ziplist = ze;
732 ziptail = ziplist;
733 } else {
734 ziplist->next_entry = ze;
735 ziplist = ze;
738 number_of_entries++;
741 static struct zipentry *
742 find_entry (const char *fname)
744 struct zipentry *ze;
746 for (ze = ziptail; ze; ze = ze->next_entry)
748 if (!strcmp (ze->filename, fname))
749 return ze;
751 return NULL;
755 static int
756 looks_like_dir (const char *fname)
758 struct zipentry *ze;
759 size_t len = strlen (fname);
761 for (ze = ziptail; ze; ze = ze->next_entry)
763 if (strlen (ze->filename) > len
764 && !strncmp (fname, ze->filename, len)
765 && ze->filename[len] == '/')
766 return 1;
768 return 0;
773 * Read the zip entries of an existing file, building `ziplist' as we go.
775 int read_entries (int fd)
777 struct zipentry *ze;
778 ub1 intbuf[4];
779 ub1 header[46];
780 ub2 len;
781 ub2 count, i;
782 off_t offset;
784 if (lseek (fd, -22, SEEK_END) == -1)
786 fprintf (stderr, "%s: %s: can't seek file\n", progname, jarfile);
787 return 1;
790 if (read (fd, intbuf, 4) < 4)
792 perror (progname);
793 return 1;
795 /* Is there a zipfile comment? */
796 while (UNPACK_UB4(intbuf, 0) != 0x06054b50)
798 if (lseek (fd, -5, SEEK_CUR) == -1 ||
799 read (fd, intbuf, 4) != 4)
801 fprintf (stderr, "%s: can't find end of central directory: %s\n",
802 progname, strerror (errno));
803 return 1;
807 /* Skip disk numbers. */
808 if (lseek (fd, 6, SEEK_CUR) == -1)
810 perror (progname);
811 return 1;
814 /* Number of entries in the central directory. */
815 if (read (fd, intbuf, 2) != 2)
817 perror (progname);
818 return 1;
820 count = UNPACK_UB2(intbuf, 0);
822 if (lseek (fd, 4, SEEK_CUR) == -1)
824 perror (progname);
825 return 1;
828 /* Offset where the central directory begins. */
829 if (read (fd, intbuf, 4) != 4)
831 perror (progname);
832 return 1;
834 offset = UNPACK_UB4(intbuf, 0);
835 end_of_entries = offset;
837 if (lseek (fd, offset, SEEK_SET) != offset)
839 perror (progname);
840 return 1;
843 if (read (fd, header, 46) != 46)
845 fprintf (stderr, "%s: %s: unexpected end of file\n",
846 progname, jarfile);
847 return 1;
850 for (i = 0; i < count; i++)
852 if (UNPACK_UB4(header, 0) != 0x02014b50)
854 fprintf (stderr, "%s: can't find central directory header\n",
855 progname);
856 return 1;
858 ze = (struct zipentry *) malloc (sizeof (struct zipentry));
859 if (!ze)
861 perror (progname);
862 return 1;
864 memset (ze, 0, sizeof (struct zipentry));
865 ze->flags = UNPACK_UB2(header, CEN_FLAGS);
866 ze->mod_time = UNPACK_UB2(header, CEN_MODTIME);
867 ze->mod_date = UNPACK_UB2(header, CEN_MODDATE);
868 ze->crc = UNPACK_UB4(header, CEN_CRC);
869 ze->usize = UNPACK_UB4(header, CEN_USIZE);
870 ze->csize = UNPACK_UB4(header, CEN_CSIZE);
871 ze->offset = UNPACK_UB4(header, CEN_OFFSET);
872 ze->compressed = (header[CEN_COMP] || header[CEN_COMP+1]);
873 len = UNPACK_UB2(header, CEN_FNLEN);
874 ze->filename = (char *) malloc ((len+1) * sizeof (char));
875 if (!ze->filename)
877 perror (progname);
878 return 1;
880 if (read (fd, ze->filename, len) != len)
882 fprintf (stderr, "%s: %s: unexpected end of file\n",
883 progname, jarfile);
884 return 1;
886 len = UNPACK_UB4(header, CEN_EFLEN);
887 len += UNPACK_UB4(header, CEN_COMLEN);
888 if (lseek (fd, len, SEEK_CUR) == -1)
890 perror (progname);
891 return 1;
893 add_entry (ze);
894 if (i < count - 1)
896 if (read (fd, header, 46) != 46)
898 fprintf (stderr, "%s: %s: unexpected end of file\n",
899 progname, jarfile);
900 return 1;
905 lseek (fd, 0, SEEK_SET);
906 return 0;
909 int make_manifest(int jfd, const char *mf_name, int updating){
910 time_t current_time;
911 int nlen; /* length of file name */
912 int mod_time; /* file modification time */
913 struct zipentry *ze;
915 nlen = 9; /* trust me on this one */
917 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
919 current_time = time(NULL);
920 if(current_time == (time_t)-1){
921 perror("time");
922 exit(1);
925 mod_time = unix2dostime(&current_time);
927 PACK_UB2(file_header, LOC_EXTRA, 0);
928 PACK_UB2(file_header, LOC_COMP, 0);
929 PACK_UB2(file_header, LOC_FNLEN, nlen);
930 PACK_UB4(file_header, LOC_MODTIME, mod_time);
932 if(verbose)
933 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
935 ze = (zipentry*)malloc(sizeof(zipentry));
936 if(ze == NULL){
937 perror("malloc");
938 exit(1);
941 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
942 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
943 strcpy(ze->filename, "META-INF/");
944 ze->filename[nlen] = '\0';
946 ze->offset = lseek(jfd, 0, SEEK_CUR);
947 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
948 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
949 ze->compressed = FALSE;
951 add_entry(ze);
953 write(jfd, file_header, 30);
954 write(jfd, "META-INF/", nlen);
956 /* if the user didn't specify an external manifest file... */
957 if(mf_name == NULL){
959 int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
960 char *mf;
962 if((mf = (char *) malloc(mf_len + 1))) {
963 uLong crc;
965 sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
967 crc = crc32(0L, Z_NULL, 0);
969 crc = crc32(crc, (const unsigned char *)mf, mf_len);
971 nlen = 20; /* once again, trust me */
973 PACK_UB2(file_header, LOC_EXTRA, 0);
974 PACK_UB2(file_header, LOC_COMP, 0);
975 PACK_UB2(file_header, LOC_FNLEN, nlen);
976 PACK_UB4(file_header, LOC_USIZE, mf_len);
978 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
980 PACK_UB4(file_header, LOC_CRC, crc);
982 if(verbose)
983 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
985 ze = (zipentry*)malloc(sizeof(zipentry));
986 if(ze == NULL){
987 perror("malloc");
988 exit(1);
991 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
992 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
993 strcpy(ze->filename, "META-INF/MANIFEST.MF");
994 ze->filename[nlen] = '\0';
996 ze->offset = lseek(jfd, 0, SEEK_CUR);
997 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
998 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
999 ze->crc = crc;
1000 ze->csize = mf_len;
1001 ze->usize = ze->csize;
1002 ze->compressed = FALSE;
1004 add_entry(ze);
1006 write(jfd, file_header, 30);
1007 write(jfd, "META-INF/MANIFEST.MF", nlen);
1008 write(jfd, mf, mf_len);
1009 free(mf);
1011 else {
1012 printf("malloc errror\n");
1013 exit(-1);
1015 } else {
1016 int mfd;
1017 struct stat statbuf;
1019 stat(mf_name, &statbuf);
1021 if(!S_ISREG(statbuf.st_mode)){
1022 fprintf(stderr, "Invalid manifest file specified.\n");
1023 exit(1);
1026 mfd = open(mf_name, O_RDONLY | O_BINARY);
1028 if(mfd < 0){
1029 fprintf(stderr, "Error opening %s.\n", mf_name);
1030 exit(1);
1033 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
1034 perror("error writing to jar");
1035 exit(1);
1040 return 0;
1043 /* Implements -C by wrapping add_to_jar. new_dir is the directory
1044 to switch to.
1046 `updating', if nonzero, will indicate that we are updating an
1047 existing file, and will need to take special care. If set, we will
1048 also expect that the linked list of zip entries will be filled in
1049 with the jar file's current contents.
1051 int
1052 add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
1053 const int updating)
1055 int retval;
1056 char old_dir[MAXPATHLEN];
1057 if (getcwd(old_dir, MAXPATHLEN) == NULL) {
1058 perror("getcwd");
1059 return 1;
1061 if (chdir(new_dir) == -1) {
1062 perror(new_dir);
1063 return 1;
1065 retval=add_to_jar(fd, file, updating);
1066 if (chdir(old_dir) == -1) {
1067 perror(old_dir);
1068 return 1;
1070 return retval;
1073 int
1074 add_to_jar (int fd, const char *file, const int updating)
1076 struct stat statbuf;
1077 DIR *dir;
1078 struct dirent *de;
1079 zipentry *ze;
1080 zipentry *existing = NULL;
1081 int stat_return;
1083 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
1084 * It fixes this:
1085 * "normal" jar : org/apache/java/io/LogRecord.class
1086 * fastjar : ./org/apache/java/io/LogRecord.class
1087 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
1088 * with both kaffe-1.0b4 and JDK.
1090 while (*file=='.' && *(file+1)=='/')
1091 file+=2;
1093 if(jarfile && !strcmp(file, jarfile)){
1094 if(verbose)
1095 printf("skipping: %s\n", file);
1096 return 0; /* we don't want to add ourselves.. */
1099 stat_return = stat(file, &statbuf);
1101 if(stat_return == -1){
1102 perror(file);
1103 return 1;
1104 } else if(S_ISDIR(statbuf.st_mode)){
1105 char *fullname;
1106 char *t_ptr;
1107 int nlen;
1108 unsigned long mod_time;
1110 dir = opendir(file);
1112 if(dir == NULL){
1113 perror("opendir");
1114 return 1;
1117 nlen = strlen(file) + 256;
1118 fullname = (char*)malloc(nlen * sizeof(char));
1119 memset(fullname, 0, (nlen * sizeof(char)));
1121 if(fullname == NULL){
1122 fprintf(stderr, "Filename is NULL!\n");
1123 return 1;
1126 strcpy(fullname, file);
1127 nlen = strlen(file);
1129 if(fullname[nlen - 1] != '/'){
1130 fullname[nlen] = '/';
1131 t_ptr = (fullname + nlen + 1);
1132 } else
1133 t_ptr = (fullname + nlen);
1136 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
1138 nlen = (t_ptr - fullname);
1140 mod_time = unix2dostime(&statbuf.st_mtime);
1142 PACK_UB2(file_header, LOC_EXTRA, 0);
1143 PACK_UB2(file_header, LOC_COMP, 0);
1144 PACK_UB2(file_header, LOC_FNLEN, nlen);
1145 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1147 ze = (zipentry*)malloc(sizeof(zipentry));
1148 if(ze == NULL){
1149 perror("malloc");
1150 exit(1);
1153 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1154 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
1155 strcpy(ze->filename, fullname);
1156 ze->filename[nlen] = '\0';
1158 ze->offset = lseek(fd, 0, SEEK_CUR);
1159 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1160 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1161 ze->compressed = FALSE;
1163 if (updating)
1165 if ((existing = find_entry (ze->filename)) != NULL)
1167 if (existing->usize != 0)
1169 /* XXX overwriting non-directory with directory? */
1170 fprintf (stderr, "%s: %s: can't overwrite non-directory with directory\n",
1171 progname, fullname);
1172 return 1;
1175 if (lseek (fd, end_of_entries, SEEK_SET) == -1)
1177 fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
1178 perror ("lseek");
1179 return 1;
1183 if (!existing)
1185 add_entry (ze);
1186 write (fd, file_header, 30);
1187 write (fd, fullname, nlen);
1188 end_of_entries = lseek (fd, 0, SEEK_CUR);
1190 if (verbose)
1191 printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
1194 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
1195 if(de->d_name[0] == '.')
1196 continue;
1197 if(jarfile && !strcmp(de->d_name, jarfile)){
1198 /* we don't want to add ourselves. Believe me */
1199 if(verbose)
1200 printf("skipping: %s\n", de->d_name);
1201 continue;
1204 strcpy(t_ptr, de->d_name);
1206 if (add_to_jar(fd, fullname, updating)) {
1207 fprintf(stderr, "Error adding file to jar!\n");
1208 return 1;
1212 free(fullname);
1213 closedir(dir);
1215 } else if(S_ISREG(statbuf.st_mode)){
1216 int add_fd;
1218 add_fd = open(file, O_RDONLY | O_BINARY);
1219 if(add_fd < 0){
1220 fprintf(stderr, "Error opening %s.\n", file);
1221 return 1;
1224 if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
1225 fprintf(stderr, "Error adding file to jar!\n");
1226 return 1;
1229 } else {
1230 fprintf(stderr, "Illegal file specified: %s\n", file);
1232 return 0;
1235 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
1236 const int updating)
1238 unsigned short file_name_length;
1239 unsigned long mod_time;
1240 ub1 rd_buff[RDSZ];
1241 uLong crc = 0;
1242 off_t offset = 0;
1243 int rdamt;
1244 struct zipentry *ze;
1245 struct zipentry *existing = NULL;
1247 if (updating)
1249 existing = find_entry (fname);
1250 if (existing && looks_like_dir (fname))
1252 fprintf (stderr, "%s: %s is a directory in the archive\n",
1253 progname, fname);
1254 return 1;
1258 mod_time = unix2dostime(&(statbuf->st_mtime));
1259 file_name_length = strlen(fname);
1261 if(!seekable && !do_compress){
1262 crc = crc32(0L, Z_NULL, 0);
1264 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
1265 crc = crc32(crc, rd_buff, rdamt);
1267 lseek(ffd, 0, SEEK_SET);
1270 /* data descriptor */
1271 if(!seekable && do_compress){
1272 PACK_UB2(file_header, LOC_EXTRA, 8);
1273 } else {
1274 PACK_UB2(file_header, LOC_EXTRA, 0);
1277 if(do_compress){
1278 PACK_UB2(file_header, LOC_COMP, 8);
1279 } else {
1280 PACK_UB2(file_header, LOC_COMP, 0);
1283 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1284 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1286 if(!seekable && !do_compress){
1287 PACK_UB4(file_header, LOC_CRC, crc);
1288 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1289 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1290 } else
1291 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1293 ze = (zipentry*)malloc(sizeof(zipentry));
1294 if(ze == NULL){
1295 perror("malloc");
1296 exit(1);
1299 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1300 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1301 strcpy(ze->filename, fname);
1303 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1304 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1306 if(!seekable && !do_compress)
1307 ze->crc = crc;
1309 ze->csize = statbuf->st_size;
1310 ze->usize = ze->csize;
1312 if (existing)
1313 ze->offset = existing->offset;
1314 else if (updating)
1315 ze->offset = end_of_entries;
1316 else
1317 ze->offset = lseek(jfd, 0, SEEK_CUR);
1319 if(do_compress)
1320 ze->compressed = TRUE;
1321 else
1322 ze->compressed = FALSE;
1324 if (!existing)
1325 add_entry(ze);
1326 if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
1328 perror ("lseek");
1329 return 1;
1332 /* We can safely write the header here, since it will be the same size
1333 as before */
1335 /* Write the local header */
1336 write(jfd, file_header, 30);
1338 /* write the file name to the zip file */
1339 write(jfd, fname, file_name_length);
1342 if(verbose){
1343 if (existing)
1344 printf ("updating: %s ", fname);
1345 else
1346 printf("adding: %s ", fname);
1347 fflush(stdout);
1350 if(do_compress){
1351 /* compress the file */
1352 compress_file(ffd, jfd, ze, existing);
1353 } else {
1354 /* If we are not writing the last entry, make space for it. */
1355 if (existing && existing->next_entry)
1357 if (ze->usize > existing->usize)
1359 if (shift_down (jfd, existing->next_entry->offset,
1360 ze->usize - existing->usize, existing->next_entry))
1362 fprintf (stderr, "%s: %s\n", progname, strerror (errno));
1363 return 1;
1368 /* Write the contents of the file (uncompressed) to the zip file */
1369 /* calculate the CRC as we go along */
1370 ze->crc = crc32(0L, Z_NULL, 0);
1372 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1373 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1374 if(write(jfd, rd_buff, rdamt) != rdamt){
1375 perror("write");
1376 return 0;
1380 close(ffd);
1382 /* write out data descriptor */
1383 PACK_UB4(data_descriptor, 4, ze->crc);
1384 PACK_UB4(data_descriptor, 8, ze->csize);
1385 PACK_UB4(data_descriptor, 12, ze->usize);
1387 /* we need to seek back and fill the header */
1388 if(seekable){
1389 offset = (ze->csize + strlen(ze->filename) + 16);
1391 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1392 perror("lseek");
1393 exit(1);
1396 if(write(jfd, (data_descriptor + 4), 12) != 12){
1397 perror("write");
1398 return 0;
1401 offset -= 12;
1403 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1404 perror("lseek");
1405 exit(1);
1407 } else if(do_compress){
1408 /* Sun's jar tool will only allow a data descriptor if the entry is
1409 compressed, but we'll save 16 bytes/entry if we only use it when
1410 we can't seek back on the file */
1411 /* Technically, you CAN'T have a data descriptor unless the data
1412 part has an obvious end, which DEFLATED does. Otherwise, there
1413 would not be any way to determine where the data descriptor is.
1414 Store an uncompressed file that ends with 0x504b0708, and see.
1415 -- csm */
1417 if(write(jfd, data_descriptor, 16) != 16){
1418 perror("write");
1419 return 0;
1423 if (existing)
1425 int dd = (existing->flags & (1 << 3)) ? 12 : 0;
1426 if (existing->next_entry && ze->csize < existing->csize + dd)
1428 if (shift_up (jfd, existing->next_entry->offset,
1429 existing->csize + dd - ze->csize,
1430 existing->next_entry))
1432 perror (progname);
1433 return 1;
1436 /* Replace the existing entry data with this entry's. */
1437 existing->csize = ze->csize;
1438 existing->usize = ze->usize;
1439 existing->crc = ze->crc;
1440 existing->mod_time = ze->mod_time;
1441 existing->mod_date = ze->mod_date;
1442 free (ze->filename);
1443 free (ze);
1445 else if (updating)
1446 end_of_entries = lseek (jfd, 0, SEEK_CUR);
1448 if(verbose)
1449 printf("(in=%d) (out=%d) (%s %d%%)\n",
1450 (int)ze->usize, (int)ze->csize,
1451 (do_compress ? "deflated" : "stored"),
1452 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1454 return 0;
1457 int create_central_header(int fd){
1458 ub1 header[46];
1459 ub1 end_header[22];
1460 int start_offset;
1461 int dir_size;
1462 int total_in = 0, total_out = 22;
1464 zipentry *ze;
1466 /* magic number */
1467 header[0] = 'P';
1468 header[1] = 'K';
1469 header[2] = 1;
1470 header[3] = 2;
1471 /* version made by */
1472 header[4] = 10;
1473 header[5] = 0;
1474 /* version needed to extract */
1475 header[6] = 10;
1476 header[7] = 0;
1477 /* bit flag */
1478 header[8] = 0;
1479 header[9] = 0;
1480 /* compression method */
1481 header[10] = 0;
1482 header[11] = 0;
1483 /* file mod time */
1484 header[12] = 0;
1485 header[13] = 0;
1486 /* file mod date */
1487 header[14] = 0;
1488 header[15] = 0;
1489 /* crc 32 */
1490 header[16] = 0;
1491 header[17] = 0;
1492 header[18] = 0;
1493 header[19] = 0;
1494 /* compressed size */
1495 header[20] = 0;
1496 header[21] = 0;
1497 header[22] = 0;
1498 header[23] = 0;
1499 /* uncompressed size */
1500 header[24] = 0;
1501 header[25] = 0;
1502 header[26] = 0;
1503 header[27] = 0;
1504 /* filename length */
1505 header[28] = 0;
1506 header[29] = 0;
1507 /* extra field length */
1508 header[30] = 0;
1509 header[31] = 0;
1510 /* file comment length */
1511 header[32] = 0;
1512 header[33] = 0;
1513 /* disk number start */
1514 header[34] = 0;
1515 header[35] = 0;
1516 /* internal file attribs */
1517 header[36] = 0;
1518 header[37] = 0;
1519 /* external file attribs */
1520 header[38] = 0;
1521 header[39] = 0;
1522 header[40] = 0;
1523 header[41] = 0;
1524 /* relative offset of local header */
1525 header[42] = 0;
1526 header[43] = 0;
1527 header[44] = 0;
1528 header[45] = 0;
1530 start_offset = lseek(fd, 0, SEEK_CUR);
1532 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1534 total_in += ze->usize;
1535 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1537 if(ze->compressed){
1538 PACK_UB2(header, CEN_COMP, 8);
1539 } else {
1540 PACK_UB2(header, CEN_COMP, 0);
1543 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1544 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1545 PACK_UB4(header, CEN_CRC, ze->crc);
1546 PACK_UB4(header, CEN_CSIZE, ze->csize);
1547 PACK_UB4(header, CEN_USIZE, ze->usize);
1548 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1549 PACK_UB4(header, CEN_OFFSET, ze->offset);
1551 write(fd, header, 46);
1553 write(fd, ze->filename, strlen(ze->filename));
1556 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1558 /* magic number */
1559 end_header[0] = 0x50;
1560 end_header[1] = 0x4b;
1561 end_header[2] = 0x05;
1562 end_header[3] = 0x06;
1563 /* number of this disk */
1564 end_header[4] = 0;
1565 end_header[5] = 0;
1566 /* number of disk w/ start of central header */
1567 end_header[6] = 0;
1568 end_header[7] = 0;
1569 /* total number of entries in central dir on this disk*/
1570 PACK_UB2(end_header, 8, number_of_entries);
1571 /* total number of entries in central dir*/
1572 PACK_UB2(end_header, 10, number_of_entries);
1573 /* size of central dir. */
1574 PACK_UB4(end_header, 12, dir_size);
1575 /* offset of start of central dir */
1576 PACK_UB4(end_header, 16, start_offset);
1577 /* zipfile comment length */
1578 end_header[20] = 0;
1579 end_header[21] = 0;
1581 write(fd, end_header, 22);
1583 if(verbose)
1584 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1585 total_in,
1586 total_out,
1587 (do_compress ? "deflated" : "stored"),
1588 (int)((1 - (total_out / (float)total_in)) * 100)
1591 return 0;
1594 int extract_jar(int fd, char **files, int file_num){
1595 int rdamt;
1596 int out_a, in_a;
1597 ub4 signature;
1598 ub4 csize;
1599 ub4 crc;
1600 ub2 fnlen;
1601 ub2 eflen;
1602 ub2 flags;
1603 ub2 method;
1604 ub1 *filename = NULL;
1605 int filename_len = 0;
1606 ub4 rd_buff[RDSZ];
1607 pb_file pbf;
1608 ub1 scratch[16];
1609 zipentry ze;
1610 int f_fd;
1611 int dir;
1612 int handle;
1613 int j;
1615 init_inflation();
1617 pb_init(&pbf, fd);
1619 for(;;){
1620 f_fd = 0;
1621 crc = 0;
1622 ze.crc = 0;
1624 dir = FALSE; /* by default, the file isn't a dir */
1625 handle = TRUE; /* by default we'll extract/create the file */
1627 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1628 perror("read");
1629 break;
1632 signature = UNPACK_UB4(scratch, 0);
1634 #ifdef DEBUG
1635 printf("signature is %x\n", signature);
1636 #endif
1637 if(signature == 0x08074b50){
1638 #ifdef DEBUG
1639 printf("skipping data descriptor\n");
1640 #endif
1641 pb_read(&pbf, scratch, 12);
1642 continue;
1643 } else if(signature == 0x02014b50){
1644 #ifdef DEBUG
1645 printf("Central header reached.. we're all done!\n");
1646 #endif
1647 break;
1648 }else if(signature != 0x04034b50){
1649 printf("Ick! %#x\n", signature);
1650 break;
1653 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1654 perror("read");
1655 break;
1658 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1659 #ifdef DEBUG
1660 printf("Compressed size is %u\n", csize);
1661 #endif
1663 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1664 #ifdef DEBUG
1665 printf("Filename length is %hu\n", fnlen);
1666 #endif
1668 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1669 #ifdef DEBUG
1670 printf("Extra field length is %hu\n", eflen);
1671 #endif
1673 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1674 #ifdef DEBUG
1675 printf("Flags are %#hx\n", flags);
1676 #endif
1678 method = UNPACK_UB2(file_header, LOC_COMP);
1679 #ifdef DEBUG
1680 printf("Compression method is %#hx\n", method);
1681 #endif
1683 /* if there isn't a data descriptor */
1684 if(!(flags & 0x0008)){
1685 crc = UNPACK_UB4(file_header, LOC_CRC);
1686 #ifdef DEBUG
1687 printf("CRC is %x\n", crc);
1688 #endif
1691 if(filename_len < fnlen + 1){
1692 if(filename != NULL)
1693 free(filename);
1695 filename = malloc(sizeof(ub1) * (fnlen + 1));
1696 filename_len = fnlen + 1;
1699 pb_read(&pbf, filename, fnlen);
1700 filename[fnlen] = '\0';
1702 #ifdef DEBUG
1703 printf("filename is %s\n", filename);
1704 #endif
1706 if(file_num > 0){
1707 handle = FALSE;
1709 for(j = 0; j < file_num; j++)
1710 if(strcmp(files[j], (const char *)filename) == 0){
1711 handle = TRUE;
1712 break;
1716 if(!handle)
1717 f_fd = -1;
1719 /* OK, there is some directory information in the file. Nothing to do
1720 but ensure the directory(s) exist, and create them if they don't.
1721 What a pain! */
1722 if(strchr((const char *)filename, '/') != NULL && handle){
1723 /* Loop through all the directories in the path, (everything w/ a '/') */
1724 const ub1 *start = filename;
1725 char *tmp_buff;
1726 struct stat sbuf;
1728 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1730 for(;;){
1731 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1733 if(idx == NULL)
1734 break;
1735 else if(idx == start){
1736 start++;
1737 continue;
1739 start = idx + 1;
1741 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1742 tmp_buff[(idx - filename)] = '\0';
1744 #ifdef DEBUG
1745 printf("checking the existance of %s\n", tmp_buff);
1746 #endif
1748 if(stat(tmp_buff, &sbuf) < 0){
1749 if(errno != ENOENT){
1750 perror("stat");
1751 exit(1);
1754 } else if(S_ISDIR(sbuf.st_mode)){
1755 #ifdef DEBUG
1756 printf("Directory exists\n");
1757 #endif
1758 continue;
1759 }else {
1760 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1761 tmp_buff);
1762 exit(1);
1765 #ifdef DEBUG
1766 printf("Making directory..\n");
1767 #endif
1768 if(mkdir(tmp_buff, 0755) < 0){
1769 perror("mkdir");
1770 exit(1);
1772 if(verbose && handle)
1773 printf("%10s: %s/\n", "created", tmp_buff);
1777 /* only a directory */
1778 if(strlen((const char *)start) == 0)
1779 dir = TRUE;
1781 #ifdef DEBUG
1782 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1783 #endif
1785 /* If the entry was just a directory, don't write to file, etc */
1786 if(strlen((const char *)start) == 0)
1787 f_fd = -1;
1789 free(tmp_buff);
1792 if(f_fd != -1 && handle){
1793 f_fd = open((const char *)filename,
1794 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1796 if(f_fd < 0){
1797 fprintf(stderr, "Error extracting JAR archive!\n");
1798 perror((const char *)filename);
1799 exit(1);
1803 if(method != 8 && flags & 0x0008){
1804 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1805 exit(1);
1808 if (eflen > 0)
1809 consume(&pbf, eflen);
1811 if(method == 8 || flags & 0x0008){
1813 inflate_file(&pbf, f_fd, &ze);
1814 } else {
1816 #ifdef DEBUG
1817 printf("writing stored data.. (%d bytes)\n", csize);
1818 #endif
1820 out_a = 0;
1821 in_a = csize;
1823 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1825 while(out_a < (int)csize){
1826 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1827 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1828 perror("read");
1829 exit(1);
1832 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1834 if(f_fd >= 0)
1835 write(f_fd, rd_buff, rdamt);
1837 out_a += rdamt;
1838 in_a -= rdamt;
1840 #ifdef DEBUG
1841 printf("%d bytes written\n", out_a);
1842 #endif
1846 /* if there is a data descriptor left, compare the CRC */
1847 if(flags & 0x0008){
1849 if(pb_read(&pbf, scratch, 16) != 16){
1850 perror("read");
1851 exit(1);
1854 signature = UNPACK_UB4(scratch, 0);
1856 if(signature != 0x08074b50){
1857 fprintf(stderr, "Error! Missing data descriptor!\n");
1858 exit(1);
1861 crc = UNPACK_UB4(scratch, 4);
1865 if(crc != ze.crc){
1866 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1867 ze.crc, crc);
1868 exit(1);
1871 close(f_fd);
1873 if(verbose && dir == FALSE && handle)
1874 printf("%10s: %s\n",
1875 (method == 8 ? "inflated" : "extracted"),
1876 filename);
1879 return 0;
1882 int list_jar(int fd, char **files, int file_num){
1883 ub4 signature;
1884 ub4 csize;
1885 ub4 usize;
1886 ub4 mdate;
1887 ub4 tmp;
1888 ub2 fnlen;
1889 ub2 eflen;
1890 ub2 clen;
1891 ub2 flags;
1892 ub2 method;
1893 ub2 cen_size;
1894 ub1 *filename = NULL;
1895 ub1 scratch[16];
1896 ub1 cen_header[46];
1897 int filename_len = 0;
1898 off_t size;
1899 int i, j;
1900 time_t tdate;
1901 struct tm *s_tm;
1902 char ascii_date[31];
1903 zipentry ze;
1905 #ifdef DEBUG
1906 printf("Listing jar file, looking for %d files\n", file_num);
1907 #endif
1909 /* This should be the start of the central-header-end section */
1910 if(seekable){
1911 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1912 perror("lseek");
1913 exit(1);
1916 if(read(fd, &tmp, sizeof(ub4)) != 4){
1917 perror("read");
1918 exit(1);
1921 #ifdef WORDS_BIGENDIAN
1922 tmp = L2BI(tmp);
1923 #endif
1925 if(tmp != 0x06054b50){
1926 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1927 exit(1);
1930 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1931 perror("lseek");
1932 exit(1);
1935 if(read(fd, &cen_size, 2) != 2){
1936 perror("read");
1937 exit(1);
1940 #ifdef WORDS_BIGENDIAN
1941 cen_size = L2BS(cen_size);
1942 #endif
1944 /* printf("%hu entries in central header\n", cen_size); */
1946 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1947 perror("lseek");
1948 exit(1);
1951 if(read(fd, &tmp, 4) != 4){
1952 perror("read");
1953 exit(1);
1956 #ifdef WORDS_BIGENDIAN
1957 tmp = L2BI(tmp);
1958 #endif
1960 /* printf("Central header offset = %d\n", tmp); */
1962 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1963 perror("lseek");
1964 exit(1);
1967 /* Loop through the entries in the central header */
1968 for(i = 0; i < cen_size; i++){
1970 if(read(fd, &cen_header, 46) != 46){
1971 perror("read");
1972 exit(1);
1975 signature = UNPACK_UB4(cen_header, 0);
1976 if(signature != 0x02014b50){
1977 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1978 exit(1);
1981 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1982 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1983 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1984 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1986 /* If we're providing verbose output, we need to make an ASCII
1987 * formatted version of the date. */
1988 if(verbose){
1989 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1990 tdate = dos2unixtime(mdate);
1991 s_tm = localtime(&tdate);
1992 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1993 ascii_date[30] = '\0';
1996 if(filename_len < fnlen + 1){
1997 if(filename != NULL)
1998 free(filename);
2000 filename = malloc(sizeof(ub1) * (fnlen + 1));
2001 filename_len = fnlen + 1;
2004 if(read(fd, filename, fnlen) != fnlen){
2005 perror("read");
2006 exit(1);
2008 filename[fnlen] = '\0';
2010 /* if the user specified a list of files on the command line,
2011 we'll only display those, otherwise we'll display everything */
2012 if(file_num > 0){
2013 for(j = 0; j < file_num; j++)
2014 if(strcmp(files[j], (const char *)filename) == 0){
2015 if(verbose)
2016 printf("%6d %s %s\n", usize, ascii_date, filename);
2017 else
2018 printf("%s\n", filename);
2019 break;
2021 } else {
2022 if(verbose)
2023 printf("%6d %s %s\n", usize, ascii_date, filename);
2024 else
2025 printf("%s\n", filename);
2028 size = eflen + clen;
2029 if(size > 0){
2030 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
2031 perror("lseek");
2032 exit(1);
2036 } else {
2037 /* the file isn't seekable.. evil! */
2038 pb_file pbf;
2040 pb_init(&pbf, fd);
2042 init_inflation();
2044 for(;;){
2045 if(pb_read(&pbf, scratch, 4) != 4){
2046 perror("read");
2047 break;
2050 signature = UNPACK_UB4(scratch, 0);
2052 #ifdef DEBUG
2053 printf("signature is %x\n", signature);
2054 #endif
2056 if(signature == 0x08074b50){
2057 #ifdef DEBUG
2058 printf("skipping data descriptor\n");
2059 #endif
2060 pb_read(&pbf, scratch, 12);
2061 continue;
2062 } else if(signature == 0x02014b50){
2063 #ifdef DEBUG
2064 printf("Central header reached.. we're all done!\n");
2065 #endif
2066 break;
2067 }else if(signature != 0x04034b50){
2068 #ifdef DEBUG
2069 printf("Ick! %#x\n", signature);
2070 #endif
2071 break;
2074 if(pb_read(&pbf, (file_header + 4), 26) != 26){
2075 perror("read");
2076 break;
2079 csize = UNPACK_UB4(file_header, LOC_CSIZE);
2080 #ifdef DEBUG
2081 printf("Compressed size is %u\n", csize);
2082 #endif
2084 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
2085 #ifdef DEBUG
2086 printf("Filename length is %hu\n", fnlen);
2087 #endif
2089 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
2090 #ifdef DEBUG
2091 printf("Extra field length is %hu\n", eflen);
2092 #endif
2094 method = UNPACK_UB2(file_header, LOC_COMP);
2095 #ifdef DEBUG
2096 printf("Compression method is %#hx\n", method);
2097 #endif
2099 flags = UNPACK_UB2(file_header, LOC_EXTRA);
2100 #ifdef DEBUG
2101 printf("Flags are %#hx\n", flags);
2102 #endif
2104 usize = UNPACK_UB4(file_header, LOC_USIZE);
2106 /* If we're providing verbose output, we need to make an ASCII
2107 * formatted version of the date. */
2108 if(verbose){
2109 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
2110 tdate = dos2unixtime(mdate);
2111 s_tm = localtime(&tdate);
2112 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
2115 if(filename_len < fnlen + 1){
2116 if(filename != NULL)
2117 free(filename);
2119 filename = malloc(sizeof(ub1) * (fnlen + 1));
2120 ascii_date[30] = '\0';
2121 filename_len = fnlen + 1;
2124 pb_read(&pbf, filename, fnlen);
2125 filename[fnlen] = '\0';
2127 /* the header is at the end. In a JAR file, this means that the data
2128 happens to be compressed. We have no choice but to inflate the
2129 data */
2130 if(flags & 0x0008){
2132 size = eflen;
2134 if(size > 0)
2135 consume(&pbf, size);
2137 if(method == 8){
2138 #ifdef DEBUG
2139 printf("inflating %s\n", filename);
2140 #endif
2141 inflate_file(&pbf, -1, &ze);
2143 usize = ze.usize;
2144 } else
2145 printf("We're shit outta luck!\n");
2147 } else {
2148 size = csize + (eflen > 0 ? eflen : 0);
2151 #ifdef DEBUG
2152 printf("Skipping %ld bytes\n", (long)size);
2153 #endif
2155 consume(&pbf, size);
2157 /* print out the listing */
2158 if(file_num > 0){
2159 for(j = 0; j < file_num; j++)
2160 if(strcmp(files[j], (const char *)filename) == 0){
2161 if(verbose)
2162 printf("%6d %s %s\n", usize, ascii_date, filename);
2163 else
2164 printf("%s\n", filename);
2165 break;
2167 } else {
2168 if(verbose)
2169 printf("%6d %s %s\n", usize, ascii_date, filename);
2170 else
2171 printf("%s\n", filename);
2175 return 0;
2178 int consume(pb_file *pbf, int amt){
2179 int tc = 0; /* total amount consumed */
2180 ub1 buff[RDSZ];
2181 int rdamt;
2183 #ifdef DEBUG
2184 printf("Consuming %d bytes\n", amt);
2185 #endif
2187 if (seekable){
2188 if (amt <= (int)pbf->buff_amt)
2189 pb_read(pbf, buff, amt);
2190 else {
2191 lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
2192 pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
2194 } else
2195 while(tc < amt){
2196 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
2197 #ifdef DEBUG
2198 printf("got %d bytes\n", rdamt);
2199 #endif
2200 tc += rdamt;
2203 #ifdef DEBUG
2204 printf("%d bytes consumed\n", amt);
2205 #endif
2207 return 0;
2210 void usage(const char *filename){
2211 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
2212 exit (1);
2215 void version ()
2217 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
2218 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
2219 printf("Copyright 2002, 2004 Free Software Foundation\n");
2220 printf("\
2221 This is free software; see the source for copying conditions. There is NO\n\
2222 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2223 exit (0);
2226 void help(const char *filename)
2228 printf("\
2229 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2231 Store many files together in a single `jar' file.\n\
2233 -c create new archive\n\
2234 -t list table of contents for archive\n\
2235 -x extract named (or all) files from archive\n\
2236 -u update existing archive\n\
2237 ", filename);
2238 printf("\n\
2239 -@ read names from stdin\n\
2240 -0 store only; use no ZIP compression\n\
2241 -C DIR FILE change to the specified directory and include\n\
2242 the following file\n\
2243 -E don't include the files found in a directory\n\
2244 -f FILE specify archive file name\n\
2245 --help print this help, then exit\n\
2246 -m FILE include manifest information from specified manifest file\n\
2247 -M Do not create a manifest file for the entries\n\
2248 -v generate verbose output on standard output\n\
2249 -V, --version display version information\n\
2251 printf("\n\
2252 If any file is a directory then it is processed recursively.\n\
2253 The manifest file name and the archive file name needs to be specified\n\
2254 in the same order the 'm' and 'f' flags are specified.\n\
2256 Example 1: to archive two class files into an archive called classes.jar: \n\
2257 jar cvf classes.jar Foo.class Bar.class \n\
2258 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2259 files in the foo/ directory into 'classes.jar': \n\
2260 jar cvfm classes.jar mymanifest -C foo/ .\n\
2263 exit(0);
2266 static char *
2267 jt_strdup(s)
2268 char *s;
2270 char *result = (char*)malloc(strlen(s) + 1);
2271 if (result == (char*)0)
2272 return (char*)0;
2273 strcpy(result, s);
2274 return result;
2277 /* Convert "tar-style" first argument to a form expected by getopt.
2278 This idea and the code comes from GNU tar. This can allocate a new
2279 argument vector. This might leak some memory, but we don't care. */
2280 static void
2281 expand_options (int *argcp, char ***argvp)
2283 int argc = *argcp;
2284 char **argv = *argvp;
2286 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
2287 if a long argument (like "--help") is detected. */
2288 if (argc > 1 && argv[1][1] != '-')
2290 char buf[3];
2291 char **new_argv;
2292 int new_argc;
2293 int args_to_expand;
2294 char *p;
2295 char **in, **out;
2297 buf[0] = '-';
2298 buf[2] = '\0';
2300 args_to_expand = strlen (argv[1]);
2301 if (argv[1][0] == '-')
2302 --args_to_expand;
2304 new_argc = argc - 1 + args_to_expand;
2305 new_argv = (char **) malloc (new_argc * sizeof (char *));
2306 in = argv;
2307 out = new_argv;
2309 *out++ = *in++;
2310 p = *in++;
2311 if (*p == '-')
2312 p++;
2313 while (*p != '\0')
2315 char *opt;
2316 buf[1] = *p;
2317 *out++ = jt_strdup (buf);
2318 /* If the option takes an argument, move the next argument
2319 to just after this option. */
2320 opt = strchr (OPTION_STRING, *p);
2321 if (opt && opt[1] == ':')
2323 if (in < argv + argc)
2324 *out++ = *in++;
2325 else
2327 fprintf(stderr, "%s: option `%s' requires an argument.\n",
2328 argv[0], buf);
2329 usage(argv[0]);
2332 ++p;
2335 /* Copy remaining options. */
2336 while (in < argv + argc)
2337 *out++ = *in++;
2339 *argcp = new_argc;
2340 *argvp = new_argv;