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.8 2001/08/29 01:35:31 apbianco Exp $
23 Revision 1.8 2001/08/29 01:35:31 apbianco
24 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
26 * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
29 (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
31 Revision 1.7 2001/08/27 23:09:37 tromey
32 * jartool.c (jarfile): Remove length limitation.
33 (main): Use jt_strdup when initializing jarfile.
35 Revision 1.6 2001/07/04 18:33:53 tromey
36 Modified from patch by Julian Hall <jules@acris.co.uk>:
37 * jartool.c (errno): Conditionally declare.
38 (O_BINARY): Conditionally define.
39 (main): Use open, not creat. Use O_BINARY everywhere.
40 (make_manifest): Use O_BINARY.
41 (add_to_jar): Likewise.
43 Revision 1.5 2001/05/03 21:40:47 danglin
44 * jartool.c (jt_strdup): New function.
45 (get_next_arg): Use jt_strdup instead of strdup.
47 Revision 1.4 2000/12/28 21:47:37 robertl
48 2000-12-28 Robert Lipe <robertl@sco.com>
50 * jartool.c (MAXPATHLEN): Provide if not defined.
52 Revision 1.3 2000/12/14 18:45:35 ghazi
55 * compress.c: Include stdlib.h and compress.h.
57 (report_str_error): Make static.
58 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
59 (hrd_inflate_str): Likewise.
61 * compress.h (init_compression, end_compression, init_inflation,
62 end_inflation): Prototype void arguments.
64 * dostime.c (rcsid): Delete.
66 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
67 Make functions static. Cast ctype function argument to `unsigned
68 char'. Add parens in if-stmts. Constify.
69 (Usage): Change into a macro.
70 (jargrep): Remove unused parameter.
72 * jartool.c: Constify. Add parens in if-stmts. Align
73 signed/unsigned char pointers in functions calls using casts.
75 (list_jar): Fix printf format specifier.
76 (usage): Chop long string into bits. Reformat.
78 * pushback.c (rcsid): Delete.
80 Revision 1.2 2000/12/13 18:11:57 tromey
81 * jartool.c (extract_jar): Use strchr, not index.
83 Revision 1.1 2000/12/09 03:08:23 apbianco
84 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
88 Revision 1.5 2000/08/24 15:01:27 cory
89 Made certain that fastjar opened the jar file before trying to update it
92 Revision 1.4 2000/08/24 13:39:21 cory
93 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
94 when byte swapping. Better safe than sorry.
96 Revision 1.3 2000/08/23 19:42:17 cory
97 Added support for more Unix platforms. The following code has been hacked
98 to work on AIX, Solaris, True 64, and HP-UX.
99 Added bigendian check. Probably works on most big and little endian platforms
102 Revision 1.2 1999/12/06 07:38:28 toast
103 fixed recursive archiving bug
105 Revision 1.1.1.1 1999/12/06 03:09:34 toast
110 Revision 1.22 1999/10/12 19:45:13 burnsbr
111 adding patch to fix compat problem
113 Revision 1.21 1999/05/10 09:15:49 burnsbr
114 fixed manifest file version info
116 Revision 1.20 1999/05/10 08:53:16 burnsbr
117 *** empty log message ***
119 Revision 1.19 1999/05/10 08:30:39 burnsbr
120 added extract / listing code
122 Revision 1.18 1999/04/28 04:24:29 burnsbr
125 Revision 1.17 1999/04/28 04:21:23 burnsbr
126 added support for -C dir-changing flag.. Updated total compression display
128 Revision 1.16 1999/04/27 10:28:22 burnsbr
129 updated version string
131 Revision 1.15 1999/04/27 10:04:06 burnsbr
134 Revision 1.14 1999/04/27 08:56:14 burnsbr
135 added -V flag, better error messages
137 Revision 1.13 1999/04/26 02:35:21 burnsbr
138 changed all sorts of stuff.. compression now works 100%
140 Revision 1.12 1999/04/23 12:00:45 burnsbr
141 90% done with compression code
143 Revision 1.11 1999/04/22 04:12:57 burnsbr
144 finished first round of Manifest file support..
145 might need to do more, digest etc..
147 Revision 1.10 1999/04/22 02:35:23 burnsbr
148 added more manifest support, about 75% done now. Replaced all the
149 redundant shifts and bit-logic with a macro or two, making the code
152 Revision 1.9 1999/04/21 09:55:16 burnsbr
155 Revision 1.8 1999/04/21 02:58:01 burnsbr
156 started manifest code
158 Revision 1.7 1999/04/20 23:15:28 burnsbr
159 added patch sent by John Bley <jbb6@acpub.duke.edu>
161 Revision 1.6 1999/04/20 08:56:02 burnsbr
164 Revision 1.5 1999/04/20 08:16:09 burnsbr
165 fixed verbose flag, did some optimization
167 Revision 1.4 1999/04/20 05:09:59 burnsbr
170 Revision 1.3 1999/04/20 05:08:54 burnsbr
188 #include <sys/stat.h>
189 #include <sys/types.h>
191 #ifdef HAVE_SYS_PARAM_H
192 #include <sys/param.h>
196 #define MAXPATHLEN 1024
210 #ifdef TM_IN_SYS_TIME
211 #include <sys/time.h>
219 #include "pushback.h"
220 #include "compress.h"
222 #ifdef WORDS_BIGENDIAN
224 #define L2BI(l) ((l & 0xff000000) >> 24) | \
225 ((l & 0x00ff0000) >> 8) | \
226 ((l & 0x0000ff00) << 8) | \
227 ((l & 0x000000ff) << 24);
229 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
233 static char version_string
[] = VERSION
;
243 void usage(const char*);
244 void add_entry(struct zipentry
*);
245 void init_headers(void);
247 int consume(pb_file
*, int);
248 int list_jar(int, char**, int);
249 int extract_jar(int, char**, int);
250 int add_file_to_jar(int, int, const char*, struct stat
*);
251 int add_to_jar(int, const char*, const char*);
252 int create_central_header(int);
253 int make_manifest(int, const char*);
254 static void init_args(char **, int);
255 static char *get_next_arg (void);
256 static char *jt_strdup (char*);
258 /* global variables */
260 ub1 data_descriptor
[16];
266 /* If non zero, then don't recurse in directory. Instead, add the
267 directory entry and relie on an explicit list of files to populate
268 the archive. This option isn't supported by the original jar tool. */
269 int use_explicit_list_only
;
271 /* If non zero, then read the entry names from stdin. This option
272 isn't supported by the original jar tool. */
273 int read_names_from_stdin
;
275 zipentry
*ziplist
; /* linked list of entries */
276 zipentry
*ziptail
; /* tail of the linked list */
278 int number_of_entries
; /* number of entries in the linked list */
280 int main(int argc
, char **argv
){
284 int action
= ACTION_NONE
;
286 int manifest_file
= FALSE
;
288 int file_first
= FALSE
;
298 number_of_entries
= 0;
305 for(i
= 0; i
< j
; i
++){
308 action
= ACTION_CREATE
;
311 action
= ACTION_LIST
;
314 action
= ACTION_EXTRACT
;
317 action
= ACTION_UPDATE
;
323 printf("%s\n", version_string
);
333 manifest_file
= TRUE
;
343 /* The following options aren't supported by the original jar tool. */
345 use_explicit_list_only
= TRUE
;
348 read_names_from_stdin
= TRUE
;
351 fprintf(stderr
, "Illegal option: %c\n", argv
[1][i
]);
356 if(action
== ACTION_NONE
){
357 fprintf(stderr
, "One of options -{ctxu} must be specified.\n");
361 /* Verify unsupported combinations and warn of the use of non
363 if(verbose
&& use_explicit_list_only
)
364 fprintf (stderr
, "Warning: using non standard '-E' option\n");
365 if(verbose
&& read_names_from_stdin
)
366 fprintf (stderr
, "Warning: using non standard '-@' option\n");
367 if(read_names_from_stdin
368 && (action
!= ACTION_CREATE
&& action
!= ACTION_UPDATE
)){
369 fprintf(stderr
, "Option '-@' is supported only with '-c' or '-u'.\n");
375 /* get the jarfile and manifest file (if any) */
376 if(file
&& file_first
){
380 jarfile
= jt_strdup (argv
[i
++]);
386 strncpy(mfile
, argv
[i
++], 256);
389 if(file
&& !file_first
){
393 jarfile
= jt_strdup (argv
[i
++]);
396 /* create the jarfile */
397 if(action
== ACTION_CREATE
){
399 jarfd
= open(jarfile
, O_CREAT
| O_BINARY
| O_WRONLY
| O_TRUNC
,
400 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
403 fprintf(stderr
, "Error opening %s for writing!\n", jarfile
);
408 /* We assume that the file is seekable */
413 jarfd
= STDOUT_FILENO
; /* jarfd is stdout otherwise */
415 /* standard out is not seekable */
418 /* don't want our output to be part of the jar file.. figured this one
419 out the hard way.. =P */
422 } else if(action
== ACTION_LIST
|| action
== ACTION_EXTRACT
){
425 jarfd
= open(jarfile
, O_RDONLY
| O_BINARY
);
428 fprintf(stderr
, "Error opening %s for reading!\n", jarfile
);
435 jarfd
= STDIN_FILENO
; /* jarfd is standard in */
437 /* we assume that the stream isn't seekable for safety */
442 if(action
== ACTION_CREATE
|| action
== ACTION_UPDATE
){
446 if((action
== ACTION_UPDATE
) && file
) {
447 if((jarfd
= open(jarfile
, O_RDWR
| O_BINARY
)) < 0) {
448 fprintf(stderr
, "Error opening %s for reading!\n", jarfile
);
458 /* Add the META-INF/ directory and the manifest */
459 if(manifest
&& manifest_file
)
460 make_manifest(jarfd
, mfile
);
462 make_manifest(jarfd
, NULL
);
465 /* now we add the files to the archive */
466 while ((arg
= get_next_arg ())){
468 if(!strcmp(arg
, "-C")){
469 const char *dir_to_change
= get_next_arg ();
470 const char *file_to_add
= get_next_arg ();
473 || add_to_jar(jarfd
, dir_to_change
, file_to_add
)){
474 printf("Error adding %s to jar archive!\n", arg
);
478 if(add_to_jar(jarfd
, NULL
, arg
)){
479 printf("Error adding %s to jar archive!\n", arg
);
484 /* de-initialize the compression DS */
488 create_central_header(jarfd
);
490 if (close(jarfd
) != 0) {
491 fprintf(stderr
, "Error closing jar archive!\n");
493 } else if(action
== ACTION_LIST
){
494 list_jar(jarfd
, &argv
[i
], (argc
- i
));
495 } else if(action
== ACTION_EXTRACT
){
496 extract_jar(jarfd
, &argv
[i
], (argc
- i
));
502 static int args_current_g
;
503 static char **args_g
;
506 init_args(args
, current
)
510 if(!read_names_from_stdin
)
513 args_current_g
= current
;
520 static int reached_end
= 0;
527 if (!args_g
[args_current_g
])
532 return args_g
[args_current_g
++];
536 /* Read the name from stdin. Delimiters are '\n' and
537 '\r'. Reading EOF indicates that we don't have anymore file
538 names characters to read. */
543 /* Get rid of '\n' and '\r' first. */
546 int c
= getc (stdin
);
547 if (c
== '\n' || c
== '\r')
560 int c
= getc (stdin
);
561 /* Exit when we get a delimiter or don't have any characters
563 if (c
== '\n'|| c
== '\r'|| c
== EOF
)
565 s
[pos
++] = (char) c
;
571 return jt_strdup (s
);
579 /* packing file header */
581 file_header
[0] = 0x50;
582 file_header
[1] = 0x4b;
583 file_header
[2] = 0x03;
584 file_header
[3] = 0x04;
585 /* version number (Unix 1.0)*/
588 /* bit flag (normal deflation)*/
589 file_header
[6] = 0x00;
591 file_header
[7] = 0x00;
592 /* do_compression method (deflation) */
596 /* last mod file time (MS-DOS format) */
599 /* last mod file date (MS-DOS format) */
607 /* compressed size */
612 /* uncompressed size */
617 /* filename length */
620 /* extra field length */
624 /* Initialize the compression DS */
625 PACK_UB4(data_descriptor
, 0, 0x08074b50);
629 void add_entry(struct zipentry
*ze
){
635 ziplist
->next_entry
= ze
;
642 int make_manifest(int jfd
, const char *mf_name
){
644 int nlen
; /* length of file name */
645 int mod_time
; /* file modification time */
648 nlen
= 9; /* trust me on this one */
650 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
652 current_time
= time(NULL
);
653 if(current_time
== (time_t)-1){
658 mod_time
= unix2dostime(¤t_time
);
660 PACK_UB2(file_header
, LOC_EXTRA
, 0);
661 PACK_UB2(file_header
, LOC_COMP
, 0);
662 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
663 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
666 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
668 ze
= (zipentry
*)malloc(sizeof(zipentry
));
674 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
675 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
676 strcpy(ze
->filename
, "META-INF/");
677 ze
->filename
[nlen
] = '\0';
679 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
680 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
681 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
682 ze
->compressed
= FALSE
;
686 write(jfd
, file_header
, 30);
687 write(jfd
, "META-INF/", nlen
);
689 /* if the user didn't specify an external manifest file... */
691 int mf_len
= 37 + strlen(VERSION
);
694 if((mf
= (char *) malloc(mf_len
+ 1))) {
697 sprintf(mf
, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION
);
699 crc
= crc32(0L, Z_NULL
, 0);
701 crc
= crc32(crc
, (const unsigned char *)mf
, mf_len
);
703 nlen
= 20; /* once again, trust me */
705 PACK_UB2(file_header
, LOC_EXTRA
, 0);
706 PACK_UB2(file_header
, LOC_COMP
, 0);
707 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
708 PACK_UB4(file_header
, LOC_USIZE
, mf_len
);
710 memcpy((file_header
+ LOC_CSIZE
), (file_header
+ LOC_USIZE
), 4);
712 PACK_UB4(file_header
, LOC_CRC
, crc
);
715 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
717 ze
= (zipentry
*)malloc(sizeof(zipentry
));
723 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
724 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
725 strcpy(ze
->filename
, "META-INF/MANIFEST.MF");
726 ze
->filename
[nlen
] = '\0';
728 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
729 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
730 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
733 ze
->usize
= ze
->csize
;
734 ze
->compressed
= FALSE
;
738 write(jfd
, file_header
, 30);
739 write(jfd
, "META-INF/MANIFEST.MF", nlen
);
740 write(jfd
, mf
, mf_len
);
744 printf("malloc errror\n");
751 stat(mf_name
, &statbuf
);
753 if(!S_ISREG(statbuf
.st_mode
)){
754 fprintf(stderr
, "Invalid manifest file specified.\n");
758 mfd
= open(mf_name
, O_RDONLY
| O_BINARY
);
761 fprintf(stderr
, "Error opening %s.\n", mf_name
);
765 if(add_file_to_jar(jfd
, mfd
, "META-INF/MANIFEST.MF", &statbuf
)){
766 perror("error writing to jar");
775 int add_to_jar(int fd
, const char *new_dir
, const char *file
){
781 char *old_dir
= NULL
;
783 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
785 * "normal" jar : org/apache/java/io/LogRecord.class
786 * fastjar : ./org/apache/java/io/LogRecord.class
787 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
788 * with both kaffe-1.0b4 and JDK.
790 while (*file
=='.' && *(file
+1)=='/')
793 /* If new_dir isn't null, we need to change to that directory. However,
794 we also need to return to the old directory when we're done */
796 old_dir
= getcwd(NULL
, 0);
798 if(chdir(new_dir
) == -1){
804 if(!strcmp(file
, jarfile
)){
806 printf("skipping: %s\n", file
);
807 return 0; /* we don't want to add ourselves.. */
810 stat_return
= stat(file
, &statbuf
);
812 if(stat_return
== -1){
815 } else if(S_ISDIR(statbuf
.st_mode
)){
819 unsigned long mod_time
;
828 nlen
= strlen(file
) + 256;
829 fullname
= (char*)malloc(nlen
* sizeof(char));
830 memset(fullname
, 0, (nlen
* sizeof(char)));
832 if(fullname
== NULL
){
833 fprintf(stderr
, "Filename is NULL!\n");
837 strcpy(fullname
, file
);
840 if(fullname
[nlen
- 1] != '/'){
841 fullname
[nlen
] = '/';
842 t_ptr
= (fullname
+ nlen
+ 1);
844 t_ptr
= (fullname
+ nlen
);
847 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
849 nlen
= (t_ptr
- fullname
);
851 mod_time
= unix2dostime(&statbuf
.st_mtime
);
853 PACK_UB2(file_header
, LOC_EXTRA
, 0);
854 PACK_UB2(file_header
, LOC_COMP
, 0);
855 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
856 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
859 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname
, 0, 0);
861 ze
= (zipentry
*)malloc(sizeof(zipentry
));
867 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
868 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
869 strcpy(ze
->filename
, fullname
);
870 ze
->filename
[nlen
] = '\0';
872 ze
->offset
= lseek(fd
, 0, SEEK_CUR
);
873 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
874 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
875 ze
->compressed
= FALSE
;
879 write(fd
, file_header
, 30);
880 write(fd
, fullname
, nlen
);
882 while(!use_explicit_list_only
&& (de
= readdir(dir
)) != NULL
){
883 if(de
->d_name
[0] == '.')
885 if(!strcmp(de
->d_name
, jarfile
)){ /* we don't want to add ourselves. Believe me */
887 printf("skipping: %s\n", de
->d_name
);
891 strcpy(t_ptr
, de
->d_name
);
893 if(add_to_jar(fd
, NULL
, fullname
)){
894 fprintf(stderr
, "Error adding file to jar!\n");
902 } else if(S_ISREG(statbuf
.st_mode
)){
905 add_fd
= open(file
, O_RDONLY
| O_BINARY
);
907 fprintf(stderr
, "Error opening %s.\n", file
);
911 if(add_file_to_jar(fd
, add_fd
, file
, &statbuf
)){
912 fprintf(stderr
, "Error adding file to jar!\n");
917 fprintf(stderr
, "Illegal file specified: %s\n", file
);
930 int add_file_to_jar(int jfd
, int ffd
, const char *fname
, struct stat
*statbuf
){
932 unsigned short file_name_length
;
933 unsigned long mod_time
;
940 mod_time
= unix2dostime(&(statbuf
->st_mtime
));
941 file_name_length
= strlen(fname
);
943 if(!seekable
&& !do_compress
){
944 crc
= crc32(0L, Z_NULL
, 0);
946 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0)
947 crc
= crc32(crc
, rd_buff
, rdamt
);
949 lseek(ffd
, 0, SEEK_SET
);
952 /* data descriptor */
953 if(!seekable
&& do_compress
){
954 PACK_UB2(file_header
, LOC_EXTRA
, 8);
956 PACK_UB2(file_header
, LOC_EXTRA
, 0);
960 PACK_UB2(file_header
, LOC_COMP
, 8);
962 PACK_UB2(file_header
, LOC_COMP
, 0);
965 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
966 PACK_UB2(file_header
, LOC_FNLEN
, file_name_length
);
968 if(!seekable
&& !do_compress
){
969 PACK_UB4(file_header
, LOC_CRC
, crc
);
970 PACK_UB4(file_header
, LOC_USIZE
, statbuf
->st_size
);
971 PACK_UB4(file_header
, LOC_CSIZE
, statbuf
->st_size
);
973 memset((file_header
+ LOC_CRC
), '\0', 12); /* clear crc/usize/csize */
975 ze
= (zipentry
*)malloc(sizeof(zipentry
));
981 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
982 ze
->filename
= (char*)malloc((file_name_length
+ 1) * sizeof(char));
983 strcpy(ze
->filename
, fname
);
985 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
986 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
988 if(!seekable
&& !do_compress
)
991 ze
->csize
= statbuf
->st_size
;
992 ze
->usize
= ze
->csize
;
993 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
995 ze
->compressed
= TRUE
;
997 ze
->compressed
= FALSE
;
1001 /* Write the local header */
1002 write(jfd
, file_header
, 30);
1004 /* write the file name to the zip file */
1005 write(jfd
, fname
, file_name_length
);
1009 printf("adding: %s ", fname
);
1014 /* compress the file */
1015 compress_file(ffd
, jfd
, ze
);
1017 /* Write the contents of the file (uncompressed) to the zip file */
1018 /* calculate the CRC as we go along */
1019 ze
->crc
= crc32(0L, Z_NULL
, 0);
1021 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0){
1022 ze
->crc
= crc32(ze
->crc
, rd_buff
, rdamt
);
1023 if(write(jfd
, rd_buff
, rdamt
) != rdamt
){
1031 /* write out data descriptor */
1032 PACK_UB4(data_descriptor
, 4, ze
->crc
);
1033 PACK_UB4(data_descriptor
, 8, ze
->csize
);
1034 PACK_UB4(data_descriptor
, 12, ze
->usize
);
1036 /* we need to seek back and fill the header */
1038 offset
= (ze
->csize
+ strlen(ze
->filename
) + 16);
1040 if(lseek(jfd
, -offset
, SEEK_CUR
) == (off_t
)-1){
1045 if(write(jfd
, (data_descriptor
+ 4), 12) != 12){
1052 if(lseek(jfd
, offset
, SEEK_CUR
) == (off_t
)-1){
1056 } else if(do_compress
){
1057 /* Sun's jar tool will only allow a data descriptor if the entry is
1058 compressed, but we'll save 16 bytes/entry if we only use it when
1059 we can't seek back on the file */
1061 if(write(jfd
, data_descriptor
, 16) != 16){
1068 printf("(in=%d) (out=%d) (%s %d%%)\n",
1069 (int)ze
->usize
, (int)ze
->csize
,
1070 (do_compress
? "deflated" : "stored"),
1071 (do_compress
? ((int)((1 - ze
->csize
/(float)ze
->usize
) * 100)) : 0));
1076 int create_central_header(int fd
){
1082 int total_in
= 0, total_out
= 22;
1086 iheader
= (int*)header
;
1093 /* version made by */
1096 /* version needed to extract */
1102 /* compression method */
1116 /* compressed size */
1121 /* uncompressed size */
1126 /* filename length */
1129 /* extra field length */
1132 /* file comment length */
1135 /* disk number start */
1138 /* internal file attribs */
1141 /* external file attribs */
1146 /* relative offset of local header */
1152 start_offset
= lseek(fd
, 0, SEEK_CUR
);
1154 for(ze
= ziptail
; ze
!= NULL
; ze
= ze
->next_entry
){
1156 total_in
+= ze
->usize
;
1157 total_out
+= ze
->csize
+ 76 + strlen(ze
->filename
) * 2;
1160 PACK_UB2(header
, CEN_COMP
, 8);
1162 PACK_UB2(header
, CEN_COMP
, 0);
1165 PACK_UB2(header
, CEN_MODTIME
, ze
->mod_time
);
1166 PACK_UB2(header
, CEN_MODDATE
, ze
->mod_date
);
1167 PACK_UB4(header
, CEN_CRC
, ze
->crc
);
1168 PACK_UB4(header
, CEN_CSIZE
, ze
->csize
);
1169 PACK_UB4(header
, CEN_USIZE
, ze
->usize
);
1170 PACK_UB2(header
, CEN_FNLEN
, strlen(ze
->filename
));
1171 PACK_UB4(header
, CEN_OFFSET
, ze
->offset
);
1173 write(fd
, header
, 46);
1175 write(fd
, ze
->filename
, strlen(ze
->filename
));
1178 dir_size
= lseek(fd
, 0, SEEK_CUR
) - start_offset
;
1181 end_header
[0] = 0x50;
1182 end_header
[1] = 0x4b;
1183 end_header
[2] = 0x05;
1184 end_header
[3] = 0x06;
1185 /* number of this disk */
1188 /* number of disk w/ start of central header */
1191 /* total number of entries in central dir on this disk*/
1192 PACK_UB2(end_header
, 8, number_of_entries
);
1193 /* total number of entries in central dir*/
1194 PACK_UB2(end_header
, 10, number_of_entries
);
1195 /* size of central dir. */
1196 PACK_UB4(end_header
, 12, dir_size
);
1197 /* offset of start of central dir */
1198 PACK_UB4(end_header
, 16, start_offset
);
1199 /* zipfile comment length */
1203 write(fd
, end_header
, 22);
1206 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1209 (do_compress
? "deflated" : "stored"),
1210 (int)((1 - (total_out
/ (float)total_in
)) * 100)
1216 int extract_jar(int fd
, char **files
, int file_num
){
1226 ub1
*filename
= NULL
;
1227 int filename_len
= 0;
1246 dir
= FALSE
; /* by default, the file isn't a dir */
1247 handle
= TRUE
; /* by default we'll extract/create the file */
1249 if((rdamt
= pb_read(&pbf
, scratch
, 4)) != 4){
1254 signature
= UNPACK_UB4(scratch
, 0);
1257 printf("signature is %x\n", signature
);
1259 if(signature
== 0x08074b50){
1261 printf("skipping data descriptor\n");
1263 pb_read(&pbf
, scratch
, 12);
1265 } else if(signature
== 0x02014b50){
1267 printf("Central header reached.. we're all done!\n");
1270 }else if(signature
!= 0x04034b50){
1271 printf("Ick! %#x\n", signature
);
1275 if((rdamt
= pb_read(&pbf
, (file_header
+ 4), 26)) != 26){
1280 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1282 printf("Compressed size is %u\n", csize
);
1285 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1287 printf("Filename length is %hu\n", fnlen
);
1290 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1292 printf("Extra field length is %hu\n", eflen
);
1295 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1297 printf("Flags are %#hx\n", flags
);
1300 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1302 printf("Compression method is %#hx\n", method
);
1305 /* if there isn't a data descriptor */
1306 if(!(flags
& 0x0008)){
1307 crc
= UNPACK_UB4(file_header
, LOC_CRC
);
1309 printf("CRC is %x\n", crc
);
1313 if(filename_len
< fnlen
+ 1){
1314 if(filename
!= NULL
)
1317 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1318 filename_len
= fnlen
+ 1;
1321 pb_read(&pbf
, filename
, fnlen
);
1322 filename
[fnlen
] = '\0';
1325 printf("filename is %s\n", filename
);
1331 for(j
= 0; j
< file_num
; j
++)
1332 if(strcmp(files
[j
], (const char *)filename
) == 0){
1341 /* OK, there is some directory information in the file. Nothing to do
1342 but ensure the directory(s) exist, and create them if they don't.
1344 if(strchr((const char *)filename
, '/') != NULL
&& handle
){
1345 /* Loop through all the directories in the path, (everything w/ a '/') */
1346 const ub1
*start
= filename
;
1350 tmp_buff
= malloc(sizeof(char) * strlen((const char *)filename
));
1353 const ub1
*idx
= (const unsigned char *)strchr((const char *)start
, '/');
1357 else if(idx
== start
){
1363 strncpy(tmp_buff
, (const char *)filename
, (idx
- filename
));
1364 tmp_buff
[(idx
- filename
)] = '\0';
1367 printf("checking the existance of %s\n", tmp_buff
);
1370 if(stat(tmp_buff
, &sbuf
) < 0){
1371 if(errno
!= ENOENT
){
1376 } else if(S_ISDIR(sbuf
.st_mode
)){
1378 printf("Directory exists\n");
1382 fprintf(stderr
, "Hmmm.. %s exists but isn't a directory!\n",
1388 printf("Making directory..\n");
1390 if(mkdir(tmp_buff
, 0755) < 0){
1394 if(verbose
&& handle
)
1395 printf("%10s: %s/\n", "created", tmp_buff
);
1399 /* only a directory */
1400 if(strlen((const char *)start
) == 0)
1404 printf("Leftovers are \"%s\" (%d)\n", start
, strlen((const char *)start
));
1407 /* If the entry was just a directory, don't write to file, etc */
1408 if(strlen((const char *)start
) == 0)
1414 if(f_fd
!= -1 && handle
){
1415 f_fd
= creat((const char *)filename
, 00644);
1418 fprintf(stderr
, "Error extracting JAR archive!\n");
1419 perror((const char *)filename
);
1424 if(method
!= 8 && flags
& 0x0008){
1425 fprintf(stderr
, "Error in JAR file! (not compressed but data desc.)\n");
1429 if(method
== 8 || flags
& 0x0008){
1431 lseek(fd
, eflen
, SEEK_CUR
);
1433 consume(&pbf
, eflen
);
1435 inflate_file(&pbf
, f_fd
, &ze
);
1439 printf("writing stored data.. (%d bytes)\n", csize
);
1445 ze
.crc
= crc32(ze
.crc
, NULL
, 0); /* initialize the crc */
1447 while(out_a
< csize
){
1448 rdamt
= (in_a
> RDSZ
? RDSZ
: in_a
);
1449 if(pb_read(&pbf
, rd_buff
, rdamt
) != rdamt
){
1454 ze
.crc
= crc32(ze
.crc
, (Bytef
*)rd_buff
, rdamt
);
1457 write(f_fd
, rd_buff
, rdamt
);
1463 printf("%d bytes written\n", out_a
);
1468 lseek(fd
, eflen
, SEEK_CUR
);
1470 consume(&pbf
, eflen
);
1473 /* if there is a data descriptor left, compare the CRC */
1476 if(pb_read(&pbf
, scratch
, 16) != 16){
1481 signature
= UNPACK_UB4(scratch
, 0);
1483 if(signature
!= 0x08074b50){
1484 fprintf(stderr
, "Error! Missing data descriptor!\n");
1488 crc
= UNPACK_UB4(scratch
, 4);
1493 fprintf(stderr
, "Error! CRCs do not match! Got %x, expected %x\n",
1500 if(verbose
&& dir
== FALSE
&& handle
)
1501 printf("%10s: %s\n",
1502 (method
== 8 ? "inflated" : "extracted"),
1509 int list_jar(int fd
, char **files
, int file_num
){
1522 ub1
*filename
= NULL
;
1525 int filename_len
= 0;
1530 char ascii_date
[30];
1534 printf("Listing jar file, looking for %d files\n", file_num
);
1537 /* This should be the start of the central-header-end section */
1539 if(lseek(fd
, -22, SEEK_END
) == (off_t
)-1){
1544 if(read(fd
, &tmp
, sizeof(ub4
)) != 4){
1549 #ifdef WORDS_BIGENDIAN
1553 if(tmp
!= 0x06054b50){
1554 fprintf(stderr
, "Error in JAR file format. zip-style comment?\n");
1558 if(lseek(fd
, 6, SEEK_CUR
) == (off_t
)-1){
1563 if(read(fd
, &cen_size
, 2) != 2){
1568 #ifdef WORDS_BIGENDIAN
1569 cen_size
= L2BS(cen_size
);
1572 /* printf("%hu entries in central header\n", cen_size); */
1574 if(lseek(fd
, 4, SEEK_CUR
) == (off_t
)-1){
1579 if(read(fd
, &tmp
, 4) != 4){
1584 #ifdef WORDS_BIGENDIAN
1588 /* printf("Central header offset = %d\n", tmp); */
1590 if(lseek(fd
, tmp
, SEEK_SET
) != tmp
){
1595 /* Loop through the entries in the central header */
1596 for(i
= 0; i
< cen_size
; i
++){
1598 if(read(fd
, &cen_header
, 46) != 46){
1603 signature
= UNPACK_UB4(cen_header
, 0);
1604 if(signature
!= 0x02014b50){
1605 fprintf(stderr
, "Error in JAR file! Cannot locate central header!\n");
1609 usize
= UNPACK_UB4(cen_header
, CEN_USIZE
);
1610 fnlen
= UNPACK_UB2(cen_header
, CEN_FNLEN
);
1611 eflen
= UNPACK_UB2(cen_header
, CEN_EFLEN
);
1612 clen
= UNPACK_UB2(cen_header
, CEN_COMLEN
);
1614 /* If we're providing verbose output, we need to make an ASCII
1615 * formatted version of the date. */
1617 mdate
= UNPACK_UB4(cen_header
, CEN_MODTIME
);
1618 tdate
= dos2unixtime(mdate
);
1619 s_tm
= localtime(&tdate
);
1620 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
1623 if(filename_len
< fnlen
){
1624 if(filename
!= NULL
)
1627 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1628 filename_len
= fnlen
+ 1;
1631 if(read(fd
, filename
, fnlen
) != fnlen
){
1635 filename
[fnlen
] = '\0';
1637 /* if the user specified a list of files on the command line,
1638 we'll only display those, otherwise we'll display everything */
1640 for(j
= 0; j
< file_num
; j
++)
1641 if(strcmp(files
[j
], (const char *)filename
) == 0){
1643 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1645 printf("%s\n", filename
);
1650 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1652 printf("%s\n", filename
);
1655 size
= eflen
+ clen
;
1657 if(lseek(fd
, size
, SEEK_CUR
) == (off_t
)-1){
1664 /* the file isn't seekable.. evil! */
1672 if((rdamt
= pb_read(&pbf
, scratch
, 4)) != 4){
1677 signature
= UNPACK_UB4(scratch
, 0);
1680 printf("signature is %x\n", signature
);
1683 if(signature
== 0x08074b50){
1685 printf("skipping data descriptor\n");
1687 pb_read(&pbf
, scratch
, 12);
1689 } else if(signature
== 0x02014b50){
1691 printf("Central header reached.. we're all done!\n");
1694 }else if(signature
!= 0x04034b50){
1696 printf("Ick! %#x\n", signature
);
1701 if((rdamt
= pb_read(&pbf
, (file_header
+ 4), 26)) != 26){
1706 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1708 printf("Compressed size is %u\n", csize
);
1711 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1713 printf("Filename length is %hu\n", fnlen
);
1716 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1718 printf("Extra field length is %hu\n", eflen
);
1721 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1723 printf("Compression method is %#hx\n", method
);
1726 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1728 printf("Flags are %#hx\n", flags
);
1731 usize
= UNPACK_UB4(file_header
, LOC_USIZE
);
1733 /* If we're providing verbose output, we need to make an ASCII
1734 * formatted version of the date. */
1736 mdate
= UNPACK_UB4(file_header
, LOC_MODTIME
);
1737 tdate
= dos2unixtime(mdate
);
1738 s_tm
= localtime(&tdate
);
1739 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
1742 if(filename_len
< fnlen
){
1743 if(filename
!= NULL
)
1746 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1747 filename_len
= fnlen
+ 1;
1750 pb_read(&pbf
, filename
, fnlen
);
1751 filename
[fnlen
] = '\0';
1753 /* the header is at the end. In a JAR file, this means that the data
1754 happens to be compressed. We have no choice but to inflate the
1761 consume(&pbf
, size
);
1765 printf("inflating %s\n", filename
);
1767 inflate_file(&pbf
, -1, &ze
);
1771 printf("We're shit outta luck!\n");
1774 size
= csize
+ (eflen
> 0 ? eflen
: 0);
1778 printf("Skipping %ld bytes\n", (long)size
);
1781 consume(&pbf
, size
);
1783 /* print out the listing */
1785 for(j
= 0; j
< file_num
; j
++)
1786 if(strcmp(files
[j
], (const char *)filename
) == 0){
1788 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1790 printf("%s\n", filename
);
1795 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1797 printf("%s\n", filename
);
1804 int consume(pb_file
*pbf
, int amt
){
1805 int tc
= 0; /* total amount consumed */
1810 printf("Consuming %d bytes\n", amt
);
1814 rdamt
= pb_read(pbf
, buff
, ((amt
- tc
) < RDSZ
? (amt
- tc
) : RDSZ
));
1816 printf("got %d bytes\n", rdamt
);
1822 printf("%d bytes consumed\n", tc
);
1828 void usage(const char *filename
){
1830 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1832 -c create new archive\n\
1833 -t list table of contents for archive\n\
1834 -x extract named (or all) files from archive\n\
1837 -u update existing archive\n\
1838 -V display version information\n\
1839 -v generate verbose output on standard output\n\
1840 -f specify archive file name\n\
1841 -m include manifest information from specified manifest file\n\
1842 -0 store only; use no ZIP compression\n\
1843 -M Do not create a manifest file for the entries\n\
1844 -C change to the specified directory and include the following file\n\
1845 -E don't include the files found in a directory\n\
1846 -@ Read names from stdin\n\
1849 If any file is a directory then it is processed recursively.\n\
1850 The manifest file name and the archive file name needs to be specified\n\
1851 in the same order the 'm' and 'f' flags are specified.\n\
1853 Example 1: to archive two class files into an archive called classes.jar: \n\
1854 jar cvf classes.jar Foo.class Bar.class \n\
1855 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1856 files in the foo/ directory into 'classes.jar': \n\
1857 jar cvfm classes.jar mymanifest -C foo/ .\n\
1867 char *result
= (char*)malloc(strlen(s
) + 1);
1868 if (result
== (char*)0)