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 #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);
261 void usage(const char*);
262 void help(const char *);
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 */
281 ub1 data_descriptor[16];
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){
320 int action = ACTION_NONE;
327 /* These are used to collect file names and `-C' options for the
328 second pass through the command line. */
337 number_of_entries = 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) {
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;
357 action = ACTION_CREATE;
360 action = ACTION_LIST;
363 action = ACTION_EXTRACT;
366 action = ACTION_UPDATE;
391 /* The following options aren't supported by the original jar tool. */
393 use_explicit_list_only = TRUE;
396 read_names_from_stdin = TRUE;
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++];
408 if(action == ACTION_NONE){
409 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
413 if(action == ACTION_UPDATE){
414 fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
418 /* Verify unsupported combinations and warn of the use of non
420 if(verbose && use_explicit_list_only)
421 fprintf (stderr, "Warning: using non standard '-E' option\n");
422 if(verbose && read_names_from_stdin)
423 fprintf (stderr, "Warning: using non standard '-@' option\n");
424 if(read_names_from_stdin
425 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
426 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
430 /* create the jarfile */
431 if(action == ACTION_CREATE){
433 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
434 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
437 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
442 /* We assume that the file is seekable */
447 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
449 /* standard out is not seekable */
452 /* don't want our output to be part of the jar file.. figured this one
453 out the hard way.. =P */
456 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
459 jarfd = open(jarfile, O_RDONLY | O_BINARY);
462 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
469 jarfd = STDIN_FILENO; /* jarfd is standard in */
471 /* we assume that the stream isn't seekable for safety */
476 if(action == ACTION_CREATE || action == ACTION_UPDATE){
480 if((action == ACTION_UPDATE) && jarfile) {
481 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
482 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
492 /* Add the META-INF/ directory and the manifest */
493 if(manifest && mfile)
494 make_manifest(jarfd, mfile);
496 make_manifest(jarfd, NULL);
498 init_args (new_argv, new_argc);
499 /* now we add the files to the archive */
500 while ((arg = get_next_arg ())){
502 if(!strcmp(arg, "-C")){
503 const char *dir_to_change = get_next_arg ();
504 const char *file_to_add = get_next_arg ();
507 || add_to_jar(jarfd, dir_to_change, file_to_add)){
508 printf("Error adding %s to jar archive!\n", arg);
512 if(add_to_jar(jarfd, NULL, arg)){
513 printf("Error adding %s to jar archive!\n", arg);
518 /* de-initialize the compression DS */
522 create_central_header(jarfd);
524 if (close(jarfd) != 0) {
525 fprintf(stderr, "Error closing jar archive!\n");
527 } else if(action == ACTION_LIST){
528 list_jar(jarfd, &new_argv[0], new_argc);
529 } else if(action == ACTION_EXTRACT){
530 extract_jar(jarfd, &new_argv[0], new_argc);
536 static int args_current_g;
537 static char **args_g;
540 init_args(args, current)
544 if(!read_names_from_stdin)
547 args_current_g = current;
554 static int reached_end = 0;
561 if (!args_g [args_current_g])
566 return args_g [args_current_g++];
570 /* Read the name from stdin. Delimiters are '\n' and
571 '\r'. Reading EOF indicates that we don't have anymore file
572 names characters to read. */
577 /* Get rid of '\n' and '\r' first. */
580 int c = getc (stdin);
581 if (c == '\n' || c == '\r')
594 int c = getc (stdin);
595 /* Exit when we get a delimiter or don't have any characters
597 if (c == '\n'|| c == '\r'|| c == EOF)
599 s [pos++] = (char) c;
605 return jt_strdup (s);
613 /* packing file header */
615 file_header[0] = 0x50;
616 file_header[1] = 0x4b;
617 file_header[2] = 0x03;
618 file_header[3] = 0x04;
619 /* version number (Unix 1.0)*/
622 /* bit flag (normal deflation)*/
623 file_header[6] = 0x00;
625 file_header[7] = 0x00;
626 /* do_compression method (deflation) */
630 /* last mod file time (MS-DOS format) */
633 /* last mod file date (MS-DOS format) */
641 /* compressed size */
646 /* uncompressed size */
651 /* filename length */
654 /* extra field length */
658 /* Initialize the compression DS */
659 PACK_UB4(data_descriptor, 0, 0x08074b50);
663 void add_entry(struct zipentry *ze){
669 ziplist->next_entry = ze;
676 int make_manifest(int jfd, const char *mf_name){
678 int nlen; /* length of file name */
679 int mod_time; /* file modification time */
682 nlen = 9; /* trust me on this one */
684 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
686 current_time = time(NULL);
687 if(current_time == (time_t)-1){
692 mod_time = unix2dostime(¤t_time);
694 PACK_UB2(file_header, LOC_EXTRA, 0);
695 PACK_UB2(file_header, LOC_COMP, 0);
696 PACK_UB2(file_header, LOC_FNLEN, nlen);
697 PACK_UB4(file_header, LOC_MODTIME, mod_time);
700 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
702 ze = (zipentry*)malloc(sizeof(zipentry));
708 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
709 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
710 strcpy(ze->filename, "META-INF/");
711 ze->filename[nlen] = '\0';
713 ze->offset = lseek(jfd, 0, SEEK_CUR);
714 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
715 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
716 ze->compressed = FALSE;
720 write(jfd, file_header, 30);
721 write(jfd, "META-INF/", nlen);
723 /* if the user didn't specify an external manifest file... */
725 int mf_len = 37 + strlen(VERSION);
728 if((mf = (char *) malloc(mf_len + 1))) {
731 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
733 crc = crc32(0L, Z_NULL, 0);
735 crc = crc32(crc, (const unsigned char *)mf, mf_len);
737 nlen = 20; /* once again, trust me */
739 PACK_UB2(file_header, LOC_EXTRA, 0);
740 PACK_UB2(file_header, LOC_COMP, 0);
741 PACK_UB2(file_header, LOC_FNLEN, nlen);
742 PACK_UB4(file_header, LOC_USIZE, mf_len);
744 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
746 PACK_UB4(file_header, LOC_CRC, crc);
749 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
751 ze = (zipentry*)malloc(sizeof(zipentry));
757 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
758 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
759 strcpy(ze->filename, "META-INF/MANIFEST.MF");
760 ze->filename[nlen] = '\0';
762 ze->offset = lseek(jfd, 0, SEEK_CUR);
763 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
764 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
767 ze->usize = ze->csize;
768 ze->compressed = FALSE;
772 write(jfd, file_header, 30);
773 write(jfd, "META-INF/MANIFEST.MF", nlen);
774 write(jfd, mf, mf_len);
778 printf("malloc errror\n");
785 stat(mf_name, &statbuf);
787 if(!S_ISREG(statbuf.st_mode)){
788 fprintf(stderr, "Invalid manifest file specified.\n");
792 mfd = open(mf_name, O_RDONLY | O_BINARY);
795 fprintf(stderr, "Error opening %s.\n", mf_name);
799 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
800 perror("error writing to jar");
809 int add_to_jar(int fd, const char *new_dir, const char *file){
815 char *old_dir = NULL;
817 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
819 * "normal" jar : org/apache/java/io/LogRecord.class
820 * fastjar : ./org/apache/java/io/LogRecord.class
821 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
822 * with both kaffe-1.0b4 and JDK.
824 while (*file=='.' && *(file+1)=='/')
827 /* If new_dir isn't null, we need to change to that directory. However,
828 we also need to return to the old directory when we're done */
830 old_dir = getcwd(NULL, 0);
832 if(chdir(new_dir) == -1){
838 if(!strcmp(file, jarfile)){
840 printf("skipping: %s\n", file);
841 return 0; /* we don't want to add ourselves.. */
844 stat_return = stat(file, &statbuf);
846 if(stat_return == -1){
849 } else if(S_ISDIR(statbuf.st_mode)){
853 unsigned long mod_time;
862 nlen = strlen(file) + 256;
863 fullname = (char*)malloc(nlen * sizeof(char));
864 memset(fullname, 0, (nlen * sizeof(char)));
866 if(fullname == NULL){
867 fprintf(stderr, "Filename is NULL!\n");
871 strcpy(fullname, file);
874 if(fullname[nlen - 1] != '/'){
875 fullname[nlen] = '/';
876 t_ptr = (fullname + nlen + 1);
878 t_ptr = (fullname + nlen);
881 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
883 nlen = (t_ptr - fullname);
885 mod_time = unix2dostime(&statbuf.st_mtime);
887 PACK_UB2(file_header, LOC_EXTRA, 0);
888 PACK_UB2(file_header, LOC_COMP, 0);
889 PACK_UB2(file_header, LOC_FNLEN, nlen);
890 PACK_UB4(file_header, LOC_MODTIME, mod_time);
893 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
895 ze = (zipentry*)malloc(sizeof(zipentry));
901 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
902 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
903 strcpy(ze->filename, fullname);
904 ze->filename[nlen] = '\0';
906 ze->offset = lseek(fd, 0, SEEK_CUR);
907 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
908 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
909 ze->compressed = FALSE;
913 write(fd, file_header, 30);
914 write(fd, fullname, nlen);
916 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
917 if(de->d_name[0] == '.')
919 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
921 printf("skipping: %s\n", de->d_name);
925 strcpy(t_ptr, de->d_name);
927 if(add_to_jar(fd, NULL, fullname)){
928 fprintf(stderr, "Error adding file to jar!\n");
936 } else if(S_ISREG(statbuf.st_mode)){
939 add_fd = open(file, O_RDONLY | O_BINARY);
941 fprintf(stderr, "Error opening %s.\n", file);
945 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
946 fprintf(stderr, "Error adding file to jar!\n");
951 fprintf(stderr, "Illegal file specified: %s\n", file);
964 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
966 unsigned short file_name_length;
967 unsigned long mod_time;
974 mod_time = unix2dostime(&(statbuf->st_mtime));
975 file_name_length = strlen(fname);
977 if(!seekable && !do_compress){
978 crc = crc32(0L, Z_NULL, 0);
980 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
981 crc = crc32(crc, rd_buff, rdamt);
983 lseek(ffd, 0, SEEK_SET);
986 /* data descriptor */
987 if(!seekable && do_compress){
988 PACK_UB2(file_header, LOC_EXTRA, 8);
990 PACK_UB2(file_header, LOC_EXTRA, 0);
994 PACK_UB2(file_header, LOC_COMP, 8);
996 PACK_UB2(file_header, LOC_COMP, 0);
999 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1000 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1002 if(!seekable && !do_compress){
1003 PACK_UB4(file_header, LOC_CRC, crc);
1004 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1005 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1007 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1009 ze = (zipentry*)malloc(sizeof(zipentry));
1015 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1016 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1017 strcpy(ze->filename, fname);
1019 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1020 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1022 if(!seekable && !do_compress)
1025 ze->csize = statbuf->st_size;
1026 ze->usize = ze->csize;
1027 ze->offset = lseek(jfd, 0, SEEK_CUR);
1029 ze->compressed = TRUE;
1031 ze->compressed = FALSE;
1035 /* Write the local header */
1036 write(jfd, file_header, 30);
1038 /* write the file name to the zip file */
1039 write(jfd, fname, file_name_length);
1043 printf("adding: %s ", fname);
1048 /* compress the file */
1049 compress_file(ffd, jfd, ze);
1051 /* Write the contents of the file (uncompressed) to the zip file */
1052 /* calculate the CRC as we go along */
1053 ze->crc = crc32(0L, Z_NULL, 0);
1055 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1056 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1057 if(write(jfd, rd_buff, rdamt) != rdamt){
1065 /* write out data descriptor */
1066 PACK_UB4(data_descriptor, 4, ze->crc);
1067 PACK_UB4(data_descriptor, 8, ze->csize);
1068 PACK_UB4(data_descriptor, 12, ze->usize);
1070 /* we need to seek back and fill the header */
1072 offset = (ze->csize + strlen(ze->filename) + 16);
1074 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1079 if(write(jfd, (data_descriptor + 4), 12) != 12){
1086 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1090 } else if(do_compress){
1091 /* Sun's jar tool will only allow a data descriptor if the entry is
1092 compressed, but we'll save 16 bytes/entry if we only use it when
1093 we can't seek back on the file */
1095 if(write(jfd, data_descriptor, 16) != 16){
1102 printf("(in=%d) (out=%d) (%s %d%%)\n",
1103 (int)ze->usize, (int)ze->csize,
1104 (do_compress ? "deflated" : "stored"),
1105 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1110 int create_central_header(int fd){
1116 int total_in = 0, total_out = 22;
1120 iheader = (int*)header;
1127 /* version made by */
1130 /* version needed to extract */
1136 /* compression method */
1150 /* compressed size */
1155 /* uncompressed size */
1160 /* filename length */
1163 /* extra field length */
1166 /* file comment length */
1169 /* disk number start */
1172 /* internal file attribs */
1175 /* external file attribs */
1180 /* relative offset of local header */
1186 start_offset = lseek(fd, 0, SEEK_CUR);
1188 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1190 total_in += ze->usize;
1191 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1194 PACK_UB2(header, CEN_COMP, 8);
1196 PACK_UB2(header, CEN_COMP, 0);
1199 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1200 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1201 PACK_UB4(header, CEN_CRC, ze->crc);
1202 PACK_UB4(header, CEN_CSIZE, ze->csize);
1203 PACK_UB4(header, CEN_USIZE, ze->usize);
1204 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1205 PACK_UB4(header, CEN_OFFSET, ze->offset);
1207 write(fd, header, 46);
1209 write(fd, ze->filename, strlen(ze->filename));
1212 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1215 end_header[0] = 0x50;
1216 end_header[1] = 0x4b;
1217 end_header[2] = 0x05;
1218 end_header[3] = 0x06;
1219 /* number of this disk */
1222 /* number of disk w/ start of central header */
1225 /* total number of entries in central dir on this disk*/
1226 PACK_UB2(end_header, 8, number_of_entries);
1227 /* total number of entries in central dir*/
1228 PACK_UB2(end_header, 10, number_of_entries);
1229 /* size of central dir. */
1230 PACK_UB4(end_header, 12, dir_size);
1231 /* offset of start of central dir */
1232 PACK_UB4(end_header, 16, start_offset);
1233 /* zipfile comment length */
1237 write(fd, end_header, 22);
1240 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1243 (do_compress ? "deflated" : "stored"),
1244 (int)((1 - (total_out / (float)total_in)) * 100)
1250 int extract_jar(int fd, char **files, int file_num){
1260 ub1 *filename = NULL;
1261 int filename_len = 0;
1280 dir = FALSE; /* by default, the file isn't a dir */
1281 handle = TRUE; /* by default we'll extract/create the file */
1283 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1288 signature = UNPACK_UB4(scratch, 0);
1291 printf("signature is %x\n", signature);
1293 if(signature == 0x08074b50){
1295 printf("skipping data descriptor\n");
1297 pb_read(&pbf, scratch, 12);
1299 } else if(signature == 0x02014b50){
1301 printf("Central header reached.. we're all done!\n");
1304 }else if(signature != 0x04034b50){
1305 printf("Ick! %#x\n", signature);
1309 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1314 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1316 printf("Compressed size is %u\n", csize);
1319 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1321 printf("Filename length is %hu\n", fnlen);
1324 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1326 printf("Extra field length is %hu\n", eflen);
1329 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1331 printf("Flags are %#hx\n", flags);
1334 method = UNPACK_UB2(file_header, LOC_COMP);
1336 printf("Compression method is %#hx\n", method);
1339 /* if there isn't a data descriptor */
1340 if(!(flags & 0x0008)){
1341 crc = UNPACK_UB4(file_header, LOC_CRC);
1343 printf("CRC is %x\n", crc);
1347 if(filename_len < fnlen + 1){
1348 if(filename != NULL)
1351 filename = malloc(sizeof(ub1) * (fnlen + 1));
1352 filename_len = fnlen + 1;
1355 pb_read(&pbf, filename, fnlen);
1356 filename[fnlen] = '\0';
1359 printf("filename is %s\n", filename);
1365 for(j = 0; j < file_num; j++)
1366 if(strcmp(files[j], (const char *)filename) == 0){
1375 /* OK, there is some directory information in the file. Nothing to do
1376 but ensure the directory(s) exist, and create them if they don't.
1378 if(strchr((const char *)filename, '/') != NULL && handle){
1379 /* Loop through all the directories in the path, (everything w/ a '/') */
1380 const ub1 *start = filename;
1384 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1387 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1391 else if(idx == start){
1397 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1398 tmp_buff[(idx - filename)] = '\0';
1401 printf("checking the existance of %s\n", tmp_buff);
1404 if(stat(tmp_buff, &sbuf) < 0){
1405 if(errno != ENOENT){
1410 } else if(S_ISDIR(sbuf.st_mode)){
1412 printf("Directory exists\n");
1416 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1422 printf("Making directory..\n");
1424 if(mkdir(tmp_buff, 0755) < 0){
1428 if(verbose && handle)
1429 printf("%10s: %s/\n", "created", tmp_buff);
1433 /* only a directory */
1434 if(strlen((const char *)start) == 0)
1438 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1441 /* If the entry was just a directory, don't write to file, etc */
1442 if(strlen((const char *)start) == 0)
1448 if(f_fd != -1 && handle){
1449 f_fd = creat((const char *)filename, 00644);
1452 fprintf(stderr, "Error extracting JAR archive!\n");
1453 perror((const char *)filename);
1458 if(method != 8 && flags & 0x0008){
1459 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1463 if(method == 8 || flags & 0x0008){
1465 lseek(fd, eflen, SEEK_CUR);
1467 consume(&pbf, eflen);
1469 inflate_file(&pbf, f_fd, &ze);
1473 printf("writing stored data.. (%d bytes)\n", csize);
1479 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1481 while(out_a < (int)csize){
1482 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1483 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1488 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1491 write(f_fd, rd_buff, rdamt);
1497 printf("%d bytes written\n", out_a);
1502 lseek(fd, eflen, SEEK_CUR);
1504 consume(&pbf, eflen);
1507 /* if there is a data descriptor left, compare the CRC */
1510 if(pb_read(&pbf, scratch, 16) != 16){
1515 signature = UNPACK_UB4(scratch, 0);
1517 if(signature != 0x08074b50){
1518 fprintf(stderr, "Error! Missing data descriptor!\n");
1522 crc = UNPACK_UB4(scratch, 4);
1527 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1534 if(verbose && dir == FALSE && handle)
1535 printf("%10s: %s\n",
1536 (method == 8 ? "inflated" : "extracted"),
1543 int list_jar(int fd, char **files, int file_num){
1556 ub1 *filename = NULL;
1559 int filename_len = 0;
1564 char ascii_date[30];
1568 printf("Listing jar file, looking for %d files\n", file_num);
1571 /* This should be the start of the central-header-end section */
1573 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1578 if(read(fd, &tmp, sizeof(ub4)) != 4){
1583 #ifdef WORDS_BIGENDIAN
1587 if(tmp != 0x06054b50){
1588 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1592 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1597 if(read(fd, &cen_size, 2) != 2){
1602 #ifdef WORDS_BIGENDIAN
1603 cen_size = L2BS(cen_size);
1606 /* printf("%hu entries in central header\n", cen_size); */
1608 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1613 if(read(fd, &tmp, 4) != 4){
1618 #ifdef WORDS_BIGENDIAN
1622 /* printf("Central header offset = %d\n", tmp); */
1624 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1629 /* Loop through the entries in the central header */
1630 for(i = 0; i < cen_size; i++){
1632 if(read(fd, &cen_header, 46) != 46){
1637 signature = UNPACK_UB4(cen_header, 0);
1638 if(signature != 0x02014b50){
1639 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1643 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1644 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1645 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1646 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1648 /* If we're providing verbose output, we need to make an ASCII
1649 * formatted version of the date. */
1651 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1652 tdate = dos2unixtime(mdate);
1653 s_tm = localtime(&tdate);
1654 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1657 if(filename_len < fnlen){
1658 if(filename != NULL)
1661 filename = malloc(sizeof(ub1) * (fnlen + 1));
1662 filename_len = fnlen + 1;
1665 if(read(fd, filename, fnlen) != fnlen){
1669 filename[fnlen] = '\0';
1671 /* if the user specified a list of files on the command line,
1672 we'll only display those, otherwise we'll display everything */
1674 for(j = 0; j < file_num; j++)
1675 if(strcmp(files[j], (const char *)filename) == 0){
1677 printf("%6d %s %s\n", usize, ascii_date, filename);
1679 printf("%s\n", filename);
1684 printf("%6d %s %s\n", usize, ascii_date, filename);
1686 printf("%s\n", filename);
1689 size = eflen + clen;
1691 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1698 /* the file isn't seekable.. evil! */
1706 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1711 signature = UNPACK_UB4(scratch, 0);
1714 printf("signature is %x\n", signature);
1717 if(signature == 0x08074b50){
1719 printf("skipping data descriptor\n");
1721 pb_read(&pbf, scratch, 12);
1723 } else if(signature == 0x02014b50){
1725 printf("Central header reached.. we're all done!\n");
1728 }else if(signature != 0x04034b50){
1730 printf("Ick! %#x\n", signature);
1735 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1740 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1742 printf("Compressed size is %u\n", csize);
1745 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1747 printf("Filename length is %hu\n", fnlen);
1750 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1752 printf("Extra field length is %hu\n", eflen);
1755 method = UNPACK_UB2(file_header, LOC_COMP);
1757 printf("Compression method is %#hx\n", method);
1760 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1762 printf("Flags are %#hx\n", flags);
1765 usize = UNPACK_UB4(file_header, LOC_USIZE);
1767 /* If we're providing verbose output, we need to make an ASCII
1768 * formatted version of the date. */
1770 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1771 tdate = dos2unixtime(mdate);
1772 s_tm = localtime(&tdate);
1773 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1776 if(filename_len < fnlen){
1777 if(filename != NULL)
1780 filename = malloc(sizeof(ub1) * (fnlen + 1));
1781 filename_len = fnlen + 1;
1784 pb_read(&pbf, filename, fnlen);
1785 filename[fnlen] = '\0';
1787 /* the header is at the end. In a JAR file, this means that the data
1788 happens to be compressed. We have no choice but to inflate the
1795 consume(&pbf, size);
1799 printf("inflating %s\n", filename);
1801 inflate_file(&pbf, -1, &ze);
1805 printf("We're shit outta luck!\n");
1808 size = csize + (eflen > 0 ? eflen : 0);
1812 printf("Skipping %ld bytes\n", (long)size);
1815 consume(&pbf, size);
1817 /* print out the listing */
1819 for(j = 0; j < file_num; j++)
1820 if(strcmp(files[j], (const char *)filename) == 0){
1822 printf("%6d %s %s\n", usize, ascii_date, filename);
1824 printf("%s\n", filename);
1829 printf("%6d %s %s\n", usize, ascii_date, filename);
1831 printf("%s\n", filename);
1838 int consume(pb_file *pbf, int amt){
1839 int tc = 0; /* total amount consumed */
1844 printf("Consuming %d bytes\n", amt);
1848 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1850 printf("got %d bytes\n", rdamt);
1856 printf("%d bytes consumed\n", tc);
1862 void usage(const char *filename){
1863 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
1869 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
1870 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1871 printf("Copyright 2002 Free Software Foundation\n");
1873 This is free software; see the source for copying conditions. There is NO\n\
1874 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1878 void help(const char *filename)
1881 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1883 Store many files together in a single `jar' file.\n\
1885 -c create new archive\n\
1886 -t list table of contents for archive\n\
1887 -x extract named (or all) files from archive\n\
1888 -u update existing archive\n\
1891 -@ read names from stdin\n\
1892 -0 store only; use no ZIP compression\n\
1893 -C DIR FILE change to the specified directory and include\n\
1894 the following file\n\
1895 -E don't include the files found in a directory\n\
1896 -f FILE specify archive file name\n\
1897 --help print this help, then exit\n\
1898 -m FILE include manifest information from specified manifest file\n\
1899 -M Do not create a manifest file for the entries\n\
1900 -v generate verbose output on standard output\n\
1901 -V, --version display version information\n\
1904 If any file is a directory then it is processed recursively.\n\
1905 The manifest file name and the archive file name needs to be specified\n\
1906 in the same order the 'm' and 'f' flags are specified.\n\
1908 Example 1: to archive two class files into an archive called classes.jar: \n\
1909 jar cvf classes.jar Foo.class Bar.class \n\
1910 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1911 files in the foo/ directory into 'classes.jar': \n\
1912 jar cvfm classes.jar mymanifest -C foo/ .\n\
1922 char *result = (char*)malloc(strlen(s) + 1);
1923 if (result == (char*)0)
1929 /* Convert "tar-style" first argument to a form expected by getopt.
1930 This idea and the code comes from GNU tar. This can allocate a new
1931 argument vector. This might leak some memory, but we don't care. */
1933 expand_options (int *argcp, char ***argvp)
1936 char **argv = *argvp;
1938 if (argc > 1 && argv[1][0] != '-')
1949 new_argc = argc - 1 + strlen (argv[1]);
1950 new_argv = (char **) malloc (new_argc * sizeof (char *));
1955 for (p = *in++; *p; ++p)
1959 *out++ = jt_strdup (buf);
1960 /* If the option takes an argument, move the next argument
1961 to just after this option. */
1962 opt = strchr (OPTION_STRING, *p);
1963 if (opt && opt[1] == ':')
1965 if (in < argv + argc)
1969 fprintf(stderr, "%s: option `%s' requires an argument.\n",
1976 /* Copy remaining options. */
1977 while (in < argv + argc)