#include "config.h"
#include <zlib.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <string.h>
#include <errno.h>
#ifdef TM_IN_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#include <getopt.h>
#include "jartool.h"
#include "zipfile.h"
#include "dostime.h"
#include "pushback.h"
#include "compress.h"
#ifdef MKDIR_TAKES_ONE_ARG
# define mkdir(a,b) mkdir(a)
#endif
#ifdef WORDS_BIGENDIAN
#define L2BI(l) ((l & 0xff000000) >> 24) | \
((l & 0x00ff0000) >> 8) | \
((l & 0x0000ff00) << 8) | \
((l & 0x000000ff) << 24);
#define L2BS(l) ((l & 0xff00) >> 8) | ((l & 0x00ff) << 8);
#endif
#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 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);
ub1 file_header[30];
ub1 data_descriptor[16];
int do_compress;
int seekable;
int verbose;
char *jarfile;
int use_explicit_list_only;
int read_names_from_stdin;
zipentry *ziplist;
zipentry *ziptail;
int number_of_entries;
#define LONG_OPT(Num) ((Num) + 128)
#define OPT_HELP LONG_OPT (0)
#define OPTION_STRING "-ctxuvVf:m:C:0ME@"
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 = NULL;
int action = ACTION_NONE;
int manifest = TRUE;
int opt;
int j;
int jarfd = -1;
int new_argc;
char **new_argv;
do_compress = TRUE;
verbose = FALSE;
ziplist = NULL;
number_of_entries = 0;
if(argc < 2)
usage(argv[0]);
j = strlen(argv[1]);
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";
case 1:
new_argv[new_argc++] = optarg;
break;
case 'c':
action = ACTION_CREATE;
break;
case 't':
action = ACTION_LIST;
break;
case 'x':
action = ACTION_EXTRACT;
break;
case 'u':
action = ACTION_UPDATE;
break;
case 'v':
verbose = TRUE;
break;
case 'V':
version();
exit(0);
case 'f':
jarfile = optarg;
break;
case 'm':
mfile = optarg;
break;
case '0':
do_compress = FALSE;
break;
case 'M':
manifest = FALSE;
break;
case OPT_HELP:
help(argv[0]);
break;
case 'E':
use_explicit_list_only = TRUE;
break;
case '@':
read_names_from_stdin = TRUE;
break;
default:
usage(argv[0]);
}
}
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);
}
if(verbose && use_explicit_list_only)
fprintf (stderr, "Warning: using non standard '-E' option\n");
if(verbose && read_names_from_stdin)
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]);
}
if(action == ACTION_CREATE){
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);
exit(1);
}
seekable = TRUE;
} else {
jarfd = STDOUT_FILENO;
seekable = FALSE;
verbose = FALSE;
}
} else if(action == ACTION_LIST || action == ACTION_EXTRACT){
if(jarfile){
jarfd = open(jarfile, O_RDONLY | O_BINARY);
if(jarfd < 0){
fprintf(stderr, "Error opening %s for reading!\n", jarfile);
perror(jarfile);
exit(1);
}
seekable = TRUE;
} else {
jarfd = STDIN_FILENO;
seekable = FALSE;
}
}
if(action == ACTION_CREATE || action == ACTION_UPDATE){
const char *arg;
init_headers();
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);
exit(1);
}
}
if(do_compress)
init_compression();
if(manifest && mfile)
make_manifest(jarfd, mfile);
else if(manifest)
make_manifest(jarfd, NULL);
init_args (new_argv, 0);
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);
exit(1);
}
} else {
if(add_to_jar(jarfd, NULL, arg)){
printf("Error adding %s to jar archive!\n", arg);
exit(1);
}
}
}
if(do_compress)
end_compression();
create_central_header(jarfd);
if (close(jarfd) != 0) {
fprintf(stderr, "Error closing jar archive!\n");
}
} else if(action == ACTION_LIST){
list_jar(jarfd, &new_argv[0], new_argc);
} else if(action == ACTION_EXTRACT){
extract_jar(jarfd, &new_argv[0], new_argc);
}
exit(0);
}
static int args_current_g;
static char **args_g;
static void
init_args(args, current)
char **args;
int current;
{
if(!read_names_from_stdin)
{
args_g = args;
args_current_g = current;
}
}
static char *
get_next_arg ()
{
static int reached_end = 0;
if (reached_end)
return NULL;
if (args_g)
{
if (!args_g [args_current_g])
{
reached_end = 1;
return NULL;
}
return args_g [args_current_g++];
}
else
{
char s [MAXPATHLEN];
int pos = 0;
while (1)
{
int c = getc (stdin);
if (c == '\n' || c == '\r')
continue;
else
{
if (c == EOF)
return NULL;
ungetc (c, stdin);
break;
}
}
while (1)
{
int c = getc (stdin);
if (c == '\n'|| c == '\r'|| c == EOF)
break;
s [pos++] = (char) c;
}
if (pos)
{
s [pos] = '\0';
return jt_strdup (s);
}
else
return NULL;
}
}
void init_headers(){
file_header[0] = 0x50;
file_header[1] = 0x4b;
file_header[2] = 0x03;
file_header[3] = 0x04;
file_header[4] = 10;
file_header[5] = 0;
file_header[6] = 0x00;
file_header[7] = 0x00;
file_header[8] = 0;
file_header[9] = 0;
file_header[10] = 0;
file_header[11] = 0;
file_header[12] = 0;
file_header[13] = 0;
file_header[14] = 0;
file_header[15] = 0;
file_header[16] = 0;
file_header[17] = 0;
file_header[18] = 0;
file_header[19] = 0;
file_header[20] = 0;
file_header[21] = 0;
file_header[22] = 0;
file_header[23] = 0;
file_header[24] = 0;
file_header[25] = 0;
file_header[26] = 0;
file_header[27] = 0;
file_header[28] = 0;
file_header[29] = 0;
PACK_UB4(data_descriptor, 0, 0x08074b50);
}
void add_entry(struct zipentry *ze){
if(ziplist == NULL){
ziplist = ze;
ziptail = ziplist;
} else {
ziplist->next_entry = ze;
ziplist = ze;
}
number_of_entries++;
}
int make_manifest(int jfd, const char *mf_name){
time_t current_time;
int nlen;
int mod_time;
struct zipentry *ze;
nlen = 9;
memset((file_header + 12), '\0', 16);
current_time = time(NULL);
if(current_time == (time_t)-1){
perror("time");
exit(1);
}
mod_time = unix2dostime(¤t_time);
PACK_UB2(file_header, LOC_EXTRA, 0);
PACK_UB2(file_header, LOC_COMP, 0);
PACK_UB2(file_header, LOC_FNLEN, nlen);
PACK_UB4(file_header, LOC_MODTIME, mod_time);
if(verbose)
printf("adding: META-INF/ (in=0) (out=0) (stored 0%%)\n");
ze = (zipentry*)malloc(sizeof(zipentry));
if(ze == NULL){
perror("malloc");
exit(1);
}
memset(ze, 0, sizeof(zipentry));
ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
strcpy(ze->filename, "META-INF/");
ze->filename[nlen] = '\0';
ze->offset = lseek(jfd, 0, SEEK_CUR);
ze->mod_time = (ub2)(mod_time & 0x0000ffff);
ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
ze->compressed = FALSE;
add_entry(ze);
write(jfd, file_header, 30);
write(jfd, "META-INF/", nlen);
if(mf_name == NULL){
int mf_len = 37 + strlen(VERSION);
char *mf;
if((mf = (char *) malloc(mf_len + 1))) {
uLong crc;
sprintf(mf, "Manifest-Version: 1.0\nCreated-By: %s\n\n", VERSION);
crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, (const unsigned char *)mf, mf_len);
nlen = 20;
PACK_UB2(file_header, LOC_EXTRA, 0);
PACK_UB2(file_header, LOC_COMP, 0);
PACK_UB2(file_header, LOC_FNLEN, nlen);
PACK_UB4(file_header, LOC_USIZE, mf_len);
memcpy((file_header + LOC_CSIZE), (file_header + LOC_USIZE), 4);
PACK_UB4(file_header, LOC_CRC, crc);
if(verbose)
printf("adding: META-INF/MANIFEST.MF (in=56) (out=56) (stored 0%%)\n");
ze = (zipentry*)malloc(sizeof(zipentry));
if(ze == NULL){
perror("malloc");
exit(1);
}
memset(ze, 0, sizeof(zipentry));
ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
strcpy(ze->filename, "META-INF/MANIFEST.MF");
ze->filename[nlen] = '\0';
ze->offset = lseek(jfd, 0, SEEK_CUR);
ze->mod_time = (ub2)(mod_time & 0x0000ffff);
ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
ze->crc = crc;
ze->csize = mf_len;
ze->usize = ze->csize;
ze->compressed = FALSE;
add_entry(ze);
write(jfd, file_header, 30);
write(jfd, "META-INF/MANIFEST.MF", nlen);
write(jfd, mf, mf_len);
free(mf);
}
else {
printf("malloc errror\n");
exit(-1);
}
} else {
int mfd;
struct stat statbuf;
stat(mf_name, &statbuf);
if(!S_ISREG(statbuf.st_mode)){
fprintf(stderr, "Invalid manifest file specified.\n");
exit(1);
}
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)){
perror("error writing to jar");
exit(1);
}
}
return 0;
}
int add_to_jar(int fd, const char *new_dir, const char *file){
struct stat statbuf;
DIR *dir;
struct dirent *de;
zipentry *ze;
int stat_return;
char *old_dir = NULL;
while (*file=='.' && *(file+1)=='/')
file+=2;
if(new_dir != NULL){
old_dir = getcwd(NULL, 0);
if(chdir(new_dir) == -1){
perror(new_dir);
return 1;
}
}
if(jarfile && !strcmp(file, jarfile)){
if(verbose)
printf("skipping: %s\n", file);
return 0;
}
stat_return = stat(file, &statbuf);
if(stat_return == -1){
perror(file);
return 1;
} else if(S_ISDIR(statbuf.st_mode)){
char *fullname;
char *t_ptr;
int nlen;
unsigned long mod_time;
dir = opendir(file);
if(dir == NULL){
perror("opendir");
return 1;
}
nlen = strlen(file) + 256;
fullname = (char*)malloc(nlen * sizeof(char));
memset(fullname, 0, (nlen * sizeof(char)));
if(fullname == NULL){
fprintf(stderr, "Filename is NULL!\n");
return 1;
}
strcpy(fullname, file);
nlen = strlen(file);
if(fullname[nlen - 1] != '/'){
fullname[nlen] = '/';
t_ptr = (fullname + nlen + 1);
} else
t_ptr = (fullname + nlen);
memset((file_header + 12), '\0', 16);
nlen = (t_ptr - fullname);
mod_time = unix2dostime(&statbuf.st_mtime);
PACK_UB2(file_header, LOC_EXTRA, 0);
PACK_UB2(file_header, LOC_COMP, 0);
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");
exit(1);
}
memset(ze, 0, sizeof(zipentry));
ze->filename = (char*)malloc((nlen + 1) * sizeof(char) + 1);
strcpy(ze->filename, fullname);
ze->filename[nlen] = '\0';
ze->offset = lseek(fd, 0, SEEK_CUR);
ze->mod_time = (ub2)(mod_time & 0x0000ffff);
ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
ze->compressed = FALSE;
add_entry(ze);
write(fd, file_header, 30);
write(fd, fullname, nlen);
while(!use_explicit_list_only && (de = readdir(dir)) != NULL){
if(de->d_name[0] == '.')
continue;
if(jarfile && !strcmp(de->d_name, jarfile)){
if(verbose)
printf("skipping: %s\n", de->d_name);
continue;
}
strcpy(t_ptr, de->d_name);
if(add_to_jar(fd, NULL, fullname)){
fprintf(stderr, "Error adding file to jar!\n");
return 1;
}
}
free(fullname);
closedir(dir);
} else if(S_ISREG(statbuf.st_mode)){
int add_fd;
add_fd = open(file, O_RDONLY | O_BINARY);
if(add_fd < 0){
fprintf(stderr, "Error opening %s.\n", file);
return 0;
}
if(add_file_to_jar(fd, add_fd, file, &statbuf)){
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){
unsigned short file_name_length;
unsigned long mod_time;
ub1 rd_buff[RDSZ];
uLong crc = 0;
off_t offset = 0;
int rdamt;
struct zipentry *ze;
mod_time = unix2dostime(&(statbuf->st_mtime));
file_name_length = strlen(fname);
if(!seekable && !do_compress){
crc = crc32(0L, Z_NULL, 0);
while((rdamt = read(ffd, rd_buff, RDSZ)) != 0)
crc = crc32(crc, rd_buff, rdamt);
lseek(ffd, 0, SEEK_SET);
}
if(!seekable && do_compress){
PACK_UB2(file_header, LOC_EXTRA, 8);
} else {
PACK_UB2(file_header, LOC_EXTRA, 0);
}
if(do_compress){
PACK_UB2(file_header, LOC_COMP, 8);
} else {
PACK_UB2(file_header, LOC_COMP, 0);
}
PACK_UB4(file_header, LOC_MODTIME, mod_time);
PACK_UB2(file_header, LOC_FNLEN, file_name_length);
if(!seekable && !do_compress){
PACK_UB4(file_header, LOC_CRC, crc);
PACK_UB4(file_header, LOC_USIZE, statbuf->st_size);
PACK_UB4(file_header, LOC_CSIZE, statbuf->st_size);
} else
memset((file_header + LOC_CRC), '\0', 12);
ze = (zipentry*)malloc(sizeof(zipentry));
if(ze == NULL){
perror("malloc");
exit(1);
}
memset(ze, 0, sizeof(zipentry));
ze->filename = (char*)malloc((file_name_length + 1) * sizeof(char));
strcpy(ze->filename, fname);
ze->mod_time = (ub2)(mod_time & 0x0000ffff);
ze->mod_date = (ub2)((mod_time & 0xffff0000) >> 16);
if(!seekable && !do_compress)
ze->crc = crc;
ze->csize = statbuf->st_size;
ze->usize = ze->csize;
ze->offset = lseek(jfd, 0, SEEK_CUR);
if(do_compress)
ze->compressed = TRUE;
else
ze->compressed = FALSE;
add_entry(ze);
write(jfd, file_header, 30);
write(jfd, fname, file_name_length);
if(verbose){
printf("adding: %s ", fname);
fflush(stdout);
}
if(do_compress){
compress_file(ffd, jfd, ze);
} else {
ze->crc = crc32(0L, Z_NULL, 0);
while((rdamt = read(ffd, rd_buff, RDSZ)) != 0){
ze->crc = crc32(ze->crc, rd_buff, rdamt);
if(write(jfd, rd_buff, rdamt) != rdamt){
perror("write");
return 0;
}
}
}
close(ffd);
PACK_UB4(data_descriptor, 4, ze->crc);
PACK_UB4(data_descriptor, 8, ze->csize);
PACK_UB4(data_descriptor, 12, ze->usize);
if(seekable){
offset = (ze->csize + strlen(ze->filename) + 16);
if(lseek(jfd, -offset, SEEK_CUR) == (off_t)-1){
perror("lseek");
exit(1);
}
if(write(jfd, (data_descriptor + 4), 12) != 12){
perror("write");
return 0;
}
offset -= 12;
if(lseek(jfd, offset, SEEK_CUR) == (off_t)-1){
perror("lseek");
exit(1);
}
} else if(do_compress){
if(write(jfd, data_descriptor, 16) != 16){
perror("write");
return 0;
}
}
if(verbose)
printf("(in=%d) (out=%d) (%s %d%%)\n",
(int)ze->usize, (int)ze->csize,
(do_compress ? "deflated" : "stored"),
(do_compress ? ((int)((1 - ze->csize/(float)ze->usize) * 100)) : 0));
return 0;
}
int create_central_header(int fd){
ub1 header[46];
ub1 end_header[22];
int start_offset;
int dir_size;
int *iheader;
int total_in = 0, total_out = 22;
zipentry *ze;
iheader = (int*)header;
header[0] = 'P';
header[1] = 'K';
header[2] = 1;
header[3] = 2;
header[4] = 10;
header[5] = 0;
header[6] = 10;
header[7] = 0;
header[8] = 0;
header[9] = 0;
header[10] = 0;
header[11] = 0;
header[12] = 0;
header[13] = 0;
header[14] = 0;
header[15] = 0;
header[16] = 0;
header[17] = 0;
header[18] = 0;
header[19] = 0;
header[20] = 0;
header[21] = 0;
header[22] = 0;
header[23] = 0;
header[24] = 0;
header[25] = 0;
header[26] = 0;
header[27] = 0;
header[28] = 0;
header[29] = 0;
header[30] = 0;
header[31] = 0;
header[32] = 0;
header[33] = 0;
header[34] = 0;
header[35] = 0;
header[36] = 0;
header[37] = 0;
header[38] = 0;
header[39] = 0;
header[40] = 0;
header[41] = 0;
header[42] = 0;
header[43] = 0;
header[44] = 0;
header[45] = 0;
start_offset = lseek(fd, 0, SEEK_CUR);
for(ze = ziptail; ze != NULL; ze = ze->next_entry){
total_in += ze->usize;
total_out += ze->csize + 76 + strlen(ze->filename) * 2;
if(ze->compressed){
PACK_UB2(header, CEN_COMP, 8);
} else {
PACK_UB2(header, CEN_COMP, 0);
}
PACK_UB2(header, CEN_MODTIME, ze->mod_time);
PACK_UB2(header, CEN_MODDATE, ze->mod_date);
PACK_UB4(header, CEN_CRC, ze->crc);
PACK_UB4(header, CEN_CSIZE, ze->csize);
PACK_UB4(header, CEN_USIZE, ze->usize);
PACK_UB2(header, CEN_FNLEN, strlen(ze->filename));
PACK_UB4(header, CEN_OFFSET, ze->offset);
write(fd, header, 46);
write(fd, ze->filename, strlen(ze->filename));
}
dir_size = lseek(fd, 0, SEEK_CUR) - start_offset;
end_header[0] = 0x50;
end_header[1] = 0x4b;
end_header[2] = 0x05;
end_header[3] = 0x06;
end_header[4] = 0;
end_header[5] = 0;
end_header[6] = 0;
end_header[7] = 0;
PACK_UB2(end_header, 8, number_of_entries);
PACK_UB2(end_header, 10, number_of_entries);
PACK_UB4(end_header, 12, dir_size);
PACK_UB4(end_header, 16, start_offset);
end_header[20] = 0;
end_header[21] = 0;
write(fd, end_header, 22);
if(verbose)
printf("Total:\n------\n(in = %d) (out = %d) (%s %d%%)\n",
total_in,
total_out,
(do_compress ? "deflated" : "stored"),
(int)((1 - (total_out / (float)total_in)) * 100)
);
return 0;
}
int extract_jar(int fd, char **files, int file_num){
int rdamt;
int out_a, in_a;
ub4 signature;
ub4 csize;
ub4 crc;
ub2 fnlen;
ub2 eflen;
ub2 flags;
ub2 method;
ub1 *filename = NULL;
int filename_len = 0;
ub4 rd_buff[RDSZ];
pb_file pbf;
ub1 scratch[16];
zipentry ze;
int f_fd;
int dir;
int handle;
int j;
init_inflation();
pb_init(&pbf, fd);
for(;;){
f_fd = 0;
crc = 0;
ze.crc = 0;
dir = FALSE;
handle = TRUE;
if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
perror("read");
break;
}
signature = UNPACK_UB4(scratch, 0);
#ifdef DEBUG
printf("signature is %x\n", signature);
#endif
if(signature == 0x08074b50){
#ifdef DEBUG
printf("skipping data descriptor\n");
#endif
pb_read(&pbf, scratch, 12);
continue;
} else if(signature == 0x02014b50){
#ifdef DEBUG
printf("Central header reached.. we're all done!\n");
#endif
break;
}else if(signature != 0x04034b50){
printf("Ick! %#x\n", signature);
break;
}
if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
perror("read");
break;
}
csize = UNPACK_UB4(file_header, LOC_CSIZE);
#ifdef DEBUG
printf("Compressed size is %u\n", csize);
#endif
fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
#ifdef DEBUG
printf("Filename length is %hu\n", fnlen);
#endif
eflen = UNPACK_UB2(file_header, LOC_EFLEN);
#ifdef DEBUG
printf("Extra field length is %hu\n", eflen);
#endif
flags = UNPACK_UB2(file_header, LOC_EXTRA);
#ifdef DEBUG
printf("Flags are %#hx\n", flags);
#endif
method = UNPACK_UB2(file_header, LOC_COMP);
#ifdef DEBUG
printf("Compression method is %#hx\n", method);
#endif
if(!(flags & 0x0008)){
crc = UNPACK_UB4(file_header, LOC_CRC);
#ifdef DEBUG
printf("CRC is %x\n", crc);
#endif
}
if(filename_len < fnlen + 1){
if(filename != NULL)
free(filename);
filename = malloc(sizeof(ub1) * (fnlen + 1));
filename_len = fnlen + 1;
}
pb_read(&pbf, filename, fnlen);
filename[fnlen] = '\0';
#ifdef DEBUG
printf("filename is %s\n", filename);
#endif
if(file_num > 0){
handle = FALSE;
for(j = 0; j < file_num; j++)
if(strcmp(files[j], (const char *)filename) == 0){
handle = TRUE;
break;
}
}
if(!handle)
f_fd = -1;
if(strchr((const char *)filename, '/') != NULL && handle){
const ub1 *start = filename;
char *tmp_buff;
struct stat sbuf;
tmp_buff = malloc(sizeof(char) * strlen((const char *)filename));
for(;;){
const ub1 *idx = (const unsigned char *)strchr((const char *)start, '/');
if(idx == NULL)
break;
else if(idx == start){
start++;
continue;
}
start = idx + 1;
strncpy(tmp_buff, (const char *)filename, (idx - filename));
tmp_buff[(idx - filename)] = '\0';
#ifdef DEBUG
printf("checking the existance of %s\n", tmp_buff);
#endif
if(stat(tmp_buff, &sbuf) < 0){
if(errno != ENOENT){
perror("stat");
exit(1);
}
} else if(S_ISDIR(sbuf.st_mode)){
#ifdef DEBUG
printf("Directory exists\n");
#endif
continue;
}else {
fprintf(stderr, "Hmmm.. %s exists but isn't a directory!\n",
tmp_buff);
exit(1);
}
#ifdef DEBUG
printf("Making directory..\n");
#endif
if(mkdir(tmp_buff, 0755) < 0){
perror("mkdir");
exit(1);
}
if(verbose && handle)
printf("%10s: %s/\n", "created", tmp_buff);
}
if(strlen((const char *)start) == 0)
dir = TRUE;
#ifdef DEBUG
printf("Leftovers are \"%s\" (%d)\n", start, strlen((const char *)start));
#endif
if(strlen((const char *)start) == 0)
f_fd = -1;
free(tmp_buff);
}
if(f_fd != -1 && handle){
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");
perror((const char *)filename);
exit(1);
}
}
if(method != 8 && flags & 0x0008){
fprintf(stderr, "Error in JAR file! (not compressed but data desc.)\n");
exit(1);
}
if(method == 8 || flags & 0x0008){
consume(&pbf, eflen);
inflate_file(&pbf, f_fd, &ze);
} else {
#ifdef DEBUG
printf("writing stored data.. (%d bytes)\n", csize);
#endif
out_a = 0;
in_a = csize;
ze.crc = crc32(ze.crc, NULL, 0);
while(out_a < (int)csize){
rdamt = (in_a > RDSZ ? RDSZ : in_a);
if(pb_read(&pbf, rd_buff, rdamt) != rdamt){
perror("read");
exit(1);
}
ze.crc = crc32(ze.crc, (Bytef*)rd_buff, rdamt);
if(f_fd >= 0)
write(f_fd, rd_buff, rdamt);
out_a += rdamt;
in_a -= rdamt;
#ifdef DEBUG
printf("%d bytes written\n", out_a);
#endif
}
consume(&pbf, eflen);
}
if(flags & 0x0008){
if(pb_read(&pbf, scratch, 16) != 16){
perror("read");
exit(1);
}
signature = UNPACK_UB4(scratch, 0);
if(signature != 0x08074b50){
fprintf(stderr, "Error! Missing data descriptor!\n");
exit(1);
}
crc = UNPACK_UB4(scratch, 4);
}
if(crc != ze.crc){
fprintf(stderr, "Error! CRCs do not match! Got %x, expected %x\n",
ze.crc, crc);
exit(1);
}
close(f_fd);
if(verbose && dir == FALSE && handle)
printf("%10s: %s\n",
(method == 8 ? "inflated" : "extracted"),
filename);
}
return 0;
}
int list_jar(int fd, char **files, int file_num){
int rdamt;
ub4 signature;
ub4 csize;
ub4 usize;
ub4 mdate;
ub4 tmp;
ub2 fnlen;
ub2 eflen;
ub2 clen;
ub2 flags;
ub2 method;
ub2 cen_size;
ub1 *filename = NULL;
ub1 scratch[16];
ub1 cen_header[46];
int filename_len = 0;
off_t size;
int i, j;
time_t tdate;
struct tm *s_tm;
char ascii_date[31];
zipentry ze;
#ifdef DEBUG
printf("Listing jar file, looking for %d files\n", file_num);
#endif
if(seekable){
if(lseek(fd, -22, SEEK_END) == (off_t)-1){
perror("lseek");
exit(1);
}
if(read(fd, &tmp, sizeof(ub4)) != 4){
perror("read");
exit(1);
}
#ifdef WORDS_BIGENDIAN
tmp = L2BI(tmp);
#endif
if(tmp != 0x06054b50){
fprintf(stderr, "Error in JAR file format. zip-style comment?\n");
exit(1);
}
if(lseek(fd, 6, SEEK_CUR) == (off_t)-1){
perror("lseek");
exit(1);
}
if(read(fd, &cen_size, 2) != 2){
perror("read");
exit(1);
}
#ifdef WORDS_BIGENDIAN
cen_size = L2BS(cen_size);
#endif
if(lseek(fd, 4, SEEK_CUR) == (off_t)-1){
perror("lseek");
exit(1);
}
if(read(fd, &tmp, 4) != 4){
perror("read");
exit(1);
}
#ifdef WORDS_BIGENDIAN
tmp = L2BI(tmp);
#endif
if(lseek(fd, tmp, SEEK_SET) != (int)tmp){
perror("lseek");
exit(1);
}
for(i = 0; i < cen_size; i++){
if(read(fd, &cen_header, 46) != 46){
perror("read");
exit(1);
}
signature = UNPACK_UB4(cen_header, 0);
if(signature != 0x02014b50){
fprintf(stderr, "Error in JAR file! Cannot locate central header!\n");
exit(1);
}
usize = UNPACK_UB4(cen_header, CEN_USIZE);
fnlen = UNPACK_UB2(cen_header, CEN_FNLEN);
eflen = UNPACK_UB2(cen_header, CEN_EFLEN);
clen = UNPACK_UB2(cen_header, CEN_COMLEN);
if(verbose){
mdate = UNPACK_UB4(cen_header, CEN_MODTIME);
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 + 1){
if(filename != NULL)
free(filename);
filename = malloc(sizeof(ub1) * (fnlen + 1));
filename_len = fnlen + 1;
}
if(read(fd, filename, fnlen) != fnlen){
perror("read");
exit(1);
}
filename[fnlen] = '\0';
if(file_num > 0){
for(j = 0; j < file_num; j++)
if(strcmp(files[j], (const char *)filename) == 0){
if(verbose)
printf("%6d %s %s\n", usize, ascii_date, filename);
else
printf("%s\n", filename);
break;
}
} else {
if(verbose)
printf("%6d %s %s\n", usize, ascii_date, filename);
else
printf("%s\n", filename);
}
size = eflen + clen;
if(size > 0){
if(lseek(fd, size, SEEK_CUR) == (off_t)-1){
perror("lseek");
exit(1);
}
}
}
} else {
pb_file pbf;
pb_init(&pbf, fd);
init_inflation();
for(;;){
if((rdamt = pb_read(&pbf, scratch, 4)) != 4){
perror("read");
break;
}
signature = UNPACK_UB4(scratch, 0);
#ifdef DEBUG
printf("signature is %x\n", signature);
#endif
if(signature == 0x08074b50){
#ifdef DEBUG
printf("skipping data descriptor\n");
#endif
pb_read(&pbf, scratch, 12);
continue;
} else if(signature == 0x02014b50){
#ifdef DEBUG
printf("Central header reached.. we're all done!\n");
#endif
break;
}else if(signature != 0x04034b50){
#ifdef DEBUG
printf("Ick! %#x\n", signature);
#endif
break;
}
if((rdamt = pb_read(&pbf, (file_header + 4), 26)) != 26){
perror("read");
break;
}
csize = UNPACK_UB4(file_header, LOC_CSIZE);
#ifdef DEBUG
printf("Compressed size is %u\n", csize);
#endif
fnlen = UNPACK_UB2(file_header, LOC_FNLEN);
#ifdef DEBUG
printf("Filename length is %hu\n", fnlen);
#endif
eflen = UNPACK_UB2(file_header, LOC_EFLEN);
#ifdef DEBUG
printf("Extra field length is %hu\n", eflen);
#endif
method = UNPACK_UB2(file_header, LOC_COMP);
#ifdef DEBUG
printf("Compression method is %#hx\n", method);
#endif
flags = UNPACK_UB2(file_header, LOC_EXTRA);
#ifdef DEBUG
printf("Flags are %#hx\n", flags);
#endif
usize = UNPACK_UB4(file_header, LOC_USIZE);
if(verbose){
mdate = UNPACK_UB4(file_header, LOC_MODTIME);
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 + 1){
if(filename != NULL)
free(filename);
filename = malloc(sizeof(ub1) * (fnlen + 1));
filename_len = fnlen + 1;
}
pb_read(&pbf, filename, fnlen);
filename[fnlen] = '\0';
if(flags & 0x0008){
size = eflen;
if(size > 0)
consume(&pbf, size);
if(method == 8){
#ifdef DEBUG
printf("inflating %s\n", filename);
#endif
inflate_file(&pbf, -1, &ze);
usize = ze.usize;
} else
printf("We're shit outta luck!\n");
} else {
size = csize + (eflen > 0 ? eflen : 0);
#ifdef DEBUG
printf("Skipping %ld bytes\n", (long)size);
#endif
consume(&pbf, size);
}
if(file_num > 0){
for(j = 0; j < file_num; j++)
if(strcmp(files[j], (const char *)filename) == 0){
if(verbose)
printf("%6d %s %s\n", usize, ascii_date, filename);
else
printf("%s\n", filename);
break;
}
} else {
if(verbose)
printf("%6d %s %s\n", usize, ascii_date, filename);
else
printf("%s\n", filename);
}
}
}
return 0;
}
int consume(pb_file *pbf, int amt){
int tc = 0;
ub1 buff[RDSZ];
int rdamt;
#ifdef DEBUG
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);
}
} else
while(tc < amt){
rdamt = pb_read(pbf, buff, ((amt - tc) < RDSZ ? (amt - tc) : RDSZ));
#ifdef DEBUG
printf("got %d bytes\n", rdamt);
#endif
tc += rdamt;
}
#ifdef DEBUG
printf("%d bytes consumed\n", amt);
#endif
return 0;
}
void usage(const char *filename){
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\
\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);
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\
");
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\
\n\
Example 1: to archive two class files into an archive called classes.jar: \n\
jar cvf classes.jar Foo.class Bar.class \n\
Example 2: use an existing manifest file 'mymanifest' and archive all the\n\
files in the foo/ directory into 'classes.jar': \n\
jar cvfm classes.jar mymanifest -C foo/ .\n\
");
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;
}
static void
expand_options (int *argcp, char ***argvp)
{
int argc = *argcp;
char **argv = *argvp;
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);
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;
}
while (in < argv + argc)
*out++ = *in++;
*argcp = new_argc;
*argvp = new_argv;
}
}