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>
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
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.
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
73 * compress.c: Include stdlib.h and compress.h.
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.
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>
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
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
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
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
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
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
170 Revision 1.9 1999/04/21 09:55:16 burnsbr
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
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
188 Revision 1.3 1999/04/20 05:08:54 burnsbr
206 #include <sys/stat.h>
207 #include <sys/types.h>
209 #ifdef HAVE_SYS_PARAM_H
210 #include <sys/param.h>
214 #define MAXPATHLEN 1024
228 #ifdef TM_IN_SYS_TIME
229 #include <sys/time.h>
239 #include "pushback.h"
240 #include "compress.h"
242 /* Some systems have mkdir that takes a single argument. */
243 #ifdef MKDIR_TAKES_ONE_ARG
244 # define mkdir(a,b) mkdir(a)
248 #ifdef WORDS_BIGENDIAN
250 #define L2BI(l) ((l & 0xff000000) >> 24) | \
251 ((l & 0x00ff0000) >> 8) | \
252 ((l & 0x0000ff00) << 8) | \
253 ((l & 0x000000ff) << 24);
255 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
267 void usage(const char*);
268 void help(const char *);
270 void add_entry(struct zipentry *);
271 void init_headers(void);
273 int consume(pb_file *, int);
274 int list_jar(int, char**, int);
275 int extract_jar(int, char**, int);
276 int add_file_to_jar(int, int, const char*, struct stat*);
277 int add_to_jar(int, const char*, const char*);
278 int create_central_header(int);
279 int make_manifest(int, const char*);
280 static void init_args(char **, int);
281 static char *get_next_arg (void);
282 static char *jt_strdup (char*);
283 static void expand_options (int *argcp, char ***argvp);
285 /* global variables */
287 ub1 data_descriptor[16];
293 /* If non zero, then don't recurse in directory. Instead, add the
294 directory entry and relie on an explicit list of files to populate
295 the archive. This option isn't supported by the original jar tool. */
296 int use_explicit_list_only;
298 /* If non zero, then read the entry names from stdin. This option
299 isn't supported by the original jar tool. */
300 int read_names_from_stdin;
302 zipentry *ziplist; /* linked list of entries */
303 zipentry *ziptail; /* tail of the linked list */
305 int number_of_entries; /* number of entries in the linked list */
307 /* This is used to mark options with no short value. */
308 #define LONG_OPT(Num) ((Num) + 128)
310 #define OPT_HELP LONG_OPT (0)
312 /* This holds all options. */
313 #define OPTION_STRING "-ctxuvVf:m:C:0ME@"
315 static const struct option options[] =
317 { "help", no_argument, NULL, OPT_HELP },
318 { "version", no_argument, NULL, 'V' },
319 { NULL, no_argument, NULL, 0 }
322 int main(int argc, char **argv){
326 int action = ACTION_NONE;
333 /* These are used to collect file names and `-C' options for the
334 second pass through the command line. */
343 number_of_entries = 0;
351 new_argv = (char **) malloc (argc * sizeof (char *));
353 expand_options (&argc, &argv);
354 while ((opt = getopt_long (argc, argv, OPTION_STRING,
355 options, NULL)) != -1) {
358 new_argv[new_argc++] = (char *) "-C";
359 /* ... fall through ... */
361 /* File name or unparsed option, due to RETURN_IN_ORDER. */
362 new_argv[new_argc++] = optarg;
365 action = ACTION_CREATE;
368 action = ACTION_LIST;
371 action = ACTION_EXTRACT;
374 action = ACTION_UPDATE;
399 /* The following options aren't supported by the original jar tool. */
401 use_explicit_list_only = TRUE;
404 read_names_from_stdin = TRUE;
411 /* We might have seen `--'. In this case we want to make sure that
412 all following options are handled as file names. */
413 while (optind < argc)
414 new_argv[new_argc++] = argv[optind++];
415 new_argv[new_argc] = NULL;
417 if(action == ACTION_NONE){
418 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
422 if(action == ACTION_UPDATE){
423 fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
427 /* Verify unsupported combinations and warn of the use of non
429 if(verbose && use_explicit_list_only)
430 fprintf (stderr, "Warning: using non standard '-E' option\n");
431 if(verbose && read_names_from_stdin)
432 fprintf (stderr, "Warning: using non standard '-@' option\n");
433 if(read_names_from_stdin
434 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
435 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
439 /* create the jarfile */
440 if(action == ACTION_CREATE){
442 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
445 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
450 /* We assume that the file is seekable */
455 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
457 /* standard out is not seekable */
460 /* don't want our output to be part of the jar file.. figured this one
461 out the hard way.. =P */
464 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
467 jarfd = open(jarfile, O_RDONLY | O_BINARY);
470 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
477 jarfd = STDIN_FILENO; /* jarfd is standard in */
479 /* we assume that the stream isn't seekable for safety */
484 if(action == ACTION_CREATE || action == ACTION_UPDATE){
488 if((action == ACTION_UPDATE) && jarfile) {
489 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
490 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
500 /* Add the META-INF/ directory and the manifest */
501 if(manifest && mfile)
502 make_manifest(jarfd, mfile);
504 make_manifest(jarfd, NULL);
506 init_args (new_argv, 0);
507 /* now we add the files to the archive */
508 while ((arg = get_next_arg ())){
510 if(!strcmp(arg, "-C")){
511 const char *dir_to_change = get_next_arg ();
512 const char *file_to_add = get_next_arg ();
515 || add_to_jar(jarfd, dir_to_change, file_to_add)){
516 printf("Error adding %s to jar archive!\n", arg);
520 if(add_to_jar(jarfd, NULL, arg)){
521 printf("Error adding %s to jar archive!\n", arg);
526 /* de-initialize the compression DS */
530 create_central_header(jarfd);
532 if (close(jarfd) != 0) {
533 fprintf(stderr, "Error closing jar archive!\n");
535 } else if(action == ACTION_LIST){
536 list_jar(jarfd, &new_argv[0], new_argc);
537 } else if(action == ACTION_EXTRACT){
538 extract_jar(jarfd, &new_argv[0], new_argc);
544 static int args_current_g;
545 static char **args_g;
548 init_args(args, current)
552 if(!read_names_from_stdin)
555 args_current_g = current;
562 static int reached_end = 0;
569 if (!args_g [args_current_g])
574 return args_g [args_current_g++];
578 /* Read the name from stdin. Delimiters are '\n' and
579 '\r'. Reading EOF indicates that we don't have anymore file
580 names characters to read. */
585 /* Get rid of '\n' and '\r' first. */
588 int c = getc (stdin);
589 if (c == '\n' || c == '\r')
602 int c = getc (stdin);
603 /* Exit when we get a delimiter or don't have any characters
605 if (c == '\n'|| c == '\r'|| c == EOF)
607 s [pos++] = (char) c;
613 return jt_strdup (s);
621 /* packing file header */
623 file_header[0] = 0x50;
624 file_header[1] = 0x4b;
625 file_header[2] = 0x03;
626 file_header[3] = 0x04;
627 /* version number (Unix 1.0)*/
630 /* bit flag (normal deflation)*/
631 file_header[6] = 0x00;
633 file_header[7] = 0x00;
634 /* do_compression method (deflation) */
638 /* last mod file time (MS-DOS format) */
641 /* last mod file date (MS-DOS format) */
649 /* compressed size */
654 /* uncompressed size */
659 /* filename length */
662 /* extra field length */
666 /* Initialize the compression DS */
667 PACK_UB4(data_descriptor, 0, 0x08074b50);
671 void add_entry(struct zipentry *ze){
677 ziplist->next_entry = ze;
684 int make_manifest(int jfd, const char *mf_name){
686 int nlen; /* length of file name */
687 int mod_time; /* file modification time */
690 nlen = 9; /* trust me on this one */
692 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
694 current_time = time(NULL);
695 if(current_time == (time_t)-1){
700 mod_time = unix2dostime(¤t_time);
702 PACK_UB2(file_header, LOC_EXTRA, 0);
703 PACK_UB2(file_header, LOC_COMP, 0);
704 PACK_UB2(file_header, LOC_FNLEN, nlen);
705 PACK_UB4(file_header, LOC_MODTIME, mod_time);
708 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
710 ze = (zipentry*)malloc(sizeof(zipentry));
716 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
717 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
718 strcpy(ze->filename, "META-INF/");
719 ze->filename[nlen] = '\0';
721 ze->offset = lseek(jfd, 0, SEEK_CUR);
722 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
723 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
724 ze->compressed = FALSE;
728 write(jfd, file_header, 30);
729 write(jfd, "META-INF/", nlen);
731 /* if the user didn't specify an external manifest file... */
733 int mf_len = 37 + strlen(VERSION);
736 if((mf = (char *) malloc(mf_len + 1))) {
739 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
741 crc = crc32(0L, Z_NULL, 0);
743 crc = crc32(crc, (const unsigned char *)mf, mf_len);
745 nlen = 20; /* once again, trust me */
747 PACK_UB2(file_header, LOC_EXTRA, 0);
748 PACK_UB2(file_header, LOC_COMP, 0);
749 PACK_UB2(file_header, LOC_FNLEN, nlen);
750 PACK_UB4(file_header, LOC_USIZE, mf_len);
752 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
754 PACK_UB4(file_header, LOC_CRC, crc);
757 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
759 ze = (zipentry*)malloc(sizeof(zipentry));
765 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
766 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
767 strcpy(ze->filename, "META-INF/MANIFEST.MF");
768 ze->filename[nlen] = '\0';
770 ze->offset = lseek(jfd, 0, SEEK_CUR);
771 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
772 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
775 ze->usize = ze->csize;
776 ze->compressed = FALSE;
780 write(jfd, file_header, 30);
781 write(jfd, "META-INF/MANIFEST.MF", nlen);
782 write(jfd, mf, mf_len);
786 printf("malloc errror\n");
793 stat(mf_name, &statbuf);
795 if(!S_ISREG(statbuf.st_mode)){
796 fprintf(stderr, "Invalid manifest file specified.\n");
800 mfd = open(mf_name, O_RDONLY | O_BINARY);
803 fprintf(stderr, "Error opening %s.\n", mf_name);
807 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
808 perror("error writing to jar");
817 int add_to_jar(int fd, const char *new_dir, const char *file){
823 char *old_dir = NULL;
825 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
827 * "normal" jar : org/apache/java/io/LogRecord.class
828 * fastjar : ./org/apache/java/io/LogRecord.class
829 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
830 * with both kaffe-1.0b4 and JDK.
832 while (*file=='.' && *(file+1)=='/')
835 /* If new_dir isn't null, we need to change to that directory. However,
836 we also need to return to the old directory when we're done */
838 old_dir = getcwd(NULL, 0);
840 if(chdir(new_dir) == -1){
846 if(jarfile && !strcmp(file, jarfile)){
848 printf("skipping: %s\n", file);
849 return 0; /* we don't want to add ourselves.. */
852 stat_return = stat(file, &statbuf);
854 if(stat_return == -1){
857 } else if(S_ISDIR(statbuf.st_mode)){
861 unsigned long mod_time;
870 nlen = strlen(file) + 256;
871 fullname = (char*)malloc(nlen * sizeof(char));
872 memset(fullname, 0, (nlen * sizeof(char)));
874 if(fullname == NULL){
875 fprintf(stderr, "Filename is NULL!\n");
879 strcpy(fullname, file);
882 if(fullname[nlen - 1] != '/'){
883 fullname[nlen] = '/';
884 t_ptr = (fullname + nlen + 1);
886 t_ptr = (fullname + nlen);
889 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
891 nlen = (t_ptr - fullname);
893 mod_time = unix2dostime(&statbuf.st_mtime);
895 PACK_UB2(file_header, LOC_EXTRA, 0);
896 PACK_UB2(file_header, LOC_COMP, 0);
897 PACK_UB2(file_header, LOC_FNLEN, nlen);
898 PACK_UB4(file_header, LOC_MODTIME, mod_time);
901 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
903 ze = (zipentry*)malloc(sizeof(zipentry));
909 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
910 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
911 strcpy(ze->filename, fullname);
912 ze->filename[nlen] = '\0';
914 ze->offset = lseek(fd, 0, SEEK_CUR);
915 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
916 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
917 ze->compressed = FALSE;
921 write(fd, file_header, 30);
922 write(fd, fullname, nlen);
924 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
925 if(de->d_name[0] == '.')
927 if(jarfile && !strcmp(de->d_name, jarfile)){
928 /* we don't want to add ourselves. Believe me */
930 printf("skipping: %s\n", de->d_name);
934 strcpy(t_ptr, de->d_name);
936 if(add_to_jar(fd, NULL, fullname)){
937 fprintf(stderr, "Error adding file to jar!\n");
945 } else if(S_ISREG(statbuf.st_mode)){
948 add_fd = open(file, O_RDONLY | O_BINARY);
950 fprintf(stderr, "Error opening %s.\n", file);
954 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
955 fprintf(stderr, "Error adding file to jar!\n");
960 fprintf(stderr, "Illegal file specified: %s\n", file);
973 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
975 unsigned short file_name_length;
976 unsigned long mod_time;
983 mod_time = unix2dostime(&(statbuf->st_mtime));
984 file_name_length = strlen(fname);
986 if(!seekable && !do_compress){
987 crc = crc32(0L, Z_NULL, 0);
989 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
990 crc = crc32(crc, rd_buff, rdamt);
992 lseek(ffd, 0, SEEK_SET);
995 /* data descriptor */
996 if(!seekable && do_compress){
997 PACK_UB2(file_header, LOC_EXTRA, 8);
999 PACK_UB2(file_header, LOC_EXTRA, 0);
1003 PACK_UB2(file_header, LOC_COMP, 8);
1005 PACK_UB2(file_header, LOC_COMP, 0);
1008 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1009 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1011 if(!seekable && !do_compress){
1012 PACK_UB4(file_header, LOC_CRC, crc);
1013 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1014 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1016 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1018 ze = (zipentry*)malloc(sizeof(zipentry));
1024 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1025 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1026 strcpy(ze->filename, fname);
1028 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1029 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1031 if(!seekable && !do_compress)
1034 ze->csize = statbuf->st_size;
1035 ze->usize = ze->csize;
1036 ze->offset = lseek(jfd, 0, SEEK_CUR);
1038 ze->compressed = TRUE;
1040 ze->compressed = FALSE;
1044 /* Write the local header */
1045 write(jfd, file_header, 30);
1047 /* write the file name to the zip file */
1048 write(jfd, fname, file_name_length);
1052 printf("adding: %s ", fname);
1057 /* compress the file */
1058 compress_file(ffd, jfd, ze);
1060 /* Write the contents of the file (uncompressed) to the zip file */
1061 /* calculate the CRC as we go along */
1062 ze->crc = crc32(0L, Z_NULL, 0);
1064 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1065 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1066 if(write(jfd, rd_buff, rdamt) != rdamt){
1074 /* write out data descriptor */
1075 PACK_UB4(data_descriptor, 4, ze->crc);
1076 PACK_UB4(data_descriptor, 8, ze->csize);
1077 PACK_UB4(data_descriptor, 12, ze->usize);
1079 /* we need to seek back and fill the header */
1081 offset = (ze->csize + strlen(ze->filename) + 16);
1083 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1088 if(write(jfd, (data_descriptor + 4), 12) != 12){
1095 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1099 } else if(do_compress){
1100 /* Sun's jar tool will only allow a data descriptor if the entry is
1101 compressed, but we'll save 16 bytes/entry if we only use it when
1102 we can't seek back on the file */
1104 if(write(jfd, data_descriptor, 16) != 16){
1111 printf("(in=%d) (out=%d) (%s %d%%)\n",
1112 (int)ze->usize, (int)ze->csize,
1113 (do_compress ? "deflated" : "stored"),
1114 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1119 int create_central_header(int fd){
1125 int total_in = 0, total_out = 22;
1129 iheader = (int*)header;
1136 /* version made by */
1139 /* version needed to extract */
1145 /* compression method */
1159 /* compressed size */
1164 /* uncompressed size */
1169 /* filename length */
1172 /* extra field length */
1175 /* file comment length */
1178 /* disk number start */
1181 /* internal file attribs */
1184 /* external file attribs */
1189 /* relative offset of local header */
1195 start_offset = lseek(fd, 0, SEEK_CUR);
1197 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1199 total_in += ze->usize;
1200 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1203 PACK_UB2(header, CEN_COMP, 8);
1205 PACK_UB2(header, CEN_COMP, 0);
1208 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1209 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1210 PACK_UB4(header, CEN_CRC, ze->crc);
1211 PACK_UB4(header, CEN_CSIZE, ze->csize);
1212 PACK_UB4(header, CEN_USIZE, ze->usize);
1213 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1214 PACK_UB4(header, CEN_OFFSET, ze->offset);
1216 write(fd, header, 46);
1218 write(fd, ze->filename, strlen(ze->filename));
1221 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1224 end_header[0] = 0x50;
1225 end_header[1] = 0x4b;
1226 end_header[2] = 0x05;
1227 end_header[3] = 0x06;
1228 /* number of this disk */
1231 /* number of disk w/ start of central header */
1234 /* total number of entries in central dir on this disk*/
1235 PACK_UB2(end_header, 8, number_of_entries);
1236 /* total number of entries in central dir*/
1237 PACK_UB2(end_header, 10, number_of_entries);
1238 /* size of central dir. */
1239 PACK_UB4(end_header, 12, dir_size);
1240 /* offset of start of central dir */
1241 PACK_UB4(end_header, 16, start_offset);
1242 /* zipfile comment length */
1246 write(fd, end_header, 22);
1249 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1252 (do_compress ? "deflated" : "stored"),
1253 (int)((1 - (total_out / (float)total_in)) * 100)
1259 int extract_jar(int fd, char **files, int file_num){
1269 ub1 *filename = NULL;
1270 int filename_len = 0;
1289 dir = FALSE; /* by default, the file isn't a dir */
1290 handle = TRUE; /* by default we'll extract/create the file */
1292 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1297 signature = UNPACK_UB4(scratch, 0);
1300 printf("signature is %x\n", signature);
1302 if(signature == 0x08074b50){
1304 printf("skipping data descriptor\n");
1306 pb_read(&pbf, scratch, 12);
1308 } else if(signature == 0x02014b50){
1310 printf("Central header reached.. we're all done!\n");
1313 }else if(signature != 0x04034b50){
1314 printf("Ick! %#x\n", signature);
1318 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1323 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1325 printf("Compressed size is %u\n", csize);
1328 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1330 printf("Filename length is %hu\n", fnlen);
1333 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1335 printf("Extra field length is %hu\n", eflen);
1338 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1340 printf("Flags are %#hx\n", flags);
1343 method = UNPACK_UB2(file_header, LOC_COMP);
1345 printf("Compression method is %#hx\n", method);
1348 /* if there isn't a data descriptor */
1349 if(!(flags & 0x0008)){
1350 crc = UNPACK_UB4(file_header, LOC_CRC);
1352 printf("CRC is %x\n", crc);
1356 if(filename_len < fnlen + 1){
1357 if(filename != NULL)
1360 filename = malloc(sizeof(ub1) * (fnlen + 1));
1361 filename_len = fnlen + 1;
1364 pb_read(&pbf, filename, fnlen);
1365 filename[fnlen] = '\0';
1368 printf("filename is %s\n", filename);
1374 for(j = 0; j < file_num; j++)
1375 if(strcmp(files[j], (const char *)filename) == 0){
1384 /* OK, there is some directory information in the file. Nothing to do
1385 but ensure the directory(s) exist, and create them if they don't.
1387 if(strchr((const char *)filename, '/') != NULL && handle){
1388 /* Loop through all the directories in the path, (everything w/ a '/') */
1389 const ub1 *start = filename;
1393 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1396 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1400 else if(idx == start){
1406 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1407 tmp_buff[(idx - filename)] = '\0';
1410 printf("checking the existance of %s\n", tmp_buff);
1413 if(stat(tmp_buff, &sbuf) < 0){
1414 if(errno != ENOENT){
1419 } else if(S_ISDIR(sbuf.st_mode)){
1421 printf("Directory exists\n");
1425 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1431 printf("Making directory..\n");
1433 if(mkdir(tmp_buff, 0755) < 0){
1437 if(verbose && handle)
1438 printf("%10s: %s/\n", "created", tmp_buff);
1442 /* only a directory */
1443 if(strlen((const char *)start) == 0)
1447 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1450 /* If the entry was just a directory, don't write to file, etc */
1451 if(strlen((const char *)start) == 0)
1457 if(f_fd != -1 && handle){
1458 f_fd = open((const char *)filename,
1459 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1462 fprintf(stderr, "Error extracting JAR archive!\n");
1463 perror((const char *)filename);
1468 if(method != 8 && flags & 0x0008){
1469 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1473 if(method == 8 || flags & 0x0008){
1475 lseek(fd, eflen, SEEK_CUR);
1477 consume(&pbf, eflen);
1479 inflate_file(&pbf, f_fd, &ze);
1483 printf("writing stored data.. (%d bytes)\n", csize);
1489 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1491 while(out_a < (int)csize){
1492 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1493 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1498 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1501 write(f_fd, rd_buff, rdamt);
1507 printf("%d bytes written\n", out_a);
1512 lseek(fd, eflen, SEEK_CUR);
1514 consume(&pbf, eflen);
1517 /* if there is a data descriptor left, compare the CRC */
1520 if(pb_read(&pbf, scratch, 16) != 16){
1525 signature = UNPACK_UB4(scratch, 0);
1527 if(signature != 0x08074b50){
1528 fprintf(stderr, "Error! Missing data descriptor!\n");
1532 crc = UNPACK_UB4(scratch, 4);
1537 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1544 if(verbose && dir == FALSE && handle)
1545 printf("%10s: %s\n",
1546 (method == 8 ? "inflated" : "extracted"),
1553 int list_jar(int fd, char **files, int file_num){
1566 ub1 *filename = NULL;
1569 int filename_len = 0;
1574 char ascii_date[30];
1578 printf("Listing jar file, looking for %d files\n", file_num);
1581 /* This should be the start of the central-header-end section */
1583 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1588 if(read(fd, &tmp, sizeof(ub4)) != 4){
1593 #ifdef WORDS_BIGENDIAN
1597 if(tmp != 0x06054b50){
1598 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1602 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1607 if(read(fd, &cen_size, 2) != 2){
1612 #ifdef WORDS_BIGENDIAN
1613 cen_size = L2BS(cen_size);
1616 /* printf("%hu entries in central header\n", cen_size); */
1618 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1623 if(read(fd, &tmp, 4) != 4){
1628 #ifdef WORDS_BIGENDIAN
1632 /* printf("Central header offset = %d\n", tmp); */
1634 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1639 /* Loop through the entries in the central header */
1640 for(i = 0; i < cen_size; i++){
1642 if(read(fd, &cen_header, 46) != 46){
1647 signature = UNPACK_UB4(cen_header, 0);
1648 if(signature != 0x02014b50){
1649 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1653 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1654 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1655 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1656 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1658 /* If we're providing verbose output, we need to make an ASCII
1659 * formatted version of the date. */
1661 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1662 tdate = dos2unixtime(mdate);
1663 s_tm = localtime(&tdate);
1664 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1667 if(filename_len < fnlen + 1){
1668 if(filename != NULL)
1671 filename = malloc(sizeof(ub1) * (fnlen + 1));
1672 filename_len = fnlen + 1;
1675 if(read(fd, filename, fnlen) != fnlen){
1679 filename[fnlen] = '\0';
1681 /* if the user specified a list of files on the command line,
1682 we'll only display those, otherwise we'll display everything */
1684 for(j = 0; j < file_num; j++)
1685 if(strcmp(files[j], (const char *)filename) == 0){
1687 printf("%6d %s %s\n", usize, ascii_date, filename);
1689 printf("%s\n", filename);
1694 printf("%6d %s %s\n", usize, ascii_date, filename);
1696 printf("%s\n", filename);
1699 size = eflen + clen;
1701 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1708 /* the file isn't seekable.. evil! */
1716 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1721 signature = UNPACK_UB4(scratch, 0);
1724 printf("signature is %x\n", signature);
1727 if(signature == 0x08074b50){
1729 printf("skipping data descriptor\n");
1731 pb_read(&pbf, scratch, 12);
1733 } else if(signature == 0x02014b50){
1735 printf("Central header reached.. we're all done!\n");
1738 }else if(signature != 0x04034b50){
1740 printf("Ick! %#x\n", signature);
1745 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1750 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1752 printf("Compressed size is %u\n", csize);
1755 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1757 printf("Filename length is %hu\n", fnlen);
1760 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1762 printf("Extra field length is %hu\n", eflen);
1765 method = UNPACK_UB2(file_header, LOC_COMP);
1767 printf("Compression method is %#hx\n", method);
1770 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1772 printf("Flags are %#hx\n", flags);
1775 usize = UNPACK_UB4(file_header, LOC_USIZE);
1777 /* If we're providing verbose output, we need to make an ASCII
1778 * formatted version of the date. */
1780 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1781 tdate = dos2unixtime(mdate);
1782 s_tm = localtime(&tdate);
1783 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1786 if(filename_len < fnlen + 1){
1787 if(filename != NULL)
1790 filename = malloc(sizeof(ub1) * (fnlen + 1));
1791 filename_len = fnlen + 1;
1794 pb_read(&pbf, filename, fnlen);
1795 filename[fnlen] = '\0';
1797 /* the header is at the end. In a JAR file, this means that the data
1798 happens to be compressed. We have no choice but to inflate the
1805 consume(&pbf, size);
1809 printf("inflating %s\n", filename);
1811 inflate_file(&pbf, -1, &ze);
1815 printf("We're shit outta luck!\n");
1818 size = csize + (eflen > 0 ? eflen : 0);
1822 printf("Skipping %ld bytes\n", (long)size);
1825 consume(&pbf, size);
1827 /* print out the listing */
1829 for(j = 0; j < file_num; j++)
1830 if(strcmp(files[j], (const char *)filename) == 0){
1832 printf("%6d %s %s\n", usize, ascii_date, filename);
1834 printf("%s\n", filename);
1839 printf("%6d %s %s\n", usize, ascii_date, filename);
1841 printf("%s\n", filename);
1848 int consume(pb_file *pbf, int amt){
1849 int tc = 0; /* total amount consumed */
1854 printf("Consuming %d bytes\n", amt);
1858 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1860 printf("got %d bytes\n", rdamt);
1866 printf("%d bytes consumed\n", tc);
1872 void usage(const char *filename){
1873 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1879 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1880 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1881 printf("Copyright 2002 Free Software Foundation\n");
1883 This is free software; see the source for copying conditions. There is NO\n\
1884 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1888 void help(const char *filename)
1891 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1893 Store many files together in a single `jar' file.\n\
1895 -c create new archive\n\
1896 -t list table of contents for archive\n\
1897 -x extract named (or all) files from archive\n\
1898 -u update existing archive\n\
1901 -@ read names from stdin\n\
1902 -0 store only; use no ZIP compression\n\
1903 -C DIR FILE change to the specified directory and include\n\
1904 the following file\n\
1905 -E don't include the files found in a directory\n\
1906 -f FILE specify archive file name\n\
1907 --help print this help, then exit\n\
1908 -m FILE include manifest information from specified manifest file\n\
1909 -M Do not create a manifest file for the entries\n\
1910 -v generate verbose output on standard output\n\
1911 -V, --version display version information\n\
1914 If any file is a directory then it is processed recursively.\n\
1915 The manifest file name and the archive file name needs to be specified\n\
1916 in the same order the 'm' and 'f' flags are specified.\n\
1918 Example 1: to archive two class files into an archive called classes.jar: \n\
1919 jar cvf classes.jar Foo.class Bar.class \n\
1920 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1921 files in the foo/ directory into 'classes.jar': \n\
1922 jar cvfm classes.jar mymanifest -C foo/ .\n\
1932 char *result = (char*)malloc(strlen(s) + 1);
1933 if (result == (char*)0)
1939 /* Convert "tar-style" first argument to a form expected by getopt.
1940 This idea and the code comes from GNU tar. This can allocate a new
1941 argument vector. This might leak some memory, but we don't care. */
1943 expand_options (int *argcp, char ***argvp)
1946 char **argv = *argvp;
1948 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
1949 if a long argument (like "--help") is detected. */
1950 if (argc > 1 && argv[1][1] != '-')
1962 args_to_expand = strlen (argv[1]);
1963 if (argv[1][0] == '-')
1966 new_argc = argc - 1 + args_to_expand;
1967 new_argv = (char **) malloc (new_argc * sizeof (char *));
1979 *out++ = jt_strdup (buf);
1980 /* If the option takes an argument, move the next argument
1981 to just after this option. */
1982 opt = strchr (OPTION_STRING, *p);
1983 if (opt && opt[1] == ':')
1985 if (in < argv + argc)
1989 fprintf(stderr, "%s: option `%s' requires an argument.\n",
1997 /* Copy remaining options. */
1998 while (in < argv + argc)