2 jartool.c - main functions for fastjar utility
3 Copyright (C) 2002, 2004 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){
342 int action = ACTION_NONE;
348 /* These are used to collect file names and `-C' options for the
349 second pass through the command line. */
360 number_of_entries = 0;
366 new_argv = (char **) malloc (argc * sizeof (char *));
368 expand_options (&argc, &argv);
369 while ((opt = getopt_long (argc, argv, OPTION_STRING,
370 options, NULL)) != -1) {
373 new_argv[new_argc++] = (char *) "-C";
374 /* ... fall through ... */
376 /* File name or unparsed option, due to RETURN_IN_ORDER. */
377 new_argv[new_argc++] = optarg;
380 action = ACTION_CREATE;
383 action = ACTION_LIST;
386 action = ACTION_EXTRACT;
389 action = ACTION_UPDATE;
414 /* The following options aren't supported by the original jar tool. */
416 use_explicit_list_only = TRUE;
419 read_names_from_stdin = TRUE;
426 /* We might have seen `--'. In this case we want to make sure that
427 all following options are handled as file names. */
428 while (optind < argc)
429 new_argv[new_argc++] = argv[optind++];
430 new_argv[new_argc] = NULL;
432 if(action == ACTION_NONE){
433 fprintf(stderr, "%s: one of options -{ctxu} must be specified.\n",
438 /* Verify unsupported combinations and warn of the use of non
440 if(verbose && use_explicit_list_only)
441 fprintf (stderr, "Warning: using non standard '-E' option\n");
442 if(verbose && read_names_from_stdin)
443 fprintf (stderr, "Warning: using non standard '-@' option\n");
444 if(read_names_from_stdin
445 && (action != ACTION_CREATE && action != ACTION_UPDATE)){
446 fprintf(stderr, "%s: option '-@' is supported only with '-c' or '-u'.\n",
451 /* create the jarfile */
452 if(action == ACTION_CREATE){
454 jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
457 fprintf(stderr, "%s: error opening %s for writing: %s\n", progname,
458 jarfile, strerror (errno));
462 /* We assume that the file is seekable */
467 jarfd = STDOUT_FILENO; /* jarfd is stdout otherwise */
469 /* standard out is not seekable */
472 /* don't want our output to be part of the jar file.. figured this one
473 out the hard way.. =P */
476 } else if(action == ACTION_LIST || action == ACTION_EXTRACT){
479 jarfd = open(jarfile, O_RDONLY | O_BINARY);
482 fprintf(stderr, "%s: error opening %s for reading: %s\n", progname,
483 jarfile, strerror (errno));
489 jarfd = STDIN_FILENO; /* jarfd is standard in */
491 /* we assume that the stream isn't seekable for safety */
496 if (action == ACTION_UPDATE)
500 fprintf (stderr, "%s: `-u' mode requires a file name\n",
505 if ((jarfd = open (jarfile, O_RDWR | O_BINARY)) < 0)
507 fprintf (stderr, "Error opening %s for reading!\n", jarfile);
512 /* Assert that jarfd is seekable. */
513 if (lseek (jarfd, 0, SEEK_CUR) == -1)
515 fprintf (stderr, "%s: %s is not seekable\n", argv[0], jarfile);
522 if(action == ACTION_CREATE || action == ACTION_UPDATE){
529 if (action == ACTION_UPDATE)
531 if (read_entries (jarfd))
535 /* Add the META-INF/ directory and the manifest */
536 if(manifest && mfile)
537 make_manifest(jarfd, mfile, action == ACTION_UPDATE);
538 else if(manifest && action == ACTION_CREATE)
539 make_manifest(jarfd, NULL, FALSE);
541 init_args (new_argv, 0);
542 /* now we add the files to the archive */
543 while ((arg = get_next_arg ())){
545 if(!strcmp(arg, "-C")){
546 const char *dir_to_change = get_next_arg ();
547 const char *file_to_add = get_next_arg ();
548 if (!dir_to_change || !file_to_add) {
549 fprintf(stderr, "%s: error: missing argument for -C.\n", progname);
552 if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add,
553 action == ACTION_UPDATE))
556 "Error adding %s (in directory %s) to jar archive!\n",
557 file_to_add, dir_to_change);
561 if(add_to_jar(jarfd, arg, action == ACTION_UPDATE)){
562 fprintf(stderr, "Error adding %s to jar archive!\n", arg);
567 /* de-initialize the compression DS */
571 if (action == ACTION_UPDATE)
572 lseek (jarfd, end_of_entries, SEEK_SET);
574 create_central_header(jarfd);
576 #if ! (HAVE_FTRUNCATE || HAVE__CHSIZE)
577 #error neither ftruncate() or _chsize() available
579 /* Check if the file shrunk when we updated it. */
580 if (action == ACTION_UPDATE)
582 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(args, current)
610 if(!read_names_from_stdin)
613 args_current_g = current;
620 static int reached_end = 0;
627 if (!args_g [args_current_g])
632 return args_g [args_current_g++];
636 /* Read the name from stdin. Delimiters are '\n' and
637 '\r'. Reading EOF indicates that we don't have anymore file
638 names characters to read. */
643 /* Get rid of '\n' and '\r' first. */
646 int c = getc (stdin);
647 if (c == '\n' || c == '\r')
660 int c = getc (stdin);
661 /* Exit when we get a delimiter or don't have any characters
663 if (c == '\n'|| c == '\r'|| c == EOF)
665 s [pos++] = (char) c;
671 return jt_strdup (s);
679 /* packing file header */
681 file_header[0] = 0x50;
682 file_header[1] = 0x4b;
683 file_header[2] = 0x03;
684 file_header[3] = 0x04;
685 /* version number (Unix 1.0)*/
688 /* bit flag (normal deflation)*/
689 file_header[6] = 0x00;
691 file_header[7] = 0x00;
692 /* do_compression method (deflation) */
696 /* last mod file time (MS-DOS format) */
699 /* last mod file date (MS-DOS format) */
707 /* compressed size */
712 /* uncompressed size */
717 /* filename length */
720 /* extra field length */
724 /* Initialize the compression DS */
725 PACK_UB4(data_descriptor, 0, 0x08074b50);
729 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){
912 int nlen; /* length of file name */
913 int mod_time; /* file modification time */
916 nlen = 9; /* trust me on this one */
918 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
920 current_time = time(NULL);
921 if(current_time == (time_t)-1){
926 mod_time = unix2dostime(¤t_time);
928 PACK_UB2(file_header, LOC_EXTRA, 0);
929 PACK_UB2(file_header, LOC_COMP, 0);
930 PACK_UB2(file_header, LOC_FNLEN, nlen);
931 PACK_UB4(file_header, LOC_MODTIME, mod_time);
934 printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
936 ze = (zipentry*)malloc(sizeof(zipentry));
942 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
943 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
944 strcpy(ze->filename, "META-INF/");
945 ze->filename[nlen] = '\0';
947 ze->offset = lseek(jfd, 0, SEEK_CUR);
948 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
949 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
950 ze->compressed = FALSE;
954 write(jfd, file_header, 30);
955 write(jfd, "META-INF/", nlen);
957 /* if the user didn't specify an external manifest file... */
960 int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
963 if((mf = (char *) malloc(mf_len + 1))) {
966 sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
968 crc = crc32(0L, Z_NULL, 0);
970 crc = crc32(crc, (const unsigned char *)mf, mf_len);
972 nlen = 20; /* once again, trust me */
974 PACK_UB2(file_header, LOC_EXTRA, 0);
975 PACK_UB2(file_header, LOC_COMP, 0);
976 PACK_UB2(file_header, LOC_FNLEN, nlen);
977 PACK_UB4(file_header, LOC_USIZE, mf_len);
979 memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
981 PACK_UB4(file_header, LOC_CRC, crc);
984 printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
986 ze = (zipentry*)malloc(sizeof(zipentry));
992 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
993 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
994 strcpy(ze->filename, "META-INF/MANIFEST.MF");
995 ze->filename[nlen] = '\0';
997 ze->offset = lseek(jfd, 0, SEEK_CUR);
998 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
999 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1002 ze->usize = ze->csize;
1003 ze->compressed = FALSE;
1007 write(jfd, file_header, 30);
1008 write(jfd, "META-INF/MANIFEST.MF", nlen);
1009 write(jfd, mf, mf_len);
1013 printf("malloc errror\n");
1018 struct stat statbuf;
1020 stat(mf_name, &statbuf);
1022 if(!S_ISREG(statbuf.st_mode)){
1023 fprintf(stderr, "Invalid manifest file specified.\n");
1027 mfd = open(mf_name, O_RDONLY | O_BINARY);
1030 fprintf(stderr, "Error opening %s.\n", mf_name);
1034 if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
1035 perror("error writing to jar");
1044 /* Implements -C by wrapping add_to_jar. new_dir is the directory
1047 `updating', if nonzero, will indicate that we are updating an
1048 existing file, and will need to take special care. If set, we will
1049 also expect that the linked list of zip entries will be filled in
1050 with the jar file's current contents.
1053 add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
1057 char old_dir[MAXPATHLEN];
1058 if (getcwd(old_dir, MAXPATHLEN) == NULL) {
1062 if (chdir(new_dir) == -1) {
1066 retval=add_to_jar(fd, file, updating);
1067 if (chdir(old_dir) == -1) {
1075 add_to_jar (int fd, const char *file, const int updating)
1077 struct stat statbuf;
1081 zipentry *existing = NULL;
1084 /* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
1086 * "normal" jar : org/apache/java/io/LogRecord.class
1087 * fastjar : ./org/apache/java/io/LogRecord.class
1088 * Fastjar's preservation of the ./'s makes the jarfile unusuable for use
1089 * with both kaffe-1.0b4 and JDK.
1091 while (*file=='.' && *(file+1)=='/')
1094 if(jarfile && !strcmp(file, jarfile)){
1096 printf("skipping: %s\n", file);
1097 return 0; /* we don't want to add ourselves.. */
1100 stat_return = stat(file, &statbuf);
1102 if(stat_return == -1){
1105 } else if(S_ISDIR(statbuf.st_mode)){
1109 unsigned long mod_time;
1111 dir = opendir(file);
1118 nlen = strlen(file) + 256;
1119 fullname = (char*)malloc(nlen * sizeof(char));
1120 memset(fullname, 0, (nlen * sizeof(char)));
1122 if(fullname == NULL){
1123 fprintf(stderr, "Filename is NULL!\n");
1127 strcpy(fullname, file);
1128 nlen = strlen(file);
1130 if(fullname[nlen - 1] != '/'){
1131 fullname[nlen] = '/';
1132 t_ptr = (fullname + nlen + 1);
1134 t_ptr = (fullname + nlen);
1137 memset((file_header + 12), '\0', 16); /*clear mod time, crc, size fields*/
1139 nlen = (t_ptr - fullname);
1141 mod_time = unix2dostime(&statbuf.st_mtime);
1143 PACK_UB2(file_header, LOC_EXTRA, 0);
1144 PACK_UB2(file_header, LOC_COMP, 0);
1145 PACK_UB2(file_header, LOC_FNLEN, nlen);
1146 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1148 ze = (zipentry*)malloc(sizeof(zipentry));
1154 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1155 ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
1156 strcpy(ze->filename, fullname);
1157 ze->filename[nlen] = '\0';
1159 ze->offset = lseek(fd, 0, SEEK_CUR);
1160 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1161 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1162 ze->compressed = FALSE;
1166 if ((existing = find_entry (ze->filename)) != NULL)
1168 if (existing->usize != 0)
1170 /* XXX overwriting non-directory with directory? */
1171 fprintf (stderr, "%s: %s: can't overwrite non-directory with directory\n",
1172 progname, fullname);
1176 if (lseek (fd, end_of_entries, SEEK_SET) == -1)
1178 fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
1187 write (fd, file_header, 30);
1188 write (fd, fullname, nlen);
1189 end_of_entries = lseek (fd, 0, SEEK_CUR);
1192 printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
1195 while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
1196 if(de->d_name[0] == '.')
1198 if(jarfile && !strcmp(de->d_name, jarfile)){
1199 /* we don't want to add ourselves. Believe me */
1201 printf("skipping: %s\n", de->d_name);
1205 strcpy(t_ptr, de->d_name);
1207 if (add_to_jar(fd, fullname, updating)) {
1208 fprintf(stderr, "Error adding file to jar!\n");
1216 } else if(S_ISREG(statbuf.st_mode)){
1219 add_fd = open(file, O_RDONLY | O_BINARY);
1221 fprintf(stderr, "Error opening %s.\n", file);
1225 if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
1226 fprintf(stderr, "Error adding file to jar!\n");
1231 fprintf(stderr, "Illegal file specified: %s\n", file);
1236 int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
1239 unsigned short file_name_length;
1240 unsigned long mod_time;
1245 struct zipentry *ze;
1246 struct zipentry *existing = NULL;
1250 existing = find_entry (fname);
1251 if (existing && looks_like_dir (fname))
1253 fprintf (stderr, "%s: %s is a directory in the archive\n",
1259 mod_time = unix2dostime(&(statbuf->st_mtime));
1260 file_name_length = strlen(fname);
1262 if(!seekable && !do_compress){
1263 crc = crc32(0L, Z_NULL, 0);
1265 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
1266 crc = crc32(crc, rd_buff, rdamt);
1268 lseek(ffd, 0, SEEK_SET);
1271 /* data descriptor */
1272 if(!seekable && do_compress){
1273 PACK_UB2(file_header, LOC_EXTRA, 8);
1275 PACK_UB2(file_header, LOC_EXTRA, 0);
1279 PACK_UB2(file_header, LOC_COMP, 8);
1281 PACK_UB2(file_header, LOC_COMP, 0);
1284 PACK_UB4(file_header, LOC_MODTIME, mod_time);
1285 PACK_UB2(file_header, LOC_FNLEN, file_name_length);
1287 if(!seekable && !do_compress){
1288 PACK_UB4(file_header, LOC_CRC, crc);
1289 PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
1290 PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
1292 memset((file_header + LOC_CRC), '\0', 12); /* clear crc/usize/csize */
1294 ze = (zipentry*)malloc(sizeof(zipentry));
1300 memset(ze, 0, sizeof(zipentry)); /* clear all the fields*/
1301 ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
1302 strcpy(ze->filename, fname);
1304 ze->mod_time = (ub2)(mod_time & 0x0000ffff);
1305 ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
1307 if(!seekable && !do_compress)
1310 ze->csize = statbuf->st_size;
1311 ze->usize = ze->csize;
1314 ze->offset = existing->offset;
1316 ze->offset = end_of_entries;
1318 ze->offset = lseek(jfd, 0, SEEK_CUR);
1321 ze->compressed = TRUE;
1323 ze->compressed = FALSE;
1327 if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
1333 /* We can safely write the header here, since it will be the same size
1336 /* Write the local header */
1337 write(jfd, file_header, 30);
1339 /* write the file name to the zip file */
1340 write(jfd, fname, file_name_length);
1345 printf ("updating: %s ", fname);
1347 printf("adding: %s ", fname);
1352 /* compress the file */
1353 compress_file(ffd, jfd, ze, existing);
1355 /* If we are not writing the last entry, make space for it. */
1356 if (existing && existing->next_entry)
1358 if (ze->usize > existing->usize)
1360 if (shift_down (jfd, existing->next_entry->offset,
1361 ze->usize - existing->usize, existing->next_entry))
1363 fprintf (stderr, "%s: %s\n", progname, strerror (errno));
1369 /* Write the contents of the file (uncompressed) to the zip file */
1370 /* calculate the CRC as we go along */
1371 ze->crc = crc32(0L, Z_NULL, 0);
1373 while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
1374 ze->crc = crc32(ze->crc, rd_buff, rdamt);
1375 if(write(jfd, rd_buff, rdamt) != rdamt){
1383 /* write out data descriptor */
1384 PACK_UB4(data_descriptor, 4, ze->crc);
1385 PACK_UB4(data_descriptor, 8, ze->csize);
1386 PACK_UB4(data_descriptor, 12, ze->usize);
1388 /* we need to seek back and fill the header */
1390 offset = (ze->csize + strlen(ze->filename) + 16);
1392 if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
1397 if(write(jfd, (data_descriptor + 4), 12) != 12){
1404 if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
1408 } else if(do_compress){
1409 /* Sun's jar tool will only allow a data descriptor if the entry is
1410 compressed, but we'll save 16 bytes/entry if we only use it when
1411 we can't seek back on the file */
1412 /* Technically, you CAN'T have a data descriptor unless the data
1413 part has an obvious end, which DEFLATED does. Otherwise, there
1414 would not be any way to determine where the data descriptor is.
1415 Store an uncompressed file that ends with 0x504b0708, and see.
1418 if(write(jfd, data_descriptor, 16) != 16){
1426 int dd = (existing->flags & (1 << 3)) ? 12 : 0;
1427 if (existing->next_entry && ze->csize < existing->csize + dd)
1429 if (shift_up (jfd, existing->next_entry->offset,
1430 existing->csize + dd - ze->csize,
1431 existing->next_entry))
1437 /* Replace the existing entry data with this entry's. */
1438 existing->csize = ze->csize;
1439 existing->usize = ze->usize;
1440 existing->crc = ze->crc;
1441 existing->mod_time = ze->mod_time;
1442 existing->mod_date = ze->mod_date;
1443 free (ze->filename);
1447 end_of_entries = lseek (jfd, 0, SEEK_CUR);
1450 printf("(in=%d) (out=%d) (%s %d%%)\n",
1451 (int)ze->usize, (int)ze->csize,
1452 (do_compress ? "deflated" : "stored"),
1453 (do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
1458 int create_central_header(int fd){
1463 int total_in = 0, total_out = 22;
1472 /* version made by */
1475 /* version needed to extract */
1481 /* compression method */
1495 /* compressed size */
1500 /* uncompressed size */
1505 /* filename length */
1508 /* extra field length */
1511 /* file comment length */
1514 /* disk number start */
1517 /* internal file attribs */
1520 /* external file attribs */
1525 /* relative offset of local header */
1531 start_offset = lseek(fd, 0, SEEK_CUR);
1533 for(ze = ziptail; ze != NULL; ze = ze->next_entry){
1535 total_in += ze->usize;
1536 total_out += ze->csize + 76 + strlen(ze->filename) * 2;
1539 PACK_UB2(header, CEN_COMP, 8);
1541 PACK_UB2(header, CEN_COMP, 0);
1544 PACK_UB2(header, CEN_MODTIME, ze->mod_time);
1545 PACK_UB2(header, CEN_MODDATE, ze->mod_date);
1546 PACK_UB4(header, CEN_CRC, ze->crc);
1547 PACK_UB4(header, CEN_CSIZE, ze->csize);
1548 PACK_UB4(header, CEN_USIZE, ze->usize);
1549 PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
1550 PACK_UB4(header, CEN_OFFSET, ze->offset);
1552 write(fd, header, 46);
1554 write(fd, ze->filename, strlen(ze->filename));
1557 dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
1560 end_header[0] = 0x50;
1561 end_header[1] = 0x4b;
1562 end_header[2] = 0x05;
1563 end_header[3] = 0x06;
1564 /* number of this disk */
1567 /* number of disk w/ start of central header */
1570 /* total number of entries in central dir on this disk*/
1571 PACK_UB2(end_header, 8, number_of_entries);
1572 /* total number of entries in central dir*/
1573 PACK_UB2(end_header, 10, number_of_entries);
1574 /* size of central dir. */
1575 PACK_UB4(end_header, 12, dir_size);
1576 /* offset of start of central dir */
1577 PACK_UB4(end_header, 16, start_offset);
1578 /* zipfile comment length */
1582 write(fd, end_header, 22);
1585 printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
1588 (do_compress ? "deflated" : "stored"),
1589 (int)((1 - (total_out / (float)total_in)) * 100)
1595 int extract_jar(int fd, char **files, int file_num){
1605 ub1 *filename = NULL;
1606 int filename_len = 0;
1625 dir = FALSE; /* by default, the file isn't a dir */
1626 handle = TRUE; /* by default we'll extract/create the file */
1628 if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
1633 signature = UNPACK_UB4(scratch, 0);
1636 printf("signature is %x\n", signature);
1638 if(signature == 0x08074b50){
1640 printf("skipping data descriptor\n");
1642 pb_read(&pbf, scratch, 12);
1644 } else if(signature == 0x02014b50){
1646 printf("Central header reached.. we're all done!\n");
1649 }else if(signature != 0x04034b50){
1650 printf("Ick! %#x\n", signature);
1654 if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
1659 csize = UNPACK_UB4(file_header, LOC_CSIZE);
1661 printf("Compressed size is %u\n", csize);
1664 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
1666 printf("Filename length is %hu\n", fnlen);
1669 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
1671 printf("Extra field length is %hu\n", eflen);
1674 flags = UNPACK_UB2(file_header, LOC_EXTRA);
1676 printf("Flags are %#hx\n", flags);
1679 method = UNPACK_UB2(file_header, LOC_COMP);
1681 printf("Compression method is %#hx\n", method);
1684 /* if there isn't a data descriptor */
1685 if(!(flags & 0x0008)){
1686 crc = UNPACK_UB4(file_header, LOC_CRC);
1688 printf("CRC is %x\n", crc);
1692 if(filename_len < fnlen + 1){
1693 if(filename != NULL)
1696 filename = malloc(sizeof(ub1) * (fnlen + 1));
1697 filename_len = fnlen + 1;
1700 pb_read(&pbf, filename, fnlen);
1701 filename[fnlen] = '\0';
1704 printf("filename is %s\n", filename);
1710 for(j = 0; j < file_num; j++)
1711 if(strcmp(files[j], (const char *)filename) == 0){
1720 /* OK, there is some directory information in the file. Nothing to do
1721 but ensure the directory(s) exist, and create them if they don't.
1723 if(strchr((const char *)filename, '/') != NULL && handle){
1724 /* Loop through all the directories in the path, (everything w/ a '/') */
1725 const ub1 *start = filename;
1729 tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
1732 const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
1736 else if(idx == start){
1742 strncpy(tmp_buff, (const char *)filename, (idx - filename));
1743 tmp_buff[(idx - filename)] = '\0';
1746 printf("checking the existance of %s\n", tmp_buff);
1749 if(stat(tmp_buff, &sbuf) < 0){
1750 if(errno != ENOENT){
1755 } else if(S_ISDIR(sbuf.st_mode)){
1757 printf("Directory exists\n");
1761 fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
1767 printf("Making directory..\n");
1769 if(mkdir(tmp_buff, 0755) < 0){
1773 if(verbose && handle)
1774 printf("%10s: %s/\n", "created", tmp_buff);
1778 /* only a directory */
1779 if(strlen((const char *)start) == 0)
1783 printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
1786 /* If the entry was just a directory, don't write to file, etc */
1787 if(strlen((const char *)start) == 0)
1793 if(f_fd != -1 && handle){
1794 f_fd = open((const char *)filename,
1795 O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
1798 fprintf(stderr, "Error extracting JAR archive!\n");
1799 perror((const char *)filename);
1804 if(method != 8 && flags & 0x0008){
1805 fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
1809 if(method == 8 || flags & 0x0008){
1810 consume(&pbf, eflen);
1812 inflate_file(&pbf, f_fd, &ze);
1816 printf("writing stored data.. (%d bytes)\n", csize);
1822 ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
1824 while(out_a < (int)csize){
1825 rdamt = (in_a > RDSZ ? RDSZ : in_a);
1826 if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
1831 ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
1834 write(f_fd, rd_buff, rdamt);
1840 printf("%d bytes written\n", out_a);
1844 consume(&pbf, eflen);
1847 /* if there is a data descriptor left, compare the CRC */
1850 if(pb_read(&pbf, scratch, 16) != 16){
1855 signature = UNPACK_UB4(scratch, 0);
1857 if(signature != 0x08074b50){
1858 fprintf(stderr, "Error! Missing data descriptor!\n");
1862 crc = UNPACK_UB4(scratch, 4);
1867 fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
1874 if(verbose && dir == FALSE && handle)
1875 printf("%10s: %s\n",
1876 (method == 8 ? "inflated" : "extracted"),
1883 int list_jar(int fd, char **files, int file_num){
1895 ub1 *filename = NULL;
1898 int filename_len = 0;
1903 char ascii_date[31];
1907 printf("Listing jar file, looking for %d files\n", file_num);
1910 /* This should be the start of the central-header-end section */
1912 if(lseek(fd, -22, SEEK_END) == (off_t)-1){
1917 if(read(fd, &tmp, sizeof(ub4)) != 4){
1922 #ifdef WORDS_BIGENDIAN
1926 if(tmp != 0x06054b50){
1927 fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
1931 if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
1936 if(read(fd, &cen_size, 2) != 2){
1941 #ifdef WORDS_BIGENDIAN
1942 cen_size = L2BS(cen_size);
1945 /* printf("%hu entries in central header\n", cen_size); */
1947 if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
1952 if(read(fd, &tmp, 4) != 4){
1957 #ifdef WORDS_BIGENDIAN
1961 /* printf("Central header offset = %d\n", tmp); */
1963 if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
1968 /* Loop through the entries in the central header */
1969 for(i = 0; i < cen_size; i++){
1971 if(read(fd, &cen_header, 46) != 46){
1976 signature = UNPACK_UB4(cen_header, 0);
1977 if(signature != 0x02014b50){
1978 fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
1982 usize = UNPACK_UB4(cen_header, CEN_USIZE);
1983 fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
1984 eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
1985 clen = UNPACK_UB2(cen_header, CEN_COMLEN);
1987 /* If we're providing verbose output, we need to make an ASCII
1988 * formatted version of the date. */
1990 mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
1991 tdate = dos2unixtime(mdate);
1992 s_tm = localtime(&tdate);
1993 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
1994 ascii_date[30] = '\0';
1997 if(filename_len < fnlen + 1){
1998 if(filename != NULL)
2001 filename = malloc(sizeof(ub1) * (fnlen + 1));
2002 filename_len = fnlen + 1;
2005 if(read(fd, filename, fnlen) != fnlen){
2009 filename[fnlen] = '\0';
2011 /* if the user specified a list of files on the command line,
2012 we'll only display those, otherwise we'll display everything */
2014 for(j = 0; j < file_num; j++)
2015 if(strcmp(files[j], (const char *)filename) == 0){
2017 printf("%6d %s %s\n", usize, ascii_date, filename);
2019 printf("%s\n", filename);
2024 printf("%6d %s %s\n", usize, ascii_date, filename);
2026 printf("%s\n", filename);
2029 size = eflen + clen;
2031 if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
2038 /* the file isn't seekable.. evil! */
2046 if(pb_read(&pbf, scratch, 4) != 4){
2051 signature = UNPACK_UB4(scratch, 0);
2054 printf("signature is %x\n", signature);
2057 if(signature == 0x08074b50){
2059 printf("skipping data descriptor\n");
2061 pb_read(&pbf, scratch, 12);
2063 } else if(signature == 0x02014b50){
2065 printf("Central header reached.. we're all done!\n");
2068 }else if(signature != 0x04034b50){
2070 printf("Ick! %#x\n", signature);
2075 if(pb_read(&pbf, (file_header + 4), 26) != 26){
2080 csize = UNPACK_UB4(file_header, LOC_CSIZE);
2082 printf("Compressed size is %u\n", csize);
2085 fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
2087 printf("Filename length is %hu\n", fnlen);
2090 eflen = UNPACK_UB2(file_header, LOC_EFLEN);
2092 printf("Extra field length is %hu\n", eflen);
2095 method = UNPACK_UB2(file_header, LOC_COMP);
2097 printf("Compression method is %#hx\n", method);
2100 flags = UNPACK_UB2(file_header, LOC_EXTRA);
2102 printf("Flags are %#hx\n", flags);
2105 usize = UNPACK_UB4(file_header, LOC_USIZE);
2107 /* If we're providing verbose output, we need to make an ASCII
2108 * formatted version of the date. */
2110 mdate = UNPACK_UB4(file_header, LOC_MODTIME);
2111 tdate = dos2unixtime(mdate);
2112 s_tm = localtime(&tdate);
2113 strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
2116 if(filename_len < fnlen + 1){
2117 if(filename != NULL)
2120 filename = malloc(sizeof(ub1) * (fnlen + 1));
2121 ascii_date[30] = '\0';
2122 filename_len = fnlen + 1;
2125 pb_read(&pbf, filename, fnlen);
2126 filename[fnlen] = '\0';
2128 /* the header is at the end. In a JAR file, this means that the data
2129 happens to be compressed. We have no choice but to inflate the
2136 consume(&pbf, size);
2140 printf("inflating %s\n", filename);
2142 inflate_file(&pbf, -1, &ze);
2146 printf("We're shit outta luck!\n");
2149 size = csize + (eflen > 0 ? eflen : 0);
2153 printf("Skipping %ld bytes\n", (long)size);
2156 consume(&pbf, size);
2158 /* print out the listing */
2160 for(j = 0; j < file_num; j++)
2161 if(strcmp(files[j], (const char *)filename) == 0){
2163 printf("%6d %s %s\n", usize, ascii_date, filename);
2165 printf("%s\n", filename);
2170 printf("%6d %s %s\n", usize, ascii_date, filename);
2172 printf("%s\n", filename);
2179 int consume(pb_file *pbf, int amt){
2180 int tc = 0; /* total amount consumed */
2185 printf("Consuming %d bytes\n", amt);
2189 if (amt <= (int)pbf->buff_amt)
2190 pb_read(pbf, buff, amt);
2192 lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
2193 pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
2197 rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
2199 printf("got %d bytes\n", rdamt);
2205 printf("%d bytes consumed\n", amt);
2211 void usage(const char *filename){
2212 fprintf(stderr, "Try `%s --help' for more information.\n", filename);
2218 printf("jar (%s) %s\n\n", PACKAGE, VERSION);
2219 printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
2220 printf("Copyright 2002, 2004 Free Software Foundation\n");
2222 This is free software; see the source for copying conditions. There is NO\n\
2223 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
2227 void help(const char *filename)
2230 Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
2232 Store many files together in a single `jar' file.\n\
2234 -c create new archive\n\
2235 -t list table of contents for archive\n\
2236 -x extract named (or all) files from archive\n\
2237 -u update existing archive\n\
2240 -@ read names from stdin\n\
2241 -0 store only; use no ZIP compression\n\
2242 -C DIR FILE change to the specified directory and include\n\
2243 the following file\n\
2244 -E don't include the files found in a directory\n\
2245 -f FILE specify archive file name\n\
2246 --help print this help, then exit\n\
2247 -m FILE include manifest information from specified manifest file\n\
2248 -M Do not create a manifest file for the entries\n\
2249 -v generate verbose output on standard output\n\
2250 -V, --version display version information\n\
2253 If any file is a directory then it is processed recursively.\n\
2254 The manifest file name and the archive file name needs to be specified\n\
2255 in the same order the 'm' and 'f' flags are specified.\n\
2257 Example 1: to archive two class files into an archive called classes.jar: \n\
2258 jar cvf classes.jar Foo.class Bar.class \n\
2259 Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
2260 files in the foo/ directory into 'classes.jar': \n\
2261 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)