2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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"
243 /* Some systems have mkdir that takes a single argument. */
244 #ifdef MKDIR_TAKES_ONE_ARG
245 # define mkdir(a,b) mkdir(a)
249 #ifdef WORDS_BIGENDIAN
251 #define L2BI(l) ((l & 0xff000000) >> 24) | \
252 ((l & 0x00ff0000) >> 8) | \
253 ((l & 0x0000ff00) << 8) | \
254 ((l & 0x000000ff) << 24);
256 #define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
268 void usage(const char*);
269 void help(const char *);
271 void add_entry(struct zipentry
*);
272 void init_headers(void);
274 int consume(pb_file
*, int);
275 int list_jar(int, char**, int);
276 int extract_jar(int, char**, int);
277 int add_file_to_jar(int, int, const char*, struct stat
*, int);
278 int add_to_jar(int, const char*, int);
279 int add_to_jar_with_dir(int, const char*, const char*, int);
280 int create_central_header(int);
281 int make_manifest(int, const char*, int);
282 int read_entries (int);
283 static void init_args(char **, int);
284 static char *get_next_arg (void);
285 static char *jt_strdup (char*);
286 static void expand_options (int *argcp
, char ***argvp
);
287 static struct zipentry
*find_entry (const char *);
288 static int looks_like_dir (const char *);
290 /* global variables */
292 ub1 data_descriptor
[16];
298 /* If non zero, then don't recurse in directory. Instead, add the
299 directory entry and relie on an explicit list of files to populate
300 the archive. This option isn't supported by the original jar tool. */
301 int use_explicit_list_only
;
303 /* If non zero, then read the entry names from stdin. This option
304 isn't supported by the original jar tool. */
305 int read_names_from_stdin
;
307 zipentry
*ziplist
; /* linked list of entries */
308 zipentry
*ziptail
; /* tail of the linked list */
310 int number_of_entries
; /* number of entries in the linked list */
313 const char *progname
;
315 /* The offset of the end of the last zip entry. */
318 /* This is used to mark options with no short value. */
319 #define LONG_OPT(Num) ((Num) + 128)
321 #define OPT_HELP LONG_OPT (0)
323 /* This holds all options. */
324 #define OPTION_STRING "-ctxuvVf:m:C:0MiE@"
326 /* Define the MANIFEST content here to have it easier with calculations
327 below. This is for the case we create an empty MANIFEST.MF. */
328 #define MANIFEST_STR "Manifest-Version: 1.0\nCreated-By: "
329 #define MANIFEST_END "\n\n"
331 static const struct option options
[] =
333 { "help", no_argument
, NULL
, OPT_HELP
},
334 { "version", no_argument
, NULL
, 'V' },
335 { NULL
, no_argument
, NULL
, 0 }
338 int main(int argc
, char **argv
)
343 int action
= ACTION_NONE
;
349 /* These are used to collect file names and `-C' options for the
350 second pass through the command line. */
361 number_of_entries
= 0;
367 new_argv
= (char **) malloc (argc
* sizeof (char *));
369 expand_options (&argc
, &argv
);
370 while ((opt
= getopt_long (argc
, argv
, OPTION_STRING
,
371 options
, NULL
)) != -1) {
374 new_argv
[new_argc
++] = (char *) "-C";
375 /* ... fall through ... */
377 /* File name or unparsed option, due to RETURN_IN_ORDER. */
378 new_argv
[new_argc
++] = optarg
;
381 action
= ACTION_CREATE
;
384 action
= ACTION_LIST
;
387 action
= ACTION_EXTRACT
;
390 action
= ACTION_UPDATE
;
411 action
= ACTION_INDEX
;
418 /* The following options aren't supported by the original jar tool. */
420 use_explicit_list_only
= TRUE
;
423 read_names_from_stdin
= TRUE
;
430 if(verbose
&& action
== ACTION_INDEX
)
431 fprintf(stderr
, "Warning: '-i' option is currently a no-op\n");
433 /* FIXME: implement -i option. */
434 if(action
== ACTION_INDEX
)
437 /* We might have seen `--'. In this case we want to make sure that
438 all following options are handled as file names. */
439 while (optind
< argc
)
440 new_argv
[new_argc
++] = argv
[optind
++];
441 new_argv
[new_argc
] = NULL
;
443 if(action
== ACTION_NONE
){
444 fprintf(stderr
, "%s: one of options -{ctxu} must be specified.\n",
449 /* Verify unsupported combinations and warn of the use of non
451 if(verbose
&& use_explicit_list_only
)
452 fprintf (stderr
, "Warning: using non standard '-E' option\n");
453 if(verbose
&& read_names_from_stdin
)
454 fprintf (stderr
, "Warning: using non standard '-@' option\n");
455 if(read_names_from_stdin
456 && (action
!= ACTION_CREATE
&& action
!= ACTION_UPDATE
)){
457 fprintf(stderr
, "%s: option '-@' is supported only with '-c' or '-u'.\n",
462 /* create the jarfile */
463 if(action
== ACTION_CREATE
){
465 jarfd
= open(jarfile
, O_CREAT
| O_BINARY
| O_WRONLY
| O_TRUNC
, 0666);
468 fprintf(stderr
, "%s: error opening %s for writing: %s\n", progname
,
469 jarfile
, strerror (errno
));
473 /* We assume that the file is seekable */
478 jarfd
= STDOUT_FILENO
; /* jarfd is stdout otherwise */
480 /* standard out is not seekable */
483 /* don't want our output to be part of the jar file.. figured this one
484 out the hard way.. =P */
487 } else if(action
== ACTION_LIST
|| action
== ACTION_EXTRACT
){
490 jarfd
= open(jarfile
, O_RDONLY
| O_BINARY
);
493 fprintf(stderr
, "%s: error opening %s for reading: %s\n", progname
,
494 jarfile
, strerror (errno
));
500 jarfd
= STDIN_FILENO
; /* jarfd is standard in */
502 /* we assume that the stream isn't seekable for safety */
507 if (action
== ACTION_UPDATE
)
511 fprintf (stderr
, "%s: `-u' mode requires a file name\n",
516 if ((jarfd
= open (jarfile
, O_RDWR
| O_BINARY
)) < 0)
518 fprintf (stderr
, "Error opening %s for reading!\n", jarfile
);
523 /* Assert that jarfd is seekable. */
524 if (lseek (jarfd
, 0, SEEK_CUR
) == -1)
526 fprintf (stderr
, "%s: %s is not seekable\n", argv
[0], jarfile
);
533 if(action
== ACTION_CREATE
|| action
== ACTION_UPDATE
){
540 if (action
== ACTION_UPDATE
)
542 if (read_entries (jarfd
))
546 /* Add the META-INF/ directory and the manifest */
547 if(manifest
&& mfile
)
548 make_manifest(jarfd
, mfile
, action
== ACTION_UPDATE
);
549 else if(manifest
&& action
== ACTION_CREATE
)
550 make_manifest(jarfd
, NULL
, FALSE
);
552 init_args (new_argv
, 0);
553 /* now we add the files to the archive */
554 while ((arg
= get_next_arg ())){
556 if(!strcmp(arg
, "-C")){
557 const char *dir_to_change
= get_next_arg ();
558 const char *file_to_add
= get_next_arg ();
559 if (!dir_to_change
|| !file_to_add
) {
560 fprintf(stderr
, "%s: error: missing argument for -C.\n", progname
);
563 if (add_to_jar_with_dir(jarfd
, dir_to_change
, file_to_add
,
564 action
== ACTION_UPDATE
))
567 "Error adding %s (in directory %s) to jar archive!\n",
568 file_to_add
, dir_to_change
);
572 if(add_to_jar(jarfd
, arg
, action
== ACTION_UPDATE
)){
573 fprintf(stderr
, "Error adding %s to jar archive!\n", arg
);
578 /* de-initialize the compression DS */
582 if (action
== ACTION_UPDATE
)
583 lseek (jarfd
, end_of_entries
, SEEK_SET
);
585 create_central_header(jarfd
);
587 #if ! (HAVE_FTRUNCATE || HAVE__CHSIZE)
588 #error neither ftruncate() or _chsize() available
590 /* Check if the file shrunk when we updated it. */
591 if (action
== ACTION_UPDATE
)
593 ftruncate (jarfd
, lseek (jarfd
, 0, SEEK_CUR
));
595 _chsize (jarfd
, lseek (jarfd
, 0, SEEK_CUR
));
598 if (jarfd
!= STDIN_FILENO
&& close(jarfd
) != 0) {
599 fprintf(stderr
, "%s: error closing jar archive: %s\n",
600 progname
, strerror (errno
));
603 } else if(action
== ACTION_LIST
){
604 list_jar(jarfd
, &new_argv
[0], new_argc
);
605 } else if(action
== ACTION_EXTRACT
){
606 extract_jar(jarfd
, &new_argv
[0], new_argc
);
612 static int args_current_g
;
613 static char **args_g
;
616 init_args(char **args
, int current
)
618 if(!read_names_from_stdin
)
621 args_current_g
= current
;
628 static int reached_end
= 0;
635 if (!args_g
[args_current_g
])
640 return args_g
[args_current_g
++];
644 /* Read the name from stdin. Delimiters are '\n' and
645 '\r'. Reading EOF indicates that we don't have anymore file
646 names characters to read. */
651 /* Get rid of '\n' and '\r' first. */
654 int c
= getc (stdin
);
655 if (c
== '\n' || c
== '\r')
668 int c
= getc (stdin
);
669 /* Exit when we get a delimiter or don't have any characters
671 if (c
== '\n'|| c
== '\r'|| c
== EOF
)
673 s
[pos
++] = (char) c
;
679 return jt_strdup (s
);
686 void init_headers(void)
688 /* packing file header */
690 file_header
[0] = 0x50;
691 file_header
[1] = 0x4b;
692 file_header
[2] = 0x03;
693 file_header
[3] = 0x04;
694 /* version number (Unix 1.0)*/
697 /* bit flag (normal deflation)*/
698 file_header
[6] = 0x00;
700 file_header
[7] = 0x00;
701 /* do_compression method (deflation) */
705 /* last mod file time (MS-DOS format) */
708 /* last mod file date (MS-DOS format) */
716 /* compressed size */
721 /* uncompressed size */
726 /* filename length */
729 /* extra field length */
733 /* Initialize the compression DS */
734 PACK_UB4(data_descriptor
, 0, 0x08074b50);
738 void add_entry(struct zipentry
*ze
)
745 ziplist
->next_entry
= ze
;
752 static struct zipentry
*
753 find_entry (const char *fname
)
757 for (ze
= ziptail
; ze
; ze
= ze
->next_entry
)
759 if (!strcmp (ze
->filename
, fname
))
767 looks_like_dir (const char *fname
)
770 size_t len
= strlen (fname
);
772 for (ze
= ziptail
; ze
; ze
= ze
->next_entry
)
774 if (strlen (ze
->filename
) > len
775 && !strncmp (fname
, ze
->filename
, len
)
776 && ze
->filename
[len
] == '/')
784 * Read the zip entries of an existing file, building `ziplist' as we go.
786 int read_entries (int fd
)
795 if (lseek (fd
, -22, SEEK_END
) == -1)
797 fprintf (stderr
, "%s: %s: can't seek file\n", progname
, jarfile
);
801 if (read (fd
, intbuf
, 4) < 4)
806 /* Is there a zipfile comment? */
807 while (UNPACK_UB4(intbuf
, 0) != 0x06054b50)
809 if (lseek (fd
, -5, SEEK_CUR
) == -1 ||
810 read (fd
, intbuf
, 4) != 4)
812 fprintf (stderr
, "%s: can't find end of central directory: %s\n",
813 progname
, strerror (errno
));
818 /* Skip disk numbers. */
819 if (lseek (fd
, 6, SEEK_CUR
) == -1)
825 /* Number of entries in the central directory. */
826 if (read (fd
, intbuf
, 2) != 2)
831 count
= UNPACK_UB2(intbuf
, 0);
833 if (lseek (fd
, 4, SEEK_CUR
) == -1)
839 /* Offset where the central directory begins. */
840 if (read (fd
, intbuf
, 4) != 4)
845 offset
= UNPACK_UB4(intbuf
, 0);
846 end_of_entries
= offset
;
848 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
854 if (read (fd
, header
, 46) != 46)
856 fprintf (stderr
, "%s: %s: unexpected end of file\n",
861 for (i
= 0; i
< count
; i
++)
863 if (UNPACK_UB4(header
, 0) != 0x02014b50)
865 fprintf (stderr
, "%s: can't find central directory header\n",
869 ze
= (struct zipentry
*) malloc (sizeof (struct zipentry
));
875 memset (ze
, 0, sizeof (struct zipentry
));
876 ze
->flags
= UNPACK_UB2(header
, CEN_FLAGS
);
877 ze
->mod_time
= UNPACK_UB2(header
, CEN_MODTIME
);
878 ze
->mod_date
= UNPACK_UB2(header
, CEN_MODDATE
);
879 ze
->crc
= UNPACK_UB4(header
, CEN_CRC
);
880 ze
->usize
= UNPACK_UB4(header
, CEN_USIZE
);
881 ze
->csize
= UNPACK_UB4(header
, CEN_CSIZE
);
882 ze
->offset
= UNPACK_UB4(header
, CEN_OFFSET
);
883 ze
->compressed
= (header
[CEN_COMP
] || header
[CEN_COMP
+1]);
884 len
= UNPACK_UB2(header
, CEN_FNLEN
);
885 ze
->filename
= (char *) malloc ((len
+1) * sizeof (char));
891 if (read (fd
, ze
->filename
, len
) != len
)
893 fprintf (stderr
, "%s: %s: unexpected end of file\n",
897 len
= UNPACK_UB4(header
, CEN_EFLEN
);
898 len
+= UNPACK_UB4(header
, CEN_COMLEN
);
899 if (lseek (fd
, len
, SEEK_CUR
) == -1)
907 if (read (fd
, header
, 46) != 46)
909 fprintf (stderr
, "%s: %s: unexpected end of file\n",
916 lseek (fd
, 0, SEEK_SET
);
920 int make_manifest(int jfd
, const char *mf_name
, int updating
)
923 int nlen
; /* length of file name */
924 int mod_time
; /* file modification time */
927 nlen
= 9; /* trust me on this one */
929 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
931 current_time
= time(NULL
);
932 if(current_time
== (time_t)-1){
937 mod_time
= unix2dostime(¤t_time
);
939 PACK_UB2(file_header
, LOC_EXTRA
, 0);
940 PACK_UB2(file_header
, LOC_COMP
, 0);
941 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
942 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
945 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
947 ze
= (zipentry
*)malloc(sizeof(zipentry
));
953 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
954 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
955 strcpy(ze
->filename
, "META-INF/");
956 ze
->filename
[nlen
] = '\0';
958 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
959 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
960 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
961 ze
->compressed
= FALSE
;
965 write(jfd
, file_header
, 30);
966 write(jfd
, "META-INF/", nlen
);
968 /* if the user didn't specify an external manifest file... */
971 int mf_len
= strlen(MANIFEST_STR
) + strlen(VERSION
) + strlen(MANIFEST_END
);
974 if((mf
= (char *) malloc(mf_len
+ 1))) {
977 sprintf(mf
, "%s%s%s", MANIFEST_STR
, VERSION
, MANIFEST_END
);
979 crc
= crc32(0L, Z_NULL
, 0);
981 crc
= crc32(crc
, (const unsigned char *)mf
, mf_len
);
983 nlen
= 20; /* once again, trust me */
985 PACK_UB2(file_header
, LOC_EXTRA
, 0);
986 PACK_UB2(file_header
, LOC_COMP
, 0);
987 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
988 PACK_UB4(file_header
, LOC_USIZE
, mf_len
);
990 memcpy((file_header
+ LOC_CSIZE
), (file_header
+ LOC_USIZE
), 4);
992 PACK_UB4(file_header
, LOC_CRC
, crc
);
995 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
997 ze
= (zipentry
*)malloc(sizeof(zipentry
));
1003 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
1004 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
1005 strcpy(ze
->filename
, "META-INF/MANIFEST.MF");
1006 ze
->filename
[nlen
] = '\0';
1008 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
1009 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1010 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1013 ze
->usize
= ze
->csize
;
1014 ze
->compressed
= FALSE
;
1018 write(jfd
, file_header
, 30);
1019 write(jfd
, "META-INF/MANIFEST.MF", nlen
);
1020 write(jfd
, mf
, mf_len
);
1024 printf("malloc errror\n");
1029 struct stat statbuf
;
1031 stat(mf_name
, &statbuf
);
1033 if(!S_ISREG(statbuf
.st_mode
)){
1034 fprintf(stderr
, "Invalid manifest file specified.\n");
1038 mfd
= open(mf_name
, O_RDONLY
| O_BINARY
);
1041 fprintf(stderr
, "Error opening %s.\n", mf_name
);
1045 if(add_file_to_jar(jfd
, mfd
, "META-INF/MANIFEST.MF", &statbuf
, updating
)){
1046 perror("error writing to jar");
1055 /* Implements -C by wrapping add_to_jar. new_dir is the directory
1058 `updating', if nonzero, will indicate that we are updating an
1059 existing file, and will need to take special care. If set, we will
1060 also expect that the linked list of zip entries will be filled in
1061 with the jar file's current contents.
1064 add_to_jar_with_dir (int fd
, const char* new_dir
, const char* file
,
1068 char old_dir
[MAXPATHLEN
];
1069 if (getcwd(old_dir
, MAXPATHLEN
) == NULL
) {
1073 if (chdir(new_dir
) == -1) {
1077 retval
=add_to_jar(fd
, file
, updating
);
1078 if (chdir(old_dir
) == -1) {
1086 add_to_jar (int fd
, const char *file
, const int updating
)
1088 struct stat statbuf
;
1092 zipentry
*existing
= NULL
;
1095 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
1097 * "normal" jar : org/apache/java/io/LogRecord.class
1098 * fastjar : ./org/apache/java/io/LogRecord.class
1099 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
1100 * with both kaffe-1.0b4 and JDK.
1102 while (*file
=='.' && *(file
+1)=='/')
1105 if(jarfile
&& !strcmp(file
, jarfile
)){
1107 printf("skipping: %s\n", file
);
1108 return 0; /* we don't want to add ourselves.. */
1111 stat_return
= stat(file
, &statbuf
);
1113 if(stat_return
== -1){
1116 } else if(S_ISDIR(statbuf
.st_mode
)){
1120 unsigned long mod_time
;
1122 dir
= opendir(file
);
1129 nlen
= strlen(file
) + 256;
1130 fullname
= (char*)malloc(nlen
* sizeof(char));
1131 memset(fullname
, 0, (nlen
* sizeof(char)));
1133 if(fullname
== NULL
){
1134 fprintf(stderr
, "Filename is NULL!\n");
1138 strcpy(fullname
, file
);
1139 nlen
= strlen(file
);
1141 if(fullname
[nlen
- 1] != '/'){
1142 fullname
[nlen
] = '/';
1143 t_ptr
= (fullname
+ nlen
+ 1);
1145 t_ptr
= (fullname
+ nlen
);
1148 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
1150 nlen
= (t_ptr
- fullname
);
1152 mod_time
= unix2dostime(&statbuf
.st_mtime
);
1154 PACK_UB2(file_header
, LOC_EXTRA
, 0);
1155 PACK_UB2(file_header
, LOC_COMP
, 0);
1156 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
1157 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
1159 ze
= (zipentry
*)malloc(sizeof(zipentry
));
1165 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
1166 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
1167 strcpy(ze
->filename
, fullname
);
1168 ze
->filename
[nlen
] = '\0';
1170 ze
->offset
= lseek(fd
, 0, SEEK_CUR
);
1171 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1172 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1173 ze
->compressed
= FALSE
;
1177 if ((existing
= find_entry (ze
->filename
)) != NULL
)
1179 if (existing
->usize
!= 0)
1181 /* XXX overwriting non-directory with directory? */
1182 fprintf (stderr
, "%s: %s: can't overwrite non-directory with directory\n",
1183 progname
, fullname
);
1187 if (lseek (fd
, end_of_entries
, SEEK_SET
) == -1)
1189 fprintf (stderr
, "%s %d\n", __FILE__
, __LINE__
);
1198 write (fd
, file_header
, 30);
1199 write (fd
, fullname
, nlen
);
1200 end_of_entries
= lseek (fd
, 0, SEEK_CUR
);
1203 printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname
, 0, 0);
1206 while(!use_explicit_list_only
&& (de
= readdir(dir
)) != NULL
){
1207 if(de
->d_name
[0] == '.')
1209 if(jarfile
&& !strcmp(de
->d_name
, jarfile
)){
1210 /* we don't want to add ourselves. Believe me */
1212 printf("skipping: %s\n", de
->d_name
);
1216 strcpy(t_ptr
, de
->d_name
);
1218 if (add_to_jar(fd
, fullname
, updating
)) {
1219 fprintf(stderr
, "Error adding file to jar!\n");
1227 } else if(S_ISREG(statbuf
.st_mode
)){
1230 add_fd
= open(file
, O_RDONLY
| O_BINARY
);
1232 fprintf(stderr
, "Error opening %s.\n", file
);
1236 if(add_file_to_jar(fd
, add_fd
, file
, &statbuf
, updating
)){
1237 fprintf(stderr
, "Error adding file to jar!\n");
1242 fprintf(stderr
, "Illegal file specified: %s\n", file
);
1247 int add_file_to_jar(int jfd
, int ffd
, const char *fname
, struct stat
*statbuf
,
1250 unsigned short file_name_length
;
1251 unsigned long mod_time
;
1256 struct zipentry
*ze
;
1257 struct zipentry
*existing
= NULL
;
1261 existing
= find_entry (fname
);
1262 if (existing
&& looks_like_dir (fname
))
1264 fprintf (stderr
, "%s: %s is a directory in the archive\n",
1270 mod_time
= unix2dostime(&(statbuf
->st_mtime
));
1271 file_name_length
= strlen(fname
);
1273 if(!seekable
&& !do_compress
){
1274 crc
= crc32(0L, Z_NULL
, 0);
1276 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0)
1277 crc
= crc32(crc
, rd_buff
, rdamt
);
1279 lseek(ffd
, 0, SEEK_SET
);
1282 /* data descriptor */
1283 if(!seekable
&& do_compress
){
1284 PACK_UB2(file_header
, LOC_EXTRA
, 8);
1286 PACK_UB2(file_header
, LOC_EXTRA
, 0);
1290 PACK_UB2(file_header
, LOC_COMP
, 8);
1292 PACK_UB2(file_header
, LOC_COMP
, 0);
1295 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
1296 PACK_UB2(file_header
, LOC_FNLEN
, file_name_length
);
1298 if(!seekable
&& !do_compress
){
1299 PACK_UB4(file_header
, LOC_CRC
, crc
);
1300 PACK_UB4(file_header
, LOC_USIZE
, statbuf
->st_size
);
1301 PACK_UB4(file_header
, LOC_CSIZE
, statbuf
->st_size
);
1303 memset((file_header
+ LOC_CRC
), '\0', 12); /* clear crc/usize/csize */
1305 ze
= (zipentry
*)malloc(sizeof(zipentry
));
1311 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
1312 ze
->filename
= (char*)malloc((file_name_length
+ 1) * sizeof(char));
1313 strcpy(ze
->filename
, fname
);
1315 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1316 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1318 if(!seekable
&& !do_compress
)
1321 ze
->csize
= statbuf
->st_size
;
1322 ze
->usize
= ze
->csize
;
1325 ze
->offset
= existing
->offset
;
1327 ze
->offset
= end_of_entries
;
1329 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
1332 ze
->compressed
= TRUE
;
1334 ze
->compressed
= FALSE
;
1338 if (updating
&& lseek (jfd
, ze
->offset
, SEEK_SET
) < 0)
1344 /* We can safely write the header here, since it will be the same size
1347 /* Write the local header */
1348 write(jfd
, file_header
, 30);
1350 /* write the file name to the zip file */
1351 write(jfd
, fname
, file_name_length
);
1356 printf ("updating: %s ", fname
);
1358 printf("adding: %s ", fname
);
1363 /* compress the file */
1364 compress_file(ffd
, jfd
, ze
, existing
);
1366 /* If we are not writing the last entry, make space for it. */
1367 if (existing
&& existing
->next_entry
)
1369 if (ze
->usize
> existing
->usize
)
1371 if (shift_down (jfd
, existing
->next_entry
->offset
,
1372 ze
->usize
- existing
->usize
, existing
->next_entry
))
1374 fprintf (stderr
, "%s: %s\n", progname
, strerror (errno
));
1380 /* Write the contents of the file (uncompressed) to the zip file */
1381 /* calculate the CRC as we go along */
1382 ze
->crc
= crc32(0L, Z_NULL
, 0);
1384 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0){
1385 ze
->crc
= crc32(ze
->crc
, rd_buff
, rdamt
);
1386 if(write(jfd
, rd_buff
, rdamt
) != rdamt
){
1394 /* write out data descriptor */
1395 PACK_UB4(data_descriptor
, 4, ze
->crc
);
1396 PACK_UB4(data_descriptor
, 8, ze
->csize
);
1397 PACK_UB4(data_descriptor
, 12, ze
->usize
);
1399 /* we need to seek back and fill the header */
1401 offset
= (ze
->csize
+ strlen(ze
->filename
) + 16);
1403 if(lseek(jfd
, -offset
, SEEK_CUR
) == (off_t
)-1){
1408 if(write(jfd
, (data_descriptor
+ 4), 12) != 12){
1415 if(lseek(jfd
, offset
, SEEK_CUR
) == (off_t
)-1){
1419 } else if(do_compress
){
1420 /* Sun's jar tool will only allow a data descriptor if the entry is
1421 compressed, but we'll save 16 bytes/entry if we only use it when
1422 we can't seek back on the file */
1423 /* Technically, you CAN'T have a data descriptor unless the data
1424 part has an obvious end, which DEFLATED does. Otherwise, there
1425 would not be any way to determine where the data descriptor is.
1426 Store an uncompressed file that ends with 0x504b0708, and see.
1429 if(write(jfd
, data_descriptor
, 16) != 16){
1437 int dd
= (existing
->flags
& (1 << 3)) ? 12 : 0;
1438 if (existing
->next_entry
&& ze
->csize
< existing
->csize
+ dd
)
1440 if (shift_up (jfd
, existing
->next_entry
->offset
,
1441 existing
->csize
+ dd
- ze
->csize
,
1442 existing
->next_entry
))
1448 /* Replace the existing entry data with this entry's. */
1449 existing
->csize
= ze
->csize
;
1450 existing
->usize
= ze
->usize
;
1451 existing
->crc
= ze
->crc
;
1452 existing
->mod_time
= ze
->mod_time
;
1453 existing
->mod_date
= ze
->mod_date
;
1454 free (ze
->filename
);
1458 end_of_entries
= lseek (jfd
, 0, SEEK_CUR
);
1461 printf("(in=%d) (out=%d) (%s %d%%)\n",
1462 (int)ze
->usize
, (int)ze
->csize
,
1463 (do_compress
? "deflated" : "stored"),
1464 (do_compress
? ((int)((1 - ze
->csize
/(float)ze
->usize
) * 100)) : 0));
1469 int create_central_header(int fd
){
1474 int total_in
= 0, total_out
= 22;
1483 /* version made by */
1486 /* version needed to extract */
1492 /* compression method */
1506 /* compressed size */
1511 /* uncompressed size */
1516 /* filename length */
1519 /* extra field length */
1522 /* file comment length */
1525 /* disk number start */
1528 /* internal file attribs */
1531 /* external file attribs */
1536 /* relative offset of local header */
1542 start_offset
= lseek(fd
, 0, SEEK_CUR
);
1544 for(ze
= ziptail
; ze
!= NULL
; ze
= ze
->next_entry
){
1546 total_in
+= ze
->usize
;
1547 total_out
+= ze
->csize
+ 76 + strlen(ze
->filename
) * 2;
1550 PACK_UB2(header
, CEN_COMP
, 8);
1552 PACK_UB2(header
, CEN_COMP
, 0);
1555 PACK_UB2(header
, CEN_MODTIME
, ze
->mod_time
);
1556 PACK_UB2(header
, CEN_MODDATE
, ze
->mod_date
);
1557 PACK_UB4(header
, CEN_CRC
, ze
->crc
);
1558 PACK_UB4(header
, CEN_CSIZE
, ze
->csize
);
1559 PACK_UB4(header
, CEN_USIZE
, ze
->usize
);
1560 PACK_UB2(header
, CEN_FNLEN
, strlen(ze
->filename
));
1561 PACK_UB4(header
, CEN_OFFSET
, ze
->offset
);
1563 write(fd
, header
, 46);
1565 write(fd
, ze
->filename
, strlen(ze
->filename
));
1568 dir_size
= lseek(fd
, 0, SEEK_CUR
) - start_offset
;
1571 end_header
[0] = 0x50;
1572 end_header
[1] = 0x4b;
1573 end_header
[2] = 0x05;
1574 end_header
[3] = 0x06;
1575 /* number of this disk */
1578 /* number of disk w/ start of central header */
1581 /* total number of entries in central dir on this disk*/
1582 PACK_UB2(end_header
, 8, number_of_entries
);
1583 /* total number of entries in central dir*/
1584 PACK_UB2(end_header
, 10, number_of_entries
);
1585 /* size of central dir. */
1586 PACK_UB4(end_header
, 12, dir_size
);
1587 /* offset of start of central dir */
1588 PACK_UB4(end_header
, 16, start_offset
);
1589 /* zipfile comment length */
1593 write(fd
, end_header
, 22);
1596 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1599 (do_compress
? "deflated" : "stored"),
1600 (int)((1 - (total_out
/ (float)total_in
)) * 100)
1606 int extract_jar(int fd
, char **files
, int file_num
){
1616 ub1
*filename
= NULL
;
1617 int filename_len
= 0;
1636 dir
= FALSE
; /* by default, the file isn't a dir */
1637 handle
= TRUE
; /* by default we'll extract/create the file */
1639 if((rdamt
= pb_read(&pbf
, scratch
, 4)) != 4){
1644 signature
= UNPACK_UB4(scratch
, 0);
1647 printf("signature is %x\n", signature
);
1649 if(signature
== 0x08074b50){
1651 printf("skipping data descriptor\n");
1653 pb_read(&pbf
, scratch
, 12);
1655 } else if(signature
== 0x02014b50){
1657 printf("Central header reached.. we're all done!\n");
1660 }else if(signature
!= 0x04034b50){
1661 printf("Ick! %#x\n", signature
);
1665 if((rdamt
= pb_read(&pbf
, (file_header
+ 4), 26)) != 26){
1670 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1672 printf("Compressed size is %u\n", csize
);
1675 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1677 printf("Filename length is %hu\n", fnlen
);
1680 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1682 printf("Extra field length is %hu\n", eflen
);
1685 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1687 printf("Flags are %#hx\n", flags
);
1690 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1692 printf("Compression method is %#hx\n", method
);
1695 /* if there isn't a data descriptor */
1696 if(!(flags
& 0x0008)){
1697 crc
= UNPACK_UB4(file_header
, LOC_CRC
);
1699 printf("CRC is %x\n", crc
);
1703 if(filename_len
< fnlen
+ 1){
1704 if(filename
!= NULL
)
1707 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1708 filename_len
= fnlen
+ 1;
1711 pb_read(&pbf
, filename
, fnlen
);
1712 filename
[fnlen
] = '\0';
1715 printf("filename is %s\n", filename
);
1721 for(j
= 0; j
< file_num
; j
++)
1722 if(strcmp(files
[j
], (const char *)filename
) == 0){
1731 /* OK, there is some directory information in the file. Nothing to do
1732 but ensure the directory(s) exist, and create them if they don't.
1734 if(strchr((const char *)filename
, '/') != NULL
&& handle
){
1735 /* Loop through all the directories in the path, (everything w/ a '/') */
1736 const ub1
*start
= filename
;
1740 tmp_buff
= malloc(sizeof(char) * strlen((const char *)filename
));
1743 const ub1
*idx
= (const unsigned char *)strchr((const char *)start
, '/');
1747 else if(idx
== start
){
1753 strncpy(tmp_buff
, (const char *)filename
, (idx
- filename
));
1754 tmp_buff
[(idx
- filename
)] = '\0';
1757 printf("checking the existance of %s\n", tmp_buff
);
1760 if(stat(tmp_buff
, &sbuf
) < 0){
1761 if(errno
!= ENOENT
){
1766 } else if(S_ISDIR(sbuf
.st_mode
)){
1768 printf("Directory exists\n");
1772 fprintf(stderr
, "Hmmm.. %s exists but isn't a directory!\n",
1778 printf("Making directory..\n");
1780 if(mkdir(tmp_buff
, 0755) < 0){
1784 if(verbose
&& handle
)
1785 printf("%10s: %s/\n", "created", tmp_buff
);
1789 /* only a directory */
1790 if(strlen((const char *)start
) == 0)
1794 printf("Leftovers are \"%s\" (%d)\n", start
, strlen((const char *)start
));
1797 /* If the entry was just a directory, don't write to file, etc */
1798 if(strlen((const char *)start
) == 0)
1804 if(f_fd
!= -1 && handle
){
1805 f_fd
= open((const char *)filename
,
1806 O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644);
1809 fprintf(stderr
, "Error extracting JAR archive!\n");
1810 perror((const char *)filename
);
1815 if(method
!= 8 && flags
& 0x0008){
1816 fprintf(stderr
, "Error in JAR file! (not compressed but data desc.)\n");
1821 consume(&pbf
, eflen
);
1823 if(method
== 8 || flags
& 0x0008){
1825 inflate_file(&pbf
, f_fd
, &ze
);
1829 printf("writing stored data.. (%d bytes)\n", csize
);
1835 ze
.crc
= crc32(ze
.crc
, NULL
, 0); /* initialize the crc */
1837 while(out_a
< (int)csize
){
1838 rdamt
= (in_a
> RDSZ
? RDSZ
: in_a
);
1839 if(pb_read(&pbf
, rd_buff
, rdamt
) != rdamt
){
1844 ze
.crc
= crc32(ze
.crc
, (Bytef
*)rd_buff
, rdamt
);
1847 write(f_fd
, rd_buff
, rdamt
);
1853 printf("%d bytes written\n", out_a
);
1858 /* if there is a data descriptor left, compare the CRC */
1861 if(pb_read(&pbf
, scratch
, 16) != 16){
1866 signature
= UNPACK_UB4(scratch
, 0);
1868 if(signature
!= 0x08074b50){
1869 fprintf(stderr
, "Error! Missing data descriptor!\n");
1873 crc
= UNPACK_UB4(scratch
, 4);
1878 fprintf(stderr
, "Error! CRCs do not match! Got %x, expected %x\n",
1885 if(verbose
&& dir
== FALSE
&& handle
)
1886 printf("%10s: %s\n",
1887 (method
== 8 ? "inflated" : "extracted"),
1894 int list_jar(int fd
, char **files
, int file_num
){
1906 ub1
*filename
= NULL
;
1909 int filename_len
= 0;
1914 char ascii_date
[31];
1918 printf("Listing jar file, looking for %d files\n", file_num
);
1921 /* This should be the start of the central-header-end section */
1923 if(lseek(fd
, -22, SEEK_END
) == (off_t
)-1){
1928 if(read(fd
, &tmp
, sizeof(ub4
)) != 4){
1933 #ifdef WORDS_BIGENDIAN
1937 if(tmp
!= 0x06054b50){
1938 fprintf(stderr
, "Error in JAR file format. zip-style comment?\n");
1942 if(lseek(fd
, 6, SEEK_CUR
) == (off_t
)-1){
1947 if(read(fd
, &cen_size
, 2) != 2){
1952 #ifdef WORDS_BIGENDIAN
1953 cen_size
= L2BS(cen_size
);
1956 /* printf("%hu entries in central header\n", cen_size); */
1958 if(lseek(fd
, 4, SEEK_CUR
) == (off_t
)-1){
1963 if(read(fd
, &tmp
, 4) != 4){
1968 #ifdef WORDS_BIGENDIAN
1972 /* printf("Central header offset = %d\n", tmp); */
1974 if(lseek(fd
, tmp
, SEEK_SET
) != (int)tmp
){
1979 /* Loop through the entries in the central header */
1980 for(i
= 0; i
< cen_size
; i
++){
1982 if(read(fd
, &cen_header
, 46) != 46){
1987 signature
= UNPACK_UB4(cen_header
, 0);
1988 if(signature
!= 0x02014b50){
1989 fprintf(stderr
, "Error in JAR file! Cannot locate central header!\n");
1993 usize
= UNPACK_UB4(cen_header
, CEN_USIZE
);
1994 fnlen
= UNPACK_UB2(cen_header
, CEN_FNLEN
);
1995 eflen
= UNPACK_UB2(cen_header
, CEN_EFLEN
);
1996 clen
= UNPACK_UB2(cen_header
, CEN_COMLEN
);
1998 /* If we're providing verbose output, we need to make an ASCII
1999 * formatted version of the date. */
2001 mdate
= UNPACK_UB4(cen_header
, CEN_MODTIME
);
2002 tdate
= dos2unixtime(mdate
);
2003 s_tm
= localtime(&tdate
);
2004 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
2005 ascii_date
[30] = '\0';
2008 if(filename_len
< fnlen
+ 1){
2009 if(filename
!= NULL
)
2012 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
2013 filename_len
= fnlen
+ 1;
2016 if(read(fd
, filename
, fnlen
) != fnlen
){
2020 filename
[fnlen
] = '\0';
2022 /* if the user specified a list of files on the command line,
2023 we'll only display those, otherwise we'll display everything */
2025 for(j
= 0; j
< file_num
; j
++)
2026 if(strcmp(files
[j
], (const char *)filename
) == 0){
2028 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2030 printf("%s\n", filename
);
2035 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2037 printf("%s\n", filename
);
2040 size
= eflen
+ clen
;
2042 if(lseek(fd
, size
, SEEK_CUR
) == (off_t
)-1){
2049 /* the file isn't seekable.. evil! */
2057 if(pb_read(&pbf
, scratch
, 4) != 4){
2062 signature
= UNPACK_UB4(scratch
, 0);
2065 printf("signature is %x\n", signature
);
2068 if(signature
== 0x08074b50){
2070 printf("skipping data descriptor\n");
2072 pb_read(&pbf
, scratch
, 12);
2074 } else if(signature
== 0x02014b50){
2076 printf("Central header reached.. we're all done!\n");
2079 }else if(signature
!= 0x04034b50){
2081 printf("Ick! %#x\n", signature
);
2086 if(pb_read(&pbf
, (file_header
+ 4), 26) != 26){
2091 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
2093 printf("Compressed size is %u\n", csize
);
2096 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
2098 printf("Filename length is %hu\n", fnlen
);
2101 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
2103 printf("Extra field length is %hu\n", eflen
);
2106 method
= UNPACK_UB2(file_header
, LOC_COMP
);
2108 printf("Compression method is %#hx\n", method
);
2111 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
2113 printf("Flags are %#hx\n", flags
);
2116 usize
= UNPACK_UB4(file_header
, LOC_USIZE
);
2118 /* If we're providing verbose output, we need to make an ASCII
2119 * formatted version of the date. */
2121 mdate
= UNPACK_UB4(file_header
, LOC_MODTIME
);
2122 tdate
= dos2unixtime(mdate
);
2123 s_tm
= localtime(&tdate
);
2124 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
2127 if(filename_len
< fnlen
+ 1){
2128 if(filename
!= NULL
)
2131 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
2132 ascii_date
[30] = '\0';
2133 filename_len
= fnlen
+ 1;
2136 pb_read(&pbf
, filename
, fnlen
);
2137 filename
[fnlen
] = '\0';
2139 /* the header is at the end. In a JAR file, this means that the data
2140 happens to be compressed. We have no choice but to inflate the
2147 consume(&pbf
, size
);
2151 printf("inflating %s\n", filename
);
2153 inflate_file(&pbf
, -1, &ze
);
2157 printf("We're shit outta luck!\n");
2160 size
= csize
+ (eflen
> 0 ? eflen
: 0);
2164 printf("Skipping %ld bytes\n", (long)size
);
2167 consume(&pbf
, size
);
2169 /* print out the listing */
2171 for(j
= 0; j
< file_num
; j
++)
2172 if(strcmp(files
[j
], (const char *)filename
) == 0){
2174 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2176 printf("%s\n", filename
);
2181 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2183 printf("%s\n", filename
);
2190 int consume(pb_file
*pbf
, int amt
){
2191 int tc
= 0; /* total amount consumed */
2196 printf("Consuming %d bytes\n", amt
);
2200 if (amt
<= (int)pbf
->buff_amt
)
2201 pb_read(pbf
, buff
, amt
);
2203 lseek(pbf
->fd
, amt
- pbf
->buff_amt
, SEEK_CUR
);
2204 pb_read(pbf
, buff
, pbf
->buff_amt
); /* clear pbf */
2208 rdamt
= pb_read(pbf
, buff
, ((amt
- tc
) < RDSZ
? (amt
- tc
) : RDSZ
));
2210 printf("got %d bytes\n", rdamt
);
2216 printf("%d bytes consumed\n", amt
);
2222 void usage(const char *filename
){
2223 fprintf(stderr
, "Try `%s --help' for more information.\n", filename
);
2229 printf("jar (%s) %s\n\n", PACKAGE
, VERSION
);
2230 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
2231 printf("Copyright 2006 Free Software Foundation\n");
2233 This is free software; see the source for copying conditions. There is NO\n\
2234 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2238 void help(const char *filename
)
2241 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2243 Store many files together in a single `jar' file.\n\
2245 -c create new archive\n\
2246 -t list table of contents for archive\n\
2247 -x extract named (or all) files from archive\n\
2248 -u update existing archive\n\
2251 -@ read names from stdin\n\
2252 -0 store only; use no ZIP compression\n\
2253 -C DIR FILE change to the specified directory and include\n\
2254 the following file\n\
2255 -E don't include the files found in a directory\n\
2256 -f FILE specify archive file name\n\
2257 --help print this help, then exit\n\
2260 -m FILE include manifest information from specified manifest file\n\
2261 -M Do not create a manifest file for the entries\n\
2262 -i generate an index of the packages in this jar\n\
2263 and its Class-Path (currently unimplemented)\n\
2264 -v generate verbose output on standard output\n\
2265 -V, --version display version information\n\
2268 If any file is a directory then it is processed recursively.\n\
2269 The manifest file name and the archive file name needs to be specified\n\
2270 in the same order the 'm' and 'f' flags are specified.\n\
2272 Example 1: to archive two class files into an archive called classes.jar: \n\
2273 jar cvf classes.jar Foo.class Bar.class \n\
2274 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2275 files in the foo/ directory into 'classes.jar': \n\
2276 jar cvfm classes.jar mymanifest -C foo/ .\n\
2285 char *result
= (char*)malloc(strlen(s
) + 1);
2286 if (result
== (char*)0)
2292 /* Convert "tar-style" first argument to a form expected by getopt.
2293 This idea and the code comes from GNU tar. This can allocate a new
2294 argument vector. This might leak some memory, but we don't care. */
2296 expand_options (int *argcp
, char ***argvp
)
2299 char **argv
= *argvp
;
2301 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
2302 if a long argument (like "--help") is detected. */
2303 if (argc
> 1 && argv
[1][1] != '-')
2315 args_to_expand
= strlen (argv
[1]);
2316 if (argv
[1][0] == '-')
2319 new_argc
= argc
- 1 + args_to_expand
;
2320 new_argv
= (char **) malloc (new_argc
* sizeof (char *));
2332 *out
++ = jt_strdup (buf
);
2333 /* If the option takes an argument, move the next argument
2334 to just after this option. */
2335 opt
= strchr (OPTION_STRING
, *p
);
2336 if (opt
&& opt
[1] == ':')
2338 if (in
< argv
+ argc
)
2342 fprintf(stderr
, "%s: option `%s' requires an argument.\n",
2350 /* Copy remaining options. */
2351 while (in
< argv
+ argc
)