Makefile.tpl (bootstrap): Add bubblestrap, quickstrap, cleanstrap, and restrap target...
[official-gcc.git] / fastjar / jartool.c
blob3048ee9c213afc16d0f93948a9658b6517bfaca5
1 /*
2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002 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"
242 /* Some systems have mkdir that takes a single argument. */
243 #ifdef MKDIR_TAKES_ONE_ARG
244 # define mkdir(a,b) mkdir(a)
245 #endif
248 #ifdef WORDS_BIGENDIAN
250 #define L2BI(l) ((l & 0xff000000) >> 24) | \
251 ((l & 0x00ff0000) >> 8) | \
252 ((l & 0x0000ff00) << 8) | \
253 ((l & 0x000000ff) << 24);
255 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
257 #endif
259 #ifndef errno
260 extern int errno;
261 #endif
263 #ifndef O_BINARY
264 #define O_BINARY 0
265 #endif
267 void usage(const char*);
268 void help(const char *);
269 void version(void);
270 void add_entry(struct zipentry *);
271 void init_headers(void);
273 int consume(pb_file *, int);
274 int list_jar(int, char**, int);
275 int extract_jar(int, char**, int);
276 int add_file_to_jar(int, int, const char*, struct stat*);
277 int add_to_jar(int, const char*, const char*);
278 int create_central_header(int);
279 int make_manifest(int, const char*);
280 static void init_args(char **, int);
281 static char *get_next_arg (void);
282 static char *jt_strdup (char*);
283 static void expand_options (int *argcp, char ***argvp);
285 /* global variables */
286 ub1 file_header[30];
287 ub1 data_descriptor[16];
288 int do_compress;
289 int seekable;
290 int verbose;
291 char *jarfile;
293 /* If non zero, then don't recurse in directory. Instead, add the
294 directory entry and relie on an explicit list of files to populate
295 the archive. This option isn't supported by the original jar tool. */
296 int use_explicit_list_only;
298 /* If non zero, then read the entry names from stdin. This option
299 isn't supported by the original jar tool. */
300 int read_names_from_stdin;
302 zipentry *ziplist; /* linked list of entries */
303 zipentry *ziptail; /* tail of the linked list */
305 int number_of_entries; /* number of entries in the linked list */
307 /* This is used to mark options with no short value. */
308 #define LONG_OPT(Num) ((Num) + 128)
310 #define OPT_HELP LONG_OPT (0)
312 /* This holds all options. */
313 #define OPTION_STRING "-ctxuvVf:m:C:0ME@"
315 static const struct option options[] =
317 { "help", no_argument, NULL, OPT_HELP },
318 { "version", no_argument, NULL, 'V' },
319 { NULL, no_argument, NULL, 0 }
322 int main(int argc, char **argv){
324 char *mfile = NULL;
326 int action = ACTION_NONE;
327 int manifest = TRUE;
328 int opt;
330 int j;
331 int jarfd = -1;
333 /* These are used to collect file names and `-C' options for the
334 second pass through the command line. */
335 int new_argc;
336 char **new_argv;
338 do_compress = TRUE;
339 verbose = FALSE;
341 ziplist = NULL;
343 number_of_entries = 0;
345 if(argc < 2)
346 usage(argv[0]);
348 j = strlen(argv[1]);
350 new_argc = 0;
351 new_argv = (char **) malloc (argc * sizeof (char *));
353 expand_options (&argc, &argv);
354 while ((opt = getopt_long (argc, argv, OPTION_STRING,
355 options, NULL)) != -1) {
356 switch(opt){
357 case 'C':
358 new_argv[new_argc++] = (char *) "-C";
359 /* ... fall through ... */
360 case 1:
361 /* File name or unparsed option, due to RETURN_IN_ORDER. */
362 new_argv[new_argc++] = optarg;
363 break;
364 case 'c':
365 action = ACTION_CREATE;
366 break;
367 case 't':
368 action = ACTION_LIST;
369 break;
370 case 'x':
371 action = ACTION_EXTRACT;
372 break;
373 case 'u':
374 action = ACTION_UPDATE;
375 break;
376 case 'v':
377 verbose = TRUE;
378 break;
379 case 'V':
380 version();
381 exit(0);
382 case 'f':
383 jarfile = optarg;
384 break;
385 case 'm':
386 mfile = optarg;
387 break;
388 case '0':
389 do_compress = FALSE;
390 break;
391 case 'M':
392 manifest = FALSE;
393 break;
395 case OPT_HELP:
396 help(argv[0]);
397 break;
399 /* The following options aren't supported by the original jar tool. */
400 case 'E':
401 use_explicit_list_only = TRUE;
402 break;
403 case '@':
404 read_names_from_stdin = TRUE;
405 break;
406 default:
407 usage(argv[0]);
411 /* We might have seen `--'. In this case we want to make sure that
412 all following options are handled as file names. */
413 while (optind < argc)
414 new_argv[new_argc++] = argv[optind++];
415 new_argv[new_argc] = NULL;
417 if(action == ACTION_NONE){
418 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
419 usage(argv[0]);
422 if(action == ACTION_UPDATE){
423 fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
424 exit(1);
427 /* Verify unsupported combinations and warn of the use of non
428 standard features */
429 if(verbose && use_explicit_list_only)
430 fprintf (stderr, "Warning: using non standard '-E' option\n");
431 if(verbose && read_names_from_stdin)
432 fprintf (stderr, "Warning: using non standard '-@' option\n");
433 if(read_names_from_stdin
434 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
435 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
436 usage(argv[0]);
439 /* create the jarfile */
440 if(action == ACTION_CREATE){
441 if(jarfile){
442 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
444 if(jarfd < 0){
445 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
446 perror(jarfile);
447 exit(1);
450 /* We assume that the file is seekable */
451 seekable = TRUE;
453 } else {
455 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
457 /* standard out is not seekable */
458 seekable = FALSE;
460 /* don't want our output to be part of the jar file.. figured this one
461 out the hard way.. =P */
462 verbose = FALSE;
464 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
466 if(jarfile){
467 jarfd = open(jarfile, O_RDONLY | O_BINARY);
469 if(jarfd < 0){
470 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
471 perror(jarfile);
472 exit(1);
475 seekable = TRUE;
476 } else {
477 jarfd = STDIN_FILENO; /* jarfd is standard in */
479 /* we assume that the stream isn't seekable for safety */
480 seekable = FALSE;
484 if(action == ACTION_CREATE || action == ACTION_UPDATE){
485 const char *arg;
486 init_headers();
488 if((action == ACTION_UPDATE) && jarfile) {
489 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
490 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
491 perror(jarfile);
492 exit(1);
496 if(do_compress)
497 init_compression();
500 /* Add the META-INF/ directory and the manifest */
501 if(manifest && mfile)
502 make_manifest(jarfd, mfile);
503 else if(manifest)
504 make_manifest(jarfd, NULL);
506 init_args (new_argv, 0);
507 /* now we add the files to the archive */
508 while ((arg = get_next_arg ())){
510 if(!strcmp(arg, "-C")){
511 const char *dir_to_change = get_next_arg ();
512 const char *file_to_add = get_next_arg ();
513 if(!dir_to_change
514 || !file_to_add
515 || add_to_jar(jarfd, dir_to_change, file_to_add)){
516 printf("Error adding %s to jar archive!\n", arg);
517 exit(1);
519 } else {
520 if(add_to_jar(jarfd, NULL, arg)){
521 printf("Error adding %s to jar archive!\n", arg);
522 exit(1);
526 /* de-initialize the compression DS */
527 if(do_compress)
528 end_compression();
530 create_central_header(jarfd);
532 if (close(jarfd) != 0) {
533 fprintf(stderr, "Error closing jar archive!\n");
535 } else if(action == ACTION_LIST){
536 list_jar(jarfd, &new_argv[0], new_argc);
537 } else if(action == ACTION_EXTRACT){
538 extract_jar(jarfd, &new_argv[0], new_argc);
541 exit(0);
544 static int args_current_g;
545 static char **args_g;
547 static void
548 init_args(args, current)
549 char **args;
550 int current;
552 if(!read_names_from_stdin)
554 args_g = args;
555 args_current_g = current;
559 static char *
560 get_next_arg ()
562 static int reached_end = 0;
564 if (reached_end)
565 return NULL;
567 if (args_g)
569 if (!args_g [args_current_g])
571 reached_end = 1;
572 return NULL;
574 return args_g [args_current_g++];
576 else
578 /* Read the name from stdin. Delimiters are '\n' and
579 '\r'. Reading EOF indicates that we don't have anymore file
580 names characters to read. */
582 char s [MAXPATHLEN];
583 int pos = 0;
585 /* Get rid of '\n' and '\r' first. */
586 while (1)
588 int c = getc (stdin);
589 if (c == '\n' || c == '\r')
590 continue;
591 else
593 if (c == EOF)
594 return NULL;
595 ungetc (c, stdin);
596 break;
600 while (1)
602 int c = getc (stdin);
603 /* Exit when we get a delimiter or don't have any characters
604 to read */
605 if (c == '\n'|| c == '\r'|| c == EOF)
606 break;
607 s [pos++] = (char) c;
610 if (pos)
612 s [pos] = '\0';
613 return jt_strdup (s);
615 else
616 return NULL;
620 void init_headers(){
621 /* packing file header */
622 /* magic number */
623 file_header[0] = 0x50;
624 file_header[1] = 0x4b;
625 file_header[2] = 0x03;
626 file_header[3] = 0x04;
627 /* version number (Unix 1.0)*/
628 file_header[4] = 10;
629 file_header[5] = 0;
630 /* bit flag (normal deflation)*/
631 file_header[6] = 0x00;
633 file_header[7] = 0x00;
634 /* do_compression method (deflation) */
635 file_header[8] = 0;
636 file_header[9] = 0;
638 /* last mod file time (MS-DOS format) */
639 file_header[10] = 0;
640 file_header[11] = 0;
641 /* last mod file date (MS-DOS format) */
642 file_header[12] = 0;
643 file_header[13] = 0;
644 /* CRC 32 */
645 file_header[14] = 0;
646 file_header[15] = 0;
647 file_header[16] = 0;
648 file_header[17] = 0;
649 /* compressed size */
650 file_header[18] = 0;
651 file_header[19] = 0;
652 file_header[20] = 0;
653 file_header[21] = 0;
654 /* uncompressed size */
655 file_header[22] = 0;
656 file_header[23] = 0;
657 file_header[24] = 0;
658 file_header[25] = 0;
659 /* filename length */
660 file_header[26] = 0;
661 file_header[27] = 0;
662 /* extra field length */
663 file_header[28] = 0;
664 file_header[29] = 0;
666 /* Initialize the compression DS */
667 PACK_UB4(data_descriptor, 0, 0x08074b50);
671 void add_entry(struct zipentry *ze){
673 if(ziplist == NULL){
674 ziplist = ze;
675 ziptail = ziplist;
676 } else {
677 ziplist->next_entry = ze;
678 ziplist = ze;
681 number_of_entries++;
684 int make_manifest(int jfd, const char *mf_name){
685 time_t current_time;
686 int nlen; /* length of file name */
687 int mod_time; /* file modification time */
688 struct zipentry *ze;
690 nlen = 9; /* trust me on this one */
692 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
694 current_time = time(NULL);
695 if(current_time == (time_t)-1){
696 perror("time");
697 exit(1);
700 mod_time = unix2dostime(&current_time);
702 PACK_UB2(file_header, LOC_EXTRA, 0);
703 PACK_UB2(file_header, LOC_COMP, 0);
704 PACK_UB2(file_header, LOC_FNLEN, nlen);
705 PACK_UB4(file_header, LOC_MODTIME, mod_time);
707 if(verbose)
708 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
710 ze = (zipentry*)malloc(sizeof(zipentry));
711 if(ze == NULL){
712 perror("malloc");
713 exit(1);
716 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
717 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
718 strcpy(ze->filename, "META-INF/");
719 ze->filename[nlen] = '\0';
721 ze->offset = lseek(jfd, 0, SEEK_CUR);
722 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
723 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
724 ze->compressed = FALSE;
726 add_entry(ze);
728 write(jfd, file_header, 30);
729 write(jfd, "META-INF/", nlen);
731 /* if the user didn't specify an external manifest file... */
732 if(mf_name == NULL){
733 int mf_len = 37 + strlen(VERSION);
734 char *mf;
736 if((mf = (char *) malloc(mf_len + 1))) {
737 uLong crc;
739 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
741 crc = crc32(0L, Z_NULL, 0);
743 crc = crc32(crc, (const unsigned char *)mf, mf_len);
745 nlen = 20; /* once again, trust me */
747 PACK_UB2(file_header, LOC_EXTRA, 0);
748 PACK_UB2(file_header, LOC_COMP, 0);
749 PACK_UB2(file_header, LOC_FNLEN, nlen);
750 PACK_UB4(file_header, LOC_USIZE, mf_len);
752 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
754 PACK_UB4(file_header, LOC_CRC, crc);
756 if(verbose)
757 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
759 ze = (zipentry*)malloc(sizeof(zipentry));
760 if(ze == NULL){
761 perror("malloc");
762 exit(1);
765 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
766 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
767 strcpy(ze->filename, "META-INF/MANIFEST.MF");
768 ze->filename[nlen] = '\0';
770 ze->offset = lseek(jfd, 0, SEEK_CUR);
771 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
772 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
773 ze->crc = crc;
774 ze->csize = mf_len;
775 ze->usize = ze->csize;
776 ze->compressed = FALSE;
778 add_entry(ze);
780 write(jfd, file_header, 30);
781 write(jfd, "META-INF/MANIFEST.MF", nlen);
782 write(jfd, mf, mf_len);
783 free(mf);
785 else {
786 printf("malloc errror\n");
787 exit(-1);
789 } else {
790 int mfd;
791 struct stat statbuf;
793 stat(mf_name, &statbuf);
795 if(!S_ISREG(statbuf.st_mode)){
796 fprintf(stderr, "Invalid manifest file specified.\n");
797 exit(1);
800 mfd = open(mf_name, O_RDONLY | O_BINARY);
802 if(mfd < 0){
803 fprintf(stderr, "Error opening %s.\n", mf_name);
804 exit(1);
807 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
808 perror("error writing to jar");
809 exit(1);
814 return 0;
817 int add_to_jar(int fd, const char *new_dir, const char *file){
818 struct stat statbuf;
819 DIR *dir;
820 struct dirent *de;
821 zipentry *ze;
822 int stat_return;
823 char *old_dir = NULL;
825 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
826 * It fixes this:
827 * "normal" jar : org/apache/java/io/LogRecord.class
828 * fastjar : ./org/apache/java/io/LogRecord.class
829 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
830 * with both kaffe-1.0b4 and JDK.
832 while (*file=='.' && *(file+1)=='/')
833 file+=2;
835 /* If new_dir isn't null, we need to change to that directory. However,
836 we also need to return to the old directory when we're done */
837 if(new_dir != NULL){
838 old_dir = getcwd(NULL, 0);
840 if(chdir(new_dir) == -1){
841 perror(new_dir);
842 return 1;
846 if(!strcmp(file, jarfile)){
847 if(verbose)
848 printf("skipping: %s\n", file);
849 return 0; /* we don't want to add ourselves.. */
852 stat_return = stat(file, &statbuf);
854 if(stat_return == -1){
855 perror(file);
856 return 1;
857 } else if(S_ISDIR(statbuf.st_mode)){
858 char *fullname;
859 char *t_ptr;
860 int nlen;
861 unsigned long mod_time;
863 dir = opendir(file);
865 if(dir == NULL){
866 perror("opendir");
867 return 1;
870 nlen = strlen(file) + 256;
871 fullname = (char*)malloc(nlen * sizeof(char));
872 memset(fullname, 0, (nlen * sizeof(char)));
874 if(fullname == NULL){
875 fprintf(stderr, "Filename is NULL!\n");
876 return 1;
879 strcpy(fullname, file);
880 nlen = strlen(file);
882 if(fullname[nlen - 1] != '/'){
883 fullname[nlen] = '/';
884 t_ptr = (fullname + nlen + 1);
885 } else
886 t_ptr = (fullname + nlen);
889 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
891 nlen = (t_ptr - fullname);
893 mod_time = unix2dostime(&statbuf.st_mtime);
895 PACK_UB2(file_header, LOC_EXTRA, 0);
896 PACK_UB2(file_header, LOC_COMP, 0);
897 PACK_UB2(file_header, LOC_FNLEN, nlen);
898 PACK_UB4(file_header, LOC_MODTIME, mod_time);
900 if(verbose)
901 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
903 ze = (zipentry*)malloc(sizeof(zipentry));
904 if(ze == NULL){
905 perror("malloc");
906 exit(1);
909 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
910 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
911 strcpy(ze->filename, fullname);
912 ze->filename[nlen] = '\0';
914 ze->offset = lseek(fd, 0, SEEK_CUR);
915 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
916 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
917 ze->compressed = FALSE;
919 add_entry(ze);
921 write(fd, file_header, 30);
922 write(fd, fullname, nlen);
924 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
925 if(de->d_name[0] == '.')
926 continue;
927 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
928 if(verbose)
929 printf("skipping: %s\n", de->d_name);
930 continue;
933 strcpy(t_ptr, de->d_name);
935 if(add_to_jar(fd, NULL, fullname)){
936 fprintf(stderr, "Error adding file to jar!\n");
937 return 1;
941 free(fullname);
942 closedir(dir);
944 } else if(S_ISREG(statbuf.st_mode)){
945 int add_fd;
947 add_fd = open(file, O_RDONLY | O_BINARY);
948 if(add_fd < 0){
949 fprintf(stderr, "Error opening %s.\n", file);
950 return 0;
953 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
954 fprintf(stderr, "Error adding file to jar!\n");
955 return 1;
958 } else {
959 fprintf(stderr, "Illegal file specified: %s\n", file);
962 if(old_dir != NULL){
963 if(chdir(old_dir))
964 perror(old_dir);
966 free(old_dir);
969 return 0;
972 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
974 unsigned short file_name_length;
975 unsigned long mod_time;
976 ub1 rd_buff[RDSZ];
977 uLong crc = 0;
978 off_t offset = 0;
979 int rdamt;
980 struct zipentry *ze;
982 mod_time = unix2dostime(&(statbuf->st_mtime));
983 file_name_length = strlen(fname);
985 if(!seekable && !do_compress){
986 crc = crc32(0L, Z_NULL, 0);
988 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
989 crc = crc32(crc, rd_buff, rdamt);
991 lseek(ffd, 0, SEEK_SET);
994 /* data descriptor */
995 if(!seekable && do_compress){
996 PACK_UB2(file_header, LOC_EXTRA, 8);
997 } else {
998 PACK_UB2(file_header, LOC_EXTRA, 0);
1001 if(do_compress){
1002 PACK_UB2(file_header, LOC_COMP, 8);
1003 } else {
1004 PACK_UB2(file_header, LOC_COMP, 0);
1007 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1008 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1010 if(!seekable && !do_compress){
1011 PACK_UB4(file_header, LOC_CRC, crc);
1012 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1013 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1014 } else
1015 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1017 ze = (zipentry*)malloc(sizeof(zipentry));
1018 if(ze == NULL){
1019 perror("malloc");
1020 exit(1);
1023 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1024 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1025 strcpy(ze->filename, fname);
1027 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1028 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1030 if(!seekable && !do_compress)
1031 ze->crc = crc;
1033 ze->csize = statbuf->st_size;
1034 ze->usize = ze->csize;
1035 ze->offset = lseek(jfd, 0, SEEK_CUR);
1036 if(do_compress)
1037 ze->compressed = TRUE;
1038 else
1039 ze->compressed = FALSE;
1041 add_entry(ze);
1043 /* Write the local header */
1044 write(jfd, file_header, 30);
1046 /* write the file name to the zip file */
1047 write(jfd, fname, file_name_length);
1050 if(verbose){
1051 printf("adding: %s ", fname);
1052 fflush(stdout);
1055 if(do_compress){
1056 /* compress the file */
1057 compress_file(ffd, jfd, ze);
1058 } else {
1059 /* Write the contents of the file (uncompressed) to the zip file */
1060 /* calculate the CRC as we go along */
1061 ze->crc = crc32(0L, Z_NULL, 0);
1063 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1064 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1065 if(write(jfd, rd_buff, rdamt) != rdamt){
1066 perror("write");
1067 return 0;
1071 close(ffd);
1073 /* write out data descriptor */
1074 PACK_UB4(data_descriptor, 4, ze->crc);
1075 PACK_UB4(data_descriptor, 8, ze->csize);
1076 PACK_UB4(data_descriptor, 12, ze->usize);
1078 /* we need to seek back and fill the header */
1079 if(seekable){
1080 offset = (ze->csize + strlen(ze->filename) + 16);
1082 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1083 perror("lseek");
1084 exit(1);
1087 if(write(jfd, (data_descriptor + 4), 12) != 12){
1088 perror("write");
1089 return 0;
1092 offset -= 12;
1094 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1095 perror("lseek");
1096 exit(1);
1098 } else if(do_compress){
1099 /* Sun's jar tool will only allow a data descriptor if the entry is
1100 compressed, but we'll save 16 bytes/entry if we only use it when
1101 we can't seek back on the file */
1103 if(write(jfd, data_descriptor, 16) != 16){
1104 perror("write");
1105 return 0;
1109 if(verbose)
1110 printf("(in=%d) (out=%d) (%s %d%%)\n",
1111 (int)ze->usize, (int)ze->csize,
1112 (do_compress ? "deflated" : "stored"),
1113 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1115 return 0;
1118 int create_central_header(int fd){
1119 ub1 header[46];
1120 ub1 end_header[22];
1121 int start_offset;
1122 int dir_size;
1123 int *iheader;
1124 int total_in = 0, total_out = 22;
1126 zipentry *ze;
1128 iheader = (int*)header;
1130 /* magic number */
1131 header[0] = 'P';
1132 header[1] = 'K';
1133 header[2] = 1;
1134 header[3] = 2;
1135 /* version made by */
1136 header[4] = 10;
1137 header[5] = 0;
1138 /* version needed to extract */
1139 header[6] = 10;
1140 header[7] = 0;
1141 /* bit flag */
1142 header[8] = 0;
1143 header[9] = 0;
1144 /* compression method */
1145 header[10] = 0;
1146 header[11] = 0;
1147 /* file mod time */
1148 header[12] = 0;
1149 header[13] = 0;
1150 /* file mod date */
1151 header[14] = 0;
1152 header[15] = 0;
1153 /* crc 32 */
1154 header[16] = 0;
1155 header[17] = 0;
1156 header[18] = 0;
1157 header[19] = 0;
1158 /* compressed size */
1159 header[20] = 0;
1160 header[21] = 0;
1161 header[22] = 0;
1162 header[23] = 0;
1163 /* uncompressed size */
1164 header[24] = 0;
1165 header[25] = 0;
1166 header[26] = 0;
1167 header[27] = 0;
1168 /* filename length */
1169 header[28] = 0;
1170 header[29] = 0;
1171 /* extra field length */
1172 header[30] = 0;
1173 header[31] = 0;
1174 /* file comment length */
1175 header[32] = 0;
1176 header[33] = 0;
1177 /* disk number start */
1178 header[34] = 0;
1179 header[35] = 0;
1180 /* internal file attribs */
1181 header[36] = 0;
1182 header[37] = 0;
1183 /* external file attribs */
1184 header[38] = 0;
1185 header[39] = 0;
1186 header[40] = 0;
1187 header[41] = 0;
1188 /* relative offset of local header */
1189 header[42] = 0;
1190 header[43] = 0;
1191 header[44] = 0;
1192 header[45] = 0;
1194 start_offset = lseek(fd, 0, SEEK_CUR);
1196 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1198 total_in += ze->usize;
1199 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1201 if(ze->compressed){
1202 PACK_UB2(header, CEN_COMP, 8);
1203 } else {
1204 PACK_UB2(header, CEN_COMP, 0);
1207 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1208 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1209 PACK_UB4(header, CEN_CRC, ze->crc);
1210 PACK_UB4(header, CEN_CSIZE, ze->csize);
1211 PACK_UB4(header, CEN_USIZE, ze->usize);
1212 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1213 PACK_UB4(header, CEN_OFFSET, ze->offset);
1215 write(fd, header, 46);
1217 write(fd, ze->filename, strlen(ze->filename));
1220 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1222 /* magic number */
1223 end_header[0] = 0x50;
1224 end_header[1] = 0x4b;
1225 end_header[2] = 0x05;
1226 end_header[3] = 0x06;
1227 /* number of this disk */
1228 end_header[4] = 0;
1229 end_header[5] = 0;
1230 /* number of disk w/ start of central header */
1231 end_header[6] = 0;
1232 end_header[7] = 0;
1233 /* total number of entries in central dir on this disk*/
1234 PACK_UB2(end_header, 8, number_of_entries);
1235 /* total number of entries in central dir*/
1236 PACK_UB2(end_header, 10, number_of_entries);
1237 /* size of central dir. */
1238 PACK_UB4(end_header, 12, dir_size);
1239 /* offset of start of central dir */
1240 PACK_UB4(end_header, 16, start_offset);
1241 /* zipfile comment length */
1242 end_header[20] = 0;
1243 end_header[21] = 0;
1245 write(fd, end_header, 22);
1247 if(verbose)
1248 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1249 total_in,
1250 total_out,
1251 (do_compress ? "deflated" : "stored"),
1252 (int)((1 - (total_out / (float)total_in)) * 100)
1255 return 0;
1258 int extract_jar(int fd, char **files, int file_num){
1259 int rdamt;
1260 int out_a, in_a;
1261 ub4 signature;
1262 ub4 csize;
1263 ub4 crc;
1264 ub2 fnlen;
1265 ub2 eflen;
1266 ub2 flags;
1267 ub2 method;
1268 ub1 *filename = NULL;
1269 int filename_len = 0;
1270 ub4 rd_buff[RDSZ];
1271 pb_file pbf;
1272 ub1 scratch[16];
1273 zipentry ze;
1274 int f_fd;
1275 int dir;
1276 int handle;
1277 int j;
1279 init_inflation();
1281 pb_init(&pbf, fd);
1283 for(;;){
1284 f_fd = 0;
1285 crc = 0;
1286 ze.crc = 0;
1288 dir = FALSE; /* by default, the file isn't a dir */
1289 handle = TRUE; /* by default we'll extract/create the file */
1291 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1292 perror("read");
1293 break;
1296 signature = UNPACK_UB4(scratch, 0);
1298 #ifdef DEBUG
1299 printf("signature is %x\n", signature);
1300 #endif
1301 if(signature == 0x08074b50){
1302 #ifdef DEBUG
1303 printf("skipping data descriptor\n");
1304 #endif
1305 pb_read(&pbf, scratch, 12);
1306 continue;
1307 } else if(signature == 0x02014b50){
1308 #ifdef DEBUG
1309 printf("Central header reached.. we're all done!\n");
1310 #endif
1311 break;
1312 }else if(signature != 0x04034b50){
1313 printf("Ick! %#x\n", signature);
1314 break;
1317 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1318 perror("read");
1319 break;
1322 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1323 #ifdef DEBUG
1324 printf("Compressed size is %u\n", csize);
1325 #endif
1327 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1328 #ifdef DEBUG
1329 printf("Filename length is %hu\n", fnlen);
1330 #endif
1332 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1333 #ifdef DEBUG
1334 printf("Extra field length is %hu\n", eflen);
1335 #endif
1337 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1338 #ifdef DEBUG
1339 printf("Flags are %#hx\n", flags);
1340 #endif
1342 method = UNPACK_UB2(file_header, LOC_COMP);
1343 #ifdef DEBUG
1344 printf("Compression method is %#hx\n", method);
1345 #endif
1347 /* if there isn't a data descriptor */
1348 if(!(flags & 0x0008)){
1349 crc = UNPACK_UB4(file_header, LOC_CRC);
1350 #ifdef DEBUG
1351 printf("CRC is %x\n", crc);
1352 #endif
1355 if(filename_len < fnlen + 1){
1356 if(filename != NULL)
1357 free(filename);
1359 filename = malloc(sizeof(ub1) * (fnlen + 1));
1360 filename_len = fnlen + 1;
1363 pb_read(&pbf, filename, fnlen);
1364 filename[fnlen] = '\0';
1366 #ifdef DEBUG
1367 printf("filename is %s\n", filename);
1368 #endif
1370 if(file_num > 0){
1371 handle = FALSE;
1373 for(j = 0; j < file_num; j++)
1374 if(strcmp(files[j], (const char *)filename) == 0){
1375 handle = TRUE;
1376 break;
1380 if(!handle)
1381 f_fd = -1;
1383 /* OK, there is some directory information in the file. Nothing to do
1384 but ensure the directory(s) exist, and create them if they don't.
1385 What a pain! */
1386 if(strchr((const char *)filename, '/') != NULL && handle){
1387 /* Loop through all the directories in the path, (everything w/ a '/') */
1388 const ub1 *start = filename;
1389 char *tmp_buff;
1390 struct stat sbuf;
1392 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1394 for(;;){
1395 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1397 if(idx == NULL)
1398 break;
1399 else if(idx == start){
1400 start++;
1401 continue;
1403 start = idx + 1;
1405 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1406 tmp_buff[(idx - filename)] = '\0';
1408 #ifdef DEBUG
1409 printf("checking the existance of %s\n", tmp_buff);
1410 #endif
1412 if(stat(tmp_buff, &sbuf) < 0){
1413 if(errno != ENOENT){
1414 perror("stat");
1415 exit(1);
1418 } else if(S_ISDIR(sbuf.st_mode)){
1419 #ifdef DEBUG
1420 printf("Directory exists\n");
1421 #endif
1422 continue;
1423 }else {
1424 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1425 tmp_buff);
1426 exit(1);
1429 #ifdef DEBUG
1430 printf("Making directory..\n");
1431 #endif
1432 if(mkdir(tmp_buff, 0755) < 0){
1433 perror("mkdir");
1434 exit(1);
1436 if(verbose && handle)
1437 printf("%10s: %s/\n", "created", tmp_buff);
1441 /* only a directory */
1442 if(strlen((const char *)start) == 0)
1443 dir = TRUE;
1445 #ifdef DEBUG
1446 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1447 #endif
1449 /* If the entry was just a directory, don't write to file, etc */
1450 if(strlen((const char *)start) == 0)
1451 f_fd = -1;
1453 free(tmp_buff);
1456 if(f_fd != -1 && handle){
1457 f_fd = creat((const char *)filename, 00644);
1459 if(f_fd < 0){
1460 fprintf(stderr, "Error extracting JAR archive!\n");
1461 perror((const char *)filename);
1462 exit(1);
1466 if(method != 8 && flags & 0x0008){
1467 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1468 exit(1);
1471 if(method == 8 || flags & 0x0008){
1472 if(seekable)
1473 lseek(fd, eflen, SEEK_CUR);
1474 else
1475 consume(&pbf, eflen);
1477 inflate_file(&pbf, f_fd, &ze);
1478 } else {
1480 #ifdef DEBUG
1481 printf("writing stored data.. (%d bytes)\n", csize);
1482 #endif
1484 out_a = 0;
1485 in_a = csize;
1487 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1489 while(out_a < (int)csize){
1490 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1491 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1492 perror("read");
1493 exit(1);
1496 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1498 if(f_fd >= 0)
1499 write(f_fd, rd_buff, rdamt);
1501 out_a += rdamt;
1502 in_a -= rdamt;
1504 #ifdef DEBUG
1505 printf("%d bytes written\n", out_a);
1506 #endif
1509 if(seekable)
1510 lseek(fd, eflen, SEEK_CUR);
1511 else
1512 consume(&pbf, eflen);
1515 /* if there is a data descriptor left, compare the CRC */
1516 if(flags & 0x0008){
1518 if(pb_read(&pbf, scratch, 16) != 16){
1519 perror("read");
1520 exit(1);
1523 signature = UNPACK_UB4(scratch, 0);
1525 if(signature != 0x08074b50){
1526 fprintf(stderr, "Error! Missing data descriptor!\n");
1527 exit(1);
1530 crc = UNPACK_UB4(scratch, 4);
1534 if(crc != ze.crc){
1535 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1536 ze.crc, crc);
1537 exit(1);
1540 close(f_fd);
1542 if(verbose && dir == FALSE && handle)
1543 printf("%10s: %s\n",
1544 (method == 8 ? "inflated" : "extracted"),
1545 filename);
1548 return 0;
1551 int list_jar(int fd, char **files, int file_num){
1552 int rdamt;
1553 ub4 signature;
1554 ub4 csize;
1555 ub4 usize;
1556 ub4 mdate;
1557 ub4 tmp;
1558 ub2 fnlen;
1559 ub2 eflen;
1560 ub2 clen;
1561 ub2 flags;
1562 ub2 method;
1563 ub2 cen_size;
1564 ub1 *filename = NULL;
1565 ub1 scratch[16];
1566 ub1 cen_header[46];
1567 int filename_len = 0;
1568 off_t size;
1569 int i, j;
1570 time_t tdate;
1571 struct tm *s_tm;
1572 char ascii_date[30];
1573 zipentry ze;
1575 #ifdef DEBUG
1576 printf("Listing jar file, looking for %d files\n", file_num);
1577 #endif
1579 /* This should be the start of the central-header-end section */
1580 if(seekable){
1581 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1582 perror("lseek");
1583 exit(1);
1586 if(read(fd, &tmp, sizeof(ub4)) != 4){
1587 perror("read");
1588 exit(1);
1591 #ifdef WORDS_BIGENDIAN
1592 tmp = L2BI(tmp);
1593 #endif
1595 if(tmp != 0x06054b50){
1596 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1597 exit(1);
1600 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1601 perror("lseek");
1602 exit(1);
1605 if(read(fd, &cen_size, 2) != 2){
1606 perror("read");
1607 exit(1);
1610 #ifdef WORDS_BIGENDIAN
1611 cen_size = L2BS(cen_size);
1612 #endif
1614 /* printf("%hu entries in central header\n", cen_size); */
1616 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1617 perror("lseek");
1618 exit(1);
1621 if(read(fd, &tmp, 4) != 4){
1622 perror("read");
1623 exit(1);
1626 #ifdef WORDS_BIGENDIAN
1627 tmp = L2BI(tmp);
1628 #endif
1630 /* printf("Central header offset = %d\n", tmp); */
1632 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1633 perror("lseek");
1634 exit(1);
1637 /* Loop through the entries in the central header */
1638 for(i = 0; i < cen_size; i++){
1640 if(read(fd, &cen_header, 46) != 46){
1641 perror("read");
1642 exit(1);
1645 signature = UNPACK_UB4(cen_header, 0);
1646 if(signature != 0x02014b50){
1647 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1648 exit(1);
1651 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1652 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1653 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1654 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1656 /* If we're providing verbose output, we need to make an ASCII
1657 * formatted version of the date. */
1658 if(verbose){
1659 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1660 tdate = dos2unixtime(mdate);
1661 s_tm = localtime(&tdate);
1662 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1665 if(filename_len < fnlen + 1){
1666 if(filename != NULL)
1667 free(filename);
1669 filename = malloc(sizeof(ub1) * (fnlen + 1));
1670 filename_len = fnlen + 1;
1673 if(read(fd, filename, fnlen) != fnlen){
1674 perror("read");
1675 exit(1);
1677 filename[fnlen] = '\0';
1679 /* if the user specified a list of files on the command line,
1680 we'll only display those, otherwise we'll display everything */
1681 if(file_num > 0){
1682 for(j = 0; j < file_num; j++)
1683 if(strcmp(files[j], (const char *)filename) == 0){
1684 if(verbose)
1685 printf("%6d %s %s\n", usize, ascii_date, filename);
1686 else
1687 printf("%s\n", filename);
1688 break;
1690 } else {
1691 if(verbose)
1692 printf("%6d %s %s\n", usize, ascii_date, filename);
1693 else
1694 printf("%s\n", filename);
1697 size = eflen + clen;
1698 if(size > 0){
1699 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1700 perror("lseek");
1701 exit(1);
1705 } else {
1706 /* the file isn't seekable.. evil! */
1707 pb_file pbf;
1709 pb_init(&pbf, fd);
1711 init_inflation();
1713 for(;;){
1714 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1715 perror("read");
1716 break;
1719 signature = UNPACK_UB4(scratch, 0);
1721 #ifdef DEBUG
1722 printf("signature is %x\n", signature);
1723 #endif
1725 if(signature == 0x08074b50){
1726 #ifdef DEBUG
1727 printf("skipping data descriptor\n");
1728 #endif
1729 pb_read(&pbf, scratch, 12);
1730 continue;
1731 } else if(signature == 0x02014b50){
1732 #ifdef DEBUG
1733 printf("Central header reached.. we're all done!\n");
1734 #endif
1735 break;
1736 }else if(signature != 0x04034b50){
1737 #ifdef DEBUG
1738 printf("Ick! %#x\n", signature);
1739 #endif
1740 break;
1743 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1744 perror("read");
1745 break;
1748 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1749 #ifdef DEBUG
1750 printf("Compressed size is %u\n", csize);
1751 #endif
1753 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1754 #ifdef DEBUG
1755 printf("Filename length is %hu\n", fnlen);
1756 #endif
1758 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1759 #ifdef DEBUG
1760 printf("Extra field length is %hu\n", eflen);
1761 #endif
1763 method = UNPACK_UB2(file_header, LOC_COMP);
1764 #ifdef DEBUG
1765 printf("Compression method is %#hx\n", method);
1766 #endif
1768 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1769 #ifdef DEBUG
1770 printf("Flags are %#hx\n", flags);
1771 #endif
1773 usize = UNPACK_UB4(file_header, LOC_USIZE);
1775 /* If we're providing verbose output, we need to make an ASCII
1776 * formatted version of the date. */
1777 if(verbose){
1778 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1779 tdate = dos2unixtime(mdate);
1780 s_tm = localtime(&tdate);
1781 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1784 if(filename_len < fnlen + 1){
1785 if(filename != NULL)
1786 free(filename);
1788 filename = malloc(sizeof(ub1) * (fnlen + 1));
1789 filename_len = fnlen + 1;
1792 pb_read(&pbf, filename, fnlen);
1793 filename[fnlen] = '\0';
1795 /* the header is at the end. In a JAR file, this means that the data
1796 happens to be compressed. We have no choice but to inflate the
1797 data */
1798 if(flags & 0x0008){
1800 size = eflen;
1802 if(size > 0)
1803 consume(&pbf, size);
1805 if(method == 8){
1806 #ifdef DEBUG
1807 printf("inflating %s\n", filename);
1808 #endif
1809 inflate_file(&pbf, -1, &ze);
1811 usize = ze.usize;
1812 } else
1813 printf("We're shit outta luck!\n");
1815 } else {
1816 size = csize + (eflen > 0 ? eflen : 0);
1819 #ifdef DEBUG
1820 printf("Skipping %ld bytes\n", (long)size);
1821 #endif
1823 consume(&pbf, size);
1825 /* print out the listing */
1826 if(file_num > 0){
1827 for(j = 0; j < file_num; j++)
1828 if(strcmp(files[j], (const char *)filename) == 0){
1829 if(verbose)
1830 printf("%6d %s %s\n", usize, ascii_date, filename);
1831 else
1832 printf("%s\n", filename);
1833 break;
1835 } else {
1836 if(verbose)
1837 printf("%6d %s %s\n", usize, ascii_date, filename);
1838 else
1839 printf("%s\n", filename);
1843 return 0;
1846 int consume(pb_file *pbf, int amt){
1847 int tc = 0; /* total amount consumed */
1848 ub1 buff[RDSZ];
1849 int rdamt;
1851 #ifdef DEBUG
1852 printf("Consuming %d bytes\n", amt);
1853 #endif
1855 while(tc < amt){
1856 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1857 #ifdef DEBUG
1858 printf("got %d bytes\n", rdamt);
1859 #endif
1860 tc += rdamt;
1863 #ifdef DEBUG
1864 printf("%d bytes consumed\n", tc);
1865 #endif
1867 return 0;
1870 void usage(const char *filename){
1871 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1872 exit (1);
1875 void version ()
1877 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1878 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1879 printf("Copyright 2002 Free Software Foundation\n");
1880 printf("\
1881 This is free software; see the source for copying conditions. There is NO\n\
1882 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1883 exit (0);
1886 void help(const char *filename)
1888 printf("\
1889 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1891 Store many files together in a single `jar' file.\n\
1893 -c create new archive\n\
1894 -t list table of contents for archive\n\
1895 -x extract named (or all) files from archive\n\
1896 -u update existing archive\n\
1897 ", filename);
1898 printf("\n\
1899 -@ read names from stdin\n\
1900 -0 store only; use no ZIP compression\n\
1901 -C DIR FILE change to the specified directory and include\n\
1902 the following file\n\
1903 -E don't include the files found in a directory\n\
1904 -f FILE specify archive file name\n\
1905 --help print this help, then exit\n\
1906 -m FILE include manifest information from specified manifest file\n\
1907 -M Do not create a manifest file for the entries\n\
1908 -v generate verbose output on standard output\n\
1909 -V, --version display version information\n\
1911 printf("\n\
1912 If any file is a directory then it is processed recursively.\n\
1913 The manifest file name and the archive file name needs to be specified\n\
1914 in the same order the 'm' and 'f' flags are specified.\n\
1916 Example 1: to archive two class files into an archive called classes.jar: \n\
1917 jar cvf classes.jar Foo.class Bar.class \n\
1918 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1919 files in the foo/ directory into 'classes.jar': \n\
1920 jar cvfm classes.jar mymanifest -C foo/ .\n\
1923 exit(0);
1926 static char *
1927 jt_strdup(s)
1928 char *s;
1930 char *result = (char*)malloc(strlen(s) + 1);
1931 if (result == (char*)0)
1932 return (char*)0;
1933 strcpy(result, s);
1934 return result;
1937 /* Convert "tar-style" first argument to a form expected by getopt.
1938 This idea and the code comes from GNU tar. This can allocate a new
1939 argument vector. This might leak some memory, but we don't care. */
1940 static void
1941 expand_options (int *argcp, char ***argvp)
1943 int argc = *argcp;
1944 char **argv = *argvp;
1946 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
1947 if a long argument (like "--help") is detected. */
1948 if (argc > 1 && argv[1][1] != '-')
1950 char buf[3];
1951 char **new_argv;
1952 int new_argc;
1953 int args_to_expand;
1954 char *p;
1955 char **in, **out;
1957 buf[0] = '-';
1958 buf[2] = '\0';
1960 args_to_expand = strlen (argv[1]);
1961 if (argv[1][0] == '-')
1962 --args_to_expand;
1964 new_argc = argc - 1 + args_to_expand;
1965 new_argv = (char **) malloc (new_argc * sizeof (char *));
1966 in = argv;
1967 out = new_argv;
1969 *out++ = *in++;
1970 p = *in++;
1971 if (*p == '-')
1972 p++;
1973 while (*p != '\0')
1975 char *opt;
1976 buf[1] = *p;
1977 *out++ = jt_strdup (buf);
1978 /* If the option takes an argument, move the next argument
1979 to just after this option. */
1980 opt = strchr (OPTION_STRING, *p);
1981 if (opt && opt[1] == ':')
1983 if (in < argv + argc)
1984 *out++ = *in++;
1985 else
1987 fprintf(stderr, "%s: option `%s' requires an argument.\n",
1988 argv[0], buf);
1989 usage(argv[0]);
1992 ++p;
1995 /* Copy remaining options. */
1996 while (in < argv + argc)
1997 *out++ = *in++;
1999 *argcp = new_argc;
2000 *argvp = new_argv;