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*);
278 int add_to_jar_with_dir(int, const char*, const char*);
279 int create_central_header(int);
280 int make_manifest(int, const char*);
281 static void init_args(char **, int);
282 static char *get_next_arg (void);
283 static char *jt_strdup (char*);
284 static void expand_options (int *argcp
, char ***argvp
);
286 /* global variables */
288 ub1 data_descriptor
[16];
294 /* If non zero, then don't recurse in directory. Instead, add the
295 directory entry and relie on an explicit list of files to populate
296 the archive. This option isn't supported by the original jar tool. */
297 int use_explicit_list_only
;
299 /* If non zero, then read the entry names from stdin. This option
300 isn't supported by the original jar tool. */
301 int read_names_from_stdin
;
303 zipentry
*ziplist
; /* linked list of entries */
304 zipentry
*ziptail
; /* tail of the linked list */
306 int number_of_entries
; /* number of entries in the linked list */
308 /* This is used to mark options with no short value. */
309 #define LONG_OPT(Num) ((Num) + 128)
311 #define OPT_HELP LONG_OPT (0)
313 /* This holds all options. */
314 #define OPTION_STRING "-ctxuvVf:m:C:0ME@"
316 /* Define the MANIFEST content here to have it easier with calculations
317 below. This is for the case we create an empty MANIFEST.MF. */
318 #define MANIFEST_STR "Manifest-Version: 1.0\nCreated-By: "
319 #define MANIFEST_END "\n\n"
321 static const struct option options
[] =
323 { "help", no_argument
, NULL
, OPT_HELP
},
324 { "version", no_argument
, NULL
, 'V' },
325 { NULL
, no_argument
, NULL
, 0 }
328 int main(int argc
, char **argv
){
332 int action
= ACTION_NONE
;
338 /* These are used to collect file names and `-C' options for the
339 second pass through the command line. */
348 number_of_entries
= 0;
354 new_argv
= (char **) malloc (argc
* sizeof (char *));
356 expand_options (&argc
, &argv
);
357 while ((opt
= getopt_long (argc
, argv
, OPTION_STRING
,
358 options
, NULL
)) != -1) {
361 new_argv
[new_argc
++] = (char *) "-C";
362 /* ... fall through ... */
364 /* File name or unparsed option, due to RETURN_IN_ORDER. */
365 new_argv
[new_argc
++] = optarg
;
368 action
= ACTION_CREATE
;
371 action
= ACTION_LIST
;
374 action
= ACTION_EXTRACT
;
377 action
= ACTION_UPDATE
;
402 /* The following options aren't supported by the original jar tool. */
404 use_explicit_list_only
= TRUE
;
407 read_names_from_stdin
= TRUE
;
414 /* We might have seen `--'. In this case we want to make sure that
415 all following options are handled as file names. */
416 while (optind
< argc
)
417 new_argv
[new_argc
++] = argv
[optind
++];
418 new_argv
[new_argc
] = NULL
;
420 if(action
== ACTION_NONE
){
421 fprintf(stderr
, "One of options -{ctxu} must be specified.\n");
425 if(action
== ACTION_UPDATE
){
426 fprintf(stderr
, "%s: `-u' mode unimplemented.\n", argv
[0]);
430 /* Verify unsupported combinations and warn of the use of non
432 if(verbose
&& use_explicit_list_only
)
433 fprintf (stderr
, "Warning: using non standard '-E' option\n");
434 if(verbose
&& read_names_from_stdin
)
435 fprintf (stderr
, "Warning: using non standard '-@' option\n");
436 if(read_names_from_stdin
437 && (action
!= ACTION_CREATE
&& action
!= ACTION_UPDATE
)){
438 fprintf(stderr
, "Option '-@' is supported only with '-c' or '-u'.\n");
442 /* create the jarfile */
443 if(action
== ACTION_CREATE
){
445 jarfd
= open(jarfile
, O_CREAT
| O_BINARY
| O_WRONLY
| O_TRUNC
, 0666);
448 fprintf(stderr
, "Error opening %s for writing!\n", jarfile
);
453 /* We assume that the file is seekable */
458 jarfd
= STDOUT_FILENO
; /* jarfd is stdout otherwise */
460 /* standard out is not seekable */
463 /* don't want our output to be part of the jar file.. figured this one
464 out the hard way.. =P */
467 } else if(action
== ACTION_LIST
|| action
== ACTION_EXTRACT
){
470 jarfd
= open(jarfile
, O_RDONLY
| O_BINARY
);
473 fprintf(stderr
, "Error opening %s for reading!\n", jarfile
);
480 jarfd
= STDIN_FILENO
; /* jarfd is standard in */
482 /* we assume that the stream isn't seekable for safety */
487 if(action
== ACTION_CREATE
|| action
== ACTION_UPDATE
){
491 if((action
== ACTION_UPDATE
) && jarfile
) {
492 if((jarfd
= open(jarfile
, O_RDWR
| O_BINARY
)) < 0) {
493 fprintf(stderr
, "Error opening %s for reading!\n", jarfile
);
503 /* Add the META-INF/ directory and the manifest */
504 if(manifest
&& mfile
)
505 make_manifest(jarfd
, mfile
);
507 make_manifest(jarfd
, NULL
);
509 init_args (new_argv
, 0);
510 /* now we add the files to the archive */
511 while ((arg
= get_next_arg ())){
513 if(!strcmp(arg
, "-C")){
514 const char *dir_to_change
= get_next_arg ();
515 const char *file_to_add
= get_next_arg ();
516 if (!dir_to_change
|| !file_to_add
) {
517 fprintf(stderr
, "Error: missing argument for -C.\n");
520 if (add_to_jar_with_dir(jarfd
, dir_to_change
, file_to_add
)) {
522 "Error adding %s (in directory %s) to jar archive!\n",
523 file_to_add
, dir_to_change
);
527 if(add_to_jar(jarfd
, arg
)){
528 fprintf(stderr
, "Error adding %s to jar archive!\n", arg
);
533 /* de-initialize the compression DS */
537 create_central_header(jarfd
);
539 if (close(jarfd
) != 0) {
540 fprintf(stderr
, "Error closing jar archive!\n");
542 } else if(action
== ACTION_LIST
){
543 list_jar(jarfd
, &new_argv
[0], new_argc
);
544 } else if(action
== ACTION_EXTRACT
){
545 extract_jar(jarfd
, &new_argv
[0], new_argc
);
551 static int args_current_g
;
552 static char **args_g
;
555 init_args(args
, current
)
559 if(!read_names_from_stdin
)
562 args_current_g
= current
;
569 static int reached_end
= 0;
576 if (!args_g
[args_current_g
])
581 return args_g
[args_current_g
++];
585 /* Read the name from stdin. Delimiters are '\n' and
586 '\r'. Reading EOF indicates that we don't have anymore file
587 names characters to read. */
592 /* Get rid of '\n' and '\r' first. */
595 int c
= getc (stdin
);
596 if (c
== '\n' || c
== '\r')
609 int c
= getc (stdin
);
610 /* Exit when we get a delimiter or don't have any characters
612 if (c
== '\n'|| c
== '\r'|| c
== EOF
)
614 s
[pos
++] = (char) c
;
620 return jt_strdup (s
);
628 /* packing file header */
630 file_header
[0] = 0x50;
631 file_header
[1] = 0x4b;
632 file_header
[2] = 0x03;
633 file_header
[3] = 0x04;
634 /* version number (Unix 1.0)*/
637 /* bit flag (normal deflation)*/
638 file_header
[6] = 0x00;
640 file_header
[7] = 0x00;
641 /* do_compression method (deflation) */
645 /* last mod file time (MS-DOS format) */
648 /* last mod file date (MS-DOS format) */
656 /* compressed size */
661 /* uncompressed size */
666 /* filename length */
669 /* extra field length */
673 /* Initialize the compression DS */
674 PACK_UB4(data_descriptor
, 0, 0x08074b50);
678 void add_entry(struct zipentry
*ze
){
684 ziplist
->next_entry
= ze
;
691 int make_manifest(int jfd
, const char *mf_name
){
693 int nlen
; /* length of file name */
694 int mod_time
; /* file modification time */
697 nlen
= 9; /* trust me on this one */
699 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
701 current_time
= time(NULL
);
702 if(current_time
== (time_t)-1){
707 mod_time
= unix2dostime(¤t_time
);
709 PACK_UB2(file_header
, LOC_EXTRA
, 0);
710 PACK_UB2(file_header
, LOC_COMP
, 0);
711 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
712 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
715 printf("adding: META-INF/ (in=0) (out=0) (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/");
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);
731 ze
->compressed
= FALSE
;
735 write(jfd
, file_header
, 30);
736 write(jfd
, "META-INF/", nlen
);
738 /* if the user didn't specify an external manifest file... */
741 int mf_len
= strlen(MANIFEST_STR
) + strlen(VERSION
) + strlen(MANIFEST_END
);
744 if((mf
= (char *) malloc(mf_len
+ 1))) {
747 sprintf(mf
, "%s%s%s", MANIFEST_STR
, VERSION
, MANIFEST_END
);
749 crc
= crc32(0L, Z_NULL
, 0);
751 crc
= crc32(crc
, (const unsigned char *)mf
, mf_len
);
753 nlen
= 20; /* once again, trust me */
755 PACK_UB2(file_header
, LOC_EXTRA
, 0);
756 PACK_UB2(file_header
, LOC_COMP
, 0);
757 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
758 PACK_UB4(file_header
, LOC_USIZE
, mf_len
);
760 memcpy((file_header
+ LOC_CSIZE
), (file_header
+ LOC_USIZE
), 4);
762 PACK_UB4(file_header
, LOC_CRC
, crc
);
765 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
767 ze
= (zipentry
*)malloc(sizeof(zipentry
));
773 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
774 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
775 strcpy(ze
->filename
, "META-INF/MANIFEST.MF");
776 ze
->filename
[nlen
] = '\0';
778 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
779 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
780 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
783 ze
->usize
= ze
->csize
;
784 ze
->compressed
= FALSE
;
788 write(jfd
, file_header
, 30);
789 write(jfd
, "META-INF/MANIFEST.MF", nlen
);
790 write(jfd
, mf
, mf_len
);
794 printf("malloc errror\n");
801 stat(mf_name
, &statbuf
);
803 if(!S_ISREG(statbuf
.st_mode
)){
804 fprintf(stderr
, "Invalid manifest file specified.\n");
808 mfd
= open(mf_name
, O_RDONLY
| O_BINARY
);
811 fprintf(stderr
, "Error opening %s.\n", mf_name
);
815 if(add_file_to_jar(jfd
, mfd
, "META-INF/MANIFEST.MF", &statbuf
)){
816 perror("error writing to jar");
825 /* Implements -C by wrapping add_to_jar. new_dir is the directory
828 add_to_jar_with_dir (int fd
, const char* new_dir
, const char* file
)
831 char old_dir
[MAXPATHLEN
];
832 if (getcwd(old_dir
, MAXPATHLEN
) == NULL
) {
836 if (chdir(new_dir
) == -1) {
840 retval
=add_to_jar(fd
, file
);
841 if (chdir(old_dir
) == -1) {
849 add_to_jar (int fd
, const char *file
) {
856 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
858 * "normal" jar : org/apache/java/io/LogRecord.class
859 * fastjar : ./org/apache/java/io/LogRecord.class
860 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
861 * with both kaffe-1.0b4 and JDK.
863 while (*file
=='.' && *(file
+1)=='/')
866 if(jarfile
&& !strcmp(file
, jarfile
)){
868 printf("skipping: %s\n", file
);
869 return 0; /* we don't want to add ourselves.. */
872 stat_return
= stat(file
, &statbuf
);
874 if(stat_return
== -1){
877 } else if(S_ISDIR(statbuf
.st_mode
)){
881 unsigned long mod_time
;
890 nlen
= strlen(file
) + 256;
891 fullname
= (char*)malloc(nlen
* sizeof(char));
892 memset(fullname
, 0, (nlen
* sizeof(char)));
894 if(fullname
== NULL
){
895 fprintf(stderr
, "Filename is NULL!\n");
899 strcpy(fullname
, file
);
902 if(fullname
[nlen
- 1] != '/'){
903 fullname
[nlen
] = '/';
904 t_ptr
= (fullname
+ nlen
+ 1);
906 t_ptr
= (fullname
+ nlen
);
909 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
911 nlen
= (t_ptr
- fullname
);
913 mod_time
= unix2dostime(&statbuf
.st_mtime
);
915 PACK_UB2(file_header
, LOC_EXTRA
, 0);
916 PACK_UB2(file_header
, LOC_COMP
, 0);
917 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
918 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
921 printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname
, 0, 0);
923 ze
= (zipentry
*)malloc(sizeof(zipentry
));
929 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
930 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
931 strcpy(ze
->filename
, fullname
);
932 ze
->filename
[nlen
] = '\0';
934 ze
->offset
= lseek(fd
, 0, SEEK_CUR
);
935 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
936 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
937 ze
->compressed
= FALSE
;
941 write(fd
, file_header
, 30);
942 write(fd
, fullname
, nlen
);
944 while(!use_explicit_list_only
&& (de
= readdir(dir
)) != NULL
){
945 if(de
->d_name
[0] == '.')
947 if(jarfile
&& !strcmp(de
->d_name
, jarfile
)){
948 /* we don't want to add ourselves. Believe me */
950 printf("skipping: %s\n", de
->d_name
);
954 strcpy(t_ptr
, de
->d_name
);
956 if (add_to_jar(fd
, fullname
)) {
957 fprintf(stderr
, "Error adding file to jar!\n");
965 } else if(S_ISREG(statbuf
.st_mode
)){
968 add_fd
= open(file
, O_RDONLY
| O_BINARY
);
970 fprintf(stderr
, "Error opening %s.\n", file
);
974 if(add_file_to_jar(fd
, add_fd
, file
, &statbuf
)){
975 fprintf(stderr
, "Error adding file to jar!\n");
980 fprintf(stderr
, "Illegal file specified: %s\n", file
);
985 int add_file_to_jar(int jfd
, int ffd
, const char *fname
, struct stat
*statbuf
){
987 unsigned short file_name_length
;
988 unsigned long mod_time
;
995 mod_time
= unix2dostime(&(statbuf
->st_mtime
));
996 file_name_length
= strlen(fname
);
998 if(!seekable
&& !do_compress
){
999 crc
= crc32(0L, Z_NULL
, 0);
1001 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0)
1002 crc
= crc32(crc
, rd_buff
, rdamt
);
1004 lseek(ffd
, 0, SEEK_SET
);
1007 /* data descriptor */
1008 if(!seekable
&& do_compress
){
1009 PACK_UB2(file_header
, LOC_EXTRA
, 8);
1011 PACK_UB2(file_header
, LOC_EXTRA
, 0);
1015 PACK_UB2(file_header
, LOC_COMP
, 8);
1017 PACK_UB2(file_header
, LOC_COMP
, 0);
1020 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
1021 PACK_UB2(file_header
, LOC_FNLEN
, file_name_length
);
1023 if(!seekable
&& !do_compress
){
1024 PACK_UB4(file_header
, LOC_CRC
, crc
);
1025 PACK_UB4(file_header
, LOC_USIZE
, statbuf
->st_size
);
1026 PACK_UB4(file_header
, LOC_CSIZE
, statbuf
->st_size
);
1028 memset((file_header
+ LOC_CRC
), '\0', 12); /* clear crc/usize/csize */
1030 ze
= (zipentry
*)malloc(sizeof(zipentry
));
1036 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
1037 ze
->filename
= (char*)malloc((file_name_length
+ 1) * sizeof(char));
1038 strcpy(ze
->filename
, fname
);
1040 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1041 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1043 if(!seekable
&& !do_compress
)
1046 ze
->csize
= statbuf
->st_size
;
1047 ze
->usize
= ze
->csize
;
1048 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
1050 ze
->compressed
= TRUE
;
1052 ze
->compressed
= FALSE
;
1056 /* Write the local header */
1057 write(jfd
, file_header
, 30);
1059 /* write the file name to the zip file */
1060 write(jfd
, fname
, file_name_length
);
1064 printf("adding: %s ", fname
);
1069 /* compress the file */
1070 compress_file(ffd
, jfd
, ze
);
1072 /* Write the contents of the file (uncompressed) to the zip file */
1073 /* calculate the CRC as we go along */
1074 ze
->crc
= crc32(0L, Z_NULL
, 0);
1076 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0){
1077 ze
->crc
= crc32(ze
->crc
, rd_buff
, rdamt
);
1078 if(write(jfd
, rd_buff
, rdamt
) != rdamt
){
1086 /* write out data descriptor */
1087 PACK_UB4(data_descriptor
, 4, ze
->crc
);
1088 PACK_UB4(data_descriptor
, 8, ze
->csize
);
1089 PACK_UB4(data_descriptor
, 12, ze
->usize
);
1091 /* we need to seek back and fill the header */
1093 offset
= (ze
->csize
+ strlen(ze
->filename
) + 16);
1095 if(lseek(jfd
, -offset
, SEEK_CUR
) == (off_t
)-1){
1100 if(write(jfd
, (data_descriptor
+ 4), 12) != 12){
1107 if(lseek(jfd
, offset
, SEEK_CUR
) == (off_t
)-1){
1111 } else if(do_compress
){
1112 /* Sun's jar tool will only allow a data descriptor if the entry is
1113 compressed, but we'll save 16 bytes/entry if we only use it when
1114 we can't seek back on the file */
1116 if(write(jfd
, data_descriptor
, 16) != 16){
1123 printf("(in=%d) (out=%d) (%s %d%%)\n",
1124 (int)ze
->usize
, (int)ze
->csize
,
1125 (do_compress
? "deflated" : "stored"),
1126 (do_compress
? ((int)((1 - ze
->csize
/(float)ze
->usize
) * 100)) : 0));
1131 int create_central_header(int fd
){
1136 int total_in
= 0, total_out
= 22;
1145 /* version made by */
1148 /* version needed to extract */
1154 /* compression method */
1168 /* compressed size */
1173 /* uncompressed size */
1178 /* filename length */
1181 /* extra field length */
1184 /* file comment length */
1187 /* disk number start */
1190 /* internal file attribs */
1193 /* external file attribs */
1198 /* relative offset of local header */
1204 start_offset
= lseek(fd
, 0, SEEK_CUR
);
1206 for(ze
= ziptail
; ze
!= NULL
; ze
= ze
->next_entry
){
1208 total_in
+= ze
->usize
;
1209 total_out
+= ze
->csize
+ 76 + strlen(ze
->filename
) * 2;
1212 PACK_UB2(header
, CEN_COMP
, 8);
1214 PACK_UB2(header
, CEN_COMP
, 0);
1217 PACK_UB2(header
, CEN_MODTIME
, ze
->mod_time
);
1218 PACK_UB2(header
, CEN_MODDATE
, ze
->mod_date
);
1219 PACK_UB4(header
, CEN_CRC
, ze
->crc
);
1220 PACK_UB4(header
, CEN_CSIZE
, ze
->csize
);
1221 PACK_UB4(header
, CEN_USIZE
, ze
->usize
);
1222 PACK_UB2(header
, CEN_FNLEN
, strlen(ze
->filename
));
1223 PACK_UB4(header
, CEN_OFFSET
, ze
->offset
);
1225 write(fd
, header
, 46);
1227 write(fd
, ze
->filename
, strlen(ze
->filename
));
1230 dir_size
= lseek(fd
, 0, SEEK_CUR
) - start_offset
;
1233 end_header
[0] = 0x50;
1234 end_header
[1] = 0x4b;
1235 end_header
[2] = 0x05;
1236 end_header
[3] = 0x06;
1237 /* number of this disk */
1240 /* number of disk w/ start of central header */
1243 /* total number of entries in central dir on this disk*/
1244 PACK_UB2(end_header
, 8, number_of_entries
);
1245 /* total number of entries in central dir*/
1246 PACK_UB2(end_header
, 10, number_of_entries
);
1247 /* size of central dir. */
1248 PACK_UB4(end_header
, 12, dir_size
);
1249 /* offset of start of central dir */
1250 PACK_UB4(end_header
, 16, start_offset
);
1251 /* zipfile comment length */
1255 write(fd
, end_header
, 22);
1258 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1261 (do_compress
? "deflated" : "stored"),
1262 (int)((1 - (total_out
/ (float)total_in
)) * 100)
1268 int extract_jar(int fd
, char **files
, int file_num
){
1278 ub1
*filename
= NULL
;
1279 int filename_len
= 0;
1298 dir
= FALSE
; /* by default, the file isn't a dir */
1299 handle
= TRUE
; /* by default we'll extract/create the file */
1301 if((rdamt
= pb_read(&pbf
, scratch
, 4)) != 4){
1306 signature
= UNPACK_UB4(scratch
, 0);
1309 printf("signature is %x\n", signature
);
1311 if(signature
== 0x08074b50){
1313 printf("skipping data descriptor\n");
1315 pb_read(&pbf
, scratch
, 12);
1317 } else if(signature
== 0x02014b50){
1319 printf("Central header reached.. we're all done!\n");
1322 }else if(signature
!= 0x04034b50){
1323 printf("Ick! %#x\n", signature
);
1327 if((rdamt
= pb_read(&pbf
, (file_header
+ 4), 26)) != 26){
1332 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1334 printf("Compressed size is %u\n", csize
);
1337 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1339 printf("Filename length is %hu\n", fnlen
);
1342 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1344 printf("Extra field length is %hu\n", eflen
);
1347 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1349 printf("Flags are %#hx\n", flags
);
1352 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1354 printf("Compression method is %#hx\n", method
);
1357 /* if there isn't a data descriptor */
1358 if(!(flags
& 0x0008)){
1359 crc
= UNPACK_UB4(file_header
, LOC_CRC
);
1361 printf("CRC is %x\n", crc
);
1365 if(filename_len
< fnlen
+ 1){
1366 if(filename
!= NULL
)
1369 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1370 filename_len
= fnlen
+ 1;
1373 pb_read(&pbf
, filename
, fnlen
);
1374 filename
[fnlen
] = '\0';
1377 printf("filename is %s\n", filename
);
1383 for(j
= 0; j
< file_num
; j
++)
1384 if(strcmp(files
[j
], (const char *)filename
) == 0){
1393 /* OK, there is some directory information in the file. Nothing to do
1394 but ensure the directory(s) exist, and create them if they don't.
1396 if(strchr((const char *)filename
, '/') != NULL
&& handle
){
1397 /* Loop through all the directories in the path, (everything w/ a '/') */
1398 const ub1
*start
= filename
;
1402 tmp_buff
= malloc(sizeof(char) * strlen((const char *)filename
));
1405 const ub1
*idx
= (const unsigned char *)strchr((const char *)start
, '/');
1409 else if(idx
== start
){
1415 strncpy(tmp_buff
, (const char *)filename
, (idx
- filename
));
1416 tmp_buff
[(idx
- filename
)] = '\0';
1419 printf("checking the existance of %s\n", tmp_buff
);
1422 if(stat(tmp_buff
, &sbuf
) < 0){
1423 if(errno
!= ENOENT
){
1428 } else if(S_ISDIR(sbuf
.st_mode
)){
1430 printf("Directory exists\n");
1434 fprintf(stderr
, "Hmmm.. %s exists but isn't a directory!\n",
1440 printf("Making directory..\n");
1442 if(mkdir(tmp_buff
, 0755) < 0){
1446 if(verbose
&& handle
)
1447 printf("%10s: %s/\n", "created", tmp_buff
);
1451 /* only a directory */
1452 if(strlen((const char *)start
) == 0)
1456 printf("Leftovers are \"%s\" (%d)\n", start
, strlen((const char *)start
));
1459 /* If the entry was just a directory, don't write to file, etc */
1460 if(strlen((const char *)start
) == 0)
1466 if(f_fd
!= -1 && handle
){
1467 f_fd
= open((const char *)filename
,
1468 O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644);
1471 fprintf(stderr
, "Error extracting JAR archive!\n");
1472 perror((const char *)filename
);
1477 if(method
!= 8 && flags
& 0x0008){
1478 fprintf(stderr
, "Error in JAR file! (not compressed but data desc.)\n");
1482 if(method
== 8 || flags
& 0x0008){
1483 consume(&pbf
, eflen
);
1485 inflate_file(&pbf
, f_fd
, &ze
);
1489 printf("writing stored data.. (%d bytes)\n", csize
);
1495 ze
.crc
= crc32(ze
.crc
, NULL
, 0); /* initialize the crc */
1497 while(out_a
< (int)csize
){
1498 rdamt
= (in_a
> RDSZ
? RDSZ
: in_a
);
1499 if(pb_read(&pbf
, rd_buff
, rdamt
) != rdamt
){
1504 ze
.crc
= crc32(ze
.crc
, (Bytef
*)rd_buff
, rdamt
);
1507 write(f_fd
, rd_buff
, rdamt
);
1513 printf("%d bytes written\n", out_a
);
1517 consume(&pbf
, eflen
);
1520 /* if there is a data descriptor left, compare the CRC */
1523 if(pb_read(&pbf
, scratch
, 16) != 16){
1528 signature
= UNPACK_UB4(scratch
, 0);
1530 if(signature
!= 0x08074b50){
1531 fprintf(stderr
, "Error! Missing data descriptor!\n");
1535 crc
= UNPACK_UB4(scratch
, 4);
1540 fprintf(stderr
, "Error! CRCs do not match! Got %x, expected %x\n",
1547 if(verbose
&& dir
== FALSE
&& handle
)
1548 printf("%10s: %s\n",
1549 (method
== 8 ? "inflated" : "extracted"),
1556 int list_jar(int fd
, char **files
, int file_num
){
1568 ub1
*filename
= NULL
;
1571 int filename_len
= 0;
1576 char ascii_date
[31];
1580 printf("Listing jar file, looking for %d files\n", file_num
);
1583 /* This should be the start of the central-header-end section */
1585 if(lseek(fd
, -22, SEEK_END
) == (off_t
)-1){
1590 if(read(fd
, &tmp
, sizeof(ub4
)) != 4){
1595 #ifdef WORDS_BIGENDIAN
1599 if(tmp
!= 0x06054b50){
1600 fprintf(stderr
, "Error in JAR file format. zip-style comment?\n");
1604 if(lseek(fd
, 6, SEEK_CUR
) == (off_t
)-1){
1609 if(read(fd
, &cen_size
, 2) != 2){
1614 #ifdef WORDS_BIGENDIAN
1615 cen_size
= L2BS(cen_size
);
1618 /* printf("%hu entries in central header\n", cen_size); */
1620 if(lseek(fd
, 4, SEEK_CUR
) == (off_t
)-1){
1625 if(read(fd
, &tmp
, 4) != 4){
1630 #ifdef WORDS_BIGENDIAN
1634 /* printf("Central header offset = %d\n", tmp); */
1636 if(lseek(fd
, tmp
, SEEK_SET
) != (int)tmp
){
1641 /* Loop through the entries in the central header */
1642 for(i
= 0; i
< cen_size
; i
++){
1644 if(read(fd
, &cen_header
, 46) != 46){
1649 signature
= UNPACK_UB4(cen_header
, 0);
1650 if(signature
!= 0x02014b50){
1651 fprintf(stderr
, "Error in JAR file! Cannot locate central header!\n");
1655 usize
= UNPACK_UB4(cen_header
, CEN_USIZE
);
1656 fnlen
= UNPACK_UB2(cen_header
, CEN_FNLEN
);
1657 eflen
= UNPACK_UB2(cen_header
, CEN_EFLEN
);
1658 clen
= UNPACK_UB2(cen_header
, CEN_COMLEN
);
1660 /* If we're providing verbose output, we need to make an ASCII
1661 * formatted version of the date. */
1663 mdate
= UNPACK_UB4(cen_header
, CEN_MODTIME
);
1664 tdate
= dos2unixtime(mdate
);
1665 s_tm
= localtime(&tdate
);
1666 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
1667 ascii_date
[30] = '\0';
1670 if(filename_len
< fnlen
+ 1){
1671 if(filename
!= NULL
)
1674 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1675 filename_len
= fnlen
+ 1;
1678 if(read(fd
, filename
, fnlen
) != fnlen
){
1682 filename
[fnlen
] = '\0';
1684 /* if the user specified a list of files on the command line,
1685 we'll only display those, otherwise we'll display everything */
1687 for(j
= 0; j
< file_num
; j
++)
1688 if(strcmp(files
[j
], (const char *)filename
) == 0){
1690 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1692 printf("%s\n", filename
);
1697 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1699 printf("%s\n", filename
);
1702 size
= eflen
+ clen
;
1704 if(lseek(fd
, size
, SEEK_CUR
) == (off_t
)-1){
1711 /* the file isn't seekable.. evil! */
1719 if(pb_read(&pbf
, scratch
, 4) != 4){
1724 signature
= UNPACK_UB4(scratch
, 0);
1727 printf("signature is %x\n", signature
);
1730 if(signature
== 0x08074b50){
1732 printf("skipping data descriptor\n");
1734 pb_read(&pbf
, scratch
, 12);
1736 } else if(signature
== 0x02014b50){
1738 printf("Central header reached.. we're all done!\n");
1741 }else if(signature
!= 0x04034b50){
1743 printf("Ick! %#x\n", signature
);
1748 if(pb_read(&pbf
, (file_header
+ 4), 26) != 26){
1753 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1755 printf("Compressed size is %u\n", csize
);
1758 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1760 printf("Filename length is %hu\n", fnlen
);
1763 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1765 printf("Extra field length is %hu\n", eflen
);
1768 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1770 printf("Compression method is %#hx\n", method
);
1773 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1775 printf("Flags are %#hx\n", flags
);
1778 usize
= UNPACK_UB4(file_header
, LOC_USIZE
);
1780 /* If we're providing verbose output, we need to make an ASCII
1781 * formatted version of the date. */
1783 mdate
= UNPACK_UB4(file_header
, LOC_MODTIME
);
1784 tdate
= dos2unixtime(mdate
);
1785 s_tm
= localtime(&tdate
);
1786 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
1789 if(filename_len
< fnlen
+ 1){
1790 if(filename
!= NULL
)
1793 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1794 ascii_date
[30] = '\0';
1795 filename_len
= fnlen
+ 1;
1798 pb_read(&pbf
, filename
, fnlen
);
1799 filename
[fnlen
] = '\0';
1801 /* the header is at the end. In a JAR file, this means that the data
1802 happens to be compressed. We have no choice but to inflate the
1809 consume(&pbf
, size
);
1813 printf("inflating %s\n", filename
);
1815 inflate_file(&pbf
, -1, &ze
);
1819 printf("We're shit outta luck!\n");
1822 size
= csize
+ (eflen
> 0 ? eflen
: 0);
1826 printf("Skipping %ld bytes\n", (long)size
);
1829 consume(&pbf
, size
);
1831 /* print out the listing */
1833 for(j
= 0; j
< file_num
; j
++)
1834 if(strcmp(files
[j
], (const char *)filename
) == 0){
1836 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1838 printf("%s\n", filename
);
1843 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
1845 printf("%s\n", filename
);
1852 int consume(pb_file
*pbf
, int amt
){
1853 int tc
= 0; /* total amount consumed */
1858 printf("Consuming %d bytes\n", amt
);
1862 if (amt
<= (int)pbf
->buff_amt
)
1863 pb_read(pbf
, buff
, amt
);
1865 lseek(pbf
->fd
, amt
- pbf
->buff_amt
, SEEK_CUR
);
1866 pb_read(pbf
, buff
, pbf
->buff_amt
); /* clear pbf */
1870 rdamt
= pb_read(pbf
, buff
, ((amt
- tc
) < RDSZ
? (amt
- tc
) : RDSZ
));
1872 printf("got %d bytes\n", rdamt
);
1878 printf("%d bytes consumed\n", amt
);
1884 void usage(const char *filename
){
1885 fprintf(stderr
, "Try `%s --help' for more information.\n", filename
);
1891 printf("jar (%s) %s\n\n", PACKAGE
, VERSION
);
1892 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
1893 printf("Copyright 2002 Free Software Foundation\n");
1895 This is free software; see the source for copying conditions. There is NO\n\
1896 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
1900 void help(const char *filename
)
1903 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
1905 Store many files together in a single `jar' file.\n\
1907 -c create new archive\n\
1908 -t list table of contents for archive\n\
1909 -x extract named (or all) files from archive\n\
1910 -u update existing archive\n\
1913 -@ read names from stdin\n\
1914 -0 store only; use no ZIP compression\n\
1915 -C DIR FILE change to the specified directory and include\n\
1916 the following file\n\
1917 -E don't include the files found in a directory\n\
1918 -f FILE specify archive file name\n\
1919 --help print this help, then exit\n\
1920 -m FILE include manifest information from specified manifest file\n\
1921 -M Do not create a manifest file for the entries\n\
1922 -v generate verbose output on standard output\n\
1923 -V, --version display version information\n\
1926 If any file is a directory then it is processed recursively.\n\
1927 The manifest file name and the archive file name needs to be specified\n\
1928 in the same order the 'm' and 'f' flags are specified.\n\
1930 Example 1: to archive two class files into an archive called classes.jar: \n\
1931 jar cvf classes.jar Foo.class Bar.class \n\
1932 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
1933 files in the foo/ directory into 'classes.jar': \n\
1934 jar cvfm classes.jar mymanifest -C foo/ .\n\
1944 char *result
= (char*)malloc(strlen(s
) + 1);
1945 if (result
== (char*)0)
1951 /* Convert "tar-style" first argument to a form expected by getopt.
1952 This idea and the code comes from GNU tar. This can allocate a new
1953 argument vector. This might leak some memory, but we don't care. */
1955 expand_options (int *argcp
, char ***argvp
)
1958 char **argv
= *argvp
;
1960 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
1961 if a long argument (like "--help") is detected. */
1962 if (argc
> 1 && argv
[1][1] != '-')
1974 args_to_expand
= strlen (argv
[1]);
1975 if (argv
[1][0] == '-')
1978 new_argc
= argc
- 1 + args_to_expand
;
1979 new_argv
= (char **) malloc (new_argc
* sizeof (char *));
1991 *out
++ = jt_strdup (buf
);
1992 /* If the option takes an argument, move the next argument
1993 to just after this option. */
1994 opt
= strchr (OPTION_STRING
, *p
);
1995 if (opt
&& opt
[1] == ':')
1997 if (in
< argv
+ argc
)
2001 fprintf(stderr
, "%s: option `%s' requires an argument.\n",
2009 /* Copy remaining options. */
2010 while (in
< argv
+ argc
)