classpath
[Top][All Lists]
Advanced

[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);
}






reply via email to

[Prev in Thread] Current Thread [Next in Thread]