Logo Search packages:      
Sourcecode: jamvm version File versions

zip.c

/*
 * Copyright (C) 2003, 2004 Robert Lougher <rob@lougher.demon.co.uk>.
 *
 * This file is part of JamVM.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "jam.h"
#include "hash.h"
#include "zip.h"

#ifdef USE_ZIP
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <sys/mman.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define READ_LE_INT(p) (p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24))
#define READ_LE_SHORT(p) (p[0]|(p[1]<<8))

#define HASHTABSZE 1<<8
#define HASH(ptr) utf8Hash(ptr)
#define COMPARE(ptr1, ptr2, hash1, hash2) (hash1 == hash2) && utf8Comp(ptr1, ptr2) 
#define PREPARE(ptr) ptr
#define SCAVENGE(ptr) FALSE
#define FOUND(ptr)

ZipFile *processArchive(char *path) {
    unsigned char magic[4];
    unsigned char *data;
    int idx, entries;
    int fd, len;

    HashTable *hash_table;
    ZipFile *zip;

    if((fd = open(path, O_RDONLY)) == -1)
        return NULL;

    /* First 4 bytes must be the marker for the first file entry */

    if(read(fd, &magic[0], 4) != 4 || READ_LE_INT(magic) != 0x04034b50)
        goto error;

    len = lseek(fd, 0, SEEK_END);

    /* Mmap the file into memory */

    if((data = (char*)mmap(0, len, PROT_READ | PROT_WRITE , MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED)
        goto error;

    /* Locate the central directory trailer by searching backwards for
       the trailer marker. */

    for(idx = len-1; idx > 4; idx--)
        if(data[idx] == 0x06 && READ_LE_INT((data+idx-3)) == 0x06054b50)
            break;

    if(idx < 5 || len-idx < 17)
        goto error2;

    idx++;

    /* Bytes 10-11 of the trailer hold the number of entries in the 
       directory -- this is 6 bytes after the marker */

    entries = READ_LE_SHORT((data+idx+6));

    /* Create and initialise hash table to hold the directory.
       Do not create lock -- we're single threaded (bootstrapping)
       and once entries are added, table is read-only */

    hash_table = (HashTable*)sysMalloc(sizeof(HashTable));
    initHashTable((*hash_table), HASHTABSZE, FALSE);

    /* Bytes 16-19 of the trailer hold the offset from the start of the file
       of the first directory entry -- this is 12 bytes after the marker */

    idx = READ_LE_INT((data+idx+12));

    /* Scan the directory list and add the entries to the hash table */

    while(entries--) {
        char *found, *pathname;
        int path_len, comment_len, extra_len;

        /* Check directory entry marker is present */
        if((READ_LE_INT((data+idx)) & 0xffffff00) != 0x02014b00)
            goto error2;

        /* Bytes 28-29 of the dir entry hold the length of the pathname */
        path_len = READ_LE_SHORT((data+idx+28));

        extra_len = READ_LE_SHORT((data+idx+30));
        comment_len = READ_LE_SHORT((data+idx+32));

        /* The pathname starts at byte 46 of the dir entry */
        pathname = data+idx+46;

        /* Terminate the pathname -- will overwrite first byte of next marker */
        pathname[path_len] = 0;

        /* Add if absent, no scavenge, not locked */
        findHashEntry((*hash_table), pathname, found, TRUE, FALSE, FALSE);

        idx += 46+path_len+extra_len+comment_len;
    }

    zip = (ZipFile*) sysMalloc(sizeof(ZipFile));

    zip->data = data;
    zip->dir_hash = hash_table;

    return zip;

error2:
    munmap(data, len);

error:
    close(fd);
    return NULL;
}

char *findArchiveDirEntry(char *pathname, ZipFile *zip) {
    char *found;

    /* Do not add if absent, no scavenge, not locked */
    findHashEntry((*zip->dir_hash), pathname, found, FALSE, FALSE, FALSE);

    return found;
}

char *findArchiveEntry(char *pathname, ZipFile *zip, int *uncomp_len) {
    int offset, path_len, comp_len, extra_len, comp_method, err;
    char *decomp_buff, *comp_data;
    unsigned char *dir_entry;
    z_stream stream;

    if((dir_entry = findArchiveDirEntry(pathname, zip)) == NULL)
        return NULL;

    /* Found the file -- the pathname points directly into the
       directory entry.  Read the values relative to it */

    /* Offset of the file entry relative to the start of the file */
    offset = READ_LE_INT((dir_entry-4));

    extra_len = READ_LE_SHORT((zip->data+offset+28));

    /* Get the path_len again -- faster than doing a strlen */
    path_len = READ_LE_SHORT((dir_entry-18));

    /* The file's length when uncompressed -- this is passed out */
    *uncomp_len = READ_LE_INT((dir_entry-22));

    /* The file's length compressed, i.e. the data size in the file */
    comp_len = READ_LE_INT((dir_entry-26));

    comp_method = READ_LE_SHORT((dir_entry-36));

    /* Calculate the data start -- the data is after the pathname in the
       file entry, which starts 30 bytes in */

    comp_data = zip->data+offset+30+path_len+extra_len;

    decomp_buff = (char *) sysMalloc(*uncomp_len);

    if(comp_method == 0) {
        memcpy(decomp_buff, comp_data, comp_len);
        return decomp_buff;
    }

    stream.next_in = comp_data;
    stream.avail_in = comp_len;
    stream.next_out = decomp_buff;
    stream.avail_out = *uncomp_len;

    stream.zalloc = Z_NULL;
    stream.zfree = Z_NULL;

    if(inflateInit2(&stream, -15) != Z_OK)
        goto error;

    err = inflate(&stream, Z_SYNC_FLUSH);
    inflateEnd(&stream);

    if(err == Z_STREAM_END || (err == Z_OK && stream.avail_in == 0))
        return decomp_buff;

error:
    free(decomp_buff);
    return NULL;
}
#else
ZipFile *processArchive(char *path) {
    return NULL;
}

char *findArchiveDirEntry(char *pathname, ZipFile *zip) {
    return NULL;
}

char *findArchiveEntry(char *pathname, ZipFile *zip, int *entry_len) {
    return NULL;
}
#endif

Generated by  Doxygen 1.6.0   Back to index