2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002, 2004, 2005 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"
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:0ME@"
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
;
415 /* The following options aren't supported by the original jar tool. */
417 use_explicit_list_only
= TRUE
;
420 read_names_from_stdin
= TRUE
;
427 /* We might have seen `--'. In this case we want to make sure that
428 all following options are handled as file names. */
429 while (optind
< argc
)
430 new_argv
[new_argc
++] = argv
[optind
++];
431 new_argv
[new_argc
] = NULL
;
433 if(action
== ACTION_NONE
){
434 fprintf(stderr
, "%s: one of options -{ctxu} must be specified.\n",
439 /* Verify unsupported combinations and warn of the use of non
441 if(verbose
&& use_explicit_list_only
)
442 fprintf (stderr
, "Warning: using non standard '-E' option\n");
443 if(verbose
&& read_names_from_stdin
)
444 fprintf (stderr
, "Warning: using non standard '-@' option\n");
445 if(read_names_from_stdin
446 && (action
!= ACTION_CREATE
&& action
!= ACTION_UPDATE
)){
447 fprintf(stderr
, "%s: option '-@' is supported only with '-c' or '-u'.\n",
452 /* create the jarfile */
453 if(action
== ACTION_CREATE
){
455 jarfd
= open(jarfile
, O_CREAT
| O_BINARY
| O_WRONLY
| O_TRUNC
, 0666);
458 fprintf(stderr
, "%s: error opening %s for writing: %s\n", progname
,
459 jarfile
, strerror (errno
));
463 /* We assume that the file is seekable */
468 jarfd
= STDOUT_FILENO
; /* jarfd is stdout otherwise */
470 /* standard out is not seekable */
473 /* don't want our output to be part of the jar file.. figured this one
474 out the hard way.. =P */
477 } else if(action
== ACTION_LIST
|| action
== ACTION_EXTRACT
){
480 jarfd
= open(jarfile
, O_RDONLY
| O_BINARY
);
483 fprintf(stderr
, "%s: error opening %s for reading: %s\n", progname
,
484 jarfile
, strerror (errno
));
490 jarfd
= STDIN_FILENO
; /* jarfd is standard in */
492 /* we assume that the stream isn't seekable for safety */
497 if (action
== ACTION_UPDATE
)
501 fprintf (stderr
, "%s: `-u' mode requires a file name\n",
506 if ((jarfd
= open (jarfile
, O_RDWR
| O_BINARY
)) < 0)
508 fprintf (stderr
, "Error opening %s for reading!\n", jarfile
);
513 /* Assert that jarfd is seekable. */
514 if (lseek (jarfd
, 0, SEEK_CUR
) == -1)
516 fprintf (stderr
, "%s: %s is not seekable\n", argv
[0], jarfile
);
523 if(action
== ACTION_CREATE
|| action
== ACTION_UPDATE
){
530 if (action
== ACTION_UPDATE
)
532 if (read_entries (jarfd
))
536 /* Add the META-INF/ directory and the manifest */
537 if(manifest
&& mfile
)
538 make_manifest(jarfd
, mfile
, action
== ACTION_UPDATE
);
539 else if(manifest
&& action
== ACTION_CREATE
)
540 make_manifest(jarfd
, NULL
, FALSE
);
542 init_args (new_argv
, 0);
543 /* now we add the files to the archive */
544 while ((arg
= get_next_arg ())){
546 if(!strcmp(arg
, "-C")){
547 const char *dir_to_change
= get_next_arg ();
548 const char *file_to_add
= get_next_arg ();
549 if (!dir_to_change
|| !file_to_add
) {
550 fprintf(stderr
, "%s: error: missing argument for -C.\n", progname
);
553 if (add_to_jar_with_dir(jarfd
, dir_to_change
, file_to_add
,
554 action
== ACTION_UPDATE
))
557 "Error adding %s (in directory %s) to jar archive!\n",
558 file_to_add
, dir_to_change
);
562 if(add_to_jar(jarfd
, arg
, action
== ACTION_UPDATE
)){
563 fprintf(stderr
, "Error adding %s to jar archive!\n", arg
);
568 /* de-initialize the compression DS */
572 if (action
== ACTION_UPDATE
)
573 lseek (jarfd
, end_of_entries
, SEEK_SET
);
575 create_central_header(jarfd
);
577 #if ! (HAVE_FTRUNCATE || HAVE__CHSIZE)
578 #error neither ftruncate() or _chsize() available
580 /* Check if the file shrunk when we updated it. */
581 if (action
== ACTION_UPDATE
)
583 ftruncate (jarfd
, lseek (jarfd
, 0, SEEK_CUR
));
585 _chsize (jarfd
, lseek (jarfd
, 0, SEEK_CUR
));
588 if (jarfd
!= STDIN_FILENO
&& close(jarfd
) != 0) {
589 fprintf(stderr
, "%s: error closing jar archive: %s\n",
590 progname
, strerror (errno
));
593 } else if(action
== ACTION_LIST
){
594 list_jar(jarfd
, &new_argv
[0], new_argc
);
595 } else if(action
== ACTION_EXTRACT
){
596 extract_jar(jarfd
, &new_argv
[0], new_argc
);
602 static int args_current_g
;
603 static char **args_g
;
606 init_args(char **args
, int current
)
608 if(!read_names_from_stdin
)
611 args_current_g
= current
;
618 static int reached_end
= 0;
625 if (!args_g
[args_current_g
])
630 return args_g
[args_current_g
++];
634 /* Read the name from stdin. Delimiters are '\n' and
635 '\r'. Reading EOF indicates that we don't have anymore file
636 names characters to read. */
641 /* Get rid of '\n' and '\r' first. */
644 int c
= getc (stdin
);
645 if (c
== '\n' || c
== '\r')
658 int c
= getc (stdin
);
659 /* Exit when we get a delimiter or don't have any characters
661 if (c
== '\n'|| c
== '\r'|| c
== EOF
)
663 s
[pos
++] = (char) c
;
669 return jt_strdup (s
);
676 void init_headers(void)
678 /* packing file header */
680 file_header
[0] = 0x50;
681 file_header
[1] = 0x4b;
682 file_header
[2] = 0x03;
683 file_header
[3] = 0x04;
684 /* version number (Unix 1.0)*/
687 /* bit flag (normal deflation)*/
688 file_header
[6] = 0x00;
690 file_header
[7] = 0x00;
691 /* do_compression method (deflation) */
695 /* last mod file time (MS-DOS format) */
698 /* last mod file date (MS-DOS format) */
706 /* compressed size */
711 /* uncompressed size */
716 /* filename length */
719 /* extra field length */
723 /* Initialize the compression DS */
724 PACK_UB4(data_descriptor
, 0, 0x08074b50);
728 void add_entry(struct zipentry
*ze
)
735 ziplist
->next_entry
= ze
;
742 static struct zipentry
*
743 find_entry (const char *fname
)
747 for (ze
= ziptail
; ze
; ze
= ze
->next_entry
)
749 if (!strcmp (ze
->filename
, fname
))
757 looks_like_dir (const char *fname
)
760 size_t len
= strlen (fname
);
762 for (ze
= ziptail
; ze
; ze
= ze
->next_entry
)
764 if (strlen (ze
->filename
) > len
765 && !strncmp (fname
, ze
->filename
, len
)
766 && ze
->filename
[len
] == '/')
774 * Read the zip entries of an existing file, building `ziplist' as we go.
776 int read_entries (int fd
)
785 if (lseek (fd
, -22, SEEK_END
) == -1)
787 fprintf (stderr
, "%s: %s: can't seek file\n", progname
, jarfile
);
791 if (read (fd
, intbuf
, 4) < 4)
796 /* Is there a zipfile comment? */
797 while (UNPACK_UB4(intbuf
, 0) != 0x06054b50)
799 if (lseek (fd
, -5, SEEK_CUR
) == -1 ||
800 read (fd
, intbuf
, 4) != 4)
802 fprintf (stderr
, "%s: can't find end of central directory: %s\n",
803 progname
, strerror (errno
));
808 /* Skip disk numbers. */
809 if (lseek (fd
, 6, SEEK_CUR
) == -1)
815 /* Number of entries in the central directory. */
816 if (read (fd
, intbuf
, 2) != 2)
821 count
= UNPACK_UB2(intbuf
, 0);
823 if (lseek (fd
, 4, SEEK_CUR
) == -1)
829 /* Offset where the central directory begins. */
830 if (read (fd
, intbuf
, 4) != 4)
835 offset
= UNPACK_UB4(intbuf
, 0);
836 end_of_entries
= offset
;
838 if (lseek (fd
, offset
, SEEK_SET
) != offset
)
844 if (read (fd
, header
, 46) != 46)
846 fprintf (stderr
, "%s: %s: unexpected end of file\n",
851 for (i
= 0; i
< count
; i
++)
853 if (UNPACK_UB4(header
, 0) != 0x02014b50)
855 fprintf (stderr
, "%s: can't find central directory header\n",
859 ze
= (struct zipentry
*) malloc (sizeof (struct zipentry
));
865 memset (ze
, 0, sizeof (struct zipentry
));
866 ze
->flags
= UNPACK_UB2(header
, CEN_FLAGS
);
867 ze
->mod_time
= UNPACK_UB2(header
, CEN_MODTIME
);
868 ze
->mod_date
= UNPACK_UB2(header
, CEN_MODDATE
);
869 ze
->crc
= UNPACK_UB4(header
, CEN_CRC
);
870 ze
->usize
= UNPACK_UB4(header
, CEN_USIZE
);
871 ze
->csize
= UNPACK_UB4(header
, CEN_CSIZE
);
872 ze
->offset
= UNPACK_UB4(header
, CEN_OFFSET
);
873 ze
->compressed
= (header
[CEN_COMP
] || header
[CEN_COMP
+1]);
874 len
= UNPACK_UB2(header
, CEN_FNLEN
);
875 ze
->filename
= (char *) malloc ((len
+1) * sizeof (char));
881 if (read (fd
, ze
->filename
, len
) != len
)
883 fprintf (stderr
, "%s: %s: unexpected end of file\n",
887 len
= UNPACK_UB4(header
, CEN_EFLEN
);
888 len
+= UNPACK_UB4(header
, CEN_COMLEN
);
889 if (lseek (fd
, len
, SEEK_CUR
) == -1)
897 if (read (fd
, header
, 46) != 46)
899 fprintf (stderr
, "%s: %s: unexpected end of file\n",
906 lseek (fd
, 0, SEEK_SET
);
910 int make_manifest(int jfd
, const char *mf_name
, int updating
)
913 int nlen
; /* length of file name */
914 int mod_time
; /* file modification time */
917 nlen
= 9; /* trust me on this one */
919 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
921 current_time
= time(NULL
);
922 if(current_time
== (time_t)-1){
927 mod_time
= unix2dostime(¤t_time
);
929 PACK_UB2(file_header
, LOC_EXTRA
, 0);
930 PACK_UB2(file_header
, LOC_COMP
, 0);
931 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
932 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
935 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
937 ze
= (zipentry
*)malloc(sizeof(zipentry
));
943 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
944 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
945 strcpy(ze
->filename
, "META-INF/");
946 ze
->filename
[nlen
] = '\0';
948 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
949 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
950 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
951 ze
->compressed
= FALSE
;
955 write(jfd
, file_header
, 30);
956 write(jfd
, "META-INF/", nlen
);
958 /* if the user didn't specify an external manifest file... */
961 int mf_len
= strlen(MANIFEST_STR
) + strlen(VERSION
) + strlen(MANIFEST_END
);
964 if((mf
= (char *) malloc(mf_len
+ 1))) {
967 sprintf(mf
, "%s%s%s", MANIFEST_STR
, VERSION
, MANIFEST_END
);
969 crc
= crc32(0L, Z_NULL
, 0);
971 crc
= crc32(crc
, (const unsigned char *)mf
, mf_len
);
973 nlen
= 20; /* once again, trust me */
975 PACK_UB2(file_header
, LOC_EXTRA
, 0);
976 PACK_UB2(file_header
, LOC_COMP
, 0);
977 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
978 PACK_UB4(file_header
, LOC_USIZE
, mf_len
);
980 memcpy((file_header
+ LOC_CSIZE
), (file_header
+ LOC_USIZE
), 4);
982 PACK_UB4(file_header
, LOC_CRC
, crc
);
985 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
987 ze
= (zipentry
*)malloc(sizeof(zipentry
));
993 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
994 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
995 strcpy(ze
->filename
, "META-INF/MANIFEST.MF");
996 ze
->filename
[nlen
] = '\0';
998 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
999 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1000 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1003 ze
->usize
= ze
->csize
;
1004 ze
->compressed
= FALSE
;
1008 write(jfd
, file_header
, 30);
1009 write(jfd
, "META-INF/MANIFEST.MF", nlen
);
1010 write(jfd
, mf
, mf_len
);
1014 printf("malloc errror\n");
1019 struct stat statbuf
;
1021 stat(mf_name
, &statbuf
);
1023 if(!S_ISREG(statbuf
.st_mode
)){
1024 fprintf(stderr
, "Invalid manifest file specified.\n");
1028 mfd
= open(mf_name
, O_RDONLY
| O_BINARY
);
1031 fprintf(stderr
, "Error opening %s.\n", mf_name
);
1035 if(add_file_to_jar(jfd
, mfd
, "META-INF/MANIFEST.MF", &statbuf
, updating
)){
1036 perror("error writing to jar");
1045 /* Implements -C by wrapping add_to_jar. new_dir is the directory
1048 `updating', if nonzero, will indicate that we are updating an
1049 existing file, and will need to take special care. If set, we will
1050 also expect that the linked list of zip entries will be filled in
1051 with the jar file's current contents.
1054 add_to_jar_with_dir (int fd
, const char* new_dir
, const char* file
,
1058 char old_dir
[MAXPATHLEN
];
1059 if (getcwd(old_dir
, MAXPATHLEN
) == NULL
) {
1063 if (chdir(new_dir
) == -1) {
1067 retval
=add_to_jar(fd
, file
, updating
);
1068 if (chdir(old_dir
) == -1) {
1076 add_to_jar (int fd
, const char *file
, const int updating
)
1078 struct stat statbuf
;
1082 zipentry
*existing
= NULL
;
1085 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
1087 * "normal" jar : org/apache/java/io/LogRecord.class
1088 * fastjar : ./org/apache/java/io/LogRecord.class
1089 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
1090 * with both kaffe-1.0b4 and JDK.
1092 while (*file
=='.' && *(file
+1)=='/')
1095 if(jarfile
&& !strcmp(file
, jarfile
)){
1097 printf("skipping: %s\n", file
);
1098 return 0; /* we don't want to add ourselves.. */
1101 stat_return
= stat(file
, &statbuf
);
1103 if(stat_return
== -1){
1106 } else if(S_ISDIR(statbuf
.st_mode
)){
1110 unsigned long mod_time
;
1112 dir
= opendir(file
);
1119 nlen
= strlen(file
) + 256;
1120 fullname
= (char*)malloc(nlen
* sizeof(char));
1121 memset(fullname
, 0, (nlen
* sizeof(char)));
1123 if(fullname
== NULL
){
1124 fprintf(stderr
, "Filename is NULL!\n");
1128 strcpy(fullname
, file
);
1129 nlen
= strlen(file
);
1131 if(fullname
[nlen
- 1] != '/'){
1132 fullname
[nlen
] = '/';
1133 t_ptr
= (fullname
+ nlen
+ 1);
1135 t_ptr
= (fullname
+ nlen
);
1138 memset((file_header
+ 12), '\0', 16); /*clear mod time, crc, size fields*/
1140 nlen
= (t_ptr
- fullname
);
1142 mod_time
= unix2dostime(&statbuf
.st_mtime
);
1144 PACK_UB2(file_header
, LOC_EXTRA
, 0);
1145 PACK_UB2(file_header
, LOC_COMP
, 0);
1146 PACK_UB2(file_header
, LOC_FNLEN
, nlen
);
1147 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
1149 ze
= (zipentry
*)malloc(sizeof(zipentry
));
1155 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
1156 ze
->filename
= (char*)malloc((nlen
+ 1) * sizeof(char) + 1);
1157 strcpy(ze
->filename
, fullname
);
1158 ze
->filename
[nlen
] = '\0';
1160 ze
->offset
= lseek(fd
, 0, SEEK_CUR
);
1161 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1162 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1163 ze
->compressed
= FALSE
;
1167 if ((existing
= find_entry (ze
->filename
)) != NULL
)
1169 if (existing
->usize
!= 0)
1171 /* XXX overwriting non-directory with directory? */
1172 fprintf (stderr
, "%s: %s: can't overwrite non-directory with directory\n",
1173 progname
, fullname
);
1177 if (lseek (fd
, end_of_entries
, SEEK_SET
) == -1)
1179 fprintf (stderr
, "%s %d\n", __FILE__
, __LINE__
);
1188 write (fd
, file_header
, 30);
1189 write (fd
, fullname
, nlen
);
1190 end_of_entries
= lseek (fd
, 0, SEEK_CUR
);
1193 printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname
, 0, 0);
1196 while(!use_explicit_list_only
&& (de
= readdir(dir
)) != NULL
){
1197 if(de
->d_name
[0] == '.')
1199 if(jarfile
&& !strcmp(de
->d_name
, jarfile
)){
1200 /* we don't want to add ourselves. Believe me */
1202 printf("skipping: %s\n", de
->d_name
);
1206 strcpy(t_ptr
, de
->d_name
);
1208 if (add_to_jar(fd
, fullname
, updating
)) {
1209 fprintf(stderr
, "Error adding file to jar!\n");
1217 } else if(S_ISREG(statbuf
.st_mode
)){
1220 add_fd
= open(file
, O_RDONLY
| O_BINARY
);
1222 fprintf(stderr
, "Error opening %s.\n", file
);
1226 if(add_file_to_jar(fd
, add_fd
, file
, &statbuf
, updating
)){
1227 fprintf(stderr
, "Error adding file to jar!\n");
1232 fprintf(stderr
, "Illegal file specified: %s\n", file
);
1237 int add_file_to_jar(int jfd
, int ffd
, const char *fname
, struct stat
*statbuf
,
1240 unsigned short file_name_length
;
1241 unsigned long mod_time
;
1246 struct zipentry
*ze
;
1247 struct zipentry
*existing
= NULL
;
1251 existing
= find_entry (fname
);
1252 if (existing
&& looks_like_dir (fname
))
1254 fprintf (stderr
, "%s: %s is a directory in the archive\n",
1260 mod_time
= unix2dostime(&(statbuf
->st_mtime
));
1261 file_name_length
= strlen(fname
);
1263 if(!seekable
&& !do_compress
){
1264 crc
= crc32(0L, Z_NULL
, 0);
1266 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0)
1267 crc
= crc32(crc
, rd_buff
, rdamt
);
1269 lseek(ffd
, 0, SEEK_SET
);
1272 /* data descriptor */
1273 if(!seekable
&& do_compress
){
1274 PACK_UB2(file_header
, LOC_EXTRA
, 8);
1276 PACK_UB2(file_header
, LOC_EXTRA
, 0);
1280 PACK_UB2(file_header
, LOC_COMP
, 8);
1282 PACK_UB2(file_header
, LOC_COMP
, 0);
1285 PACK_UB4(file_header
, LOC_MODTIME
, mod_time
);
1286 PACK_UB2(file_header
, LOC_FNLEN
, file_name_length
);
1288 if(!seekable
&& !do_compress
){
1289 PACK_UB4(file_header
, LOC_CRC
, crc
);
1290 PACK_UB4(file_header
, LOC_USIZE
, statbuf
->st_size
);
1291 PACK_UB4(file_header
, LOC_CSIZE
, statbuf
->st_size
);
1293 memset((file_header
+ LOC_CRC
), '\0', 12); /* clear crc/usize/csize */
1295 ze
= (zipentry
*)malloc(sizeof(zipentry
));
1301 memset(ze
, 0, sizeof(zipentry
)); /* clear all the fields*/
1302 ze
->filename
= (char*)malloc((file_name_length
+ 1) * sizeof(char));
1303 strcpy(ze
->filename
, fname
);
1305 ze
->mod_time
= (ub2
)(mod_time
& 0x0000ffff);
1306 ze
->mod_date
= (ub2
)((mod_time
& 0xffff0000) >> 16);
1308 if(!seekable
&& !do_compress
)
1311 ze
->csize
= statbuf
->st_size
;
1312 ze
->usize
= ze
->csize
;
1315 ze
->offset
= existing
->offset
;
1317 ze
->offset
= end_of_entries
;
1319 ze
->offset
= lseek(jfd
, 0, SEEK_CUR
);
1322 ze
->compressed
= TRUE
;
1324 ze
->compressed
= FALSE
;
1328 if (updating
&& lseek (jfd
, ze
->offset
, SEEK_SET
) < 0)
1334 /* We can safely write the header here, since it will be the same size
1337 /* Write the local header */
1338 write(jfd
, file_header
, 30);
1340 /* write the file name to the zip file */
1341 write(jfd
, fname
, file_name_length
);
1346 printf ("updating: %s ", fname
);
1348 printf("adding: %s ", fname
);
1353 /* compress the file */
1354 compress_file(ffd
, jfd
, ze
, existing
);
1356 /* If we are not writing the last entry, make space for it. */
1357 if (existing
&& existing
->next_entry
)
1359 if (ze
->usize
> existing
->usize
)
1361 if (shift_down (jfd
, existing
->next_entry
->offset
,
1362 ze
->usize
- existing
->usize
, existing
->next_entry
))
1364 fprintf (stderr
, "%s: %s\n", progname
, strerror (errno
));
1370 /* Write the contents of the file (uncompressed) to the zip file */
1371 /* calculate the CRC as we go along */
1372 ze
->crc
= crc32(0L, Z_NULL
, 0);
1374 while((rdamt
= read(ffd
, rd_buff
, RDSZ
)) != 0){
1375 ze
->crc
= crc32(ze
->crc
, rd_buff
, rdamt
);
1376 if(write(jfd
, rd_buff
, rdamt
) != rdamt
){
1384 /* write out data descriptor */
1385 PACK_UB4(data_descriptor
, 4, ze
->crc
);
1386 PACK_UB4(data_descriptor
, 8, ze
->csize
);
1387 PACK_UB4(data_descriptor
, 12, ze
->usize
);
1389 /* we need to seek back and fill the header */
1391 offset
= (ze
->csize
+ strlen(ze
->filename
) + 16);
1393 if(lseek(jfd
, -offset
, SEEK_CUR
) == (off_t
)-1){
1398 if(write(jfd
, (data_descriptor
+ 4), 12) != 12){
1405 if(lseek(jfd
, offset
, SEEK_CUR
) == (off_t
)-1){
1409 } else if(do_compress
){
1410 /* Sun's jar tool will only allow a data descriptor if the entry is
1411 compressed, but we'll save 16 bytes/entry if we only use it when
1412 we can't seek back on the file */
1413 /* Technically, you CAN'T have a data descriptor unless the data
1414 part has an obvious end, which DEFLATED does. Otherwise, there
1415 would not be any way to determine where the data descriptor is.
1416 Store an uncompressed file that ends with 0x504b0708, and see.
1419 if(write(jfd
, data_descriptor
, 16) != 16){
1427 int dd
= (existing
->flags
& (1 << 3)) ? 12 : 0;
1428 if (existing
->next_entry
&& ze
->csize
< existing
->csize
+ dd
)
1430 if (shift_up (jfd
, existing
->next_entry
->offset
,
1431 existing
->csize
+ dd
- ze
->csize
,
1432 existing
->next_entry
))
1438 /* Replace the existing entry data with this entry's. */
1439 existing
->csize
= ze
->csize
;
1440 existing
->usize
= ze
->usize
;
1441 existing
->crc
= ze
->crc
;
1442 existing
->mod_time
= ze
->mod_time
;
1443 existing
->mod_date
= ze
->mod_date
;
1444 free (ze
->filename
);
1448 end_of_entries
= lseek (jfd
, 0, SEEK_CUR
);
1451 printf("(in=%d) (out=%d) (%s %d%%)\n",
1452 (int)ze
->usize
, (int)ze
->csize
,
1453 (do_compress
? "deflated" : "stored"),
1454 (do_compress
? ((int)((1 - ze
->csize
/(float)ze
->usize
) * 100)) : 0));
1459 int create_central_header(int fd
){
1464 int total_in
= 0, total_out
= 22;
1473 /* version made by */
1476 /* version needed to extract */
1482 /* compression method */
1496 /* compressed size */
1501 /* uncompressed size */
1506 /* filename length */
1509 /* extra field length */
1512 /* file comment length */
1515 /* disk number start */
1518 /* internal file attribs */
1521 /* external file attribs */
1526 /* relative offset of local header */
1532 start_offset
= lseek(fd
, 0, SEEK_CUR
);
1534 for(ze
= ziptail
; ze
!= NULL
; ze
= ze
->next_entry
){
1536 total_in
+= ze
->usize
;
1537 total_out
+= ze
->csize
+ 76 + strlen(ze
->filename
) * 2;
1540 PACK_UB2(header
, CEN_COMP
, 8);
1542 PACK_UB2(header
, CEN_COMP
, 0);
1545 PACK_UB2(header
, CEN_MODTIME
, ze
->mod_time
);
1546 PACK_UB2(header
, CEN_MODDATE
, ze
->mod_date
);
1547 PACK_UB4(header
, CEN_CRC
, ze
->crc
);
1548 PACK_UB4(header
, CEN_CSIZE
, ze
->csize
);
1549 PACK_UB4(header
, CEN_USIZE
, ze
->usize
);
1550 PACK_UB2(header
, CEN_FNLEN
, strlen(ze
->filename
));
1551 PACK_UB4(header
, CEN_OFFSET
, ze
->offset
);
1553 write(fd
, header
, 46);
1555 write(fd
, ze
->filename
, strlen(ze
->filename
));
1558 dir_size
= lseek(fd
, 0, SEEK_CUR
) - start_offset
;
1561 end_header
[0] = 0x50;
1562 end_header
[1] = 0x4b;
1563 end_header
[2] = 0x05;
1564 end_header
[3] = 0x06;
1565 /* number of this disk */
1568 /* number of disk w/ start of central header */
1571 /* total number of entries in central dir on this disk*/
1572 PACK_UB2(end_header
, 8, number_of_entries
);
1573 /* total number of entries in central dir*/
1574 PACK_UB2(end_header
, 10, number_of_entries
);
1575 /* size of central dir. */
1576 PACK_UB4(end_header
, 12, dir_size
);
1577 /* offset of start of central dir */
1578 PACK_UB4(end_header
, 16, start_offset
);
1579 /* zipfile comment length */
1583 write(fd
, end_header
, 22);
1586 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1589 (do_compress
? "deflated" : "stored"),
1590 (int)((1 - (total_out
/ (float)total_in
)) * 100)
1596 int extract_jar(int fd
, char **files
, int file_num
){
1606 ub1
*filename
= NULL
;
1607 int filename_len
= 0;
1626 dir
= FALSE
; /* by default, the file isn't a dir */
1627 handle
= TRUE
; /* by default we'll extract/create the file */
1629 if((rdamt
= pb_read(&pbf
, scratch
, 4)) != 4){
1634 signature
= UNPACK_UB4(scratch
, 0);
1637 printf("signature is %x\n", signature
);
1639 if(signature
== 0x08074b50){
1641 printf("skipping data descriptor\n");
1643 pb_read(&pbf
, scratch
, 12);
1645 } else if(signature
== 0x02014b50){
1647 printf("Central header reached.. we're all done!\n");
1650 }else if(signature
!= 0x04034b50){
1651 printf("Ick! %#x\n", signature
);
1655 if((rdamt
= pb_read(&pbf
, (file_header
+ 4), 26)) != 26){
1660 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
1662 printf("Compressed size is %u\n", csize
);
1665 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
1667 printf("Filename length is %hu\n", fnlen
);
1670 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
1672 printf("Extra field length is %hu\n", eflen
);
1675 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
1677 printf("Flags are %#hx\n", flags
);
1680 method
= UNPACK_UB2(file_header
, LOC_COMP
);
1682 printf("Compression method is %#hx\n", method
);
1685 /* if there isn't a data descriptor */
1686 if(!(flags
& 0x0008)){
1687 crc
= UNPACK_UB4(file_header
, LOC_CRC
);
1689 printf("CRC is %x\n", crc
);
1693 if(filename_len
< fnlen
+ 1){
1694 if(filename
!= NULL
)
1697 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
1698 filename_len
= fnlen
+ 1;
1701 pb_read(&pbf
, filename
, fnlen
);
1702 filename
[fnlen
] = '\0';
1705 printf("filename is %s\n", filename
);
1711 for(j
= 0; j
< file_num
; j
++)
1712 if(strcmp(files
[j
], (const char *)filename
) == 0){
1721 /* OK, there is some directory information in the file. Nothing to do
1722 but ensure the directory(s) exist, and create them if they don't.
1724 if(strchr((const char *)filename
, '/') != NULL
&& handle
){
1725 /* Loop through all the directories in the path, (everything w/ a '/') */
1726 const ub1
*start
= filename
;
1730 tmp_buff
= malloc(sizeof(char) * strlen((const char *)filename
));
1733 const ub1
*idx
= (const unsigned char *)strchr((const char *)start
, '/');
1737 else if(idx
== start
){
1743 strncpy(tmp_buff
, (const char *)filename
, (idx
- filename
));
1744 tmp_buff
[(idx
- filename
)] = '\0';
1747 printf("checking the existance of %s\n", tmp_buff
);
1750 if(stat(tmp_buff
, &sbuf
) < 0){
1751 if(errno
!= ENOENT
){
1756 } else if(S_ISDIR(sbuf
.st_mode
)){
1758 printf("Directory exists\n");
1762 fprintf(stderr
, "Hmmm.. %s exists but isn't a directory!\n",
1768 printf("Making directory..\n");
1770 if(mkdir(tmp_buff
, 0755) < 0){
1774 if(verbose
&& handle
)
1775 printf("%10s: %s/\n", "created", tmp_buff
);
1779 /* only a directory */
1780 if(strlen((const char *)start
) == 0)
1784 printf("Leftovers are \"%s\" (%d)\n", start
, strlen((const char *)start
));
1787 /* If the entry was just a directory, don't write to file, etc */
1788 if(strlen((const char *)start
) == 0)
1794 if(f_fd
!= -1 && handle
){
1795 f_fd
= open((const char *)filename
,
1796 O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
, 0644);
1799 fprintf(stderr
, "Error extracting JAR archive!\n");
1800 perror((const char *)filename
);
1805 if(method
!= 8 && flags
& 0x0008){
1806 fprintf(stderr
, "Error in JAR file! (not compressed but data desc.)\n");
1811 consume(&pbf
, eflen
);
1813 if(method
== 8 || flags
& 0x0008){
1815 inflate_file(&pbf
, f_fd
, &ze
);
1819 printf("writing stored data.. (%d bytes)\n", csize
);
1825 ze
.crc
= crc32(ze
.crc
, NULL
, 0); /* initialize the crc */
1827 while(out_a
< (int)csize
){
1828 rdamt
= (in_a
> RDSZ
? RDSZ
: in_a
);
1829 if(pb_read(&pbf
, rd_buff
, rdamt
) != rdamt
){
1834 ze
.crc
= crc32(ze
.crc
, (Bytef
*)rd_buff
, rdamt
);
1837 write(f_fd
, rd_buff
, rdamt
);
1843 printf("%d bytes written\n", out_a
);
1848 /* if there is a data descriptor left, compare the CRC */
1851 if(pb_read(&pbf
, scratch
, 16) != 16){
1856 signature
= UNPACK_UB4(scratch
, 0);
1858 if(signature
!= 0x08074b50){
1859 fprintf(stderr
, "Error! Missing data descriptor!\n");
1863 crc
= UNPACK_UB4(scratch
, 4);
1868 fprintf(stderr
, "Error! CRCs do not match! Got %x, expected %x\n",
1875 if(verbose
&& dir
== FALSE
&& handle
)
1876 printf("%10s: %s\n",
1877 (method
== 8 ? "inflated" : "extracted"),
1884 int list_jar(int fd
, char **files
, int file_num
){
1896 ub1
*filename
= NULL
;
1899 int filename_len
= 0;
1904 char ascii_date
[31];
1908 printf("Listing jar file, looking for %d files\n", file_num
);
1911 /* This should be the start of the central-header-end section */
1913 if(lseek(fd
, -22, SEEK_END
) == (off_t
)-1){
1918 if(read(fd
, &tmp
, sizeof(ub4
)) != 4){
1923 #ifdef WORDS_BIGENDIAN
1927 if(tmp
!= 0x06054b50){
1928 fprintf(stderr
, "Error in JAR file format. zip-style comment?\n");
1932 if(lseek(fd
, 6, SEEK_CUR
) == (off_t
)-1){
1937 if(read(fd
, &cen_size
, 2) != 2){
1942 #ifdef WORDS_BIGENDIAN
1943 cen_size
= L2BS(cen_size
);
1946 /* printf("%hu entries in central header\n", cen_size); */
1948 if(lseek(fd
, 4, SEEK_CUR
) == (off_t
)-1){
1953 if(read(fd
, &tmp
, 4) != 4){
1958 #ifdef WORDS_BIGENDIAN
1962 /* printf("Central header offset = %d\n", tmp); */
1964 if(lseek(fd
, tmp
, SEEK_SET
) != (int)tmp
){
1969 /* Loop through the entries in the central header */
1970 for(i
= 0; i
< cen_size
; i
++){
1972 if(read(fd
, &cen_header
, 46) != 46){
1977 signature
= UNPACK_UB4(cen_header
, 0);
1978 if(signature
!= 0x02014b50){
1979 fprintf(stderr
, "Error in JAR file! Cannot locate central header!\n");
1983 usize
= UNPACK_UB4(cen_header
, CEN_USIZE
);
1984 fnlen
= UNPACK_UB2(cen_header
, CEN_FNLEN
);
1985 eflen
= UNPACK_UB2(cen_header
, CEN_EFLEN
);
1986 clen
= UNPACK_UB2(cen_header
, CEN_COMLEN
);
1988 /* If we're providing verbose output, we need to make an ASCII
1989 * formatted version of the date. */
1991 mdate
= UNPACK_UB4(cen_header
, CEN_MODTIME
);
1992 tdate
= dos2unixtime(mdate
);
1993 s_tm
= localtime(&tdate
);
1994 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
1995 ascii_date
[30] = '\0';
1998 if(filename_len
< fnlen
+ 1){
1999 if(filename
!= NULL
)
2002 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
2003 filename_len
= fnlen
+ 1;
2006 if(read(fd
, filename
, fnlen
) != fnlen
){
2010 filename
[fnlen
] = '\0';
2012 /* if the user specified a list of files on the command line,
2013 we'll only display those, otherwise we'll display everything */
2015 for(j
= 0; j
< file_num
; j
++)
2016 if(strcmp(files
[j
], (const char *)filename
) == 0){
2018 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2020 printf("%s\n", filename
);
2025 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2027 printf("%s\n", filename
);
2030 size
= eflen
+ clen
;
2032 if(lseek(fd
, size
, SEEK_CUR
) == (off_t
)-1){
2039 /* the file isn't seekable.. evil! */
2047 if(pb_read(&pbf
, scratch
, 4) != 4){
2052 signature
= UNPACK_UB4(scratch
, 0);
2055 printf("signature is %x\n", signature
);
2058 if(signature
== 0x08074b50){
2060 printf("skipping data descriptor\n");
2062 pb_read(&pbf
, scratch
, 12);
2064 } else if(signature
== 0x02014b50){
2066 printf("Central header reached.. we're all done!\n");
2069 }else if(signature
!= 0x04034b50){
2071 printf("Ick! %#x\n", signature
);
2076 if(pb_read(&pbf
, (file_header
+ 4), 26) != 26){
2081 csize
= UNPACK_UB4(file_header
, LOC_CSIZE
);
2083 printf("Compressed size is %u\n", csize
);
2086 fnlen
= UNPACK_UB2(file_header
, LOC_FNLEN
);
2088 printf("Filename length is %hu\n", fnlen
);
2091 eflen
= UNPACK_UB2(file_header
, LOC_EFLEN
);
2093 printf("Extra field length is %hu\n", eflen
);
2096 method
= UNPACK_UB2(file_header
, LOC_COMP
);
2098 printf("Compression method is %#hx\n", method
);
2101 flags
= UNPACK_UB2(file_header
, LOC_EXTRA
);
2103 printf("Flags are %#hx\n", flags
);
2106 usize
= UNPACK_UB4(file_header
, LOC_USIZE
);
2108 /* If we're providing verbose output, we need to make an ASCII
2109 * formatted version of the date. */
2111 mdate
= UNPACK_UB4(file_header
, LOC_MODTIME
);
2112 tdate
= dos2unixtime(mdate
);
2113 s_tm
= localtime(&tdate
);
2114 strftime(ascii_date
, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm
);
2117 if(filename_len
< fnlen
+ 1){
2118 if(filename
!= NULL
)
2121 filename
= malloc(sizeof(ub1
) * (fnlen
+ 1));
2122 ascii_date
[30] = '\0';
2123 filename_len
= fnlen
+ 1;
2126 pb_read(&pbf
, filename
, fnlen
);
2127 filename
[fnlen
] = '\0';
2129 /* the header is at the end. In a JAR file, this means that the data
2130 happens to be compressed. We have no choice but to inflate the
2137 consume(&pbf
, size
);
2141 printf("inflating %s\n", filename
);
2143 inflate_file(&pbf
, -1, &ze
);
2147 printf("We're shit outta luck!\n");
2150 size
= csize
+ (eflen
> 0 ? eflen
: 0);
2154 printf("Skipping %ld bytes\n", (long)size
);
2157 consume(&pbf
, size
);
2159 /* print out the listing */
2161 for(j
= 0; j
< file_num
; j
++)
2162 if(strcmp(files
[j
], (const char *)filename
) == 0){
2164 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2166 printf("%s\n", filename
);
2171 printf("%6d %s %s\n", usize
, ascii_date
, filename
);
2173 printf("%s\n", filename
);
2180 int consume(pb_file
*pbf
, int amt
){
2181 int tc
= 0; /* total amount consumed */
2186 printf("Consuming %d bytes\n", amt
);
2190 if (amt
<= (int)pbf
->buff_amt
)
2191 pb_read(pbf
, buff
, amt
);
2193 lseek(pbf
->fd
, amt
- pbf
->buff_amt
, SEEK_CUR
);
2194 pb_read(pbf
, buff
, pbf
->buff_amt
); /* clear pbf */
2198 rdamt
= pb_read(pbf
, buff
, ((amt
- tc
) < RDSZ
? (amt
- tc
) : RDSZ
));
2200 printf("got %d bytes\n", rdamt
);
2206 printf("%d bytes consumed\n", amt
);
2212 void usage(const char *filename
){
2213 fprintf(stderr
, "Try `%s --help' for more information.\n", filename
);
2219 printf("jar (%s) %s\n\n", PACKAGE
, VERSION
);
2220 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
2221 printf("Copyright 2002, 2004 Free Software Foundation\n");
2223 This is free software; see the source for copying conditions. There is NO\n\
2224 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2228 void help(const char *filename
)
2231 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2233 Store many files together in a single `jar' file.\n\
2235 -c create new archive\n\
2236 -t list table of contents for archive\n\
2237 -x extract named (or all) files from archive\n\
2238 -u update existing archive\n\
2241 -@ read names from stdin\n\
2242 -0 store only; use no ZIP compression\n\
2243 -C DIR FILE change to the specified directory and include\n\
2244 the following file\n\
2245 -E don't include the files found in a directory\n\
2246 -f FILE specify archive file name\n\
2247 --help print this help, then exit\n\
2248 -m FILE include manifest information from specified manifest file\n\
2249 -M Do not create a manifest file for the entries\n\
2250 -v generate verbose output on standard output\n\
2251 -V, --version display version information\n\
2254 If any file is a directory then it is processed recursively.\n\
2255 The manifest file name and the archive file name needs to be specified\n\
2256 in the same order the 'm' and 'f' flags are specified.\n\
2258 Example 1: to archive two class files into an archive called classes.jar: \n\
2259 jar cvf classes.jar Foo.class Bar.class \n\
2260 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2261 files in the foo/ directory into 'classes.jar': \n\
2262 jar cvfm classes.jar mymanifest -C foo/ .\n\
2271 char *result
= (char*)malloc(strlen(s
) + 1);
2272 if (result
== (char*)0)
2278 /* Convert "tar-style" first argument to a form expected by getopt.
2279 This idea and the code comes from GNU tar. This can allocate a new
2280 argument vector. This might leak some memory, but we don't care. */
2282 expand_options (int *argcp
, char ***argvp
)
2285 char **argv
= *argvp
;
2287 /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
2288 if a long argument (like "--help") is detected. */
2289 if (argc
> 1 && argv
[1][1] != '-')
2301 args_to_expand
= strlen (argv
[1]);
2302 if (argv
[1][0] == '-')
2305 new_argc
= argc
- 1 + args_to_expand
;
2306 new_argv
= (char **) malloc (new_argc
* sizeof (char *));
2318 *out
++ = jt_strdup (buf
);
2319 /* If the option takes an argument, move the next argument
2320 to just after this option. */
2321 opt
= strchr (OPTION_STRING
, *p
);
2322 if (opt
&& opt
[1] == ':')
2324 if (in
< argv
+ argc
)
2328 fprintf(stderr
, "%s: option `%s' requires an argument.\n",
2336 /* Copy remaining options. */
2337 while (in
< argv
+ argc
)