[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: SystemClassLoader
From: |
Archie Cobbs |
Subject: |
Re: SystemClassLoader |
Date: |
Mon, 8 Dec 2003 15:50:52 -0600 (CST) |
Etienne Gagnon wrote:
> I would be interested in using your code if it was licensed under the
> less restrictive LGPL. Some people even suggested that it would be
> very nice of you if you could put this code in the public domain, so
> that any jvm, regardless of license, would be able to use it...
Here it is.. have at it :-) You'll need to replace the calls to
_jc_vm_alloc(), _jc_post_exception(), etc.
I'm not a license freak so if LGPL doesn't work for somebody let me know.
Cheers,
-Archie
__________________________________________________________________________
Archie Cobbs * Halloo Communications * http://www.halloo.com
/*
*
* Copyright (C) 2003 Archie L. Cobbs <address@hidden>
* All rights reserved.
*
* See the file "AUTHORS" for the name of all copyright holders.
*
* This software is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software in the file "COPYING.LIB"; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*
* $Id: zip.c,v 1.7 2003/11/28 22:07:06 archie Exp $
*/
#include "libjc.h"
/* ZIP file constants */
#define ZIP_DIRECTORY_SIGNATURE 0x06054b50
#define ZIP_DIRENTRY_SIGNATURE 0x02014b50
#define ZIP_ENTRY_SIGNATURE 0x03044b50
#define ZIP_DIRECTORY_INFO_LEN 22
#define ZIP_LOCAL_HEADER_EXTRA_OFFSET 28
#define ZIP_LOCAL_HEADER_LEN 30
#define ZIP_METHOD_STORED 0
#define ZIP_METHOD_DEFLATED 8
/* Public functions */
extern _jc_zip *_jc_zip_open(_jc_env *env, const char *path);
extern void _jc_zip_close(_jc_zip **zipp);
extern int _jc_zip_search(_jc_zip *zip, const char *name);
extern jint _jc_zip_read(_jc_env *env, _jc_zip *zip,
int indx, u_char *data);
/* Internal functions */
static int _jc_zip_scan(_jc_zip *zip, ...);
static jint _jc_zip_unstore(_jc_env *env, _jc_zip *zip,
_jc_zip_entry *zent, void *data);
static jint _jc_zip_inflate(_jc_env *env, _jc_zip *zip,
_jc_zip_entry *zent, void *data);
static void *_jc_zip_alloc(void *cookie, unsigned int num,
unsigned int size);
static void _jc_zip_free(void *cookie, void *mem);
static int _jc_zip_entry_cmp(const void *v1, const void *v2);
/*
* Open a ZIP file.
*
* If there is an error, an InternalError is posted and NULL is returned.
*/
_jc_zip *
_jc_zip_open(_jc_env *env, const char *path)
{
jshort num_entries;
jint signature;
jint offset;
_jc_zip *zip;
int i;
/* Create new zip structure */
if ((zip = _jc_vm_zalloc(env, sizeof(*zip))) == NULL)
goto fail;
zip->fd = -1;
if ((zip->path = _jc_vm_strdup(env, path)) == NULL)
goto fail;
/* Open file */
if ((zip->fd = open(path, O_RDONLY)) == -1) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't open ZIP file `%s': %s", zip->path, strerror(errno));
goto fail;
}
(void)fcntl(zip->fd, F_SETFD, 1);
/* Seek to start of central directory meta-info at end of file */
if (lseek(zip->fd, (off_t)-ZIP_DIRECTORY_INFO_LEN, SEEK_END) == -1) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't seek to directory meta-info in ZIP file `%s': %s",
zip->path, strerror(errno));
goto fail;
}
/* Read central directory meta-info */
if (_jc_zip_scan(zip, -4, &signature, 6, NULL,
-2, &num_entries, 4, NULL, -4, &offset, 0) != JNI_OK) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't read directory meta-info in ZIP file `%s': %s",
zip->path, strerror(errno));
goto fail;
}
/* Check signature */
if (signature != ZIP_DIRECTORY_SIGNATURE) {
_jc_post_exception_msg(env, _JC_InternalError,
"invalid directory signature 0x%08x in ZIP file `%s'",
signature, zip->path);
goto fail;
}
/* Seek to start of central directory */
if (lseek(zip->fd, (off_t)offset, SEEK_SET) == -1) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't seek to directory in ZIP file `%s': %s",
zip->path, strerror(errno));
goto fail;
}
/* Allocate directory entries */
if ((zip->entries = _jc_vm_zalloc(env,
num_entries * sizeof(*zip->entries))) == NULL)
goto fail;
zip->num_entries = num_entries;
/* Read directory entries */
for (i = 0; i < num_entries; i++) {
_jc_zip_entry *const zent = &zip->entries[i];
jshort comment_len;
jshort extra_len;
jshort name_len;
off_t pos;
/* Read directory entry */
if (_jc_zip_scan(zip, -4, &signature, 6, NULL,
-2, &zent->method, 4, NULL, -4, &zent->crc,
-4, &zent->comp_len, -4, &zent->uncomp_len,
-2, &name_len, -2, &extra_len, -2, &comment_len,
8, NULL, -4, &offset, 0) != JNI_OK) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't read entry directory #%d in ZIP file `%s':"
" %s", i + 1, zip->path, strerror(errno));
goto fail;
}
/* Check signature */
if (signature != ZIP_DIRENTRY_SIGNATURE) {
_jc_post_exception_msg(env, _JC_InternalError,
"invalid signature 0x%08x for directory entry #%d"
" in ZIP file `%s'",
signature, i + 1, zip->path);
goto fail;
}
/* Allocate buffer for file name */
if ((zent->name = _jc_vm_alloc(env, name_len + 1)) == NULL)
goto fail;
/* Read entry file name */
if (_jc_zip_scan(zip, name_len, zent->name, 0) != JNI_OK) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't read directory entry #%d in ZIP file `%s':"
" %s", i + 1, zip->path, strerror(errno));
goto fail;
}
zent->name[name_len] = '\0';
/* Skip over extra and comment fields */
if (_jc_zip_scan(zip,
extra_len + comment_len, NULL, 0) != JNI_OK) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't read entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, strerror(errno));
goto fail;
}
/* Save current position */
if ((pos = lseek(zip->fd, 0, SEEK_CUR)) == (off_t)-1) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't seek entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, strerror(errno));
goto fail;
}
/* Jump to entry local header extra data length field */
if (lseek(zip->fd,
(off_t)(offset + ZIP_LOCAL_HEADER_EXTRA_OFFSET),
SEEK_SET) == (off_t)-1) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't seek entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, strerror(errno));
goto fail;
}
/* Read length of local extra data */
if (_jc_zip_scan(zip, -2, &extra_len, 0) != JNI_OK) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't read entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, strerror(errno));
goto fail;
}
/* Compute offset of actual file data */
zent->offset = offset
+ ZIP_LOCAL_HEADER_LEN + name_len + extra_len;
/* Jump back to previous position in directory */
if (lseek(zip->fd, pos, SEEK_SET) == (off_t)-1) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't seek entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, strerror(errno));
goto fail;
}
}
/* Sort the entries by name for faster searching */
qsort(zip->entries, zip->num_entries,
sizeof(*zip->entries), _jc_zip_entry_cmp);
/* Done */
return zip;
fail:
/* Clean up after failure */
_jc_zip_close(&zip);
return NULL;
}
/*
* Close a ZIP file.
*/
void
_jc_zip_close(_jc_zip **zipp)
{
_jc_zip *zip = *zipp;
/* Sanity check */
if (zip == NULL)
return;
*zipp = NULL;
/* Free resources */
while (zip->num_entries > 0) {
_jc_zip_entry *const zent = &zip->entries[--zip->num_entries];
_jc_vm_free(&zent->name);
}
_jc_vm_free(&zip->entries);
_jc_vm_free(&zip->path);
if (zip->fd != -1)
close(zip->fd);
_jc_vm_free(&zip);
}
/*
* Search for an entry in a ZIP file.
*
* Returns the entry index, or -1 if unsuccessful.
*/
int
_jc_zip_search(_jc_zip *zip, const char *name)
{
int base;
int lim;
/* Search for entry using binary search */
for (base = 0, lim = zip->num_entries; lim != 0; lim >>= 1) {
const int target = base + (lim >> 1);
const _jc_zip_entry *const zent = &zip->entries[target];
int diff;
if ((diff = strcmp(name, zent->name)) == 0)
return target;
if (diff > 0) {
base = target + 1;
lim--;
}
}
return -1;
}
/*
* Extract an entry from a ZIP file and return its length.
*
* Posts an exception and returns -1 if unsuccessful.
*/
jint
_jc_zip_read(_jc_env *env, _jc_zip *zip, int indx, u_char *data)
{
_jc_zip_entry *const zent = &zip->entries[indx];
/* Sanity check */
_JC_ASSERT(indx >= 0 && indx < zip->num_entries);
/* Decompress data */
switch (zent->method) {
case ZIP_METHOD_STORED:
if (_jc_zip_unstore(env, zip, zent, data) != JNI_OK)
return JNI_ERR;
break;
case ZIP_METHOD_DEFLATED:
if (_jc_zip_inflate(env, zip, zent, data) != JNI_OK)
return JNI_ERR;
break;
default:
_jc_post_exception_msg(env, _JC_InternalError,
"unsupported compression method %d for entry `%s'"
" in ZIP file `%s'", zent->method, zent->name, zip->path);
return JNI_ERR;
}
/* Done */
return JNI_OK;
}
/*
* Extract a stored entry.
*/
static jint
_jc_zip_unstore(_jc_env *env, _jc_zip *zip, _jc_zip_entry *zent, void *data)
{
int i;
int r;
/* Sanity check */
if (zent->comp_len != zent->uncomp_len) {
_jc_post_exception_msg(env, _JC_InternalError,
"inconsistent lengths %d != %d for entry `%s'"
" in ZIP file `%s'", zent->comp_len,
zent->uncomp_len, zent->name, zip->path);
return JNI_ERR;
}
/* Read data */
for (i = 0; i < zent->comp_len; i += r) {
if ((r = pread(zip->fd, (char *)data + i,
zent->comp_len - i, zent->offset + i)) == -1) {
_jc_post_exception_msg(env, _JC_InternalError,
"can't read entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, strerror(errno));
return JNI_ERR;
}
if (r == 0) {
_jc_post_exception_msg(env, _JC_InternalError,
"premature EOF reading entry `%s' in ZIP file `%s'",
zent->name, zip->path);
return JNI_ERR;
}
}
/* Done */
return JNI_OK;
}
/*
* Extract a deflated entry.
*/
static jint
_jc_zip_inflate(_jc_env *env, _jc_zip *zip, _jc_zip_entry *zent, void *data)
{
z_stream zs;
int i;
int r;
/* Initialize decompression state */
memset(&zs, 0, sizeof(zs));
zs.zalloc = _jc_zip_alloc;
zs.zfree = _jc_zip_free;
zs.opaque = env;
zs.next_out = data;
zs.avail_out = zent->uncomp_len;
switch (inflateInit2(&zs, -MAX_WBITS)) {
case Z_OK:
break;
case Z_MEM_ERROR:
return JNI_ERR;
case Z_VERSION_ERROR:
_jc_post_exception_msg(env, _JC_InternalError,
"error reading entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, "incompatible zlib version");
return JNI_ERR;
default:
_JC_ASSERT(JNI_FALSE);
}
/* Read and inflate data */
for (i = 0; i < zent->comp_len; i += r) {
char buf[512];
int to_read;
int flush;
/* Read the next chunk of data */
to_read = zent->comp_len - i;
if (to_read > sizeof(buf))
to_read = sizeof(buf);
if ((r = pread(zip->fd, buf,
to_read, zent->offset + i)) == -1) {
_jc_post_exception_msg(env, _JC_InternalError,
"error reading entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, strerror(errno));
goto fail;
}
if (r == 0) {
_jc_post_exception_msg(env, _JC_InternalError,
"error reading entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, "premature EOF");
goto fail;
}
/*
* Check for uncompressed data appearing to be too big.
* A bug in zlib somewhere causes this to happen sometimes.
*/
if (zs.avail_out == 0) {
r = inflateEnd(&zs);
_JC_ASSERT(r == Z_OK);
return JNI_OK;
}
/* Decompress the chunk we just read */
zs.next_in = buf;
zs.avail_in = r;
flush = (i + r == zent->comp_len) ? Z_FINISH : Z_SYNC_FLUSH;
switch (inflate(&zs, flush)) {
case Z_OK:
_JC_ASSERT(zs.avail_in == 0);
continue;
case Z_BUF_ERROR: /* bug in zlib causes this */
case Z_STREAM_END:
if (zs.avail_out != 0 || i + r != zent->comp_len)
goto bad_length;
r = inflateEnd(&zs);
_JC_ASSERT(r == Z_OK);
return JNI_OK;
case Z_NEED_DICT:
_jc_post_exception_msg(env, _JC_InternalError,
"error reading entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, "dictionary required");
goto fail;
case Z_DATA_ERROR:
_jc_post_exception_msg(env, _JC_InternalError,
"error reading entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, "corrupted entry");
goto fail;
case Z_MEM_ERROR:
goto fail;
default:
_JC_ASSERT(JNI_FALSE);
}
}
bad_length:
/* Uncompressed data had the wrong length */
_jc_post_exception_msg(env, _JC_InternalError,
"error reading entry `%s' in ZIP file `%s': %s",
zent->name, zip->path, "incorrect length");
fail:
/* Clean up after failure */
inflateEnd(&zs);
return JNI_ERR;
}
/*
* Function to read consecutive data bytes from the ZIP file and perform
* optional conversion from little endian. A negative count means 16 or
* 32 bit conversion is performed.
*
* NOTE: Does not post an exception in the error case.
*/
static int
_jc_zip_scan(_jc_zip *zip, ...)
{
jint rtn = JNI_OK;
va_list args;
va_start(args, zip);
while (JNI_TRUE) {
const int count = va_arg(args, int);
const int num_bytes = (count < 0 ? -count : count);
void *const data = va_arg(args, void *);
u_char buf[16];
u_char *dest;
int i;
int r;
/* A zero count terminates the list */
if (count == 0)
break;
/* Read bytes into provided buffer or byte-swap buffer */
if (data == NULL || count < 0) {
_JC_ASSERT(num_bytes <= sizeof(buf));
dest = buf;
} else
dest = data;
for (i = 0; i < num_bytes; i += r) {
if ((r = read(zip->fd,
dest + i, num_bytes - i)) == -1) {
rtn = JNI_ERR;
goto done;
}
if (r == 0) {
errno = ESPIPE; /* XXX */
rtn = JNI_ERR;
goto done;
}
}
/* If no conversion needed, just continue */
if (data == NULL || count >= 0)
continue;
/* Do byte-swapping */
switch (num_bytes) {
case 1:
*((u_char *)data) = buf[0];
break;
case 2:
*((jshort *)data) = buf[0] | (buf[1] << 8);
break;
case 4:
*((jint *)data) = buf[0] | (buf[1] << 8)
| (buf[2] << 16) | (buf[3] << 24);
break;
default:
_JC_ASSERT(JNI_FALSE);
}
}
done:
/* Clean up and return */
va_end(args);
return rtn;
}
/*
* Function to sort ZIP entries by name.
*/
static int
_jc_zip_entry_cmp(const void *v1, const void *v2)
{
const _jc_zip_entry *const zent1 = v1;
const _jc_zip_entry *const zent2 = v2;
return strcmp(zent1->name, zent2->name);
}
/*
* Memory allocation callback for zlib.
*/
static void *
_jc_zip_alloc(void *cookie, unsigned int num, unsigned int size)
{
_jc_env *const env = cookie;
return _jc_vm_alloc(env, num * size);
}
/*
* Memory free callback for zlib.
*/
static void
_jc_zip_free(void *cookie, void *mem)
{
_jc_vm_free(&mem);
}