/*
jartool.c - main functions for fastjar utility
- Copyright (C) 1999, 2000 Bryan Burns
+ Copyright (C) 2002, 2004 Free Software Foundation
+ Copyright (C) 1999, 2000, 2001 Bryan Burns
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-/* $Id: jartool.c,v 1.3 2000/12/14 18:45:35 ghazi Exp $
+/*
+ Revision 1.10 2002/01/03 04:57:56 rodrigc
+ 2001-01-02 Craig Rodrigues <rodrigc@gcc.gnu.org>
+
+ PR bootstrap/5117
+ * configure.in (AC_CHECK_HEADERS): Check for stdlib.h.
+ * Makefile.am: Move grepjar to bin_PROGRAMS.
+ * config.h.in: Regenerated.
+ * Makefile.in: Regenerated.
+ * aclocal.m4: Regenerated.
+ * jargrep.c: Eliminate some signed/unsigned and default
+ uninitialized warnings. Use HAVE_STDLIB_H instead of
+ STDC_HEADERS macro.
+ * jartool.c: Likewise.
+ * compress.c: Likewise.
+
+ Revision 1.9 2001/10/12 00:49:42 bryce
+ * jatool.c (extract_jar): Account for null termination when
+ determining whether to expand "filename".
+
+ Revision 1.8 2001/08/29 01:35:31 apbianco
+ 2001-08-28 Alexandre Petit-Bianco <apbianco@redhat.com>
+
+ * jartool.c (add_to_jar): Return 1 if `stat' initialy failed.
+ Fixes PR java/3949.
+
+ (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg01641.html)
+
+ Revision 1.7 2001/08/27 23:09:37 tromey
+ * jartool.c (jarfile): Remove length limitation.
+ (main): Use jt_strdup when initializing jarfile.
+
+ Revision 1.6 2001/07/04 18:33:53 tromey
+ Modified from patch by Julian Hall <jules@acris.co.uk>:
+ * jartool.c (errno): Conditionally declare.
+ (O_BINARY): Conditionally define.
+ (main): Use open, not creat. Use O_BINARY everywhere.
+ (make_manifest): Use O_BINARY.
+ (add_to_jar): Likewise.
+
+ Revision 1.5 2001/05/03 21:40:47 danglin
+ * jartool.c (jt_strdup): New function.
+ (get_next_arg): Use jt_strdup instead of strdup.
+
+ Revision 1.4 2000/12/28 21:47:37 robertl
+ 2000-12-28 Robert Lipe <robertl@sco.com>
+
+ * jartool.c (MAXPATHLEN): Provide if not defined.
- $Log: jartool.c,v $
Revision 1.3 2000/12/14 18:45:35 ghazi
Warning fixes:
#include <zlib.h>
-#ifdef STDC_HEADERS
+#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <time.h>
#endif
+#include <getopt.h>
+
#include "jartool.h"
#include "zipfile.h"
#include "dostime.h"
#include "pushback.h"
#include "compress.h"
+#include "shift.h"
+
+/* Some systems have mkdir that takes a single argument. */
+#ifdef MKDIR_TAKES_ONE_ARG
+# define mkdir(a,b) mkdir(a)
+#endif
+
#ifdef WORDS_BIGENDIAN
#endif
-static char version_string[] = VERSION;
-
+#ifndef errno
extern int errno;
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
void usage(const char*);
+void help(const char *);
+void version(void);
void add_entry(struct zipentry *);
void init_headers(void);
int consume(pb_file *, int);
int list_jar(int, char**, int);
int extract_jar(int, char**, int);
-int add_file_to_jar(int, int, const char*, struct stat*);
-int add_to_jar(int, const char*, const char*);
+int add_file_to_jar(int, int, const char*, struct stat*, int);
+int add_to_jar(int, const char*, int);
+int add_to_jar_with_dir(int, const char*, const char*, int);
int create_central_header(int);
-int make_manifest(int, const char*);
+int make_manifest(int, const char*, int);
+int read_entries (int);
static void init_args(char **, int);
static char *get_next_arg (void);
+static char *jt_strdup (char*);
+static void expand_options (int *argcp, char ***argvp);
+static struct zipentry *find_entry (const char *);
+static int looks_like_dir (const char *);
/* global variables */
ub1 file_header[30];
int do_compress;
int seekable;
int verbose;
-char jarfile[256];
+char *jarfile;
/* If non zero, then don't recurse in directory. Instead, add the
directory entry and relie on an explicit list of files to populate
int number_of_entries; /* number of entries in the linked list */
+/* What we go by. */
+const char *progname;
+
+/* The offset of the end of the last zip entry. */
+ub4 end_of_entries;
+
+/* This is used to mark options with no short value. */
+#define LONG_OPT(Num) ((Num) + 128)
+
+#define OPT_HELP LONG_OPT (0)
+
+/* This holds all options. */
+#define OPTION_STRING "-ctxuvVf:m:C:0ME@"
+
+/* Define the MANIFEST content here to have it easier with calculations
+ below. This is for the case we create an empty MANIFEST.MF. */
+#define MANIFEST_STR "Manifest-Version: 1.0\nCreated-By: "
+#define MANIFEST_END "\n\n"
+
+static const struct option options[] =
+{
+ { "help", no_argument, NULL, OPT_HELP },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, no_argument, NULL, 0 }
+};
+
int main(int argc, char **argv){
- char mfile[256];
+ char *mfile = NULL;
int action = ACTION_NONE;
int manifest = TRUE;
- int manifest_file = FALSE;
- int file = FALSE;
- int file_first = FALSE;
+ int opt;
- int i, j;
int jarfd = -1;
+ /* These are used to collect file names and `-C' options for the
+ second pass through the command line. */
+ int new_argc;
+ char **new_argv;
+
+ progname = argv[0];
+
do_compress = TRUE;
verbose = FALSE;
if(argc < 2)
usage(argv[0]);
- j = strlen(argv[1]);
-
- for(i = 0; i < j; i++){
- switch(argv[1][i]){
+ new_argc = 0;
+ new_argv = (char **) malloc (argc * sizeof (char *));
+
+ expand_options (&argc, &argv);
+ while ((opt = getopt_long (argc, argv, OPTION_STRING,
+ options, NULL)) != -1) {
+ switch(opt){
+ case 'C':
+ new_argv[new_argc++] = (char *) "-C";
+ /* ... fall through ... */
+ case 1:
+ /* File name or unparsed option, due to RETURN_IN_ORDER. */
+ new_argv[new_argc++] = optarg;
+ break;
case 'c':
action = ACTION_CREATE;
break;
verbose = TRUE;
break;
case 'V':
- printf("%s\n", version_string);
+ version();
exit(0);
case 'f':
- file = TRUE;
- if(!manifest_file)
- file_first = TRUE;
- else
- file_first = FALSE;
+ jarfile = optarg;
break;
case 'm':
- manifest_file = TRUE;
+ mfile = optarg;
break;
case '0':
do_compress = FALSE;
case 'M':
manifest = FALSE;
break;
- case '-':
+
+ case OPT_HELP:
+ help(argv[0]);
break;
+
/* The following options aren't supported by the original jar tool. */
case 'E':
use_explicit_list_only = TRUE;
read_names_from_stdin = TRUE;
break;
default:
- fprintf(stderr, "Illegal option: %c\n", argv[1][i]);
usage(argv[0]);
}
}
+ /* We might have seen `--'. In this case we want to make sure that
+ all following options are handled as file names. */
+ while (optind < argc)
+ new_argv[new_argc++] = argv[optind++];
+ new_argv[new_argc] = NULL;
+
if(action == ACTION_NONE){
- fprintf(stderr, "One of options -{ctxu} must be specified.\n");
+ fprintf(stderr, "%s: one of options -{ctxu} must be specified.\n",
+ progname);
usage(argv[0]);
}
fprintf (stderr, "Warning: using non standard '-@' option\n");
if(read_names_from_stdin
&& (action != ACTION_CREATE && action != ACTION_UPDATE)){
- fprintf(stderr, "Option '-@' is supported only with '-c' or '-u'.\n");
- usage(argv[0]);
- }
-
- i = 2;
-
- /* get the jarfile and manifest file (if any) */
- if(file && file_first){
- if(i >= argc)
+ fprintf(stderr, "%s: option '-@' is supported only with '-c' or '-u'.\n",
+ progname);
usage(argv[0]);
-
- strncpy(jarfile, argv[i++], 256);
- }
- if(manifest_file){
- if(i >= argc)
- usage(argv[0]);
-
- strncpy(mfile, argv[i++], 256);
- }
-
- if(file && !file_first){
- if(i >= argc)
- usage(argv[0]);
-
- strncpy(jarfile, argv[i++], 256);
}
/* create the jarfile */
if(action == ACTION_CREATE){
- if(file){
- jarfd = creat(jarfile, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
+ if(jarfile){
+ jarfd = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC, 0666);
+
if(jarfd < 0){
- fprintf(stderr, "Error opening %s for writing!\n", jarfile);
- perror(jarfile);
+ fprintf(stderr, "%s: error opening %s for writing: %s\n", progname,
+ jarfile, strerror (errno));
exit(1);
}
}
} else if(action == ACTION_LIST || action == ACTION_EXTRACT){
- if(file){
- jarfd = open(jarfile, O_RDONLY);
+ if(jarfile){
+ jarfd = open(jarfile, O_RDONLY | O_BINARY);
if(jarfd < 0){
- fprintf(stderr, "Error opening %s for reading!\n", jarfile);
- perror(jarfile);
+ fprintf(stderr, "%s: error opening %s for reading: %s\n", progname,
+ jarfile, strerror (errno));
exit(1);
}
}
}
+ if (action == ACTION_UPDATE)
+ {
+ if (!jarfile)
+ {
+ fprintf (stderr, "%s: `-u' mode requires a file name\n",
+ argv[0]);
+ exit (1);
+ }
+
+ if ((jarfd = open (jarfile, O_RDWR | O_BINARY)) < 0)
+ {
+ fprintf (stderr, "Error opening %s for reading!\n", jarfile);
+ perror (jarfile);
+ exit (1);
+ }
+
+ /* Assert that jarfd is seekable. */
+ if (lseek (jarfd, 0, SEEK_CUR) == -1)
+ {
+ fprintf (stderr, "%s: %s is not seekable\n", argv[0], jarfile);
+ exit (1);
+ }
+
+ seekable = TRUE;
+ }
+
if(action == ACTION_CREATE || action == ACTION_UPDATE){
const char *arg;
init_headers();
- if((action == ACTION_UPDATE) && file) {
- if((jarfd = open(jarfile, O_RDWR)) < 0) {
- fprintf(stderr, "Error opening %s for reading!\n", jarfile);
- perror(jarfile);
- exit(1);
- }
- }
-
if(do_compress)
init_compression();
-
+
+ if (action == ACTION_UPDATE)
+ {
+ if (read_entries (jarfd))
+ exit (1);
+ }
/* Add the META-INF/ directory and the manifest */
- if(manifest && manifest_file)
- make_manifest(jarfd, mfile);
- else if(manifest)
- make_manifest(jarfd, NULL);
-
- init_args (argv, i);
+ if(manifest && mfile)
+ make_manifest(jarfd, mfile, action == ACTION_UPDATE);
+ else if(manifest && action == ACTION_CREATE)
+ make_manifest(jarfd, NULL, FALSE);
+
+ init_args (new_argv, 0);
/* now we add the files to the archive */
while ((arg = get_next_arg ())){
if(!strcmp(arg, "-C")){
const char *dir_to_change = get_next_arg ();
const char *file_to_add = get_next_arg ();
- if(!dir_to_change
- || !file_to_add
- || add_to_jar(jarfd, dir_to_change, file_to_add)){
- printf("Error adding %s to jar archive!\n", arg);
+ if (!dir_to_change || !file_to_add) {
+ fprintf(stderr, "%s: error: missing argument for -C.\n", progname);
exit(1);
}
+ if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add,
+ action == ACTION_UPDATE))
+ {
+ fprintf(stderr,
+ "Error adding %s (in directory %s) to jar archive!\n",
+ file_to_add, dir_to_change);
+ exit(1);
+ }
} else {
- if(add_to_jar(jarfd, NULL, arg)){
- printf("Error adding %s to jar archive!\n", arg);
+ if(add_to_jar(jarfd, arg, action == ACTION_UPDATE)){
+ fprintf(stderr, "Error adding %s to jar archive!\n", arg);
exit(1);
}
}
/* de-initialize the compression DS */
if(do_compress)
end_compression();
+
+ if (action == ACTION_UPDATE)
+ lseek (jarfd, end_of_entries, SEEK_SET);
create_central_header(jarfd);
-
- if (close(jarfd) != 0) {
- fprintf(stderr, "Error closing jar archive!\n");
+
+#if ! (HAVE_FTRUNCATE || HAVE__CHSIZE)
+ #error neither ftruncate() or _chsize() available
+#endif
+ /* Check if the file shrunk when we updated it. */
+ if (action == ACTION_UPDATE)
+#if HAVE_FTRUNCATE
+ ftruncate (jarfd, lseek (jarfd, 0, SEEK_CUR));
+#else
+ _chsize (jarfd, lseek (jarfd, 0, SEEK_CUR));
+#endif
+
+ if (jarfd != STDIN_FILENO && close(jarfd) != 0) {
+ fprintf(stderr, "%s: error closing jar archive: %s\n",
+ progname, strerror (errno));
+ exit (1);
}
} else if(action == ACTION_LIST){
- list_jar(jarfd, &argv[i], (argc - i));
+ list_jar(jarfd, &new_argv[0], new_argc);
} else if(action == ACTION_EXTRACT){
- extract_jar(jarfd, &argv[i], (argc - i));
+ extract_jar(jarfd, &new_argv[0], new_argc);
}
exit(0);
if (pos)
{
s [pos] = '\0';
- return strdup (s);
+ return jt_strdup (s);
}
else
return NULL;
number_of_entries++;
}
-int make_manifest(int jfd, const char *mf_name){
+static struct zipentry *
+find_entry (const char *fname)
+{
+ struct zipentry *ze;
+
+ for (ze = ziptail; ze; ze = ze->next_entry)
+ {
+ if (!strcmp (ze->filename, fname))
+ return ze;
+ }
+ return NULL;
+}
+
+
+static int
+looks_like_dir (const char *fname)
+{
+ struct zipentry *ze;
+ size_t len = strlen (fname);
+
+ for (ze = ziptail; ze; ze = ze->next_entry)
+ {
+ if (strlen (ze->filename) > len
+ && !strncmp (fname, ze->filename, len)
+ && ze->filename[len] == '/')
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * Read the zip entries of an existing file, building `ziplist' as we go.
+ */
+int read_entries (int fd)
+{
+ struct zipentry *ze;
+ ub1 intbuf[4];
+ ub1 header[46];
+ ub2 len;
+ ub2 count, i;
+ off_t offset;
+
+ if (lseek (fd, -22, SEEK_END) == -1)
+ {
+ fprintf (stderr, "%s: %s: can't seek file\n", progname, jarfile);
+ return 1;
+ }
+
+ if (read (fd, intbuf, 4) < 4)
+ {
+ perror (progname);
+ return 1;
+ }
+ /* Is there a zipfile comment? */
+ while (UNPACK_UB4(intbuf, 0) != 0x06054b50)
+ {
+ if (lseek (fd, -5, SEEK_CUR) == -1 ||
+ read (fd, intbuf, 4) != 4)
+ {
+ fprintf (stderr, "%s: can't find end of central directory: %s\n",
+ progname, strerror (errno));
+ return 1;
+ }
+ }
+
+ /* Skip disk numbers. */
+ if (lseek (fd, 6, SEEK_CUR) == -1)
+ {
+ perror (progname);
+ return 1;
+ }
+
+ /* Number of entries in the central directory. */
+ if (read (fd, intbuf, 2) != 2)
+ {
+ perror (progname);
+ return 1;
+ }
+ count = UNPACK_UB2(intbuf, 0);
+
+ if (lseek (fd, 4, SEEK_CUR) == -1)
+ {
+ perror (progname);
+ return 1;
+ }
+
+ /* Offset where the central directory begins. */
+ if (read (fd, intbuf, 4) != 4)
+ {
+ perror (progname);
+ return 1;
+ }
+ offset = UNPACK_UB4(intbuf, 0);
+ end_of_entries = offset;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ {
+ perror (progname);
+ return 1;
+ }
+
+ if (read (fd, header, 46) != 46)
+ {
+ fprintf (stderr, "%s: %s: unexpected end of file\n",
+ progname, jarfile);
+ return 1;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ if (UNPACK_UB4(header, 0) != 0x02014b50)
+ {
+ fprintf (stderr, "%s: can't find central directory header\n",
+ progname);
+ return 1;
+ }
+ ze = (struct zipentry *) malloc (sizeof (struct zipentry));
+ if (!ze)
+ {
+ perror (progname);
+ return 1;
+ }
+ memset (ze, 0, sizeof (struct zipentry));
+ ze->flags = UNPACK_UB2(header, CEN_FLAGS);
+ ze->mod_time = UNPACK_UB2(header, CEN_MODTIME);
+ ze->mod_date = UNPACK_UB2(header, CEN_MODDATE);
+ ze->crc = UNPACK_UB4(header, CEN_CRC);
+ ze->usize = UNPACK_UB4(header, CEN_USIZE);
+ ze->csize = UNPACK_UB4(header, CEN_CSIZE);
+ ze->offset = UNPACK_UB4(header, CEN_OFFSET);
+ ze->compressed = (header[CEN_COMP] || header[CEN_COMP+1]);
+ len = UNPACK_UB2(header, CEN_FNLEN);
+ ze->filename = (char *) malloc ((len+1) * sizeof (char));
+ if (!ze->filename)
+ {
+ perror (progname);
+ return 1;
+ }
+ if (read (fd, ze->filename, len) != len)
+ {
+ fprintf (stderr, "%s: %s: unexpected end of file\n",
+ progname, jarfile);
+ return 1;
+ }
+ len = UNPACK_UB4(header, CEN_EFLEN);
+ len += UNPACK_UB4(header, CEN_COMLEN);
+ if (lseek (fd, len, SEEK_CUR) == -1)
+ {
+ perror (progname);
+ return 1;
+ }
+ add_entry (ze);
+ if (i < count - 1)
+ {
+ if (read (fd, header, 46) != 46)
+ {
+ fprintf (stderr, "%s: %s: unexpected end of file\n",
+ progname, jarfile);
+ return 1;
+ }
+ }
+ }
+
+ lseek (fd, 0, SEEK_SET);
+ return 0;
+}
+
+int make_manifest(int jfd, const char *mf_name, int updating){
time_t current_time;
int nlen; /* length of file name */
int mod_time; /* file modification time */
/* if the user didn't specify an external manifest file... */
if(mf_name == NULL){
- int mf_len = 37 + strlen(VERSION);
+
+ int mf_len = strlen(MANIFEST_STR) + strlen(VERSION) + strlen(MANIFEST_END);
char *mf;
if((mf = (char *) malloc(mf_len + 1))) {
uLong crc;
- sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
+ sprintf(mf, "%s%s%s", MANIFEST_STR, VERSION, MANIFEST_END);
crc = crc32(0L, Z_NULL, 0);
exit(1);
}
- mfd = open(mf_name, O_RDONLY);
+ mfd = open(mf_name, O_RDONLY | O_BINARY);
if(mfd < 0){
fprintf(stderr, "Error opening %s.\n", mf_name);
exit(1);
}
- if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf)){
+ if(add_file_to_jar(jfd, mfd, "META-INF/MANIFEST.MF", &statbuf, updating)){
perror("error writing to jar");
exit(1);
}
return 0;
}
-int add_to_jar(int fd, const char *new_dir, const char *file){
+/* Implements -C by wrapping add_to_jar. new_dir is the directory
+ to switch to.
+
+ `updating', if nonzero, will indicate that we are updating an
+ existing file, and will need to take special care. If set, we will
+ also expect that the linked list of zip entries will be filled in
+ with the jar file's current contents.
+ */
+int
+add_to_jar_with_dir (int fd, const char* new_dir, const char* file,
+ const int updating)
+{
+ int retval;
+ char old_dir[MAXPATHLEN];
+ if (getcwd(old_dir, MAXPATHLEN) == NULL) {
+ perror("getcwd");
+ return 1;
+ }
+ if (chdir(new_dir) == -1) {
+ perror(new_dir);
+ return 1;
+ }
+ retval=add_to_jar(fd, file, updating);
+ if (chdir(old_dir) == -1) {
+ perror(old_dir);
+ return 1;
+ }
+ return retval;
+}
+
+int
+add_to_jar (int fd, const char *file, const int updating)
+{
struct stat statbuf;
DIR *dir;
struct dirent *de;
zipentry *ze;
+ zipentry *existing = NULL;
int stat_return;
- char *old_dir = NULL;
-
+
/* This is a quick compatibility fix -- Simon Weijgers <simon@weijgers.com>
* It fixes this:
* "normal" jar : org/apache/java/io/LogRecord.class
while (*file=='.' && *(file+1)=='/')
file+=2;
- /* If new_dir isn't null, we need to change to that directory. However,
- we also need to return to the old directory when we're done */
- if(new_dir != NULL){
- old_dir = getcwd(NULL, 0);
-
- if(chdir(new_dir) == -1){
- perror(new_dir);
- return 1;
- }
- }
-
- if(!strcmp(file, jarfile)){
+ if(jarfile && !strcmp(file, jarfile)){
if(verbose)
printf("skipping: %s\n", file);
return 0; /* we don't want to add ourselves.. */
if(stat_return == -1){
perror(file);
+ return 1;
} else if(S_ISDIR(statbuf.st_mode)){
char *fullname;
char *t_ptr;
PACK_UB2(file_header, LOC_FNLEN, nlen);
PACK_UB4(file_header, LOC_MODTIME, mod_time);
- if(verbose)
- printf("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
-
ze = (zipentry*)malloc(sizeof(zipentry));
if(ze == NULL){
perror("malloc");
ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
ze->compressed = FALSE;
- add_entry(ze);
+ if (updating)
+ {
+ if ((existing = find_entry (ze->filename)) != NULL)
+ {
+ if (existing->usize != 0)
+ {
+ /* XXX overwriting non-directory with directory? */
+ fprintf (stderr, "%s: %s: can't overwrite non-directory with directory\n",
+ progname, fullname);
+ return 1;
+ }
+ }
+ if (lseek (fd, end_of_entries, SEEK_SET) == -1)
+ {
+ fprintf (stderr, "%s %d\n", __FILE__, __LINE__);
+ perror ("lseek");
+ return 1;
+ }
+ }
- write(fd, file_header, 30);
- write(fd, fullname, nlen);
+ if (!existing)
+ {
+ add_entry (ze);
+ write (fd, file_header, 30);
+ write (fd, fullname, nlen);
+ end_of_entries = lseek (fd, 0, SEEK_CUR);
+
+ if (verbose)
+ printf ("adding: %s (in=%d) (out=%d) (stored 0%%)\n", fullname, 0, 0);
+ }
while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
if(de->d_name[0] == '.')
continue;
- if(!strcmp(de->d_name, jarfile)){ /* we don't want to add ourselves. Believe me */
+ if(jarfile && !strcmp(de->d_name, jarfile)){
+ /* we don't want to add ourselves. Believe me */
if(verbose)
printf("skipping: %s\n", de->d_name);
continue;
strcpy(t_ptr, de->d_name);
- if(add_to_jar(fd, NULL, fullname)){
+ if (add_to_jar(fd, fullname, updating)) {
fprintf(stderr, "Error adding file to jar!\n");
return 1;
}
} else if(S_ISREG(statbuf.st_mode)){
int add_fd;
- add_fd = open(file, O_RDONLY);
+ add_fd = open(file, O_RDONLY | O_BINARY);
if(add_fd < 0){
fprintf(stderr, "Error opening %s.\n", file);
- return 0;
+ return 1;
}
- if(add_file_to_jar(fd, add_fd, file, &statbuf)){
+ if(add_file_to_jar(fd, add_fd, file, &statbuf, updating)){
fprintf(stderr, "Error adding file to jar!\n");
return 1;
}
} else {
fprintf(stderr, "Illegal file specified: %s\n", file);
}
-
- if(old_dir != NULL){
- if(chdir(old_dir))
- perror(old_dir);
-
- free(old_dir);
- }
-
return 0;
}
-int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf){
-
+int add_file_to_jar(int jfd, int ffd, const char *fname, struct stat *statbuf,
+ const int updating)
+{
unsigned short file_name_length;
unsigned long mod_time;
ub1 rd_buff[RDSZ];
off_t offset = 0;
int rdamt;
struct zipentry *ze;
+ struct zipentry *existing = NULL;
+
+ if (updating)
+ {
+ existing = find_entry (fname);
+ if (existing && looks_like_dir (fname))
+ {
+ fprintf (stderr, "%s: %s is a directory in the archive\n",
+ progname, fname);
+ return 1;
+ }
+ }
mod_time = unix2dostime(&(statbuf->st_mtime));
file_name_length = strlen(fname);
ze->csize = statbuf->st_size;
ze->usize = ze->csize;
- ze->offset = lseek(jfd, 0, SEEK_CUR);
+
+ if (existing)
+ ze->offset = existing->offset;
+ else if (updating)
+ ze->offset = end_of_entries;
+ else
+ ze->offset = lseek(jfd, 0, SEEK_CUR);
+
if(do_compress)
ze->compressed = TRUE;
else
ze->compressed = FALSE;
-
- add_entry(ze);
+
+ if (!existing)
+ add_entry(ze);
+ if (updating && lseek (jfd, ze->offset, SEEK_SET) < 0)
+ {
+ perror ("lseek");
+ return 1;
+ }
+
+ /* We can safely write the header here, since it will be the same size
+ as before */
/* Write the local header */
write(jfd, file_header, 30);
if(verbose){
- printf("adding: %s ", fname);
+ if (existing)
+ printf ("updating: %s ", fname);
+ else
+ printf("adding: %s ", fname);
fflush(stdout);
}
if(do_compress){
/* compress the file */
- compress_file(ffd, jfd, ze);
+ compress_file(ffd, jfd, ze, existing);
} else {
+ /* If we are not writing the last entry, make space for it. */
+ if (existing && existing->next_entry)
+ {
+ if (ze->usize > existing->usize)
+ {
+ if (shift_down (jfd, existing->next_entry->offset,
+ ze->usize - existing->usize, existing->next_entry))
+ {
+ fprintf (stderr, "%s: %s\n", progname, strerror (errno));
+ return 1;
+ }
+ }
+ }
+
/* Write the contents of the file (uncompressed) to the zip file */
/* calculate the CRC as we go along */
ze->crc = crc32(0L, Z_NULL, 0);
/* Sun's jar tool will only allow a data descriptor if the entry is
compressed, but we'll save 16 bytes/entry if we only use it when
we can't seek back on the file */
+ /* Technically, you CAN'T have a data descriptor unless the data
+ part has an obvious end, which DEFLATED does. Otherwise, there
+ would not be any way to determine where the data descriptor is.
+ Store an uncompressed file that ends with 0x504b0708, and see.
+ -- csm */
if(write(jfd, data_descriptor, 16) != 16){
perror("write");
return 0;
}
}
+
+ if (existing)
+ {
+ int dd = (existing->flags & (1 << 3)) ? 12 : 0;
+ if (existing->next_entry && ze->csize < existing->csize + dd)
+ {
+ if (shift_up (jfd, existing->next_entry->offset,
+ existing->csize + dd - ze->csize,
+ existing->next_entry))
+ {
+ perror (progname);
+ return 1;
+ }
+ }
+ /* Replace the existing entry data with this entry's. */
+ existing->csize = ze->csize;
+ existing->usize = ze->usize;
+ existing->crc = ze->crc;
+ existing->mod_time = ze->mod_time;
+ existing->mod_date = ze->mod_date;
+ free (ze->filename);
+ free (ze);
+ }
+ else if (updating)
+ end_of_entries = lseek (jfd, 0, SEEK_CUR);
if(verbose)
printf("(in=%d) (out=%d) (%s %d%%)\n",
ub1 end_header[22];
int start_offset;
int dir_size;
- int *iheader;
int total_in = 0, total_out = 22;
zipentry *ze;
- iheader = (int*)header;
-
/* magic number */
header[0] = 'P';
header[1] = 'K';
#endif
}
- if(filename_len < fnlen){
+ if(filename_len < fnlen + 1){
if(filename != NULL)
free(filename);
}
if(f_fd != -1 && handle){
- f_fd = creat((const char *)filename, 00644);
+ f_fd = open((const char *)filename,
+ O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if(f_fd < 0){
fprintf(stderr, "Error extracting JAR archive!\n");
exit(1);
}
+ if (eflen > 0)
+ consume(&pbf, eflen);
+
if(method == 8 || flags & 0x0008){
- if(seekable)
- lseek(fd, eflen, SEEK_CUR);
- else
- consume(&pbf, eflen);
inflate_file(&pbf, f_fd, &ze);
} else {
ze.crc = crc32(ze.crc, NULL, 0); /* initialize the crc */
- while(out_a < csize){
+ while(out_a < (int)csize){
rdamt = (in_a > RDSZ ? RDSZ : in_a);
if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
perror("read");
printf("%d bytes written\n", out_a);
#endif
}
-
- if(seekable)
- lseek(fd, eflen, SEEK_CUR);
- else
- consume(&pbf, eflen);
}
/* if there is a data descriptor left, compare the CRC */
}
int list_jar(int fd, char **files, int file_num){
- int rdamt;
ub4 signature;
ub4 csize;
ub4 usize;
int i, j;
time_t tdate;
struct tm *s_tm;
- char ascii_date[30];
+ char ascii_date[31];
zipentry ze;
#ifdef DEBUG
/* printf("Central header offset = %d\n", tmp); */
- if(lseek(fd, tmp, SEEK_SET) != tmp){
+ if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
perror("lseek");
exit(1);
}
tdate = dos2unixtime(mdate);
s_tm = localtime(&tdate);
strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
+ ascii_date[30] = '\0';
}
- if(filename_len < fnlen){
+ if(filename_len < fnlen + 1){
if(filename != NULL)
free(filename);
init_inflation();
for(;;){
- if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
+ if(pb_read(&pbf, scratch, 4) != 4){
perror("read");
break;
}
break;
}
- if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
+ if(pb_read(&pbf, (file_header + 4), 26) != 26){
perror("read");
break;
}
strftime(ascii_date, 30, "%a %b %d %H:%M:%S %Z %Y", s_tm);
}
- if(filename_len < fnlen){
+ if(filename_len < fnlen + 1){
if(filename != NULL)
free(filename);
filename = malloc(sizeof(ub1) * (fnlen + 1));
+ ascii_date[30] = '\0';
filename_len = fnlen + 1;
}
printf("Consuming %d bytes\n", amt);
#endif
+ if (seekable){
+ if (amt <= (int)pbf->buff_amt)
+ pb_read(pbf, buff, amt);
+ else {
+ lseek(pbf->fd, amt - pbf->buff_amt, SEEK_CUR);
+ pb_read(pbf, buff, pbf->buff_amt); /* clear pbf */
+ }
+ } else
while(tc < amt){
rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
#ifdef DEBUG
}
#ifdef DEBUG
- printf("%d bytes consumed\n", tc);
+ printf("%d bytes consumed\n", amt);
#endif
return 0;
}
void usage(const char *filename){
- fprintf(stderr, "\
+ fprintf(stderr, "Try `%s --help' for more information.\n", filename);
+ exit (1);
+}
+
+void version ()
+{
+ printf("jar (%s) %s\n\n", PACKAGE, VERSION);
+ printf("Copyright 1999, 2000, 2001 Bryan Burns\n");
+ printf("Copyright 2002, 2004 Free Software Foundation\n");
+ printf("\
+This is free software; see the source for copying conditions. There is NO\n\
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
+ exit (0);
+}
+
+void help(const char *filename)
+{
+ printf("\
Usage: %s {ctxuV}[vfm0ME@] [jar-file] [manifest-file] [-C dir] files ...\n\
-Options\n\
- -c create new archive\n\
- -t list table of contents for archive\n\
- -x extract named (or all) files from archive\n\
+\n\
+Store many files together in a single `jar' file.\n\
+\n\
+ -c create new archive\n\
+ -t list table of contents for archive\n\
+ -x extract named (or all) files from archive\n\
+ -u update existing archive\n\
", filename);
- fprintf(stderr, "\
- -u update existing archive\n\
- -V display version information\n\
- -v generate verbose output on standard output\n\
- -f specify archive file name\n\
- -m include manifest information from specified manifest file\n\
- -0 store only; use no ZIP compression\n\
- -M Do not create a manifest file for the entries\n\
- -C change to the specified directory and include the following file\n\
- -E don't include the files found in a directory\n\
- -@ Read names from stdin\n\
+ printf("\n\
+ -@ read names from stdin\n\
+ -0 store only; use no ZIP compression\n\
+ -C DIR FILE change to the specified directory and include\n\
+ the following file\n\
+ -E don't include the files found in a directory\n\
+ -f FILE specify archive file name\n\
+ --help print this help, then exit\n\
+ -m FILE include manifest information from specified manifest file\n\
+ -M Do not create a manifest file for the entries\n\
+ -v generate verbose output on standard output\n\
+ -V, --version display version information\n\
");
- fprintf(stderr, "\
+ printf("\n\
If any file is a directory then it is processed recursively.\n\
The manifest file name and the archive file name needs to be specified\n\
in the same order the 'm' and 'f' flags are specified.\n\
jar cvfm classes.jar mymanifest -C foo/ .\n\
");
- exit(1);
+ exit(0);
+}
+
+static char *
+jt_strdup(s)
+ char *s;
+{
+ char *result = (char*)malloc(strlen(s) + 1);
+ if (result == (char*)0)
+ return (char*)0;
+ strcpy(result, s);
+ return result;
+}
+
+/* Convert "tar-style" first argument to a form expected by getopt.
+ This idea and the code comes from GNU tar. This can allocate a new
+ argument vector. This might leak some memory, but we don't care. */
+static void
+expand_options (int *argcp, char ***argvp)
+{
+ int argc = *argcp;
+ char **argv = *argvp;
+
+ /* Accept arguments with a leading "-" (eg "-cvf"), but don't do expansion
+ if a long argument (like "--help") is detected. */
+ if (argc > 1 && argv[1][1] != '-')
+ {
+ char buf[3];
+ char **new_argv;
+ int new_argc;
+ int args_to_expand;
+ char *p;
+ char **in, **out;
+
+ buf[0] = '-';
+ buf[2] = '\0';
+
+ args_to_expand = strlen (argv[1]);
+ if (argv[1][0] == '-')
+ --args_to_expand;
+
+ new_argc = argc - 1 + args_to_expand;
+ new_argv = (char **) malloc (new_argc * sizeof (char *));
+ in = argv;
+ out = new_argv;
+
+ *out++ = *in++;
+ p = *in++;
+ if (*p == '-')
+ p++;
+ while (*p != '\0')
+ {
+ char *opt;
+ buf[1] = *p;
+ *out++ = jt_strdup (buf);
+ /* If the option takes an argument, move the next argument
+ to just after this option. */
+ opt = strchr (OPTION_STRING, *p);
+ if (opt && opt[1] == ':')
+ {
+ if (in < argv + argc)
+ *out++ = *in++;
+ else
+ {
+ fprintf(stderr, "%s: option `%s' requires an argument.\n",
+ argv[0], buf);
+ usage(argv[0]);
+ }
+ }
+ ++p;
+ }
+
+ /* Copy remaining options. */
+ while (in < argv + argc)
+ *out++ = *in++;
+
+ *argcp = new_argc;
+ *argvp = new_argv;
+ }
}