* config/h8300/h8300.c (print_operand): Remove redundant code.
[official-gcc.git] / fastjar / jartool.c
blobafd08a758f282ce2200db7910b355c0ad2422379
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 except `-C', which is handled specially. */
307 #define OPTION_STRING "-ctxuvVf:m: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 1:
352 /* File name or unparsed option, due to RETURN_IN_ORDER. In
353 particular `-C' is handled here and not elsewhere. */
354 new_argv[new_argc++] = optarg;
355 break;
356 case 'c':
357 action = ACTION_CREATE;
358 break;
359 case 't':
360 action = ACTION_LIST;
361 break;
362 case 'x':
363 action = ACTION_EXTRACT;
364 break;
365 case 'u':
366 action = ACTION_UPDATE;
367 break;
368 case 'v':
369 verbose = TRUE;
370 break;
371 case 'V':
372 version();
373 exit(0);
374 case 'f':
375 jarfile = optarg;
376 break;
377 case 'm':
378 mfile = optarg;
379 break;
380 case '0':
381 do_compress = FALSE;
382 break;
383 case 'M':
384 manifest = FALSE;
385 break;
387 case OPT_HELP:
388 help(argv[0]);
389 break;
391 /* The following options aren't supported by the original jar tool. */
392 case 'E':
393 use_explicit_list_only = TRUE;
394 break;
395 case '@':
396 read_names_from_stdin = TRUE;
397 break;
398 default:
399 usage(argv[0]);
403 /* We might have seen `--'. In this case we want to make sure that
404 all following options are handled as file names. */
405 while (optind < argc)
406 new_argv[new_argc++] = argv[optind++];
407 new_argv[new_argc] = NULL;
409 if(action == ACTION_NONE){
410 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
411 usage(argv[0]);
414 if(action == ACTION_UPDATE){
415 fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
416 exit(1);
419 /* Verify unsupported combinations and warn of the use of non
420 standard features */
421 if(verbose && use_explicit_list_only)
422 fprintf (stderr, "Warning: using non standard '-E' option\n");
423 if(verbose && read_names_from_stdin)
424 fprintf (stderr, "Warning: using non standard '-@' option\n");
425 if(read_names_from_stdin
426 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
427 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
428 usage(argv[0]);
431 /* create the jarfile */
432 if(action == ACTION_CREATE){
433 if(jarfile){
434 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
435 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
437 if(jarfd < 0){
438 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
439 perror(jarfile);
440 exit(1);
443 /* We assume that the file is seekable */
444 seekable = TRUE;
446 } else {
448 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
450 /* standard out is not seekable */
451 seekable = FALSE;
453 /* don't want our output to be part of the jar file.. figured this one
454 out the hard way.. =P */
455 verbose = FALSE;
457 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
459 if(jarfile){
460 jarfd = open(jarfile, O_RDONLY | O_BINARY);
462 if(jarfd < 0){
463 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
464 perror(jarfile);
465 exit(1);
468 seekable = TRUE;
469 } else {
470 jarfd = STDIN_FILENO; /* jarfd is standard in */
472 /* we assume that the stream isn't seekable for safety */
473 seekable = FALSE;
477 if(action == ACTION_CREATE || action == ACTION_UPDATE){
478 const char *arg;
479 init_headers();
481 if((action == ACTION_UPDATE) && jarfile) {
482 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
483 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
484 perror(jarfile);
485 exit(1);
489 if(do_compress)
490 init_compression();
493 /* Add the META-INF/ directory and the manifest */
494 if(manifest && mfile)
495 make_manifest(jarfd, mfile);
496 else if(manifest)
497 make_manifest(jarfd, NULL);
499 init_args (new_argv, 0);
500 /* now we add the files to the archive */
501 while ((arg = get_next_arg ())){
503 if(!strcmp(arg, "-C")){
504 const char *dir_to_change = get_next_arg ();
505 const char *file_to_add = get_next_arg ();
506 if(!dir_to_change
507 || !file_to_add
508 || add_to_jar(jarfd, dir_to_change, file_to_add)){
509 printf("Error adding %s to jar archive!\n", arg);
510 exit(1);
512 } else {
513 if(add_to_jar(jarfd, NULL, arg)){
514 printf("Error adding %s to jar archive!\n", arg);
515 exit(1);
519 /* de-initialize the compression DS */
520 if(do_compress)
521 end_compression();
523 create_central_header(jarfd);
525 if (close(jarfd) != 0) {
526 fprintf(stderr, "Error closing jar archive!\n");
528 } else if(action == ACTION_LIST){
529 list_jar(jarfd, &new_argv[0], new_argc);
530 } else if(action == ACTION_EXTRACT){
531 extract_jar(jarfd, &new_argv[0], new_argc);
534 exit(0);
537 static int args_current_g;
538 static char **args_g;
540 static void
541 init_args(args, current)
542 char **args;
543 int current;
545 if(!read_names_from_stdin)
547 args_g = args;
548 args_current_g = current;
552 static char *
553 get_next_arg ()
555 static int reached_end = 0;
557 if (reached_end)
558 return NULL;
560 if (args_g)
562 if (!args_g [args_current_g])
564 reached_end = 1;
565 return NULL;
567 return args_g [args_current_g++];
569 else
571 /* Read the name from stdin. Delimiters are '\n' and
572 '\r'. Reading EOF indicates that we don't have anymore file
573 names characters to read. */
575 char s [MAXPATHLEN];
576 int pos = 0;
578 /* Get rid of '\n' and '\r' first. */
579 while (1)
581 int c = getc (stdin);
582 if (c == '\n' || c == '\r')
583 continue;
584 else
586 if (c == EOF)
587 return NULL;
588 ungetc (c, stdin);
589 break;
593 while (1)
595 int c = getc (stdin);
596 /* Exit when we get a delimiter or don't have any characters
597 to read */
598 if (c == '\n'|| c == '\r'|| c == EOF)
599 break;
600 s [pos++] = (char) c;
603 if (pos)
605 s [pos] = '\0';
606 return jt_strdup (s);
608 else
609 return NULL;
613 void init_headers(){
614 /* packing file header */
615 /* magic number */
616 file_header[0] = 0x50;
617 file_header[1] = 0x4b;
618 file_header[2] = 0x03;
619 file_header[3] = 0x04;
620 /* version number (Unix 1.0)*/
621 file_header[4] = 10;
622 file_header[5] = 0;
623 /* bit flag (normal deflation)*/
624 file_header[6] = 0x00;
626 file_header[7] = 0x00;
627 /* do_compression method (deflation) */
628 file_header[8] = 0;
629 file_header[9] = 0;
631 /* last mod file time (MS-DOS format) */
632 file_header[10] = 0;
633 file_header[11] = 0;
634 /* last mod file date (MS-DOS format) */
635 file_header[12] = 0;
636 file_header[13] = 0;
637 /* CRC 32 */
638 file_header[14] = 0;
639 file_header[15] = 0;
640 file_header[16] = 0;
641 file_header[17] = 0;
642 /* compressed size */
643 file_header[18] = 0;
644 file_header[19] = 0;
645 file_header[20] = 0;
646 file_header[21] = 0;
647 /* uncompressed size */
648 file_header[22] = 0;
649 file_header[23] = 0;
650 file_header[24] = 0;
651 file_header[25] = 0;
652 /* filename length */
653 file_header[26] = 0;
654 file_header[27] = 0;
655 /* extra field length */
656 file_header[28] = 0;
657 file_header[29] = 0;
659 /* Initialize the compression DS */
660 PACK_UB4(data_descriptor, 0, 0x08074b50);
664 void add_entry(struct zipentry *ze){
666 if(ziplist == NULL){
667 ziplist = ze;
668 ziptail = ziplist;
669 } else {
670 ziplist->next_entry = ze;
671 ziplist = ze;
674 number_of_entries++;
677 int make_manifest(int jfd, const char *mf_name){
678 time_t current_time;
679 int nlen; /* length of file name */
680 int mod_time; /* file modification time */
681 struct zipentry *ze;
683 nlen = 9; /* trust me on this one */
685 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
687 current_time = time(NULL);
688 if(current_time == (time_t)-1){
689 perror("time");
690 exit(1);
693 mod_time = unix2dostime(&current_time);
695 PACK_UB2(file_header, LOC_EXTRA, 0);
696 PACK_UB2(file_header, LOC_COMP, 0);
697 PACK_UB2(file_header, LOC_FNLEN, nlen);
698 PACK_UB4(file_header, LOC_MODTIME, mod_time);
700 if(verbose)
701 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
703 ze = (zipentry*)malloc(sizeof(zipentry));
704 if(ze == NULL){
705 perror("malloc");
706 exit(1);
709 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
710 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
711 strcpy(ze->filename, "META-INF/");
712 ze->filename[nlen] = '\0';
714 ze->offset = lseek(jfd, 0, SEEK_CUR);
715 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
716 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
717 ze->compressed = FALSE;
719 add_entry(ze);
721 write(jfd, file_header, 30);
722 write(jfd, "META-INF/", nlen);
724 /* if the user didn't specify an external manifest file... */
725 if(mf_name == NULL){
726 int mf_len = 37 + strlen(VERSION);
727 char *mf;
729 if((mf = (char *) malloc(mf_len + 1))) {
730 uLong crc;
732 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
734 crc = crc32(0L, Z_NULL, 0);
736 crc = crc32(crc, (const unsigned char *)mf, mf_len);
738 nlen = 20; /* once again, trust me */
740 PACK_UB2(file_header, LOC_EXTRA, 0);
741 PACK_UB2(file_header, LOC_COMP, 0);
742 PACK_UB2(file_header, LOC_FNLEN, nlen);
743 PACK_UB4(file_header, LOC_USIZE, mf_len);
745 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
747 PACK_UB4(file_header, LOC_CRC, crc);
749 if(verbose)
750 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
752 ze = (zipentry*)malloc(sizeof(zipentry));
753 if(ze == NULL){
754 perror("malloc");
755 exit(1);
758 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
759 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
760 strcpy(ze->filename, "META-INF/MANIFEST.MF");
761 ze->filename[nlen] = '\0';
763 ze->offset = lseek(jfd, 0, SEEK_CUR);
764 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
765 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
766 ze->crc = crc;
767 ze->csize = mf_len;
768 ze->usize = ze->csize;
769 ze->compressed = FALSE;
771 add_entry(ze);
773 write(jfd, file_header, 30);
774 write(jfd, "META-INF/MANIFEST.MF", nlen);
775 write(jfd, mf, mf_len);
776 free(mf);
778 else {
779 printf("malloc errror\n");
780 exit(-1);
782 } else {
783 int mfd;
784 struct stat statbuf;
786 stat(mf_name, &statbuf);
788 if(!S_ISREG(statbuf.st_mode)){
789 fprintf(stderr, "Invalid manifest file specified.\n");
790 exit(1);
793 mfd = open(mf_name, O_RDONLY | O_BINARY);
795 if(mfd < 0){
796 fprintf(stderr, "Error opening %s.\n", mf_name);
797 exit(1);
800 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
801 perror("error writing to jar");
802 exit(1);
807 return 0;
810 int add_to_jar(int fd, const char *new_dir, const char *file){
811 struct stat statbuf;
812 DIR *dir;
813 struct dirent *de;
814 zipentry *ze;
815 int stat_return;
816 char *old_dir = NULL;
818 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
819 * It fixes this:
820 * "normal" jar : org/apache/java/io/LogRecord.class
821 * fastjar : ./org/apache/java/io/LogRecord.class
822 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
823 * with both kaffe-1.0b4 and JDK.
825 while (*file=='.' && *(file+1)=='/')
826 file+=2;
828 /* If new_dir isn't null, we need to change to that directory. However,
829 we also need to return to the old directory when we're done */
830 if(new_dir != NULL){
831 old_dir = getcwd(NULL, 0);
833 if(chdir(new_dir) == -1){
834 perror(new_dir);
835 return 1;
839 if(!strcmp(file, jarfile)){
840 if(verbose)
841 printf("skipping: %s\n", file);
842 return 0; /* we don't want to add ourselves.. */
845 stat_return = stat(file, &statbuf);
847 if(stat_return == -1){
848 perror(file);
849 return 1;
850 } else if(S_ISDIR(statbuf.st_mode)){
851 char *fullname;
852 char *t_ptr;
853 int nlen;
854 unsigned long mod_time;
856 dir = opendir(file);
858 if(dir == NULL){
859 perror("opendir");
860 return 1;
863 nlen = strlen(file) + 256;
864 fullname = (char*)malloc(nlen * sizeof(char));
865 memset(fullname, 0, (nlen * sizeof(char)));
867 if(fullname == NULL){
868 fprintf(stderr, "Filename is NULL!\n");
869 return 1;
872 strcpy(fullname, file);
873 nlen = strlen(file);
875 if(fullname[nlen - 1] != '/'){
876 fullname[nlen] = '/';
877 t_ptr = (fullname + nlen + 1);
878 } else
879 t_ptr = (fullname + nlen);
882 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
884 nlen = (t_ptr - fullname);
886 mod_time = unix2dostime(&statbuf.st_mtime);
888 PACK_UB2(file_header, LOC_EXTRA, 0);
889 PACK_UB2(file_header, LOC_COMP, 0);
890 PACK_UB2(file_header, LOC_FNLEN, nlen);
891 PACK_UB4(file_header, LOC_MODTIME, mod_time);
893 if(verbose)
894 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
896 ze = (zipentry*)malloc(sizeof(zipentry));
897 if(ze == NULL){
898 perror("malloc");
899 exit(1);
902 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
903 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
904 strcpy(ze->filename, fullname);
905 ze->filename[nlen] = '\0';
907 ze->offset = lseek(fd, 0, SEEK_CUR);
908 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
909 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
910 ze->compressed = FALSE;
912 add_entry(ze);
914 write(fd, file_header, 30);
915 write(fd, fullname, nlen);
917 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
918 if(de->d_name[0] == '.')
919 continue;
920 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
921 if(verbose)
922 printf("skipping: %s\n", de->d_name);
923 continue;
926 strcpy(t_ptr, de->d_name);
928 if(add_to_jar(fd, NULL, fullname)){
929 fprintf(stderr, "Error adding file to jar!\n");
930 return 1;
934 free(fullname);
935 closedir(dir);
937 } else if(S_ISREG(statbuf.st_mode)){
938 int add_fd;
940 add_fd = open(file, O_RDONLY | O_BINARY);
941 if(add_fd < 0){
942 fprintf(stderr, "Error opening %s.\n", file);
943 return 0;
946 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
947 fprintf(stderr, "Error adding file to jar!\n");
948 return 1;
951 } else {
952 fprintf(stderr, "Illegal file specified: %s\n", file);
955 if(old_dir != NULL){
956 if(chdir(old_dir))
957 perror(old_dir);
959 free(old_dir);
962 return 0;
965 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
967 unsigned short file_name_length;
968 unsigned long mod_time;
969 ub1 rd_buff[RDSZ];
970 uLong crc = 0;
971 off_t offset = 0;
972 int rdamt;
973 struct zipentry *ze;
975 mod_time = unix2dostime(&(statbuf->st_mtime));
976 file_name_length = strlen(fname);
978 if(!seekable && !do_compress){
979 crc = crc32(0L, Z_NULL, 0);
981 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
982 crc = crc32(crc, rd_buff, rdamt);
984 lseek(ffd, 0, SEEK_SET);
987 /* data descriptor */
988 if(!seekable && do_compress){
989 PACK_UB2(file_header, LOC_EXTRA, 8);
990 } else {
991 PACK_UB2(file_header, LOC_EXTRA, 0);
994 if(do_compress){
995 PACK_UB2(file_header, LOC_COMP, 8);
996 } else {
997 PACK_UB2(file_header, LOC_COMP, 0);
1000 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1001 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1003 if(!seekable && !do_compress){
1004 PACK_UB4(file_header, LOC_CRC, crc);
1005 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1006 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1007 } else
1008 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1010 ze = (zipentry*)malloc(sizeof(zipentry));
1011 if(ze == NULL){
1012 perror("malloc");
1013 exit(1);
1016 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1017 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1018 strcpy(ze->filename, fname);
1020 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1021 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1023 if(!seekable && !do_compress)
1024 ze->crc = crc;
1026 ze->csize = statbuf->st_size;
1027 ze->usize = ze->csize;
1028 ze->offset = lseek(jfd, 0, SEEK_CUR);
1029 if(do_compress)
1030 ze->compressed = TRUE;
1031 else
1032 ze->compressed = FALSE;
1034 add_entry(ze);
1036 /* Write the local header */
1037 write(jfd, file_header, 30);
1039 /* write the file name to the zip file */
1040 write(jfd, fname, file_name_length);
1043 if(verbose){
1044 printf("adding: %s ", fname);
1045 fflush(stdout);
1048 if(do_compress){
1049 /* compress the file */
1050 compress_file(ffd, jfd, ze);
1051 } else {
1052 /* Write the contents of the file (uncompressed) to the zip file */
1053 /* calculate the CRC as we go along */
1054 ze->crc = crc32(0L, Z_NULL, 0);
1056 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1057 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1058 if(write(jfd, rd_buff, rdamt) != rdamt){
1059 perror("write");
1060 return 0;
1064 close(ffd);
1066 /* write out data descriptor */
1067 PACK_UB4(data_descriptor, 4, ze->crc);
1068 PACK_UB4(data_descriptor, 8, ze->csize);
1069 PACK_UB4(data_descriptor, 12, ze->usize);
1071 /* we need to seek back and fill the header */
1072 if(seekable){
1073 offset = (ze->csize + strlen(ze->filename) + 16);
1075 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1076 perror("lseek");
1077 exit(1);
1080 if(write(jfd, (data_descriptor + 4), 12) != 12){
1081 perror("write");
1082 return 0;
1085 offset -= 12;
1087 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1088 perror("lseek");
1089 exit(1);
1091 } else if(do_compress){
1092 /* Sun's jar tool will only allow a data descriptor if the entry is
1093 compressed, but we'll save 16 bytes/entry if we only use it when
1094 we can't seek back on the file */
1096 if(write(jfd, data_descriptor, 16) != 16){
1097 perror("write");
1098 return 0;
1102 if(verbose)
1103 printf("(in=%d) (out=%d) (%s %d%%)\n",
1104 (int)ze->usize, (int)ze->csize,
1105 (do_compress ? "deflated" : "stored"),
1106 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1108 return 0;
1111 int create_central_header(int fd){
1112 ub1 header[46];
1113 ub1 end_header[22];
1114 int start_offset;
1115 int dir_size;
1116 int *iheader;
1117 int total_in = 0, total_out = 22;
1119 zipentry *ze;
1121 iheader = (int*)header;
1123 /* magic number */
1124 header[0] = 'P';
1125 header[1] = 'K';
1126 header[2] = 1;
1127 header[3] = 2;
1128 /* version made by */
1129 header[4] = 10;
1130 header[5] = 0;
1131 /* version needed to extract */
1132 header[6] = 10;
1133 header[7] = 0;
1134 /* bit flag */
1135 header[8] = 0;
1136 header[9] = 0;
1137 /* compression method */
1138 header[10] = 0;
1139 header[11] = 0;
1140 /* file mod time */
1141 header[12] = 0;
1142 header[13] = 0;
1143 /* file mod date */
1144 header[14] = 0;
1145 header[15] = 0;
1146 /* crc 32 */
1147 header[16] = 0;
1148 header[17] = 0;
1149 header[18] = 0;
1150 header[19] = 0;
1151 /* compressed size */
1152 header[20] = 0;
1153 header[21] = 0;
1154 header[22] = 0;
1155 header[23] = 0;
1156 /* uncompressed size */
1157 header[24] = 0;
1158 header[25] = 0;
1159 header[26] = 0;
1160 header[27] = 0;
1161 /* filename length */
1162 header[28] = 0;
1163 header[29] = 0;
1164 /* extra field length */
1165 header[30] = 0;
1166 header[31] = 0;
1167 /* file comment length */
1168 header[32] = 0;
1169 header[33] = 0;
1170 /* disk number start */
1171 header[34] = 0;
1172 header[35] = 0;
1173 /* internal file attribs */
1174 header[36] = 0;
1175 header[37] = 0;
1176 /* external file attribs */
1177 header[38] = 0;
1178 header[39] = 0;
1179 header[40] = 0;
1180 header[41] = 0;
1181 /* relative offset of local header */
1182 header[42] = 0;
1183 header[43] = 0;
1184 header[44] = 0;
1185 header[45] = 0;
1187 start_offset = lseek(fd, 0, SEEK_CUR);
1189 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1191 total_in += ze->usize;
1192 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1194 if(ze->compressed){
1195 PACK_UB2(header, CEN_COMP, 8);
1196 } else {
1197 PACK_UB2(header, CEN_COMP, 0);
1200 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1201 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1202 PACK_UB4(header, CEN_CRC, ze->crc);
1203 PACK_UB4(header, CEN_CSIZE, ze->csize);
1204 PACK_UB4(header, CEN_USIZE, ze->usize);
1205 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1206 PACK_UB4(header, CEN_OFFSET, ze->offset);
1208 write(fd, header, 46);
1210 write(fd, ze->filename, strlen(ze->filename));
1213 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1215 /* magic number */
1216 end_header[0] = 0x50;
1217 end_header[1] = 0x4b;
1218 end_header[2] = 0x05;
1219 end_header[3] = 0x06;
1220 /* number of this disk */
1221 end_header[4] = 0;
1222 end_header[5] = 0;
1223 /* number of disk w/ start of central header */
1224 end_header[6] = 0;
1225 end_header[7] = 0;
1226 /* total number of entries in central dir on this disk*/
1227 PACK_UB2(end_header, 8, number_of_entries);
1228 /* total number of entries in central dir*/
1229 PACK_UB2(end_header, 10, number_of_entries);
1230 /* size of central dir. */
1231 PACK_UB4(end_header, 12, dir_size);
1232 /* offset of start of central dir */
1233 PACK_UB4(end_header, 16, start_offset);
1234 /* zipfile comment length */
1235 end_header[20] = 0;
1236 end_header[21] = 0;
1238 write(fd, end_header, 22);
1240 if(verbose)
1241 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1242 total_in,
1243 total_out,
1244 (do_compress ? "deflated" : "stored"),
1245 (int)((1 - (total_out / (float)total_in)) * 100)
1248 return 0;
1251 int extract_jar(int fd, char **files, int file_num){
1252 int rdamt;
1253 int out_a, in_a;
1254 ub4 signature;
1255 ub4 csize;
1256 ub4 crc;
1257 ub2 fnlen;
1258 ub2 eflen;
1259 ub2 flags;
1260 ub2 method;
1261 ub1 *filename = NULL;
1262 int filename_len = 0;
1263 ub4 rd_buff[RDSZ];
1264 pb_file pbf;
1265 ub1 scratch[16];
1266 zipentry ze;
1267 int f_fd;
1268 int dir;
1269 int handle;
1270 int j;
1272 init_inflation();
1274 pb_init(&pbf, fd);
1276 for(;;){
1277 f_fd = 0;
1278 crc = 0;
1279 ze.crc = 0;
1281 dir = FALSE; /* by default, the file isn't a dir */
1282 handle = TRUE; /* by default we'll extract/create the file */
1284 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1285 perror("read");
1286 break;
1289 signature = UNPACK_UB4(scratch, 0);
1291 #ifdef DEBUG
1292 printf("signature is %x\n", signature);
1293 #endif
1294 if(signature == 0x08074b50){
1295 #ifdef DEBUG
1296 printf("skipping data descriptor\n");
1297 #endif
1298 pb_read(&pbf, scratch, 12);
1299 continue;
1300 } else if(signature == 0x02014b50){
1301 #ifdef DEBUG
1302 printf("Central header reached.. we're all done!\n");
1303 #endif
1304 break;
1305 }else if(signature != 0x04034b50){
1306 printf("Ick! %#x\n", signature);
1307 break;
1310 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1311 perror("read");
1312 break;
1315 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1316 #ifdef DEBUG
1317 printf("Compressed size is %u\n", csize);
1318 #endif
1320 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1321 #ifdef DEBUG
1322 printf("Filename length is %hu\n", fnlen);
1323 #endif
1325 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1326 #ifdef DEBUG
1327 printf("Extra field length is %hu\n", eflen);
1328 #endif
1330 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1331 #ifdef DEBUG
1332 printf("Flags are %#hx\n", flags);
1333 #endif
1335 method = UNPACK_UB2(file_header, LOC_COMP);
1336 #ifdef DEBUG
1337 printf("Compression method is %#hx\n", method);
1338 #endif
1340 /* if there isn't a data descriptor */
1341 if(!(flags & 0x0008)){
1342 crc = UNPACK_UB4(file_header, LOC_CRC);
1343 #ifdef DEBUG
1344 printf("CRC is %x\n", crc);
1345 #endif
1348 if(filename_len < fnlen + 1){
1349 if(filename != NULL)
1350 free(filename);
1352 filename = malloc(sizeof(ub1) * (fnlen + 1));
1353 filename_len = fnlen + 1;
1356 pb_read(&pbf, filename, fnlen);
1357 filename[fnlen] = '\0';
1359 #ifdef DEBUG
1360 printf("filename is %s\n", filename);
1361 #endif
1363 if(file_num > 0){
1364 handle = FALSE;
1366 for(j = 0; j < file_num; j++)
1367 if(strcmp(files[j], (const char *)filename) == 0){
1368 handle = TRUE;
1369 break;
1373 if(!handle)
1374 f_fd = -1;
1376 /* OK, there is some directory information in the file. Nothing to do
1377 but ensure the directory(s) exist, and create them if they don't.
1378 What a pain! */
1379 if(strchr((const char *)filename, '/') != NULL && handle){
1380 /* Loop through all the directories in the path, (everything w/ a '/') */
1381 const ub1 *start = filename;
1382 char *tmp_buff;
1383 struct stat sbuf;
1385 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1387 for(;;){
1388 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1390 if(idx == NULL)
1391 break;
1392 else if(idx == start){
1393 start++;
1394 continue;
1396 start = idx + 1;
1398 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1399 tmp_buff[(idx - filename)] = '\0';
1401 #ifdef DEBUG
1402 printf("checking the existance of %s\n", tmp_buff);
1403 #endif
1405 if(stat(tmp_buff, &sbuf) < 0){
1406 if(errno != ENOENT){
1407 perror("stat");
1408 exit(1);
1411 } else if(S_ISDIR(sbuf.st_mode)){
1412 #ifdef DEBUG
1413 printf("Directory exists\n");
1414 #endif
1415 continue;
1416 }else {
1417 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1418 tmp_buff);
1419 exit(1);
1422 #ifdef DEBUG
1423 printf("Making directory..\n");
1424 #endif
1425 if(mkdir(tmp_buff, 0755) < 0){
1426 perror("mkdir");
1427 exit(1);
1429 if(verbose && handle)
1430 printf("%10s: %s/\n", "created", tmp_buff);
1434 /* only a directory */
1435 if(strlen((const char *)start) == 0)
1436 dir = TRUE;
1438 #ifdef DEBUG
1439 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1440 #endif
1442 /* If the entry was just a directory, don't write to file, etc */
1443 if(strlen((const char *)start) == 0)
1444 f_fd = -1;
1446 free(tmp_buff);
1449 if(f_fd != -1 && handle){
1450 f_fd = creat((const char *)filename, 00644);
1452 if(f_fd < 0){
1453 fprintf(stderr, "Error extracting JAR archive!\n");
1454 perror((const char *)filename);
1455 exit(1);
1459 if(method != 8 && flags & 0x0008){
1460 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1461 exit(1);
1464 if(method == 8 || flags & 0x0008){
1465 if(seekable)
1466 lseek(fd, eflen, SEEK_CUR);
1467 else
1468 consume(&pbf, eflen);
1470 inflate_file(&pbf, f_fd, &ze);
1471 } else {
1473 #ifdef DEBUG
1474 printf("writing stored data.. (%d bytes)\n", csize);
1475 #endif
1477 out_a = 0;
1478 in_a = csize;
1480 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1482 while(out_a < (int)csize){
1483 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1484 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1485 perror("read");
1486 exit(1);
1489 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1491 if(f_fd >= 0)
1492 write(f_fd, rd_buff, rdamt);
1494 out_a += rdamt;
1495 in_a -= rdamt;
1497 #ifdef DEBUG
1498 printf("%d bytes written\n", out_a);
1499 #endif
1502 if(seekable)
1503 lseek(fd, eflen, SEEK_CUR);
1504 else
1505 consume(&pbf, eflen);
1508 /* if there is a data descriptor left, compare the CRC */
1509 if(flags & 0x0008){
1511 if(pb_read(&pbf, scratch, 16) != 16){
1512 perror("read");
1513 exit(1);
1516 signature = UNPACK_UB4(scratch, 0);
1518 if(signature != 0x08074b50){
1519 fprintf(stderr, "Error! Missing data descriptor!\n");
1520 exit(1);
1523 crc = UNPACK_UB4(scratch, 4);
1527 if(crc != ze.crc){
1528 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1529 ze.crc, crc);
1530 exit(1);
1533 close(f_fd);
1535 if(verbose && dir == FALSE && handle)
1536 printf("%10s: %s\n",
1537 (method == 8 ? "inflated" : "extracted"),
1538 filename);
1541 return 0;
1544 int list_jar(int fd, char **files, int file_num){
1545 int rdamt;
1546 ub4 signature;
1547 ub4 csize;
1548 ub4 usize;
1549 ub4 mdate;
1550 ub4 tmp;
1551 ub2 fnlen;
1552 ub2 eflen;
1553 ub2 clen;
1554 ub2 flags;
1555 ub2 method;
1556 ub2 cen_size;
1557 ub1 *filename = NULL;
1558 ub1 scratch[16];
1559 ub1 cen_header[46];
1560 int filename_len = 0;
1561 off_t size;
1562 int i, j;
1563 time_t tdate;
1564 struct tm *s_tm;
1565 char ascii_date[30];
1566 zipentry ze;
1568 #ifdef DEBUG
1569 printf("Listing jar file, looking for %d files\n", file_num);
1570 #endif
1572 /* This should be the start of the central-header-end section */
1573 if(seekable){
1574 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1575 perror("lseek");
1576 exit(1);
1579 if(read(fd, &tmp, sizeof(ub4)) != 4){
1580 perror("read");
1581 exit(1);
1584 #ifdef WORDS_BIGENDIAN
1585 tmp = L2BI(tmp);
1586 #endif
1588 if(tmp != 0x06054b50){
1589 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1590 exit(1);
1593 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1594 perror("lseek");
1595 exit(1);
1598 if(read(fd, &cen_size, 2) != 2){
1599 perror("read");
1600 exit(1);
1603 #ifdef WORDS_BIGENDIAN
1604 cen_size = L2BS(cen_size);
1605 #endif
1607 /* printf("%hu entries in central header\n", cen_size); */
1609 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1610 perror("lseek");
1611 exit(1);
1614 if(read(fd, &tmp, 4) != 4){
1615 perror("read");
1616 exit(1);
1619 #ifdef WORDS_BIGENDIAN
1620 tmp = L2BI(tmp);
1621 #endif
1623 /* printf("Central header offset = %d\n", tmp); */
1625 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1626 perror("lseek");
1627 exit(1);
1630 /* Loop through the entries in the central header */
1631 for(i = 0; i < cen_size; i++){
1633 if(read(fd, &cen_header, 46) != 46){
1634 perror("read");
1635 exit(1);
1638 signature = UNPACK_UB4(cen_header, 0);
1639 if(signature != 0x02014b50){
1640 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1641 exit(1);
1644 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1645 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1646 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1647 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1649 /* If we're providing verbose output, we need to make an ASCII
1650 * formatted version of the date. */
1651 if(verbose){
1652 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1653 tdate = dos2unixtime(mdate);
1654 s_tm = localtime(&tdate);
1655 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1658 if(filename_len < fnlen){
1659 if(filename != NULL)
1660 free(filename);
1662 filename = malloc(sizeof(ub1) * (fnlen + 1));
1663 filename_len = fnlen + 1;
1666 if(read(fd, filename, fnlen) != fnlen){
1667 perror("read");
1668 exit(1);
1670 filename[fnlen] = '\0';
1672 /* if the user specified a list of files on the command line,
1673 we'll only display those, otherwise we'll display everything */
1674 if(file_num > 0){
1675 for(j = 0; j < file_num; j++)
1676 if(strcmp(files[j], (const char *)filename) == 0){
1677 if(verbose)
1678 printf("%6d %s %s\n", usize, ascii_date, filename);
1679 else
1680 printf("%s\n", filename);
1681 break;
1683 } else {
1684 if(verbose)
1685 printf("%6d %s %s\n", usize, ascii_date, filename);
1686 else
1687 printf("%s\n", filename);
1690 size = eflen + clen;
1691 if(size > 0){
1692 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1693 perror("lseek");
1694 exit(1);
1698 } else {
1699 /* the file isn't seekable.. evil! */
1700 pb_file pbf;
1702 pb_init(&pbf, fd);
1704 init_inflation();
1706 for(;;){
1707 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1708 perror("read");
1709 break;
1712 signature = UNPACK_UB4(scratch, 0);
1714 #ifdef DEBUG
1715 printf("signature is %x\n", signature);
1716 #endif
1718 if(signature == 0x08074b50){
1719 #ifdef DEBUG
1720 printf("skipping data descriptor\n");
1721 #endif
1722 pb_read(&pbf, scratch, 12);
1723 continue;
1724 } else if(signature == 0x02014b50){
1725 #ifdef DEBUG
1726 printf("Central header reached.. we're all done!\n");
1727 #endif
1728 break;
1729 }else if(signature != 0x04034b50){
1730 #ifdef DEBUG
1731 printf("Ick! %#x\n", signature);
1732 #endif
1733 break;
1736 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1737 perror("read");
1738 break;
1741 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1742 #ifdef DEBUG
1743 printf("Compressed size is %u\n", csize);
1744 #endif
1746 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1747 #ifdef DEBUG
1748 printf("Filename length is %hu\n", fnlen);
1749 #endif
1751 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1752 #ifdef DEBUG
1753 printf("Extra field length is %hu\n", eflen);
1754 #endif
1756 method = UNPACK_UB2(file_header, LOC_COMP);
1757 #ifdef DEBUG
1758 printf("Compression method is %#hx\n", method);
1759 #endif
1761 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1762 #ifdef DEBUG
1763 printf("Flags are %#hx\n", flags);
1764 #endif
1766 usize = UNPACK_UB4(file_header, LOC_USIZE);
1768 /* If we're providing verbose output, we need to make an ASCII
1769 * formatted version of the date. */
1770 if(verbose){
1771 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1772 tdate = dos2unixtime(mdate);
1773 s_tm = localtime(&tdate);
1774 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1777 if(filename_len < fnlen){
1778 if(filename != NULL)
1779 free(filename);
1781 filename = malloc(sizeof(ub1) * (fnlen + 1));
1782 filename_len = fnlen + 1;
1785 pb_read(&pbf, filename, fnlen);
1786 filename[fnlen] = '\0';
1788 /* the header is at the end. In a JAR file, this means that the data
1789 happens to be compressed. We have no choice but to inflate the
1790 data */
1791 if(flags & 0x0008){
1793 size = eflen;
1795 if(size > 0)
1796 consume(&pbf, size);
1798 if(method == 8){
1799 #ifdef DEBUG
1800 printf("inflating %s\n", filename);
1801 #endif
1802 inflate_file(&pbf, -1, &ze);
1804 usize = ze.usize;
1805 } else
1806 printf("We're shit outta luck!\n");
1808 } else {
1809 size = csize + (eflen > 0 ? eflen : 0);
1812 #ifdef DEBUG
1813 printf("Skipping %ld bytes\n", (long)size);
1814 #endif
1816 consume(&pbf, size);
1818 /* print out the listing */
1819 if(file_num > 0){
1820 for(j = 0; j < file_num; j++)
1821 if(strcmp(files[j], (const char *)filename) == 0){
1822 if(verbose)
1823 printf("%6d %s %s\n", usize, ascii_date, filename);
1824 else
1825 printf("%s\n", filename);
1826 break;
1828 } else {
1829 if(verbose)
1830 printf("%6d %s %s\n", usize, ascii_date, filename);
1831 else
1832 printf("%s\n", filename);
1836 return 0;
1839 int consume(pb_file *pbf, int amt){
1840 int tc = 0; /* total amount consumed */
1841 ub1 buff[RDSZ];
1842 int rdamt;
1844 #ifdef DEBUG
1845 printf("Consuming %d bytes\n", amt);
1846 #endif
1848 while(tc < amt){
1849 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1850 #ifdef DEBUG
1851 printf("got %d bytes\n", rdamt);
1852 #endif
1853 tc += rdamt;
1856 #ifdef DEBUG
1857 printf("%d bytes consumed\n", tc);
1858 #endif
1860 return 0;
1863 void usage(const char *filename){
1864 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1865 exit (1);
1868 void version ()
1870 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1871 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1872 printf("Copyright 2002 Free Software Foundation\n");
1873 printf("\
1874 This is free software; see the source for copying conditions. There is NO\n\
1875 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1876 exit (0);
1879 void help(const char *filename)
1881 printf("\
1882 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1884 Store many files together in a single `jar' file.\n\
1886 -c create new archive\n\
1887 -t list table of contents for archive\n\
1888 -x extract named (or all) files from archive\n\
1889 -u update existing archive\n\
1890 ", filename);
1891 printf("\n\
1892 -@ read names from stdin\n\
1893 -0 store only; use no ZIP compression\n\
1894 -C DIR FILE change to the specified directory and include\n\
1895 the following file\n\
1896 -E don't include the files found in a directory\n\
1897 -f FILE specify archive file name\n\
1898 --help print this help, then exit\n\
1899 -m FILE include manifest information from specified manifest file\n\
1900 -M Do not create a manifest file for the entries\n\
1901 -v generate verbose output on standard output\n\
1902 -V, --version display version information\n\
1904 printf("\n\
1905 If any file is a directory then it is processed recursively.\n\
1906 The manifest file name and the archive file name needs to be specified\n\
1907 in the same order the 'm' and 'f' flags are specified.\n\
1909 Example 1: to archive two class files into an archive called classes.jar: \n\
1910 jar cvf classes.jar Foo.class Bar.class \n\
1911 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1912 files in the foo/ directory into 'classes.jar': \n\
1913 jar cvfm classes.jar mymanifest -C foo/ .\n\
1916 exit(0);
1919 static char *
1920 jt_strdup(s)
1921 char *s;
1923 char *result = (char*)malloc(strlen(s) + 1);
1924 if (result == (char*)0)
1925 return (char*)0;
1926 strcpy(result, s);
1927 return result;
1930 /* Convert "tar-style" first argument to a form expected by getopt.
1931 This idea and the code comes from GNU tar. This can allocate a new
1932 argument vector. This might leak some memory, but we don't care. */
1933 static void
1934 expand_options (int *argcp, char ***argvp)
1936 int argc = *argcp;
1937 char **argv = *argvp;
1939 if (argc > 1 && argv[1][0] != '-')
1941 char buf[3];
1942 char **new_argv;
1943 int new_argc;
1944 char *p;
1945 char **in, **out;
1947 buf[0] = '-';
1948 buf[2] = '\0';
1950 new_argc = argc - 1 + strlen (argv[1]);
1951 new_argv = (char **) malloc (new_argc * sizeof (char *));
1952 in = argv;
1953 out = new_argv;
1955 *out++ = *in++;
1956 for (p = *in++; *p; ++p)
1958 char *opt;
1959 buf[1] = *p;
1960 *out++ = jt_strdup (buf);
1961 /* If the option takes an argument, move the next argument
1962 to just after this option. */
1963 opt = strchr (OPTION_STRING, *p);
1964 if (opt && opt[1] == ':')
1966 if (in < argv + argc)
1967 *out++ = *in++;
1968 else
1970 fprintf(stderr, "%s: option `%s' requires an argument.\n",
1971 argv[0], buf);
1972 usage(argv[0]);
1977 /* Copy remaining options. */
1978 while (in < argv + argc)
1979 *out++ = *in++;
1981 *argcp = new_argc;
1982 *argvp = new_argv;