* decl.c (check_tag_decl): Handle RID_THREAD.
[official-gcc.git] / fastjar / jartool.c
blob2436606d5026e970d8508a33e9a5c2458797c082
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 #ifdef WORDS_BIGENDIAN
244 #define L2BI(l) ((l & 0xff000000) >> 24) | \
245 ((l & 0x00ff0000) >> 8) | \
246 ((l & 0x0000ff00) << 8) | \
247 ((l & 0x000000ff) << 24);
249 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
251 #endif
253 #ifndef errno
254 extern int errno;
255 #endif
257 #ifndef O_BINARY
258 #define O_BINARY 0
259 #endif
261 void usage(const char*);
262 void help(const char *);
263 void version(void);
264 void add_entry(struct zipentry *);
265 void init_headers(void);
267 int consume(pb_file *, int);
268 int list_jar(int, char**, int);
269 int extract_jar(int, char**, int);
270 int add_file_to_jar(int, int, const char*, struct stat*);
271 int add_to_jar(int, const char*, const char*);
272 int create_central_header(int);
273 int make_manifest(int, const char*);
274 static void init_args(char **, int);
275 static char *get_next_arg (void);
276 static char *jt_strdup (char*);
277 static void expand_options (int *argcp, char ***argvp);
279 /* global variables */
280 ub1 file_header[30];
281 ub1 data_descriptor[16];
282 int do_compress;
283 int seekable;
284 int verbose;
285 char *jarfile;
287 /* If non zero, then don't recurse in directory. Instead, add the
288 directory entry and relie on an explicit list of files to populate
289 the archive. This option isn't supported by the original jar tool. */
290 int use_explicit_list_only;
292 /* If non zero, then read the entry names from stdin. This option
293 isn't supported by the original jar tool. */
294 int read_names_from_stdin;
296 zipentry *ziplist; /* linked list of entries */
297 zipentry *ziptail; /* tail of the linked list */
299 int number_of_entries; /* number of entries in the linked list */
301 /* This is used to mark options with no short value. */
302 #define LONG_OPT(Num) ((Num) + 128)
304 #define OPT_HELP LONG_OPT (0)
306 /* This holds all options. */
307 #define OPTION_STRING "-ctxuvVf:m:C:0ME@"
309 static const struct option options[] =
311 { "help", no_argument, NULL, OPT_HELP },
312 { "version", no_argument, NULL, 'V' },
313 { NULL, no_argument, NULL, 0 }
316 int main(int argc, char **argv){
318 char *mfile = NULL;
320 int action = ACTION_NONE;
321 int manifest = TRUE;
322 int opt;
324 int j;
325 int jarfd = -1;
327 /* These are used to collect file names and `-C' options for the
328 second pass through the command line. */
329 int new_argc;
330 char **new_argv;
332 do_compress = TRUE;
333 verbose = FALSE;
335 ziplist = NULL;
337 number_of_entries = 0;
339 if(argc < 2)
340 usage(argv[0]);
342 j = strlen(argv[1]);
344 new_argc = 0;
345 new_argv = (char **) malloc (argc * sizeof (char *));
347 expand_options (&argc, &argv);
348 while ((opt = getopt_long (argc, argv, OPTION_STRING,
349 options, NULL)) != -1) {
350 switch(opt){
351 case 'C':
352 new_argv[new_argc++] = (char *) "-C";
353 /* ... fall through ... */
354 case 1:
355 /* File name or unparsed option, due to RETURN_IN_ORDER. */
356 new_argv[new_argc++] = optarg;
357 break;
358 case 'c':
359 action = ACTION_CREATE;
360 break;
361 case 't':
362 action = ACTION_LIST;
363 break;
364 case 'x':
365 action = ACTION_EXTRACT;
366 break;
367 case 'u':
368 action = ACTION_UPDATE;
369 break;
370 case 'v':
371 verbose = TRUE;
372 break;
373 case 'V':
374 version();
375 exit(0);
376 case 'f':
377 jarfile = optarg;
378 break;
379 case 'm':
380 mfile = optarg;
381 break;
382 case '0':
383 do_compress = FALSE;
384 break;
385 case 'M':
386 manifest = FALSE;
387 break;
389 case OPT_HELP:
390 help(argv[0]);
391 break;
393 /* The following options aren't supported by the original jar tool. */
394 case 'E':
395 use_explicit_list_only = TRUE;
396 break;
397 case '@':
398 read_names_from_stdin = TRUE;
399 break;
400 default:
401 usage(argv[0]);
405 /* We might have seen `--'. In this case we want to make sure that
406 all following options are handled as file names. */
407 while (optind < argc)
408 new_argv[new_argc++] = argv[optind++];
409 new_argv[new_argc] = NULL;
411 if(action == ACTION_NONE){
412 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
413 usage(argv[0]);
416 if(action == ACTION_UPDATE){
417 fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
418 exit(1);
421 /* Verify unsupported combinations and warn of the use of non
422 standard features */
423 if(verbose && use_explicit_list_only)
424 fprintf (stderr, "Warning: using non standard '-E' option\n");
425 if(verbose && read_names_from_stdin)
426 fprintf (stderr, "Warning: using non standard '-@' option\n");
427 if(read_names_from_stdin
428 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
429 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
430 usage(argv[0]);
433 /* create the jarfile */
434 if(action == ACTION_CREATE){
435 if(jarfile){
436 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
437 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
439 if(jarfd < 0){
440 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
441 perror(jarfile);
442 exit(1);
445 /* We assume that the file is seekable */
446 seekable = TRUE;
448 } else {
450 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
452 /* standard out is not seekable */
453 seekable = FALSE;
455 /* don't want our output to be part of the jar file.. figured this one
456 out the hard way.. =P */
457 verbose = FALSE;
459 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
461 if(jarfile){
462 jarfd = open(jarfile, O_RDONLY | O_BINARY);
464 if(jarfd < 0){
465 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
466 perror(jarfile);
467 exit(1);
470 seekable = TRUE;
471 } else {
472 jarfd = STDIN_FILENO; /* jarfd is standard in */
474 /* we assume that the stream isn't seekable for safety */
475 seekable = FALSE;
479 if(action == ACTION_CREATE || action == ACTION_UPDATE){
480 const char *arg;
481 init_headers();
483 if((action == ACTION_UPDATE) && jarfile) {
484 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
485 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
486 perror(jarfile);
487 exit(1);
491 if(do_compress)
492 init_compression();
495 /* Add the META-INF/ directory and the manifest */
496 if(manifest && mfile)
497 make_manifest(jarfd, mfile);
498 else if(manifest)
499 make_manifest(jarfd, NULL);
501 init_args (new_argv, 0);
502 /* now we add the files to the archive */
503 while ((arg = get_next_arg ())){
505 if(!strcmp(arg, "-C")){
506 const char *dir_to_change = get_next_arg ();
507 const char *file_to_add = get_next_arg ();
508 if(!dir_to_change
509 || !file_to_add
510 || add_to_jar(jarfd, dir_to_change, file_to_add)){
511 printf("Error adding %s to jar archive!\n", arg);
512 exit(1);
514 } else {
515 if(add_to_jar(jarfd, NULL, arg)){
516 printf("Error adding %s to jar archive!\n", arg);
517 exit(1);
521 /* de-initialize the compression DS */
522 if(do_compress)
523 end_compression();
525 create_central_header(jarfd);
527 if (close(jarfd) != 0) {
528 fprintf(stderr, "Error closing jar archive!\n");
530 } else if(action == ACTION_LIST){
531 list_jar(jarfd, &new_argv[0], new_argc);
532 } else if(action == ACTION_EXTRACT){
533 extract_jar(jarfd, &new_argv[0], new_argc);
536 exit(0);
539 static int args_current_g;
540 static char **args_g;
542 static void
543 init_args(args, current)
544 char **args;
545 int current;
547 if(!read_names_from_stdin)
549 args_g = args;
550 args_current_g = current;
554 static char *
555 get_next_arg ()
557 static int reached_end = 0;
559 if (reached_end)
560 return NULL;
562 if (args_g)
564 if (!args_g [args_current_g])
566 reached_end = 1;
567 return NULL;
569 return args_g [args_current_g++];
571 else
573 /* Read the name from stdin. Delimiters are '\n' and
574 '\r'. Reading EOF indicates that we don't have anymore file
575 names characters to read. */
577 char s [MAXPATHLEN];
578 int pos = 0;
580 /* Get rid of '\n' and '\r' first. */
581 while (1)
583 int c = getc (stdin);
584 if (c == '\n' || c == '\r')
585 continue;
586 else
588 if (c == EOF)
589 return NULL;
590 ungetc (c, stdin);
591 break;
595 while (1)
597 int c = getc (stdin);
598 /* Exit when we get a delimiter or don't have any characters
599 to read */
600 if (c == '\n'|| c == '\r'|| c == EOF)
601 break;
602 s [pos++] = (char) c;
605 if (pos)
607 s [pos] = '\0';
608 return jt_strdup (s);
610 else
611 return NULL;
615 void init_headers(){
616 /* packing file header */
617 /* magic number */
618 file_header[0] = 0x50;
619 file_header[1] = 0x4b;
620 file_header[2] = 0x03;
621 file_header[3] = 0x04;
622 /* version number (Unix 1.0)*/
623 file_header[4] = 10;
624 file_header[5] = 0;
625 /* bit flag (normal deflation)*/
626 file_header[6] = 0x00;
628 file_header[7] = 0x00;
629 /* do_compression method (deflation) */
630 file_header[8] = 0;
631 file_header[9] = 0;
633 /* last mod file time (MS-DOS format) */
634 file_header[10] = 0;
635 file_header[11] = 0;
636 /* last mod file date (MS-DOS format) */
637 file_header[12] = 0;
638 file_header[13] = 0;
639 /* CRC 32 */
640 file_header[14] = 0;
641 file_header[15] = 0;
642 file_header[16] = 0;
643 file_header[17] = 0;
644 /* compressed size */
645 file_header[18] = 0;
646 file_header[19] = 0;
647 file_header[20] = 0;
648 file_header[21] = 0;
649 /* uncompressed size */
650 file_header[22] = 0;
651 file_header[23] = 0;
652 file_header[24] = 0;
653 file_header[25] = 0;
654 /* filename length */
655 file_header[26] = 0;
656 file_header[27] = 0;
657 /* extra field length */
658 file_header[28] = 0;
659 file_header[29] = 0;
661 /* Initialize the compression DS */
662 PACK_UB4(data_descriptor, 0, 0x08074b50);
666 void add_entry(struct zipentry *ze){
668 if(ziplist == NULL){
669 ziplist = ze;
670 ziptail = ziplist;
671 } else {
672 ziplist->next_entry = ze;
673 ziplist = ze;
676 number_of_entries++;
679 int make_manifest(int jfd, const char *mf_name){
680 time_t current_time;
681 int nlen; /* length of file name */
682 int mod_time; /* file modification time */
683 struct zipentry *ze;
685 nlen = 9; /* trust me on this one */
687 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
689 current_time = time(NULL);
690 if(current_time == (time_t)-1){
691 perror("time");
692 exit(1);
695 mod_time = unix2dostime(&current_time);
697 PACK_UB2(file_header, LOC_EXTRA, 0);
698 PACK_UB2(file_header, LOC_COMP, 0);
699 PACK_UB2(file_header, LOC_FNLEN, nlen);
700 PACK_UB4(file_header, LOC_MODTIME, mod_time);
702 if(verbose)
703 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
705 ze = (zipentry*)malloc(sizeof(zipentry));
706 if(ze == NULL){
707 perror("malloc");
708 exit(1);
711 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
712 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
713 strcpy(ze->filename, "META-INF/");
714 ze->filename[nlen] = '\0';
716 ze->offset = lseek(jfd, 0, SEEK_CUR);
717 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
718 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
719 ze->compressed = FALSE;
721 add_entry(ze);
723 write(jfd, file_header, 30);
724 write(jfd, "META-INF/", nlen);
726 /* if the user didn't specify an external manifest file... */
727 if(mf_name == NULL){
728 int mf_len = 37 + strlen(VERSION);
729 char *mf;
731 if((mf = (char *) malloc(mf_len + 1))) {
732 uLong crc;
734 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
736 crc = crc32(0L, Z_NULL, 0);
738 crc = crc32(crc, (const unsigned char *)mf, mf_len);
740 nlen = 20; /* once again, trust me */
742 PACK_UB2(file_header, LOC_EXTRA, 0);
743 PACK_UB2(file_header, LOC_COMP, 0);
744 PACK_UB2(file_header, LOC_FNLEN, nlen);
745 PACK_UB4(file_header, LOC_USIZE, mf_len);
747 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
749 PACK_UB4(file_header, LOC_CRC, crc);
751 if(verbose)
752 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
754 ze = (zipentry*)malloc(sizeof(zipentry));
755 if(ze == NULL){
756 perror("malloc");
757 exit(1);
760 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
761 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
762 strcpy(ze->filename, "META-INF/MANIFEST.MF");
763 ze->filename[nlen] = '\0';
765 ze->offset = lseek(jfd, 0, SEEK_CUR);
766 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
767 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
768 ze->crc = crc;
769 ze->csize = mf_len;
770 ze->usize = ze->csize;
771 ze->compressed = FALSE;
773 add_entry(ze);
775 write(jfd, file_header, 30);
776 write(jfd, "META-INF/MANIFEST.MF", nlen);
777 write(jfd, mf, mf_len);
778 free(mf);
780 else {
781 printf("malloc errror\n");
782 exit(-1);
784 } else {
785 int mfd;
786 struct stat statbuf;
788 stat(mf_name, &statbuf);
790 if(!S_ISREG(statbuf.st_mode)){
791 fprintf(stderr, "Invalid manifest file specified.\n");
792 exit(1);
795 mfd = open(mf_name, O_RDONLY | O_BINARY);
797 if(mfd < 0){
798 fprintf(stderr, "Error opening %s.\n", mf_name);
799 exit(1);
802 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
803 perror("error writing to jar");
804 exit(1);
809 return 0;
812 int add_to_jar(int fd, const char *new_dir, const char *file){
813 struct stat statbuf;
814 DIR *dir;
815 struct dirent *de;
816 zipentry *ze;
817 int stat_return;
818 char *old_dir = NULL;
820 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
821 * It fixes this:
822 * "normal" jar : org/apache/java/io/LogRecord.class
823 * fastjar : ./org/apache/java/io/LogRecord.class
824 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
825 * with both kaffe-1.0b4 and JDK.
827 while (*file=='.' && *(file+1)=='/')
828 file+=2;
830 /* If new_dir isn't null, we need to change to that directory. However,
831 we also need to return to the old directory when we're done */
832 if(new_dir != NULL){
833 old_dir = getcwd(NULL, 0);
835 if(chdir(new_dir) == -1){
836 perror(new_dir);
837 return 1;
841 if(!strcmp(file, jarfile)){
842 if(verbose)
843 printf("skipping: %s\n", file);
844 return 0; /* we don't want to add ourselves.. */
847 stat_return = stat(file, &statbuf);
849 if(stat_return == -1){
850 perror(file);
851 return 1;
852 } else if(S_ISDIR(statbuf.st_mode)){
853 char *fullname;
854 char *t_ptr;
855 int nlen;
856 unsigned long mod_time;
858 dir = opendir(file);
860 if(dir == NULL){
861 perror("opendir");
862 return 1;
865 nlen = strlen(file) + 256;
866 fullname = (char*)malloc(nlen * sizeof(char));
867 memset(fullname, 0, (nlen * sizeof(char)));
869 if(fullname == NULL){
870 fprintf(stderr, "Filename is NULL!\n");
871 return 1;
874 strcpy(fullname, file);
875 nlen = strlen(file);
877 if(fullname[nlen - 1] != '/'){
878 fullname[nlen] = '/';
879 t_ptr = (fullname + nlen + 1);
880 } else
881 t_ptr = (fullname + nlen);
884 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
886 nlen = (t_ptr - fullname);
888 mod_time = unix2dostime(&statbuf.st_mtime);
890 PACK_UB2(file_header, LOC_EXTRA, 0);
891 PACK_UB2(file_header, LOC_COMP, 0);
892 PACK_UB2(file_header, LOC_FNLEN, nlen);
893 PACK_UB4(file_header, LOC_MODTIME, mod_time);
895 if(verbose)
896 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
898 ze = (zipentry*)malloc(sizeof(zipentry));
899 if(ze == NULL){
900 perror("malloc");
901 exit(1);
904 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
905 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
906 strcpy(ze->filename, fullname);
907 ze->filename[nlen] = '\0';
909 ze->offset = lseek(fd, 0, SEEK_CUR);
910 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
911 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
912 ze->compressed = FALSE;
914 add_entry(ze);
916 write(fd, file_header, 30);
917 write(fd, fullname, nlen);
919 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
920 if(de->d_name[0] == '.')
921 continue;
922 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
923 if(verbose)
924 printf("skipping: %s\n", de->d_name);
925 continue;
928 strcpy(t_ptr, de->d_name);
930 if(add_to_jar(fd, NULL, fullname)){
931 fprintf(stderr, "Error adding file to jar!\n");
932 return 1;
936 free(fullname);
937 closedir(dir);
939 } else if(S_ISREG(statbuf.st_mode)){
940 int add_fd;
942 add_fd = open(file, O_RDONLY | O_BINARY);
943 if(add_fd < 0){
944 fprintf(stderr, "Error opening %s.\n", file);
945 return 0;
948 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
949 fprintf(stderr, "Error adding file to jar!\n");
950 return 1;
953 } else {
954 fprintf(stderr, "Illegal file specified: %s\n", file);
957 if(old_dir != NULL){
958 if(chdir(old_dir))
959 perror(old_dir);
961 free(old_dir);
964 return 0;
967 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
969 unsigned short file_name_length;
970 unsigned long mod_time;
971 ub1 rd_buff[RDSZ];
972 uLong crc = 0;
973 off_t offset = 0;
974 int rdamt;
975 struct zipentry *ze;
977 mod_time = unix2dostime(&(statbuf->st_mtime));
978 file_name_length = strlen(fname);
980 if(!seekable && !do_compress){
981 crc = crc32(0L, Z_NULL, 0);
983 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
984 crc = crc32(crc, rd_buff, rdamt);
986 lseek(ffd, 0, SEEK_SET);
989 /* data descriptor */
990 if(!seekable && do_compress){
991 PACK_UB2(file_header, LOC_EXTRA, 8);
992 } else {
993 PACK_UB2(file_header, LOC_EXTRA, 0);
996 if(do_compress){
997 PACK_UB2(file_header, LOC_COMP, 8);
998 } else {
999 PACK_UB2(file_header, LOC_COMP, 0);
1002 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1003 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1005 if(!seekable && !do_compress){
1006 PACK_UB4(file_header, LOC_CRC, crc);
1007 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1008 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1009 } else
1010 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1012 ze = (zipentry*)malloc(sizeof(zipentry));
1013 if(ze == NULL){
1014 perror("malloc");
1015 exit(1);
1018 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1019 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1020 strcpy(ze->filename, fname);
1022 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1023 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1025 if(!seekable && !do_compress)
1026 ze->crc = crc;
1028 ze->csize = statbuf->st_size;
1029 ze->usize = ze->csize;
1030 ze->offset = lseek(jfd, 0, SEEK_CUR);
1031 if(do_compress)
1032 ze->compressed = TRUE;
1033 else
1034 ze->compressed = FALSE;
1036 add_entry(ze);
1038 /* Write the local header */
1039 write(jfd, file_header, 30);
1041 /* write the file name to the zip file */
1042 write(jfd, fname, file_name_length);
1045 if(verbose){
1046 printf("adding: %s ", fname);
1047 fflush(stdout);
1050 if(do_compress){
1051 /* compress the file */
1052 compress_file(ffd, jfd, ze);
1053 } else {
1054 /* Write the contents of the file (uncompressed) to the zip file */
1055 /* calculate the CRC as we go along */
1056 ze->crc = crc32(0L, Z_NULL, 0);
1058 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1059 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1060 if(write(jfd, rd_buff, rdamt) != rdamt){
1061 perror("write");
1062 return 0;
1066 close(ffd);
1068 /* write out data descriptor */
1069 PACK_UB4(data_descriptor, 4, ze->crc);
1070 PACK_UB4(data_descriptor, 8, ze->csize);
1071 PACK_UB4(data_descriptor, 12, ze->usize);
1073 /* we need to seek back and fill the header */
1074 if(seekable){
1075 offset = (ze->csize + strlen(ze->filename) + 16);
1077 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1078 perror("lseek");
1079 exit(1);
1082 if(write(jfd, (data_descriptor + 4), 12) != 12){
1083 perror("write");
1084 return 0;
1087 offset -= 12;
1089 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1090 perror("lseek");
1091 exit(1);
1093 } else if(do_compress){
1094 /* Sun's jar tool will only allow a data descriptor if the entry is
1095 compressed, but we'll save 16 bytes/entry if we only use it when
1096 we can't seek back on the file */
1098 if(write(jfd, data_descriptor, 16) != 16){
1099 perror("write");
1100 return 0;
1104 if(verbose)
1105 printf("(in=%d) (out=%d) (%s %d%%)\n",
1106 (int)ze->usize, (int)ze->csize,
1107 (do_compress ? "deflated" : "stored"),
1108 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1110 return 0;
1113 int create_central_header(int fd){
1114 ub1 header[46];
1115 ub1 end_header[22];
1116 int start_offset;
1117 int dir_size;
1118 int *iheader;
1119 int total_in = 0, total_out = 22;
1121 zipentry *ze;
1123 iheader = (int*)header;
1125 /* magic number */
1126 header[0] = 'P';
1127 header[1] = 'K';
1128 header[2] = 1;
1129 header[3] = 2;
1130 /* version made by */
1131 header[4] = 10;
1132 header[5] = 0;
1133 /* version needed to extract */
1134 header[6] = 10;
1135 header[7] = 0;
1136 /* bit flag */
1137 header[8] = 0;
1138 header[9] = 0;
1139 /* compression method */
1140 header[10] = 0;
1141 header[11] = 0;
1142 /* file mod time */
1143 header[12] = 0;
1144 header[13] = 0;
1145 /* file mod date */
1146 header[14] = 0;
1147 header[15] = 0;
1148 /* crc 32 */
1149 header[16] = 0;
1150 header[17] = 0;
1151 header[18] = 0;
1152 header[19] = 0;
1153 /* compressed size */
1154 header[20] = 0;
1155 header[21] = 0;
1156 header[22] = 0;
1157 header[23] = 0;
1158 /* uncompressed size */
1159 header[24] = 0;
1160 header[25] = 0;
1161 header[26] = 0;
1162 header[27] = 0;
1163 /* filename length */
1164 header[28] = 0;
1165 header[29] = 0;
1166 /* extra field length */
1167 header[30] = 0;
1168 header[31] = 0;
1169 /* file comment length */
1170 header[32] = 0;
1171 header[33] = 0;
1172 /* disk number start */
1173 header[34] = 0;
1174 header[35] = 0;
1175 /* internal file attribs */
1176 header[36] = 0;
1177 header[37] = 0;
1178 /* external file attribs */
1179 header[38] = 0;
1180 header[39] = 0;
1181 header[40] = 0;
1182 header[41] = 0;
1183 /* relative offset of local header */
1184 header[42] = 0;
1185 header[43] = 0;
1186 header[44] = 0;
1187 header[45] = 0;
1189 start_offset = lseek(fd, 0, SEEK_CUR);
1191 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1193 total_in += ze->usize;
1194 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1196 if(ze->compressed){
1197 PACK_UB2(header, CEN_COMP, 8);
1198 } else {
1199 PACK_UB2(header, CEN_COMP, 0);
1202 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1203 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1204 PACK_UB4(header, CEN_CRC, ze->crc);
1205 PACK_UB4(header, CEN_CSIZE, ze->csize);
1206 PACK_UB4(header, CEN_USIZE, ze->usize);
1207 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1208 PACK_UB4(header, CEN_OFFSET, ze->offset);
1210 write(fd, header, 46);
1212 write(fd, ze->filename, strlen(ze->filename));
1215 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1217 /* magic number */
1218 end_header[0] = 0x50;
1219 end_header[1] = 0x4b;
1220 end_header[2] = 0x05;
1221 end_header[3] = 0x06;
1222 /* number of this disk */
1223 end_header[4] = 0;
1224 end_header[5] = 0;
1225 /* number of disk w/ start of central header */
1226 end_header[6] = 0;
1227 end_header[7] = 0;
1228 /* total number of entries in central dir on this disk*/
1229 PACK_UB2(end_header, 8, number_of_entries);
1230 /* total number of entries in central dir*/
1231 PACK_UB2(end_header, 10, number_of_entries);
1232 /* size of central dir. */
1233 PACK_UB4(end_header, 12, dir_size);
1234 /* offset of start of central dir */
1235 PACK_UB4(end_header, 16, start_offset);
1236 /* zipfile comment length */
1237 end_header[20] = 0;
1238 end_header[21] = 0;
1240 write(fd, end_header, 22);
1242 if(verbose)
1243 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1244 total_in,
1245 total_out,
1246 (do_compress ? "deflated" : "stored"),
1247 (int)((1 - (total_out / (float)total_in)) * 100)
1250 return 0;
1253 int extract_jar(int fd, char **files, int file_num){
1254 int rdamt;
1255 int out_a, in_a;
1256 ub4 signature;
1257 ub4 csize;
1258 ub4 crc;
1259 ub2 fnlen;
1260 ub2 eflen;
1261 ub2 flags;
1262 ub2 method;
1263 ub1 *filename = NULL;
1264 int filename_len = 0;
1265 ub4 rd_buff[RDSZ];
1266 pb_file pbf;
1267 ub1 scratch[16];
1268 zipentry ze;
1269 int f_fd;
1270 int dir;
1271 int handle;
1272 int j;
1274 init_inflation();
1276 pb_init(&pbf, fd);
1278 for(;;){
1279 f_fd = 0;
1280 crc = 0;
1281 ze.crc = 0;
1283 dir = FALSE; /* by default, the file isn't a dir */
1284 handle = TRUE; /* by default we'll extract/create the file */
1286 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1287 perror("read");
1288 break;
1291 signature = UNPACK_UB4(scratch, 0);
1293 #ifdef DEBUG
1294 printf("signature is %x\n", signature);
1295 #endif
1296 if(signature == 0x08074b50){
1297 #ifdef DEBUG
1298 printf("skipping data descriptor\n");
1299 #endif
1300 pb_read(&pbf, scratch, 12);
1301 continue;
1302 } else if(signature == 0x02014b50){
1303 #ifdef DEBUG
1304 printf("Central header reached.. we're all done!\n");
1305 #endif
1306 break;
1307 }else if(signature != 0x04034b50){
1308 printf("Ick! %#x\n", signature);
1309 break;
1312 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1313 perror("read");
1314 break;
1317 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1318 #ifdef DEBUG
1319 printf("Compressed size is %u\n", csize);
1320 #endif
1322 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1323 #ifdef DEBUG
1324 printf("Filename length is %hu\n", fnlen);
1325 #endif
1327 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1328 #ifdef DEBUG
1329 printf("Extra field length is %hu\n", eflen);
1330 #endif
1332 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1333 #ifdef DEBUG
1334 printf("Flags are %#hx\n", flags);
1335 #endif
1337 method = UNPACK_UB2(file_header, LOC_COMP);
1338 #ifdef DEBUG
1339 printf("Compression method is %#hx\n", method);
1340 #endif
1342 /* if there isn't a data descriptor */
1343 if(!(flags & 0x0008)){
1344 crc = UNPACK_UB4(file_header, LOC_CRC);
1345 #ifdef DEBUG
1346 printf("CRC is %x\n", crc);
1347 #endif
1350 if(filename_len < fnlen + 1){
1351 if(filename != NULL)
1352 free(filename);
1354 filename = malloc(sizeof(ub1) * (fnlen + 1));
1355 filename_len = fnlen + 1;
1358 pb_read(&pbf, filename, fnlen);
1359 filename[fnlen] = '\0';
1361 #ifdef DEBUG
1362 printf("filename is %s\n", filename);
1363 #endif
1365 if(file_num > 0){
1366 handle = FALSE;
1368 for(j = 0; j < file_num; j++)
1369 if(strcmp(files[j], (const char *)filename) == 0){
1370 handle = TRUE;
1371 break;
1375 if(!handle)
1376 f_fd = -1;
1378 /* OK, there is some directory information in the file. Nothing to do
1379 but ensure the directory(s) exist, and create them if they don't.
1380 What a pain! */
1381 if(strchr((const char *)filename, '/') != NULL && handle){
1382 /* Loop through all the directories in the path, (everything w/ a '/') */
1383 const ub1 *start = filename;
1384 char *tmp_buff;
1385 struct stat sbuf;
1387 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1389 for(;;){
1390 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1392 if(idx == NULL)
1393 break;
1394 else if(idx == start){
1395 start++;
1396 continue;
1398 start = idx + 1;
1400 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1401 tmp_buff[(idx - filename)] = '\0';
1403 #ifdef DEBUG
1404 printf("checking the existance of %s\n", tmp_buff);
1405 #endif
1407 if(stat(tmp_buff, &sbuf) < 0){
1408 if(errno != ENOENT){
1409 perror("stat");
1410 exit(1);
1413 } else if(S_ISDIR(sbuf.st_mode)){
1414 #ifdef DEBUG
1415 printf("Directory exists\n");
1416 #endif
1417 continue;
1418 }else {
1419 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1420 tmp_buff);
1421 exit(1);
1424 #ifdef DEBUG
1425 printf("Making directory..\n");
1426 #endif
1427 if(mkdir(tmp_buff, 0755) < 0){
1428 perror("mkdir");
1429 exit(1);
1431 if(verbose && handle)
1432 printf("%10s: %s/\n", "created", tmp_buff);
1436 /* only a directory */
1437 if(strlen((const char *)start) == 0)
1438 dir = TRUE;
1440 #ifdef DEBUG
1441 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1442 #endif
1444 /* If the entry was just a directory, don't write to file, etc */
1445 if(strlen((const char *)start) == 0)
1446 f_fd = -1;
1448 free(tmp_buff);
1451 if(f_fd != -1 && handle){
1452 f_fd = creat((const char *)filename, 00644);
1454 if(f_fd < 0){
1455 fprintf(stderr, "Error extracting JAR archive!\n");
1456 perror((const char *)filename);
1457 exit(1);
1461 if(method != 8 && flags & 0x0008){
1462 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1463 exit(1);
1466 if(method == 8 || flags & 0x0008){
1467 if(seekable)
1468 lseek(fd, eflen, SEEK_CUR);
1469 else
1470 consume(&pbf, eflen);
1472 inflate_file(&pbf, f_fd, &ze);
1473 } else {
1475 #ifdef DEBUG
1476 printf("writing stored data.. (%d bytes)\n", csize);
1477 #endif
1479 out_a = 0;
1480 in_a = csize;
1482 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1484 while(out_a < (int)csize){
1485 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1486 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1487 perror("read");
1488 exit(1);
1491 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1493 if(f_fd >= 0)
1494 write(f_fd, rd_buff, rdamt);
1496 out_a += rdamt;
1497 in_a -= rdamt;
1499 #ifdef DEBUG
1500 printf("%d bytes written\n", out_a);
1501 #endif
1504 if(seekable)
1505 lseek(fd, eflen, SEEK_CUR);
1506 else
1507 consume(&pbf, eflen);
1510 /* if there is a data descriptor left, compare the CRC */
1511 if(flags & 0x0008){
1513 if(pb_read(&pbf, scratch, 16) != 16){
1514 perror("read");
1515 exit(1);
1518 signature = UNPACK_UB4(scratch, 0);
1520 if(signature != 0x08074b50){
1521 fprintf(stderr, "Error! Missing data descriptor!\n");
1522 exit(1);
1525 crc = UNPACK_UB4(scratch, 4);
1529 if(crc != ze.crc){
1530 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1531 ze.crc, crc);
1532 exit(1);
1535 close(f_fd);
1537 if(verbose && dir == FALSE && handle)
1538 printf("%10s: %s\n",
1539 (method == 8 ? "inflated" : "extracted"),
1540 filename);
1543 return 0;
1546 int list_jar(int fd, char **files, int file_num){
1547 int rdamt;
1548 ub4 signature;
1549 ub4 csize;
1550 ub4 usize;
1551 ub4 mdate;
1552 ub4 tmp;
1553 ub2 fnlen;
1554 ub2 eflen;
1555 ub2 clen;
1556 ub2 flags;
1557 ub2 method;
1558 ub2 cen_size;
1559 ub1 *filename = NULL;
1560 ub1 scratch[16];
1561 ub1 cen_header[46];
1562 int filename_len = 0;
1563 off_t size;
1564 int i, j;
1565 time_t tdate;
1566 struct tm *s_tm;
1567 char ascii_date[30];
1568 zipentry ze;
1570 #ifdef DEBUG
1571 printf("Listing jar file, looking for %d files\n", file_num);
1572 #endif
1574 /* This should be the start of the central-header-end section */
1575 if(seekable){
1576 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1577 perror("lseek");
1578 exit(1);
1581 if(read(fd, &tmp, sizeof(ub4)) != 4){
1582 perror("read");
1583 exit(1);
1586 #ifdef WORDS_BIGENDIAN
1587 tmp = L2BI(tmp);
1588 #endif
1590 if(tmp != 0x06054b50){
1591 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1592 exit(1);
1595 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1596 perror("lseek");
1597 exit(1);
1600 if(read(fd, &cen_size, 2) != 2){
1601 perror("read");
1602 exit(1);
1605 #ifdef WORDS_BIGENDIAN
1606 cen_size = L2BS(cen_size);
1607 #endif
1609 /* printf("%hu entries in central header\n", cen_size); */
1611 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1612 perror("lseek");
1613 exit(1);
1616 if(read(fd, &tmp, 4) != 4){
1617 perror("read");
1618 exit(1);
1621 #ifdef WORDS_BIGENDIAN
1622 tmp = L2BI(tmp);
1623 #endif
1625 /* printf("Central header offset = %d\n", tmp); */
1627 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1628 perror("lseek");
1629 exit(1);
1632 /* Loop through the entries in the central header */
1633 for(i = 0; i < cen_size; i++){
1635 if(read(fd, &cen_header, 46) != 46){
1636 perror("read");
1637 exit(1);
1640 signature = UNPACK_UB4(cen_header, 0);
1641 if(signature != 0x02014b50){
1642 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1643 exit(1);
1646 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1647 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1648 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1649 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1651 /* If we're providing verbose output, we need to make an ASCII
1652 * formatted version of the date. */
1653 if(verbose){
1654 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1655 tdate = dos2unixtime(mdate);
1656 s_tm = localtime(&tdate);
1657 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1660 if(filename_len < fnlen){
1661 if(filename != NULL)
1662 free(filename);
1664 filename = malloc(sizeof(ub1) * (fnlen + 1));
1665 filename_len = fnlen + 1;
1668 if(read(fd, filename, fnlen) != fnlen){
1669 perror("read");
1670 exit(1);
1672 filename[fnlen] = '\0';
1674 /* if the user specified a list of files on the command line,
1675 we'll only display those, otherwise we'll display everything */
1676 if(file_num > 0){
1677 for(j = 0; j < file_num; j++)
1678 if(strcmp(files[j], (const char *)filename) == 0){
1679 if(verbose)
1680 printf("%6d %s %s\n", usize, ascii_date, filename);
1681 else
1682 printf("%s\n", filename);
1683 break;
1685 } else {
1686 if(verbose)
1687 printf("%6d %s %s\n", usize, ascii_date, filename);
1688 else
1689 printf("%s\n", filename);
1692 size = eflen + clen;
1693 if(size > 0){
1694 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1695 perror("lseek");
1696 exit(1);
1700 } else {
1701 /* the file isn't seekable.. evil! */
1702 pb_file pbf;
1704 pb_init(&pbf, fd);
1706 init_inflation();
1708 for(;;){
1709 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1710 perror("read");
1711 break;
1714 signature = UNPACK_UB4(scratch, 0);
1716 #ifdef DEBUG
1717 printf("signature is %x\n", signature);
1718 #endif
1720 if(signature == 0x08074b50){
1721 #ifdef DEBUG
1722 printf("skipping data descriptor\n");
1723 #endif
1724 pb_read(&pbf, scratch, 12);
1725 continue;
1726 } else if(signature == 0x02014b50){
1727 #ifdef DEBUG
1728 printf("Central header reached.. we're all done!\n");
1729 #endif
1730 break;
1731 }else if(signature != 0x04034b50){
1732 #ifdef DEBUG
1733 printf("Ick! %#x\n", signature);
1734 #endif
1735 break;
1738 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1739 perror("read");
1740 break;
1743 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1744 #ifdef DEBUG
1745 printf("Compressed size is %u\n", csize);
1746 #endif
1748 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1749 #ifdef DEBUG
1750 printf("Filename length is %hu\n", fnlen);
1751 #endif
1753 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1754 #ifdef DEBUG
1755 printf("Extra field length is %hu\n", eflen);
1756 #endif
1758 method = UNPACK_UB2(file_header, LOC_COMP);
1759 #ifdef DEBUG
1760 printf("Compression method is %#hx\n", method);
1761 #endif
1763 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1764 #ifdef DEBUG
1765 printf("Flags are %#hx\n", flags);
1766 #endif
1768 usize = UNPACK_UB4(file_header, LOC_USIZE);
1770 /* If we're providing verbose output, we need to make an ASCII
1771 * formatted version of the date. */
1772 if(verbose){
1773 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1774 tdate = dos2unixtime(mdate);
1775 s_tm = localtime(&tdate);
1776 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1779 if(filename_len < fnlen){
1780 if(filename != NULL)
1781 free(filename);
1783 filename = malloc(sizeof(ub1) * (fnlen + 1));
1784 filename_len = fnlen + 1;
1787 pb_read(&pbf, filename, fnlen);
1788 filename[fnlen] = '\0';
1790 /* the header is at the end. In a JAR file, this means that the data
1791 happens to be compressed. We have no choice but to inflate the
1792 data */
1793 if(flags & 0x0008){
1795 size = eflen;
1797 if(size > 0)
1798 consume(&pbf, size);
1800 if(method == 8){
1801 #ifdef DEBUG
1802 printf("inflating %s\n", filename);
1803 #endif
1804 inflate_file(&pbf, -1, &ze);
1806 usize = ze.usize;
1807 } else
1808 printf("We're shit outta luck!\n");
1810 } else {
1811 size = csize + (eflen > 0 ? eflen : 0);
1814 #ifdef DEBUG
1815 printf("Skipping %ld bytes\n", (long)size);
1816 #endif
1818 consume(&pbf, size);
1820 /* print out the listing */
1821 if(file_num > 0){
1822 for(j = 0; j < file_num; j++)
1823 if(strcmp(files[j], (const char *)filename) == 0){
1824 if(verbose)
1825 printf("%6d %s %s\n", usize, ascii_date, filename);
1826 else
1827 printf("%s\n", filename);
1828 break;
1830 } else {
1831 if(verbose)
1832 printf("%6d %s %s\n", usize, ascii_date, filename);
1833 else
1834 printf("%s\n", filename);
1838 return 0;
1841 int consume(pb_file *pbf, int amt){
1842 int tc = 0; /* total amount consumed */
1843 ub1 buff[RDSZ];
1844 int rdamt;
1846 #ifdef DEBUG
1847 printf("Consuming %d bytes\n", amt);
1848 #endif
1850 while(tc < amt){
1851 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1852 #ifdef DEBUG
1853 printf("got %d bytes\n", rdamt);
1854 #endif
1855 tc += rdamt;
1858 #ifdef DEBUG
1859 printf("%d bytes consumed\n", tc);
1860 #endif
1862 return 0;
1865 void usage(const char *filename){
1866 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1867 exit (1);
1870 void version ()
1872 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1873 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1874 printf("Copyright 2002 Free Software Foundation\n");
1875 printf("\
1876 This is free software; see the source for copying conditions. There is NO\n\
1877 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1878 exit (0);
1881 void help(const char *filename)
1883 printf("\
1884 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1886 Store many files together in a single `jar' file.\n\
1888 -c create new archive\n\
1889 -t list table of contents for archive\n\
1890 -x extract named (or all) files from archive\n\
1891 -u update existing archive\n\
1892 ", filename);
1893 printf("\n\
1894 -@ read names from stdin\n\
1895 -0 store only; use no ZIP compression\n\
1896 -C DIR FILE change to the specified directory and include\n\
1897 the following file\n\
1898 -E don't include the files found in a directory\n\
1899 -f FILE specify archive file name\n\
1900 --help print this help, then exit\n\
1901 -m FILE include manifest information from specified manifest file\n\
1902 -M Do not create a manifest file for the entries\n\
1903 -v generate verbose output on standard output\n\
1904 -V, --version display version information\n\
1906 printf("\n\
1907 If any file is a directory then it is processed recursively.\n\
1908 The manifest file name and the archive file name needs to be specified\n\
1909 in the same order the 'm' and 'f' flags are specified.\n\
1911 Example 1: to archive two class files into an archive called classes.jar: \n\
1912 jar cvf classes.jar Foo.class Bar.class \n\
1913 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1914 files in the foo/ directory into 'classes.jar': \n\
1915 jar cvfm classes.jar mymanifest -C foo/ .\n\
1918 exit(0);
1921 static char *
1922 jt_strdup(s)
1923 char *s;
1925 char *result = (char*)malloc(strlen(s) + 1);
1926 if (result == (char*)0)
1927 return (char*)0;
1928 strcpy(result, s);
1929 return result;
1932 /* Convert "tar-style" first argument to a form expected by getopt.
1933 This idea and the code comes from GNU tar. This can allocate a new
1934 argument vector. This might leak some memory, but we don't care. */
1935 static void
1936 expand_options (int *argcp, char ***argvp)
1938 int argc = *argcp;
1939 char **argv = *argvp;
1941 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
1942 if a long argument (like "--help") is detected. */
1943 if (argc > 1 && argv[1][1] != '-')
1945 char buf[3];
1946 char **new_argv;
1947 int new_argc;
1948 int args_to_expand;
1949 char *p;
1950 char **in, **out;
1952 buf[0] = '-';
1953 buf[2] = '\0';
1955 args_to_expand = strlen (argv[1]);
1956 if (argv[1][0] == '-')
1957 --args_to_expand;
1959 new_argc = argc - 1 + args_to_expand;
1960 new_argv = (char **) malloc (new_argc * sizeof (char *));
1961 in = argv;
1962 out = new_argv;
1964 *out++ = *in++;
1965 p = *in++;
1966 if (*p == '-')
1967 p++;
1968 while (*p != '\0')
1970 char *opt;
1971 buf[1] = *p;
1972 *out++ = jt_strdup (buf);
1973 /* If the option takes an argument, move the next argument
1974 to just after this option. */
1975 opt = strchr (OPTION_STRING, *p);
1976 if (opt && opt[1] == ':')
1978 if (in < argv + argc)
1979 *out++ = *in++;
1980 else
1982 fprintf(stderr, "%s: option `%s' requires an argument.\n",
1983 argv[0], buf);
1984 usage(argv[0]);
1987 ++p;
1990 /* Copy remaining options. */
1991 while (in < argv + argc)
1992 *out++ = *in++;
1994 *argcp = new_argc;
1995 *argvp = new_argv;