2 jartool.c - main functions for fastjar utility
3 Copyright (C) 1999, 2000, 2001 Bryan Burns
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 /* $Id: jartool.c,v 1.5 2001/05/03 21:40:47 danglin Exp $
23 Revision 1.5 2001/05/03 21:40:47 danglin
24 * jartool.c (jt_strdup): New function.
25 (get_next_arg): Use jt_strdup instead of strdup.
27 Revision 1.4 2000/12/28 21:47:37 robertl
28 2000-12-28 Robert Lipe <robertl@sco.com>
30 * jartool.c (MAXPATHLEN): Provide if not defined.
32 Revision 1.3 2000/12/14 18:45:35 ghazi
35 * compress.c: Include stdlib.h and compress.h.
37 (report_str_error): Make static.
38 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
39 (hrd_inflate_str): Likewise.
41 * compress.h (init_compression, end_compression, init_inflation,
42 end_inflation): Prototype void arguments.
44 * dostime.c (rcsid): Delete.
46 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
47 Make functions static. Cast ctype function argument to `unsigned
48 char'. Add parens in if-stmts. Constify.
49 (Usage): Change into a macro.
50 (jargrep): Remove unused parameter.
52 * jartool.c: Constify. Add parens in if-stmts. Align
53 signed/unsigned char pointers in functions calls using casts.
55 (list_jar): Fix printf format specifier.
56 (usage): Chop long string into bits. Reformat.
58 * pushback.c (rcsid): Delete.
60 Revision 1.2 2000/12/13 18:11:57 tromey
61 * jartool.c (extract_jar): Use strchr, not index.
63 Revision 1.1 2000/12/09 03:08:23 apbianco
64 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
68 Revision 1.5 2000/08/24 15:01:27 cory
69 Made certain that fastjar opened the jar file before trying to update it
72 Revision 1.4 2000/08/24 13:39:21 cory
73 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
74 when byte swapping. Better safe than sorry.
76 Revision 1.3 2000/08/23 19:42:17 cory
77 Added support for more Unix platforms. The following code has been hacked
78 to work on AIX, Solaris, True 64, and HP-UX.
79 Added bigendian check. Probably works on most big and little endian platforms
82 Revision 1.2 1999/12/06 07:38:28 toast
83 fixed recursive archiving bug
85 Revision 1.1.1.1 1999/12/06 03:09:34 toast
90 Revision 1.22 1999/10/12 19:45:13 burnsbr
91 adding patch to fix compat problem
93 Revision 1.21 1999/05/10 09:15:49 burnsbr
94 fixed manifest file version info
96 Revision 1.20 1999/05/10 08:53:16 burnsbr
97 *** empty log message ***
99 Revision 1.19 1999/05/10 08:30:39 burnsbr
100 added extract / listing code
102 Revision 1.18 1999/04/28 04:24:29 burnsbr
105 Revision 1.17 1999/04/28 04:21:23 burnsbr
106 added support for -C dir-changing flag.. Updated total compression display
108 Revision 1.16 1999/04/27 10:28:22 burnsbr
109 updated version string
111 Revision 1.15 1999/04/27 10:04:06 burnsbr
114 Revision 1.14 1999/04/27 08:56:14 burnsbr
115 added -V flag, better error messages
117 Revision 1.13 1999/04/26 02:35:21 burnsbr
118 changed all sorts of stuff.. compression now works 100%
120 Revision 1.12 1999/04/23 12:00:45 burnsbr
121 90% done with compression code
123 Revision 1.11 1999/04/22 04:12:57 burnsbr
124 finished first round of Manifest file support..
125 might need to do more, digest etc..
127 Revision 1.10 1999/04/22 02:35:23 burnsbr
128 added more manifest support, about 75% done now. Replaced all the
129 redundant shifts and bit-logic with a macro or two, making the code
132 Revision 1.9 1999/04/21 09:55:16 burnsbr
135 Revision 1.8 1999/04/21 02:58:01 burnsbr
136 started manifest code
138 Revision 1.7 1999/04/20 23:15:28 burnsbr
139 added patch sent by John Bley <jbb6@acpub.duke.edu>
141 Revision 1.6 1999/04/20 08:56:02 burnsbr
144 Revision 1.5 1999/04/20 08:16:09 burnsbr
145 fixed verbose flag, did some optimization
147 Revision 1.4 1999/04/20 05:09:59 burnsbr
150 Revision 1.3 1999/04/20 05:08:54 burnsbr
168 #include <sys/stat.h>
169 #include <sys/types.h>
171 #ifdef HAVE_SYS_PARAM_H
172 #include <sys/param.h>
176 #define MAXPATHLEN 1024
190 #ifdef TM_IN_SYS_TIME
191 #include <sys/time.h>
199 #include "pushback.h"
200 #include "compress.h"
202 #ifdef WORDS_BIGENDIAN
204 #define L2BI(l) ((l & 0xff000000) >> 24) | \
205 ((l & 0x00ff0000) >> 8) | \
206 ((l & 0x0000ff00) << 8) | \
207 ((l & 0x000000ff) << 24);
209 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
213 static char version_string[] = VERSION;
223 void usage(const char*);
224 void add_entry(struct zipentry *);
225 void init_headers(void);
227 int consume(pb_file *, int);
228 int list_jar(int, char**, int);
229 int extract_jar(int, char**, int);
230 int add_file_to_jar(int, int, const char*, struct stat*);
231 int add_to_jar(int, const char*, const char*);
232 int create_central_header(int);
233 int make_manifest(int, const char*);
234 static void init_args(char **, int);
235 static char *get_next_arg (void);
236 static char *jt_strdup (char*);
238 /* global variables */
240 ub1 data_descriptor[16];
246 /* If non zero, then don't recurse in directory. Instead, add the
247 directory entry and relie on an explicit list of files to populate
248 the archive. This option isn't supported by the original jar tool. */
249 int use_explicit_list_only;
251 /* If non zero, then read the entry names from stdin. This option
252 isn't supported by the original jar tool. */
253 int read_names_from_stdin;
255 zipentry *ziplist; /* linked list of entries */
256 zipentry *ziptail; /* tail of the linked list */
258 int number_of_entries; /* number of entries in the linked list */
260 int main(int argc, char **argv){
264 int action = ACTION_NONE;
266 int manifest_file = FALSE;
268 int file_first = FALSE;
278 number_of_entries = 0;
285 for(i = 0; i < j; i++){
288 action = ACTION_CREATE;
291 action = ACTION_LIST;
294 action = ACTION_EXTRACT;
297 action = ACTION_UPDATE;
303 printf("%s\n", version_string);
313 manifest_file = TRUE;
323 /* The following options aren't supported by the original jar tool. */
325 use_explicit_list_only = TRUE;
328 read_names_from_stdin = TRUE;
331 fprintf(stderr, "Illegal option: %c\n", argv[1][i]);
336 if(action == ACTION_NONE){
337 fprintf(stderr, "One of options -{ctxu} must be specified.\n");
341 /* Verify unsupported combinations and warn of the use of non
343 if(verbose && use_explicit_list_only)
344 fprintf (stderr, "Warning: using non standard '-E' option\n");
345 if(verbose && read_names_from_stdin)
346 fprintf (stderr, "Warning: using non standard '-@' option\n");
347 if(read_names_from_stdin
348 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
349 fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
355 /* get the jarfile and manifest file (if any) */
356 if(file && file_first){
360 strncpy(jarfile, argv[i++], 256);
366 strncpy(mfile, argv[i++], 256);
369 if(file && !file_first){
373 strncpy(jarfile, argv[i++], 256);
376 /* create the jarfile */
377 if(action == ACTION_CREATE){
379 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
380 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
383 fprintf(stderr, "Error opening %s for writing!\n", jarfile);
388 /* We assume that the file is seekable */
393 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
395 /* standard out is not seekable */
398 /* don't want our output to be part of the jar file.. figured this one
399 out the hard way.. =P */
402 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
405 jarfd = open(jarfile, O_RDONLY | O_BINARY);
408 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
415 jarfd = STDIN_FILENO; /* jarfd is standard in */
417 /* we assume that the stream isn't seekable for safety */
422 if(action == ACTION_CREATE || action == ACTION_UPDATE){
426 if((action == ACTION_UPDATE) && file) {
427 if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
428 fprintf(stderr, "Error opening %s for reading!\n", jarfile);
438 /* Add the META-INF/ directory and the manifest */
439 if(manifest && manifest_file)
440 make_manifest(jarfd, mfile);
442 make_manifest(jarfd, NULL);
445 /* now we add the files to the archive */
446 while ((arg = get_next_arg ())){
448 if(!strcmp(arg, "-C")){
449 const char *dir_to_change = get_next_arg ();
450 const char *file_to_add = get_next_arg ();
453 || add_to_jar(jarfd, dir_to_change, file_to_add)){
454 printf("Error adding %s to jar archive!\n", arg);
458 if(add_to_jar(jarfd, NULL, arg)){
459 printf("Error adding %s to jar archive!\n", arg);
464 /* de-initialize the compression DS */
468 create_central_header(jarfd);
470 if (close(jarfd) != 0) {
471 fprintf(stderr, "Error closing jar archive!\n");
473 } else if(action == ACTION_LIST){
474 list_jar(jarfd, &argv[i], (argc - i));
475 } else if(action == ACTION_EXTRACT){
476 extract_jar(jarfd, &argv[i], (argc - i));
482 static int args_current_g;
483 static char **args_g;
486 init_args(args, current)
490 if(!read_names_from_stdin)
493 args_current_g = current;
500 static int reached_end = 0;
507 if (!args_g [args_current_g])
512 return args_g [args_current_g++];
516 /* Read the name from stdin. Delimiters are '\n' and
517 '\r'. Reading EOF indicates that we don't have anymore file
518 names characters to read. */
523 /* Get rid of '\n' and '\r' first. */
526 int c = getc (stdin);
527 if (c == '\n' || c == '\r')
540 int c = getc (stdin);
541 /* Exit when we get a delimiter or don't have any characters
543 if (c == '\n'|| c == '\r'|| c == EOF)
545 s [pos++] = (char) c;
551 return jt_strdup (s);
559 /* packing file header */
561 file_header[0] = 0x50;
562 file_header[1] = 0x4b;
563 file_header[2] = 0x03;
564 file_header[3] = 0x04;
565 /* version number (Unix 1.0)*/
568 /* bit flag (normal deflation)*/
569 file_header[6] = 0x00;
571 file_header[7] = 0x00;
572 /* do_compression method (deflation) */
576 /* last mod file time (MS-DOS format) */
579 /* last mod file date (MS-DOS format) */
587 /* compressed size */
592 /* uncompressed size */
597 /* filename length */
600 /* extra field length */
604 /* Initialize the compression DS */
605 PACK_UB4(data_descriptor, 0, 0x08074b50);
609 void add_entry(struct zipentry *ze){
615 ziplist->next_entry = ze;
622 int make_manifest(int jfd, const char *mf_name){
624 int nlen; /* length of file name */
625 int mod_time; /* file modification time */
628 nlen = 9; /* trust me on this one */
630 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
632 current_time = time(NULL);
633 if(current_time == (time_t)-1){
638 mod_time = unix2dostime(¤t_time);
640 PACK_UB2(file_header, LOC_EXTRA, 0);
641 PACK_UB2(file_header, LOC_COMP, 0);
642 PACK_UB2(file_header, LOC_FNLEN, nlen);
643 PACK_UB4(file_header, LOC_MODTIME, mod_time);
646 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
648 ze = (zipentry*)malloc(sizeof(zipentry));
654 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
655 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
656 strcpy(ze->filename, "META-INF/");
657 ze->filename[nlen] = '\0';
659 ze->offset = lseek(jfd, 0, SEEK_CUR);
660 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
661 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
662 ze->compressed = FALSE;
666 write(jfd, file_header, 30);
667 write(jfd, "META-INF/", nlen);
669 /* if the user didn't specify an external manifest file... */
671 int mf_len = 37 + strlen(VERSION);
674 if((mf = (char *) malloc(mf_len + 1))) {
677 sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
679 crc = crc32(0L, Z_NULL, 0);
681 crc = crc32(crc, (const unsigned char *)mf, mf_len);
683 nlen = 20; /* once again, trust me */
685 PACK_UB2(file_header, LOC_EXTRA, 0);
686 PACK_UB2(file_header, LOC_COMP, 0);
687 PACK_UB2(file_header, LOC_FNLEN, nlen);
688 PACK_UB4(file_header, LOC_USIZE, mf_len);
690 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
692 PACK_UB4(file_header, LOC_CRC, crc);
695 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
697 ze = (zipentry*)malloc(sizeof(zipentry));
703 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
704 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
705 strcpy(ze->filename, "META-INF/MANIFEST.MF");
706 ze->filename[nlen] = '\0';
708 ze->offset = lseek(jfd, 0, SEEK_CUR);
709 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
710 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
713 ze->usize = ze->csize;
714 ze->compressed = FALSE;
718 write(jfd, file_header, 30);
719 write(jfd, "META-INF/MANIFEST.MF", nlen);
720 write(jfd, mf, mf_len);
724 printf("malloc errror\n");
731 stat(mf_name, &statbuf);
733 if(!S_ISREG(statbuf.st_mode)){
734 fprintf(stderr, "Invalid manifest file specified.\n");
738 mfd = open(mf_name, O_RDONLY | O_BINARY);
741 fprintf(stderr, "Error opening %s.\n", mf_name);
745 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
746 perror("error writing to jar");
755 int add_to_jar(int fd, const char *new_dir, const char *file){
761 char *old_dir = NULL;
763 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
765 * "normal" jar : org/apache/java/io/LogRecord.class
766 * fastjar : ./org/apache/java/io/LogRecord.class
767 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
768 * with both kaffe-1.0b4 and JDK.
770 while (*file=='.' && *(file+1)=='/')
773 /* If new_dir isn't null, we need to change to that directory. However,
774 we also need to return to the old directory when we're done */
776 old_dir = getcwd(NULL, 0);
778 if(chdir(new_dir) == -1){
784 if(!strcmp(file, jarfile)){
786 printf("skipping: %s\n", file);
787 return 0; /* we don't want to add ourselves.. */
790 stat_return = stat(file, &statbuf);
792 if(stat_return == -1){
794 } else if(S_ISDIR(statbuf.st_mode)){
798 unsigned long mod_time;
807 nlen = strlen(file) + 256;
808 fullname = (char*)malloc(nlen * sizeof(char));
809 memset(fullname, 0, (nlen * sizeof(char)));
811 if(fullname == NULL){
812 fprintf(stderr, "Filename is NULL!\n");
816 strcpy(fullname, file);
819 if(fullname[nlen - 1] != '/'){
820 fullname[nlen] = '/';
821 t_ptr = (fullname + nlen + 1);
823 t_ptr = (fullname + nlen);
826 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
828 nlen = (t_ptr - fullname);
830 mod_time = unix2dostime(&statbuf.st_mtime);
832 PACK_UB2(file_header, LOC_EXTRA, 0);
833 PACK_UB2(file_header, LOC_COMP, 0);
834 PACK_UB2(file_header, LOC_FNLEN, nlen);
835 PACK_UB4(file_header, LOC_MODTIME, mod_time);
838 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
840 ze = (zipentry*)malloc(sizeof(zipentry));
846 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
847 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
848 strcpy(ze->filename, fullname);
849 ze->filename[nlen] = '\0';
851 ze->offset = lseek(fd, 0, SEEK_CUR);
852 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
853 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
854 ze->compressed = FALSE;
858 write(fd, file_header, 30);
859 write(fd, fullname, nlen);
861 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
862 if(de->d_name[0] == '.')
864 if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
866 printf("skipping: %s\n", de->d_name);
870 strcpy(t_ptr, de->d_name);
872 if(add_to_jar(fd, NULL, fullname)){
873 fprintf(stderr, "Error adding file to jar!\n");
881 } else if(S_ISREG(statbuf.st_mode)){
884 add_fd = open(file, O_RDONLY | O_BINARY);
886 fprintf(stderr, "Error opening %s.\n", file);
890 if(add_file_to_jar(fd, add_fd, file, &statbuf)){
891 fprintf(stderr, "Error adding file to jar!\n");
896 fprintf(stderr, "Illegal file specified: %s\n", file);
909 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
911 unsigned short file_name_length;
912 unsigned long mod_time;
919 mod_time = unix2dostime(&(statbuf->st_mtime));
920 file_name_length = strlen(fname);
922 if(!seekable && !do_compress){
923 crc = crc32(0L, Z_NULL, 0);
925 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
926 crc = crc32(crc, rd_buff, rdamt);
928 lseek(ffd, 0, SEEK_SET);
931 /* data descriptor */
932 if(!seekable && do_compress){
933 PACK_UB2(file_header, LOC_EXTRA, 8);
935 PACK_UB2(file_header, LOC_EXTRA, 0);
939 PACK_UB2(file_header, LOC_COMP, 8);
941 PACK_UB2(file_header, LOC_COMP, 0);
944 PACK_UB4(file_header, LOC_MODTIME, mod_time);
945 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
947 if(!seekable && !do_compress){
948 PACK_UB4(file_header, LOC_CRC, crc);
949 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
950 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
952 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
954 ze = (zipentry*)malloc(sizeof(zipentry));
960 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
961 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
962 strcpy(ze->filename, fname);
964 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
965 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
967 if(!seekable && !do_compress)
970 ze->csize = statbuf->st_size;
971 ze->usize = ze->csize;
972 ze->offset = lseek(jfd, 0, SEEK_CUR);
974 ze->compressed = TRUE;
976 ze->compressed = FALSE;
980 /* Write the local header */
981 write(jfd, file_header, 30);
983 /* write the file name to the zip file */
984 write(jfd, fname, file_name_length);
988 printf("adding: %s ", fname);
993 /* compress the file */
994 compress_file(ffd, jfd, ze);
996 /* Write the contents of the file (uncompressed) to the zip file */
997 /* calculate the CRC as we go along */
998 ze->crc = crc32(0L, Z_NULL, 0);
1000 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1001 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1002 if(write(jfd, rd_buff, rdamt) != rdamt){
1010 /* write out data descriptor */
1011 PACK_UB4(data_descriptor, 4, ze->crc);
1012 PACK_UB4(data_descriptor, 8, ze->csize);
1013 PACK_UB4(data_descriptor, 12, ze->usize);
1015 /* we need to seek back and fill the header */
1017 offset = (ze->csize + strlen(ze->filename) + 16);
1019 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1024 if(write(jfd, (data_descriptor + 4), 12) != 12){
1031 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1035 } else if(do_compress){
1036 /* Sun's jar tool will only allow a data descriptor if the entry is
1037 compressed, but we'll save 16 bytes/entry if we only use it when
1038 we can't seek back on the file */
1040 if(write(jfd, data_descriptor, 16) != 16){
1047 printf("(in=%d) (out=%d) (%s %d%%)\n",
1048 (int)ze->usize, (int)ze->csize,
1049 (do_compress ? "deflated" : "stored"),
1050 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1055 int create_central_header(int fd){
1061 int total_in = 0, total_out = 22;
1065 iheader = (int*)header;
1072 /* version made by */
1075 /* version needed to extract */
1081 /* compression method */
1095 /* compressed size */
1100 /* uncompressed size */
1105 /* filename length */
1108 /* extra field length */
1111 /* file comment length */
1114 /* disk number start */
1117 /* internal file attribs */
1120 /* external file attribs */
1125 /* relative offset of local header */
1131 start_offset = lseek(fd, 0, SEEK_CUR);
1133 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1135 total_in += ze->usize;
1136 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1139 PACK_UB2(header, CEN_COMP, 8);
1141 PACK_UB2(header, CEN_COMP, 0);
1144 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1145 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1146 PACK_UB4(header, CEN_CRC, ze->crc);
1147 PACK_UB4(header, CEN_CSIZE, ze->csize);
1148 PACK_UB4(header, CEN_USIZE, ze->usize);
1149 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1150 PACK_UB4(header, CEN_OFFSET, ze->offset);
1152 write(fd, header, 46);
1154 write(fd, ze->filename, strlen(ze->filename));
1157 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1160 end_header[0] = 0x50;
1161 end_header[1] = 0x4b;
1162 end_header[2] = 0x05;
1163 end_header[3] = 0x06;
1164 /* number of this disk */
1167 /* number of disk w/ start of central header */
1170 /* total number of entries in central dir on this disk*/
1171 PACK_UB2(end_header, 8, number_of_entries);
1172 /* total number of entries in central dir*/
1173 PACK_UB2(end_header, 10, number_of_entries);
1174 /* size of central dir. */
1175 PACK_UB4(end_header, 12, dir_size);
1176 /* offset of start of central dir */
1177 PACK_UB4(end_header, 16, start_offset);
1178 /* zipfile comment length */
1182 write(fd, end_header, 22);
1185 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1188 (do_compress ? "deflated" : "stored"),
1189 (int)((1 - (total_out / (float)total_in)) * 100)
1195 int extract_jar(int fd, char **files, int file_num){
1205 ub1 *filename = NULL;
1206 int filename_len = 0;
1225 dir = FALSE; /* by default, the file isn't a dir */
1226 handle = TRUE; /* by default we'll extract/create the file */
1228 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1233 signature = UNPACK_UB4(scratch, 0);
1236 printf("signature is %x\n", signature);
1238 if(signature == 0x08074b50){
1240 printf("skipping data descriptor\n");
1242 pb_read(&pbf, scratch, 12);
1244 } else if(signature == 0x02014b50){
1246 printf("Central header reached.. we're all done!\n");
1249 }else if(signature != 0x04034b50){
1250 printf("Ick! %#x\n", signature);
1254 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1259 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1261 printf("Compressed size is %u\n", csize);
1264 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1266 printf("Filename length is %hu\n", fnlen);
1269 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1271 printf("Extra field length is %hu\n", eflen);
1274 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1276 printf("Flags are %#hx\n", flags);
1279 method = UNPACK_UB2(file_header, LOC_COMP);
1281 printf("Compression method is %#hx\n", method);
1284 /* if there isn't a data descriptor */
1285 if(!(flags & 0x0008)){
1286 crc = UNPACK_UB4(file_header, LOC_CRC);
1288 printf("CRC is %x\n", crc);
1292 if(filename_len < fnlen){
1293 if(filename != NULL)
1296 filename = malloc(sizeof(ub1) * (fnlen + 1));
1297 filename_len = fnlen + 1;
1300 pb_read(&pbf, filename, fnlen);
1301 filename[fnlen] = '\0';
1304 printf("filename is %s\n", filename);
1310 for(j = 0; j < file_num; j++)
1311 if(strcmp(files[j], (const char *)filename) == 0){
1320 /* OK, there is some directory information in the file. Nothing to do
1321 but ensure the directory(s) exist, and create them if they don't.
1323 if(strchr((const char *)filename, '/') != NULL && handle){
1324 /* Loop through all the directories in the path, (everything w/ a '/') */
1325 const ub1 *start = filename;
1329 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1332 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1336 else if(idx == start){
1342 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1343 tmp_buff[(idx - filename)] = '\0';
1346 printf("checking the existance of %s\n", tmp_buff);
1349 if(stat(tmp_buff, &sbuf) < 0){
1350 if(errno != ENOENT){
1355 } else if(S_ISDIR(sbuf.st_mode)){
1357 printf("Directory exists\n");
1361 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1367 printf("Making directory..\n");
1369 if(mkdir(tmp_buff, 0755) < 0){
1373 if(verbose && handle)
1374 printf("%10s: %s/\n", "created", tmp_buff);
1378 /* only a directory */
1379 if(strlen((const char *)start) == 0)
1383 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1386 /* If the entry was just a directory, don't write to file, etc */
1387 if(strlen((const char *)start) == 0)
1393 if(f_fd != -1 && handle){
1394 f_fd = creat((const char *)filename, 00644);
1397 fprintf(stderr, "Error extracting JAR archive!\n");
1398 perror((const char *)filename);
1403 if(method != 8 && flags & 0x0008){
1404 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1408 if(method == 8 || flags & 0x0008){
1410 lseek(fd, eflen, SEEK_CUR);
1412 consume(&pbf, eflen);
1414 inflate_file(&pbf, f_fd, &ze);
1418 printf("writing stored data.. (%d bytes)\n", csize);
1424 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1426 while(out_a < csize){
1427 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1428 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1433 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1436 write(f_fd, rd_buff, rdamt);
1442 printf("%d bytes written\n", out_a);
1447 lseek(fd, eflen, SEEK_CUR);
1449 consume(&pbf, eflen);
1452 /* if there is a data descriptor left, compare the CRC */
1455 if(pb_read(&pbf, scratch, 16) != 16){
1460 signature = UNPACK_UB4(scratch, 0);
1462 if(signature != 0x08074b50){
1463 fprintf(stderr, "Error! Missing data descriptor!\n");
1467 crc = UNPACK_UB4(scratch, 4);
1472 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1479 if(verbose && dir == FALSE && handle)
1480 printf("%10s: %s\n",
1481 (method == 8 ? "inflated" : "extracted"),
1488 int list_jar(int fd, char **files, int file_num){
1501 ub1 *filename = NULL;
1504 int filename_len = 0;
1509 char ascii_date[30];
1513 printf("Listing jar file, looking for %d files\n", file_num);
1516 /* This should be the start of the central-header-end section */
1518 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1523 if(read(fd, &tmp, sizeof(ub4)) != 4){
1528 #ifdef WORDS_BIGENDIAN
1532 if(tmp != 0x06054b50){
1533 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1537 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1542 if(read(fd, &cen_size, 2) != 2){
1547 #ifdef WORDS_BIGENDIAN
1548 cen_size = L2BS(cen_size);
1551 /* printf("%hu entries in central header\n", cen_size); */
1553 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1558 if(read(fd, &tmp, 4) != 4){
1563 #ifdef WORDS_BIGENDIAN
1567 /* printf("Central header offset = %d\n", tmp); */
1569 if(lseek(fd, tmp, SEEK_SET) != tmp){
1574 /* Loop through the entries in the central header */
1575 for(i = 0; i < cen_size; i++){
1577 if(read(fd, &cen_header, 46) != 46){
1582 signature = UNPACK_UB4(cen_header, 0);
1583 if(signature != 0x02014b50){
1584 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1588 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1589 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1590 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1591 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1593 /* If we're providing verbose output, we need to make an ASCII
1594 * formatted version of the date. */
1596 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1597 tdate = dos2unixtime(mdate);
1598 s_tm = localtime(&tdate);
1599 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1602 if(filename_len < fnlen){
1603 if(filename != NULL)
1606 filename = malloc(sizeof(ub1) * (fnlen + 1));
1607 filename_len = fnlen + 1;
1610 if(read(fd, filename, fnlen) != fnlen){
1614 filename[fnlen] = '\0';
1616 /* if the user specified a list of files on the command line,
1617 we'll only display those, otherwise we'll display everything */
1619 for(j = 0; j < file_num; j++)
1620 if(strcmp(files[j], (const char *)filename) == 0){
1622 printf("%6d %s %s\n", usize, ascii_date, filename);
1624 printf("%s\n", filename);
1629 printf("%6d %s %s\n", usize, ascii_date, filename);
1631 printf("%s\n", filename);
1634 size = eflen + clen;
1636 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
1643 /* the file isn't seekable.. evil! */
1651 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1656 signature = UNPACK_UB4(scratch, 0);
1659 printf("signature is %x\n", signature);
1662 if(signature == 0x08074b50){
1664 printf("skipping data descriptor\n");
1666 pb_read(&pbf, scratch, 12);
1668 } else if(signature == 0x02014b50){
1670 printf("Central header reached.. we're all done!\n");
1673 }else if(signature != 0x04034b50){
1675 printf("Ick! %#x\n", signature);
1680 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1685 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1687 printf("Compressed size is %u\n", csize);
1690 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1692 printf("Filename length is %hu\n", fnlen);
1695 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1697 printf("Extra field length is %hu\n", eflen);
1700 method = UNPACK_UB2(file_header, LOC_COMP);
1702 printf("Compression method is %#hx\n", method);
1705 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1707 printf("Flags are %#hx\n", flags);
1710 usize = UNPACK_UB4(file_header, LOC_USIZE);
1712 /* If we're providing verbose output, we need to make an ASCII
1713 * formatted version of the date. */
1715 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
1716 tdate = dos2unixtime(mdate);
1717 s_tm = localtime(&tdate);
1718 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1721 if(filename_len < fnlen){
1722 if(filename != NULL)
1725 filename = malloc(sizeof(ub1) * (fnlen + 1));
1726 filename_len = fnlen + 1;
1729 pb_read(&pbf, filename, fnlen);
1730 filename[fnlen] = '\0';
1732 /* the header is at the end. In a JAR file, this means that the data
1733 happens to be compressed. We have no choice but to inflate the
1740 consume(&pbf, size);
1744 printf("inflating %s\n", filename);
1746 inflate_file(&pbf, -1, &ze);
1750 printf("We're shit outta luck!\n");
1753 size = csize + (eflen > 0 ? eflen : 0);
1757 printf("Skipping %ld bytes\n", (long)size);
1760 consume(&pbf, size);
1762 /* print out the listing */
1764 for(j = 0; j < file_num; j++)
1765 if(strcmp(files[j], (const char *)filename) == 0){
1767 printf("%6d %s %s\n", usize, ascii_date, filename);
1769 printf("%s\n", filename);
1774 printf("%6d %s %s\n", usize, ascii_date, filename);
1776 printf("%s\n", filename);
1783 int consume(pb_file *pbf, int amt){
1784 int tc = 0; /* total amount consumed */
1789 printf("Consuming %d bytes\n", amt);
1793 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
1795 printf("got %d bytes\n", rdamt);
1801 printf("%d bytes consumed\n", tc);
1807 void usage(const char *filename){
1809 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1811 -c create new archive\n\
1812 -t list table of contents for archive\n\
1813 -x extract named (or all) files from archive\n\
1816 -u update existing archive\n\
1817 -V display version information\n\
1818 -v generate verbose output on standard output\n\
1819 -f specify archive file name\n\
1820 -m include manifest information from specified manifest file\n\
1821 -0 store only; use no ZIP compression\n\
1822 -M Do not create a manifest file for the entries\n\
1823 -C change to the specified directory and include the following file\n\
1824 -E don't include the files found in a directory\n\
1825 -@ Read names from stdin\n\
1828 If any file is a directory then it is processed recursively.\n\
1829 The manifest file name and the archive file name needs to be specified\n\
1830 in the same order the 'm' and 'f' flags are specified.\n\
1832 Example 1: to archive two class files into an archive called classes.jar: \n\
1833 jar cvf classes.jar Foo.class Bar.class \n\
1834 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1835 files in the foo/ directory into 'classes.jar': \n\
1836 jar cvfm classes.jar mymanifest -C foo/ .\n\
1846 char *result = (char*)malloc(strlen(s) + 1);
1847 if (result == (char*)0)