2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002 Free Software Foundation
4 Copyright (C) 1999, 2000, 2001 Bryan Burns
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 Revision 1.10 2002/01/03 04:57:56 rodrigc
23 2001-01-02 Craig Rodrigues <rodrigc@gcc.gnu.org>
26 * configure.in (AC_CHECK_HEADERS): Check for stdlib.h.
27 * Makefile.am: Move grepjar to bin_PROGRAMS.
28 * config.h.in: Regenerated.
29 * Makefile.in: Regenerated.
30 * aclocal.m4: Regenerated.
31 * jargrep.c: Eliminate some signed/unsigned and default
32 uninitialized warnings. Use HAVE_STDLIB_H instead of
34 * jartool.c: Likewise.
35 * compress.c: Likewise.
37 Revision 1.9 2001/10/12 00:49:42 bryce
38 * jatool.c (extract_jar): Account for null termination when
39 determining whether to expand "filename".
41 Revision 1.8 2001/08/29 01:35:31 apbianco
42 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
44 * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
47 (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
49 Revision 1.7 2001/08/27 23:09:37 tromey
50 * jartool.c (jarfile): Remove length limitation.
51 (main): Use jt_strdup when initializing jarfile.
53 Revision 1.6 2001/07/04 18:33:53 tromey
54 Modified from patch by Julian Hall <jules@acris.co.uk>:
55 * jartool.c (errno): Conditionally declare.
56 (O_BINARY): Conditionally define.
57 (main): Use open, not creat. Use O_BINARY everywhere.
58 (make_manifest): Use O_BINARY.
59 (add_to_jar): Likewise.
61 Revision 1.5 2001/05/03 21:40:47 danglin
62 * jartool.c (jt_strdup): New function.
63 (get_next_arg): Use jt_strdup instead of strdup.
65 Revision 1.4 2000/12/28 21:47:37 robertl
66 2000-12-28 Robert Lipe <robertl@sco.com>
68 * jartool.c (MAXPATHLEN): Provide if not defined.
70 Revision 1.3 2000/12/14 18:45:35 ghazi
73 * compress.c: Include stdlib.h and compress.h.
75 (report_str_error): Make static.
76 (ez_inflate_str): Delete unused variable. Add parens in if-stmt.
77 (hrd_inflate_str): Likewise.
79 * compress.h (init_compression, end_compression, init_inflation,
80 end_inflation): Prototype void arguments.
82 * dostime.c (rcsid): Delete.
84 * jargrep.c: Include ctype.h, stdlib.h, zlib.h and compress.h.
85 Make functions static. Cast ctype function argument to `unsigned
86 char'. Add parens in if-stmts. Constify.
87 (Usage): Change into a macro.
88 (jargrep): Remove unused parameter.
90 * jartool.c: Constify. Add parens in if-stmts. Align
91 signed/unsigned char pointers in functions calls using casts.
93 (list_jar): Fix printf format specifier.
94 (usage): Chop long string into bits. Reformat.
96 * pushback.c (rcsid): Delete.
98 Revision 1.2 2000/12/13 18:11:57 tromey
99 * jartool.c (extract_jar): Use strchr, not index.
101 Revision 1.1 2000/12/09 03:08:23 apbianco
102 2000-12-08 Alexandre Petit-Bianco <apbianco@cygnus.com>
106 Revision 1.5 2000/08/24 15:01:27 cory
107 Made certain that fastjar opened the jar file before trying to update it
110 Revision 1.4 2000/08/24 13:39:21 cory
111 Changed +'s to |'s in jartool.c to insure there was no confusion with sign
112 when byte swapping. Better safe than sorry.
114 Revision 1.3 2000/08/23 19:42:17 cory
115 Added support for more Unix platforms. The following code has been hacked
116 to work on AIX, Solaris, True 64, and HP-UX.
117 Added bigendian check. Probably works on most big and little endian platforms
120 Revision 1.2 1999/12/06 07:38:28 toast
121 fixed recursive archiving bug
123 Revision 1.1.1.1 1999/12/06 03:09:34 toast
128 Revision 1.22 1999/10/12 19:45:13 burnsbr
129 adding patch to fix compat problem
131 Revision 1.21 1999/05/10 09:15:49 burnsbr
132 fixed manifest file version info
134 Revision 1.20 1999/05/10 08:53:16 burnsbr
135 *** empty log message ***
137 Revision 1.19 1999/05/10 08:30:39 burnsbr
138 added extract / listing code
140 Revision 1.18 1999/04/28 04:24:29 burnsbr
143 Revision 1.17 1999/04/28 04:21:23 burnsbr
144 added support for -C dir-changing flag.. Updated total compression display
146 Revision 1.16 1999/04/27 10:28:22 burnsbr
147 updated version string
149 Revision 1.15 1999/04/27 10:04:06 burnsbr
152 Revision 1.14 1999/04/27 08:56:14 burnsbr
153 added -V flag, better error messages
155 Revision 1.13 1999/04/26 02:35:21 burnsbr
156 changed all sorts of stuff.. compression now works 100%
158 Revision 1.12 1999/04/23 12:00:45 burnsbr
159 90% done with compression code
161 Revision 1.11 1999/04/22 04:12:57 burnsbr
162 finished first round of Manifest file support..
163 might need to do more, digest etc..
165 Revision 1.10 1999/04/22 02:35:23 burnsbr
166 added more manifest support, about 75% done now. Replaced all the
167 redundant shifts and bit-logic with a macro or two, making the code
170 Revision 1.9 1999/04/21 09:55:16 burnsbr
173 Revision 1.8 1999/04/21 02:58:01 burnsbr
174 started manifest code
176 Revision 1.7 1999/04/20 23:15:28 burnsbr
177 added patch sent by John Bley <jbb6@acpub.duke.edu>
179 Revision 1.6 1999/04/20 08:56:02 burnsbr
182 Revision 1.5 1999/04/20 08:16:09 burnsbr
183 fixed verbose flag, did some optimization
185 Revision 1.4 1999/04/20 05:09:59 burnsbr
188 Revision 1.3 1999/04/20 05:08:54 burnsbr
206 #include <sys/stat.h>
207 #include <sys/types.h>
209 #ifdef HAVE_SYS_PARAM_H
210 #include <sys/param.h>
214 #define MAXPATHLEN 1024
228 #ifdef TM_IN_SYS_TIME
229 #include <sys/time.h>
239 #include "pushback.h"
240 #include "compress.h"
242 /* Some systems have mkdir that takes a single argument. */
243 #ifdef MKDIR_TAKES_ONE_ARG
244 # define mkdir(a,b) mkdir(a)
248 #ifdef WORDS_BIGENDIAN
250 #define L2BI(l) ((l & 0xff000000) >> 24) | \
251 ((l & 0x00ff0000) >> 8) | \
252 ((l & 0x0000ff00) << 8) | \
253 ((l & 0x000000ff) << 24);
255 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
267 void usage(const char*);
268 void help(const char *);
270 void add_entry(struct zipentry
*);
271 void init_headers(void);
273 int consume(pb_file
*, int);
274 int list_jar(int, char**, int);
275 int extract_jar(int, char**, int);
276 int add_file_to_jar(int, int, const char*, struct stat
*);
277 int add_to_jar(int, const char*, const char*);
278 int create_central_header(int);
279 int make_manifest(int, const char*);
280 static void init_args(char **, int);
281 static char *get_next_arg (void);
282 static char *jt_strdup (char*);
283 static void expand_options (int *argcp
, char ***argvp
);
285 /* global variables */
287 ub1 data_descriptor
[16];
293 /* If non zero, then don't recurse in directory. Instead, add the
294 directory entry and relie on an explicit list of files to populate
295 the archive. This option isn't supported by the original jar tool. */
296 int use_explicit_list_only
;
298 /* If non zero, then read the entry names from stdin. This option
299 isn't supported by the original jar tool. */
300 int read_names_from_stdin
;
302 zipentry
*ziplist
; /* linked list of entries */
303 zipentry
*ziptail
; /* tail of the linked list */
305 int number_of_entries
; /* number of entries in the linked list */
307 /* This is used to mark options with no short value. */
308 #define LONG_OPT(Num) ((Num) + 128)
310 #define OPT_HELP LONG_OPT (0)
312 /* This holds all options. */
313 #define OPTION_STRING "-ctxuvVf:m:C:0ME@"
315 static const struct option options
[] =
317 { "help", no_argument
, NULL
, OPT_HELP
},
318 { "version", no_argument
, NULL
, 'V' },
319 { NULL
, no_argument
, NULL
, 0 }
322 int main(int argc
, char **argv
){
326 int action
= ACTION_NONE
;
332 /* These are used to collect file names and `-C' options for the
333 second pass through the command line. */
342 number_of_entries
= 0;
348 new_argv
= (char **) malloc (argc
* sizeof (char *));
350 expand_options (&argc
, &argv
);
351 while ((opt
= getopt_long (argc
, argv
, OPTION_STRING
,
352 options
, NULL
)) != -1) {
355 new_argv
[new_argc
++] = (char *) "-C";
356 /* ... fall through ... */
358 /* File name or unparsed option, due to RETURN_IN_ORDER. */
359 new_argv
[new_argc
++] = optarg
;
362 action
= ACTION_CREATE
;
365 action
= ACTION_LIST
;
368 action
= ACTION_EXTRACT
;
371 action
= ACTION_UPDATE
;
396 /* The following options aren't supported by the original jar tool. */
398 use_explicit_list_only
= TRUE
;
401 read_names_from_stdin
= TRUE
;
408 /* We might have seen `--'. In this case we want to make sure that
409 all following options are handled as file names. */
410 while (optind
< argc
)
411 new_argv
[new_argc
++] = argv
[optind
++];
412 new_argv
[new_argc
] = NULL
;
414 if(action
== ACTION_NONE
){
415 fprintf(stderr
, "One of options -{ctxu} must be specified.\n");
419 if(action
== ACTION_UPDATE
){
420 fprintf(stderr
, "%s: `-u' mode unimplemented.\n", argv
[0]);
424 /* Verify unsupported combinations and warn of the use of non
426 if(verbose
&& use_explicit_list_only
)
427 fprintf (stderr
, "Warning: using non standard '-E' option\n");
428 if(verbose
&& read_names_from_stdin
)
429 fprintf (stderr
, "Warning: using non standard '-@' option\n");
430 if(read_names_from_stdin
431 && (action
!= ACTION_CREATE
&& action
!= ACTION_UPDATE
)){
432 fprintf(stderr
, "Option '-@' is supported only with '-c' or '-u'.\n");
436 /* create the jarfile */
437 if(action
== ACTION_CREATE
){
439 jarfd
= open(jarfile
, O_CREAT
| O_BINARY
| O_WRONLY
| O_TRUNC
, 0666);
442 fprintf(stderr
, "Error opening %s for writing!\n", jarfile
);
447 /* We assume that the file is seekable */
452 jarfd
= STDOUT_FILENO
; /* jarfd is stdout otherwise */
454 /* standard out is not seekable */
457 /* don't want our output to be part of the jar file.. figured this one
458 out the hard way.. =P */
461 } else if(action
== ACTION_LIST
|| action
== ACTION_EXTRACT
){
464 jarfd
= open(jarfile
, O_RDONLY
| O_BINARY
);
467 fprintf(stderr
, "Error opening %s for reading!\n", jarfile
);
474 jarfd
= STDIN_FILENO
; /* jarfd is standard in */
476 /* we assume that the stream isn't seekable for safety */
481 if(action
== ACTION_CREATE
|| action
== ACTION_UPDATE
){
485 if((action
== ACTION_UPDATE
) && jarfile
) {
486 if((jarfd
= open(jarfile
, O_RDWR
| O_BINARY
)) < 0) {
487 fprintf(stderr
, "Error opening %s for reading!\n", jarfile
);
497 /* Add the META-INF/ directory and the manifest */
498 if(manifest
&& mfile
)
499 make_manifest(jarfd
, mfile
);
501 make_manifest(jarfd
, NULL
);
503 init_args (new_argv
, 0);
504 /* now we add the files to the archive */
505 while ((arg
= get_next_arg ())){
507 if(!strcmp(arg
, "-C")){
508 const char *dir_to_change
= get_next_arg ();
509 const char *file_to_add
= get_next_arg ();
512 || add_to_jar(jarfd
, dir_to_change
, file_to_add
)){
513 printf("Error adding %s to jar archive!\n", arg
);
517 if(add_to_jar(jarfd
, NULL
, arg
)){
518 printf("Error adding %s to jar archive!\n", arg
);
523 /* de-initialize the compression DS */
527 create_central_header(jarfd
);
529 if (close(jarfd
) != 0) {
530 fprintf(stderr
, "Error closing jar archive!\n");
532 } else if(action
== ACTION_LIST
){
533 list_jar(jarfd
, &new_argv
[0], new_argc
);
534 } else if(action
== ACTION_EXTRACT
){
535 extract_jar(jarfd
, &new_argv
[0], new_argc
);
541 static int args_current_g
;
542 static char **args_g
;
545 init_args(args
, current
)
549 if(!read_names_from_stdin
)
552 args_current_g
= current
;
559 static int reached_end
= 0;
566 if (!args_g
[args_current_g
])
571 return args_g
[args_current_g
++];
575 /* Read the name from stdin. Delimiters are '\n' and
576 '\r'. Reading EOF indicates that we don't have anymore file
577 names characters to read. */
582 /* Get rid of '\n' and '\r' first. */
585 int c
= getc (stdin
);
586 if (c
== '\n' || c
== '\r')
599 int c
= getc (stdin
);
600 /* Exit when we get a delimiter or don't have any characters
602 if (c
== '\n'|| c
== '\r'|| c
== EOF
)
604 s
[pos
++] = (char) c
;
610 return jt_strdup (s
);
618 /* packing file header */
620 file_header
[0] = 0x50;
621 file_header
[1] = 0x4b;
622 file_header
[2] = 0x03;
623 file_header
[3] = 0x04;
624 /* version number (Unix 1.0)*/
627 /* bit flag (normal deflation)*/
628 file_header
[6] = 0x00;
630 file_header
[7] = 0x00;
631 /* do_compression method (deflation) */
635 /* last mod file time (MS-DOS format) */
638 /* last mod file date (MS-DOS format) */
646 /* compressed size */
651 /* uncompressed size */
656 /* filename length */
659 /* extra field length */
663 /* Initialize the compression DS */
664 PACK_UB4(data_descriptor
, 0, 0x08074b50);
668 void add_entry(struct zipentry
*ze
){
674 ziplist
->next_entry
= ze
;
681 int make_manifest(int jfd
, const char *mf_name
){
683 int nlen
; /* length of file name */
684 int mod_time
; /* file modification time */
687 nlen
= 9; /* trust me on this one */
689 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
691 current_time
= time(NULL
);
692 if(current_time
== (time_t)-1){
697 mod_time
= unix2dostime(¤t_time
);
699 PACK_UB2(file_header
, LOC_EXTRA
, 0);
700 PACK_UB2(file_header
, LOC_COMP
, 0);
701 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
702 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
705 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
707 ze
= (zipentry
*)malloc(sizeof(zipentry
));
713 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
714 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
715 strcpy(ze
->filename
, "META-INF/");
716 ze
->filename
[nlen
] = '\0';
718 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
719 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
720 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
721 ze
->compressed
= FALSE
;
725 write(jfd
, file_header
, 30);
726 write(jfd
, "META-INF/", nlen
);
728 /* if the user didn't specify an external manifest file... */
730 int mf_len
= 37 + strlen(VERSION
);
733 if((mf
= (char *) malloc(mf_len
+ 1))) {
736 sprintf(mf
, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION
);
738 crc
= crc32(0L, Z_NULL
, 0);
740 crc
= crc32(crc
, (const unsigned char *)mf
, mf_len
);
742 nlen
= 20; /* once again, trust me */
744 PACK_UB2(file_header
, LOC_EXTRA
, 0);
745 PACK_UB2(file_header
, LOC_COMP
, 0);
746 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
747 PACK_UB4(file_header
, LOC_USIZE
, mf_len
);
749 memcpy((file_header
+ LOC_CSIZE
), (file_header
+ LOC_USIZE
), 4);
751 PACK_UB4(file_header
, LOC_CRC
, crc
);
754 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
756 ze
= (zipentry
*)malloc(sizeof(zipentry
));
762 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
763 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
764 strcpy(ze
->filename
, "META-INF/MANIFEST.MF");
765 ze
->filename
[nlen
] = '\0';
767 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
768 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
769 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
772 ze
->usize
= ze
->csize
;
773 ze
->compressed
= FALSE
;
777 write(jfd
, file_header
, 30);
778 write(jfd
, "META-INF/MANIFEST.MF", nlen
);
779 write(jfd
, mf
, mf_len
);
783 printf("malloc errror\n");
790 stat(mf_name
, &statbuf
);
792 if(!S_ISREG(statbuf
.st_mode
)){
793 fprintf(stderr
, "Invalid manifest file specified.\n");
797 mfd
= open(mf_name
, O_RDONLY
| O_BINARY
);
800 fprintf(stderr
, "Error opening %s.\n", mf_name
);
804 if(add_file_to_jar(jfd
, mfd
, "META-INF/MANIFEST.MF", &statbuf
)){
805 perror("error writing to jar");
814 int add_to_jar(int fd
, const char *new_dir
, const char *file
){
820 char *old_dir
= NULL
;
822 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
824 * "normal" jar : org/apache/java/io/LogRecord.class
825 * fastjar : ./org/apache/java/io/LogRecord.class
826 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
827 * with both kaffe-1.0b4 and JDK.
829 while (*file
=='.' && *(file
+1)=='/')
832 /* If new_dir isn't null, we need to change to that directory. However,
833 we also need to return to the old directory when we're done */
835 old_dir
= getcwd(NULL
, 0);
837 if(chdir(new_dir
) == -1){
843 if(jarfile
&& !strcmp(file
, jarfile
)){
845 printf("skipping: %s\n", file
);
846 return 0; /* we don't want to add ourselves.. */
849 stat_return
= stat(file
, &statbuf
);
851 if(stat_return
== -1){
854 } else if(S_ISDIR(statbuf
.st_mode
)){
858 unsigned long mod_time
;
867 nlen
= strlen(file
) + 256;
868 fullname
= (char*)malloc(nlen
* sizeof(char));
869 memset(fullname
, 0, (nlen
* sizeof(char)));
871 if(fullname
== NULL
){
872 fprintf(stderr
, "Filename is NULL!\n");
876 strcpy(fullname
, file
);
879 if(fullname
[nlen
- 1] != '/'){
880 fullname
[nlen
] = '/';
881 t_ptr
= (fullname
+ nlen
+ 1);
883 t_ptr
= (fullname
+ nlen
);
886 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
888 nlen
= (t_ptr
- fullname
);
890 mod_time
= unix2dostime(&statbuf
.st_mtime
);
892 PACK_UB2(file_header
, LOC_EXTRA
, 0);
893 PACK_UB2(file_header
, LOC_COMP
, 0);
894 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
895 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
898 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname
, 0, 0);
900 ze
= (zipentry
*)malloc(sizeof(zipentry
));
906 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
907 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
908 strcpy(ze
->filename
, fullname
);
909 ze
->filename
[nlen
] = '\0';
911 ze
->offset
= lseek(fd
, 0, SEEK_CUR
);
912 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
913 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
914 ze
->compressed
= FALSE
;
918 write(fd
, file_header
, 30);
919 write(fd
, fullname
, nlen
);
921 while(!use_explicit_list_only
&& (de
= readdir(dir
)) != NULL
){
922 if(de
->d_name
[0] == '.')
924 if(jarfile
&& !strcmp(de
->d_name
, jarfile
)){
925 /* we don't want to add ourselves. Believe me */
927 printf("skipping: %s\n", de
->d_name
);
931 strcpy(t_ptr
, de
->d_name
);
933 if(add_to_jar(fd
, NULL
, fullname
)){
934 fprintf(stderr
, "Error adding file to jar!\n");
942 } else if(S_ISREG(statbuf
.st_mode
)){
945 add_fd
= open(file
, O_RDONLY
| O_BINARY
);
947 fprintf(stderr
, "Error opening %s.\n", file
);
951 if(add_file_to_jar(fd
, add_fd
, file
, &statbuf
)){
952 fprintf(stderr
, "Error adding file to jar!\n");
957 fprintf(stderr
, "Illegal file specified: %s\n", file
);
970 int add_file_to_jar(int jfd
, int ffd
, const char *fname
, struct stat
*statbuf
){
972 unsigned short file_name_length
;
973 unsigned long mod_time
;
980 mod_time
= unix2dostime(&(statbuf
->st_mtime
));
981 file_name_length
= strlen(fname
);
983 if(!seekable
&& !do_compress
){
984 crc
= crc32(0L, Z_NULL
, 0);
986 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0)
987 crc
= crc32(crc
, rd_buff
, rdamt
);
989 lseek(ffd
, 0, SEEK_SET
);
992 /* data descriptor */
993 if(!seekable
&& do_compress
){
994 PACK_UB2(file_header
, LOC_EXTRA
, 8);
996 PACK_UB2(file_header
, LOC_EXTRA
, 0);
1000 PACK_UB2(file_header
, LOC_COMP
, 8);
1002 PACK_UB2(file_header
, LOC_COMP
, 0);
1005 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
1006 PACK_UB2(file_header
, LOC_FNLEN
, file_name_length
);
1008 if(!seekable
&& !do_compress
){
1009 PACK_UB4(file_header
, LOC_CRC
, crc
);
1010 PACK_UB4(file_header
, LOC_USIZE
, statbuf
->st_size
);
1011 PACK_UB4(file_header
, LOC_CSIZE
, statbuf
->st_size
);
1013 memset((file_header
+ LOC_CRC
), '\0', 12); /* clear crc/usize/csize */
1015 ze
= (zipentry
*)malloc(sizeof(zipentry
));
1021 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
1022 ze
->filename
= (char*)malloc((file_name_length
+ 1) * sizeof(char));
1023 strcpy(ze
->filename
, fname
);
1025 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1026 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1028 if(!seekable
&& !do_compress
)
1031 ze
->csize
= statbuf
->st_size
;
1032 ze
->usize
= ze
->csize
;
1033 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
1035 ze
->compressed
= TRUE
;
1037 ze
->compressed
= FALSE
;
1041 /* Write the local header */
1042 write(jfd
, file_header
, 30);
1044 /* write the file name to the zip file */
1045 write(jfd
, fname
, file_name_length
);
1049 printf("adding: %s ", fname
);
1054 /* compress the file */
1055 compress_file(ffd
, jfd
, ze
);
1057 /* Write the contents of the file (uncompressed) to the zip file */
1058 /* calculate the CRC as we go along */
1059 ze
->crc
= crc32(0L, Z_NULL
, 0);
1061 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0){
1062 ze
->crc
= crc32(ze
->crc
, rd_buff
, rdamt
);
1063 if(write(jfd
, rd_buff
, rdamt
) != rdamt
){
1071 /* write out data descriptor */
1072 PACK_UB4(data_descriptor
, 4, ze
->crc
);
1073 PACK_UB4(data_descriptor
, 8, ze
->csize
);
1074 PACK_UB4(data_descriptor
, 12, ze
->usize
);
1076 /* we need to seek back and fill the header */
1078 offset
= (ze
->csize
+ strlen(ze
->filename
) + 16);
1080 if(lseek(jfd
, -offset
, SEEK_CUR
) == (off_t
)-1){
1085 if(write(jfd
, (data_descriptor
+ 4), 12) != 12){
1092 if(lseek(jfd
, offset
, SEEK_CUR
) == (off_t
)-1){
1096 } else if(do_compress
){
1097 /* Sun's jar tool will only allow a data descriptor if the entry is
1098 compressed, but we'll save 16 bytes/entry if we only use it when
1099 we can't seek back on the file */
1101 if(write(jfd
, data_descriptor
, 16) != 16){
1108 printf("(in=%d) (out=%d) (%s %d%%)\n",
1109 (int)ze
->usize
, (int)ze
->csize
,
1110 (do_compress
? "deflated" : "stored"),
1111 (do_compress
? ((int)((1 - ze
->csize
/(float)ze
->usize
) * 100)) : 0));
1116 int create_central_header(int fd
){
1121 int total_in
= 0, total_out
= 22;
1130 /* version made by */
1133 /* version needed to extract */
1139 /* compression method */
1153 /* compressed size */
1158 /* uncompressed size */
1163 /* filename length */
1166 /* extra field length */
1169 /* file comment length */
1172 /* disk number start */
1175 /* internal file attribs */
1178 /* external file attribs */
1183 /* relative offset of local header */
1189 start_offset
= lseek(fd
, 0, SEEK_CUR
);
1191 for(ze
= ziptail
; ze
!= NULL
; ze
= ze
->next_entry
){
1193 total_in
+= ze
->usize
;
1194 total_out
+= ze
->csize
+ 76 + strlen(ze
->filename
) * 2;
1197 PACK_UB2(header
, CEN_COMP
, 8);
1199 PACK_UB2(header
, CEN_COMP
, 0);
1202 PACK_UB2(header
, CEN_MODTIME
, ze
->mod_time
);
1203 PACK_UB2(header
, CEN_MODDATE
, ze
->mod_date
);
1204 PACK_UB4(header
, CEN_CRC
, ze
->crc
);
1205 PACK_UB4(header
, CEN_CSIZE
, ze
->csize
);
1206 PACK_UB4(header
, CEN_USIZE
, ze
->usize
);
1207 PACK_UB2(header
, CEN_FNLEN
, strlen(ze
->filename
));
1208 PACK_UB4(header
, CEN_OFFSET
, ze
->offset
);
1210 write(fd
, header
, 46);
1212 write(fd
, ze
->filename
, strlen(ze
->filename
));
1215 dir_size
= lseek(fd
, 0, SEEK_CUR
) - start_offset
;
1218 end_header
[0] = 0x50;
1219 end_header
[1] = 0x4b;
1220 end_header
[2] = 0x05;
1221 end_header
[3] = 0x06;
1222 /* number of this disk */
1225 /* number of disk w/ start of central header */
1228 /* total number of entries in central dir on this disk*/
1229 PACK_UB2(end_header
, 8, number_of_entries
);
1230 /* total number of entries in central dir*/
1231 PACK_UB2(end_header
, 10, number_of_entries
);
1232 /* size of central dir. */
1233 PACK_UB4(end_header
, 12, dir_size
);
1234 /* offset of start of central dir */
1235 PACK_UB4(end_header
, 16, start_offset
);
1236 /* zipfile comment length */
1240 write(fd
, end_header
, 22);
1243 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1246 (do_compress
? "deflated" : "stored"),
1247 (int)((1 - (total_out
/ (float)total_in
)) * 100)
1253 int extract_jar(int fd
, char **files
, int file_num
){
1263 ub1
*filename
= NULL
;
1264 int filename_len
= 0;
1283 dir
= FALSE
; /* by default, the file isn't a dir */
1284 handle
= TRUE
; /* by default we'll extract/create the file */
1286 if((rdamt
= pb_read(&pbf
, scratch
, 4)) != 4){
1291 signature
= UNPACK_UB4(scratch
, 0);
1294 printf("signature is %x\n", signature
);
1296 if(signature
== 0x08074b50){
1298 printf("skipping data descriptor\n");
1300 pb_read(&pbf
, scratch
, 12);
1302 } else if(signature
== 0x02014b50){
1304 printf("Central header reached.. we're all done!\n");
1307 }else if(signature
!= 0x04034b50){
1308 printf("Ick! %#x\n", signature
);
1312 if((rdamt
= pb_read(&pbf
, (file_header
+ 4), 26)) != 26){
1317 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1319 printf("Compressed size is %u\n", csize
);
1322 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1324 printf("Filename length is %hu\n", fnlen
);
1327 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1329 printf("Extra field length is %hu\n", eflen
);
1332 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1334 printf("Flags are %#hx\n", flags
);
1337 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1339 printf("Compression method is %#hx\n", method
);
1342 /* if there isn't a data descriptor */
1343 if(!(flags
& 0x0008)){
1344 crc
= UNPACK_UB4(file_header
, LOC_CRC
);
1346 printf("CRC is %x\n", crc
);
1350 if(filename_len
< fnlen
+ 1){
1351 if(filename
!= NULL
)
1354 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1355 filename_len
= fnlen
+ 1;
1358 pb_read(&pbf
, filename
, fnlen
);
1359 filename
[fnlen
] = '\0';
1362 printf("filename is %s\n", filename
);
1368 for(j
= 0; j
< file_num
; j
++)
1369 if(strcmp(files
[j
], (const char *)filename
) == 0){
1378 /* OK, there is some directory information in the file. Nothing to do
1379 but ensure the directory(s) exist, and create them if they don't.
1381 if(strchr((const char *)filename
, '/') != NULL
&& handle
){
1382 /* Loop through all the directories in the path, (everything w/ a '/') */
1383 const ub1
*start
= filename
;
1387 tmp_buff
= malloc(sizeof(char) * strlen((const char *)filename
));
1390 const ub1
*idx
= (const unsigned char *)strchr((const char *)start
, '/');
1394 else if(idx
== start
){
1400 strncpy(tmp_buff
, (const char *)filename
, (idx
- filename
));
1401 tmp_buff
[(idx
- filename
)] = '\0';
1404 printf("checking the existance of %s\n", tmp_buff
);
1407 if(stat(tmp_buff
, &sbuf
) < 0){
1408 if(errno
!= ENOENT
){
1413 } else if(S_ISDIR(sbuf
.st_mode
)){
1415 printf("Directory exists\n");
1419 fprintf(stderr
, "Hmmm.. %s exists but isn't a directory!\n",
1425 printf("Making directory..\n");
1427 if(mkdir(tmp_buff
, 0755) < 0){
1431 if(verbose
&& handle
)
1432 printf("%10s: %s/\n", "created", tmp_buff
);
1436 /* only a directory */
1437 if(strlen((const char *)start
) == 0)
1441 printf("Leftovers are \"%s\" (%d)\n", start
, strlen((const char *)start
));
1444 /* If the entry was just a directory, don't write to file, etc */
1445 if(strlen((const char *)start
) == 0)
1451 if(f_fd
!= -1 && handle
){
1452 f_fd
= open((const char *)filename
,
1453 O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644);
1456 fprintf(stderr
, "Error extracting JAR archive!\n");
1457 perror((const char *)filename
);
1462 if(method
!= 8 && flags
& 0x0008){
1463 fprintf(stderr
, "Error in JAR file! (not compressed but data desc.)\n");
1467 if(method
== 8 || flags
& 0x0008){
1469 lseek(fd
, eflen
, SEEK_CUR
);
1471 consume(&pbf
, eflen
);
1473 inflate_file(&pbf
, f_fd
, &ze
);
1477 printf("writing stored data.. (%d bytes)\n", csize
);
1483 ze
.crc
= crc32(ze
.crc
, NULL
, 0); /* initialize the crc */
1485 while(out_a
< (int)csize
){
1486 rdamt
= (in_a
> RDSZ
? RDSZ
: in_a
);
1487 if(pb_read(&pbf
, rd_buff
, rdamt
) != rdamt
){
1492 ze
.crc
= crc32(ze
.crc
, (Bytef
*)rd_buff
, rdamt
);
1495 write(f_fd
, rd_buff
, rdamt
);
1501 printf("%d bytes written\n", out_a
);
1506 lseek(fd
, eflen
, SEEK_CUR
);
1508 consume(&pbf
, eflen
);
1511 /* if there is a data descriptor left, compare the CRC */
1514 if(pb_read(&pbf
, scratch
, 16) != 16){
1519 signature
= UNPACK_UB4(scratch
, 0);
1521 if(signature
!= 0x08074b50){
1522 fprintf(stderr
, "Error! Missing data descriptor!\n");
1526 crc
= UNPACK_UB4(scratch
, 4);
1531 fprintf(stderr
, "Error! CRCs do not match! Got %x, expected %x\n",
1538 if(verbose
&& dir
== FALSE
&& handle
)
1539 printf("%10s: %s\n",
1540 (method
== 8 ? "inflated" : "extracted"),
1547 int list_jar(int fd
, char **files
, int file_num
){
1559 ub1
*filename
= NULL
;
1562 int filename_len
= 0;
1567 char ascii_date
[30];
1571 printf("Listing jar file, looking for %d files\n", file_num
);
1574 /* This should be the start of the central-header-end section */
1576 if(lseek(fd
, -22, SEEK_END
) == (off_t
)-1){
1581 if(read(fd
, &tmp
, sizeof(ub4
)) != 4){
1586 #ifdef WORDS_BIGENDIAN
1590 if(tmp
!= 0x06054b50){
1591 fprintf(stderr
, "Error in JAR file format. zip-style comment?\n");
1595 if(lseek(fd
, 6, SEEK_CUR
) == (off_t
)-1){
1600 if(read(fd
, &cen_size
, 2) != 2){
1605 #ifdef WORDS_BIGENDIAN
1606 cen_size
= L2BS(cen_size
);
1609 /* printf("%hu entries in central header\n", cen_size); */
1611 if(lseek(fd
, 4, SEEK_CUR
) == (off_t
)-1){
1616 if(read(fd
, &tmp
, 4) != 4){
1621 #ifdef WORDS_BIGENDIAN
1625 /* printf("Central header offset = %d\n", tmp); */
1627 if(lseek(fd
, tmp
, SEEK_SET
) != (int)tmp
){
1632 /* Loop through the entries in the central header */
1633 for(i
= 0; i
< cen_size
; i
++){
1635 if(read(fd
, &cen_header
, 46) != 46){
1640 signature
= UNPACK_UB4(cen_header
, 0);
1641 if(signature
!= 0x02014b50){
1642 fprintf(stderr
, "Error in JAR file! Cannot locate central header!\n");
1646 usize
= UNPACK_UB4(cen_header
, CEN_USIZE
);
1647 fnlen
= UNPACK_UB2(cen_header
, CEN_FNLEN
);
1648 eflen
= UNPACK_UB2(cen_header
, CEN_EFLEN
);
1649 clen
= UNPACK_UB2(cen_header
, CEN_COMLEN
);
1651 /* If we're providing verbose output, we need to make an ASCII
1652 * formatted version of the date. */
1654 mdate
= UNPACK_UB4(cen_header
, CEN_MODTIME
);
1655 tdate
= dos2unixtime(mdate
);
1656 s_tm
= localtime(&tdate
);
1657 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
1660 if(filename_len
< fnlen
+ 1){
1661 if(filename
!= NULL
)
1664 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1665 filename_len
= fnlen
+ 1;
1668 if(read(fd
, filename
, fnlen
) != fnlen
){
1672 filename
[fnlen
] = '\0';
1674 /* if the user specified a list of files on the command line,
1675 we'll only display those, otherwise we'll display everything */
1677 for(j
= 0; j
< file_num
; j
++)
1678 if(strcmp(files
[j
], (const char *)filename
) == 0){
1680 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1682 printf("%s\n", filename
);
1687 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1689 printf("%s\n", filename
);
1692 size
= eflen
+ clen
;
1694 if(lseek(fd
, size
, SEEK_CUR
) == (off_t
)-1){
1701 /* the file isn't seekable.. evil! */
1709 if(pb_read(&pbf
, scratch
, 4) != 4){
1714 signature
= UNPACK_UB4(scratch
, 0);
1717 printf("signature is %x\n", signature
);
1720 if(signature
== 0x08074b50){
1722 printf("skipping data descriptor\n");
1724 pb_read(&pbf
, scratch
, 12);
1726 } else if(signature
== 0x02014b50){
1728 printf("Central header reached.. we're all done!\n");
1731 }else if(signature
!= 0x04034b50){
1733 printf("Ick! %#x\n", signature
);
1738 if(pb_read(&pbf
, (file_header
+ 4), 26) != 26){
1743 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1745 printf("Compressed size is %u\n", csize
);
1748 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1750 printf("Filename length is %hu\n", fnlen
);
1753 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1755 printf("Extra field length is %hu\n", eflen
);
1758 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1760 printf("Compression method is %#hx\n", method
);
1763 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1765 printf("Flags are %#hx\n", flags
);
1768 usize
= UNPACK_UB4(file_header
, LOC_USIZE
);
1770 /* If we're providing verbose output, we need to make an ASCII
1771 * formatted version of the date. */
1773 mdate
= UNPACK_UB4(file_header
, LOC_MODTIME
);
1774 tdate
= dos2unixtime(mdate
);
1775 s_tm
= localtime(&tdate
);
1776 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
1779 if(filename_len
< fnlen
+ 1){
1780 if(filename
!= NULL
)
1783 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1784 filename_len
= fnlen
+ 1;
1787 pb_read(&pbf
, filename
, fnlen
);
1788 filename
[fnlen
] = '\0';
1790 /* the header is at the end. In a JAR file, this means that the data
1791 happens to be compressed. We have no choice but to inflate the
1798 consume(&pbf
, size
);
1802 printf("inflating %s\n", filename
);
1804 inflate_file(&pbf
, -1, &ze
);
1808 printf("We're shit outta luck!\n");
1811 size
= csize
+ (eflen
> 0 ? eflen
: 0);
1815 printf("Skipping %ld bytes\n", (long)size
);
1818 consume(&pbf
, size
);
1820 /* print out the listing */
1822 for(j
= 0; j
< file_num
; j
++)
1823 if(strcmp(files
[j
], (const char *)filename
) == 0){
1825 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1827 printf("%s\n", filename
);
1832 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1834 printf("%s\n", filename
);
1841 int consume(pb_file
*pbf
, int amt
){
1842 int tc
= 0; /* total amount consumed */
1847 printf("Consuming %d bytes\n", amt
);
1851 rdamt
= pb_read(pbf
, buff
, ((amt
- tc
) < RDSZ
? (amt
- tc
) : RDSZ
));
1853 printf("got %d bytes\n", rdamt
);
1859 printf("%d bytes consumed\n", tc
);
1865 void usage(const char *filename
){
1866 fprintf(stderr
, "Try `%s --help' for more information.\n", filename
);
1872 printf("jar (%s) %s\n\n", PACKAGE
, VERSION
);
1873 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1874 printf("Copyright 2002 Free Software Foundation\n");
1876 This is free software; see the source for copying conditions. There is NO\n\
1877 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1881 void help(const char *filename
)
1884 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1886 Store many files together in a single `jar' file.\n\
1888 -c create new archive\n\
1889 -t list table of contents for archive\n\
1890 -x extract named (or all) files from archive\n\
1891 -u update existing archive\n\
1894 -@ read names from stdin\n\
1895 -0 store only; use no ZIP compression\n\
1896 -C DIR FILE change to the specified directory and include\n\
1897 the following file\n\
1898 -E don't include the files found in a directory\n\
1899 -f FILE specify archive file name\n\
1900 --help print this help, then exit\n\
1901 -m FILE include manifest information from specified manifest file\n\
1902 -M Do not create a manifest file for the entries\n\
1903 -v generate verbose output on standard output\n\
1904 -V, --version display version information\n\
1907 If any file is a directory then it is processed recursively.\n\
1908 The manifest file name and the archive file name needs to be specified\n\
1909 in the same order the 'm' and 'f' flags are specified.\n\
1911 Example 1: to archive two class files into an archive called classes.jar: \n\
1912 jar cvf classes.jar Foo.class Bar.class \n\
1913 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1914 files in the foo/ directory into 'classes.jar': \n\
1915 jar cvfm classes.jar mymanifest -C foo/ .\n\
1925 char *result
= (char*)malloc(strlen(s
) + 1);
1926 if (result
== (char*)0)
1932 /* Convert "tar-style" first argument to a form expected by getopt.
1933 This idea and the code comes from GNU tar. This can allocate a new
1934 argument vector. This might leak some memory, but we don't care. */
1936 expand_options (int *argcp
, char ***argvp
)
1939 char **argv
= *argvp
;
1941 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
1942 if a long argument (like "--help") is detected. */
1943 if (argc
> 1 && argv
[1][1] != '-')
1955 args_to_expand
= strlen (argv
[1]);
1956 if (argv
[1][0] == '-')
1959 new_argc
= argc
- 1 + args_to_expand
;
1960 new_argv
= (char **) malloc (new_argc
* sizeof (char *));
1972 *out
++ = jt_strdup (buf
);
1973 /* If the option takes an argument, move the next argument
1974 to just after this option. */
1975 opt
= strchr (OPTION_STRING
, *p
);
1976 if (opt
&& opt
[1] == ':')
1978 if (in
< argv
+ argc
)
1982 fprintf(stderr
, "%s: option `%s' requires an argument.\n",
1990 /* Copy remaining options. */
1991 while (in
< argv
+ argc
)