/*
jartool.c - main functions for fastjar utility
+ Copyright (C) 2002 Free Software Foundation
Copyright (C) 1999, 2000, 2001 Bryan Burns
This program is free software; you can redistribute it and/or
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-/* $Id: jartool.c,v 1.5 2001/05/03 21:40:47 danglin 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.
- $Log: jartool.c,v $
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.
#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"
+/* Some systems have mkdir that takes a single argument. */
+#ifdef MKDIR_TAKES_ONE_ARG
+# define mkdir(a,b) mkdir(a)
+#endif
+
+
#ifdef WORDS_BIGENDIAN
#define L2BI(l) ((l & 0xff000000) >> 24) | \
#endif
-static char version_string[] = VERSION;
-
#ifndef errno
extern int errno;
#endif
#endif
void usage(const char*);
+void help(const char *);
+void version(void);
void add_entry(struct zipentry *);
void init_headers(void);
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_to_jar(int, const char*);
+int add_to_jar_with_dir(int, const char*, const char*);
int create_central_header(int);
int make_manifest(int, const char*);
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);
/* 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 */
+/* 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;
+
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");
usage(argv[0]);
}
+ if(action == ACTION_UPDATE){
+ fprintf(stderr, "%s: `-u' mode unimplemented.\n", argv[0]);
+ exit(1);
+ }
+
/* Verify unsupported combinations and warn of the use of non
standard features */
if(verbose && use_explicit_list_only)
usage(argv[0]);
}
- i = 2;
-
- /* get the jarfile and manifest file (if any) */
- if(file && file_first){
- if(i >= argc)
- 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 = open(jarfile, O_CREAT | O_BINARY | O_WRONLY | O_TRUNC,
- 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);
}
} else if(action == ACTION_LIST || action == ACTION_EXTRACT){
- if(file){
+ if(jarfile){
jarfd = open(jarfile, O_RDONLY | O_BINARY);
if(jarfd < 0){
const char *arg;
init_headers();
- if((action == ACTION_UPDATE) && file) {
+ if((action == ACTION_UPDATE) && jarfile) {
if((jarfd = open(jarfile, O_RDWR | O_BINARY)) < 0) {
fprintf(stderr, "Error opening %s for reading!\n", jarfile);
perror(jarfile);
/* Add the META-INF/ directory and the manifest */
- if(manifest && manifest_file)
+ if(manifest && mfile)
make_manifest(jarfd, mfile);
else if(manifest)
make_manifest(jarfd, NULL);
- init_args (argv, i);
+ 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, "Error: missing argument for -C.\n");
+ exit(1);
+ }
+ if (add_to_jar_with_dir(jarfd, dir_to_change, file_to_add)) {
+ 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)){
+ fprintf(stderr, "Error adding %s to jar archive!\n", arg);
exit(1);
}
}
fprintf(stderr, "Error closing jar archive!\n");
}
} 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 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);
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. */
+int
+add_to_jar_with_dir (int fd, const char* new_dir, const char* file)
+{
+ 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);
+ if (chdir(old_dir) == -1) {
+ perror(old_dir);
+ return 1;
+ }
+ return retval;
+}
+
+int
+add_to_jar (int fd, const char *file) {
struct stat statbuf;
DIR *dir;
struct dirent *de;
zipentry *ze;
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;
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)) {
fprintf(stderr, "Error adding file to jar!\n");
return 1;
}
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)){
} 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;
}
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");
}
if(method == 8 || flags & 0x0008){
- if(seekable)
- lseek(fd, eflen, SEEK_CUR);
- else
consume(&pbf, eflen);
inflate_file(&pbf, f_fd, &ze);
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");
#endif
}
- if(seekable)
- lseek(fd, eflen, SEEK_CUR);
- else
consume(&pbf, eflen);
}
}
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 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 *
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;
+ }
+}