Milán Major

netscape-revival

1 branch
Code

sun-java/md/car_md.c

#include "prtypes.h"
#include "prlong.h"
#include "prmem.h"
#include "prprf.h"
#include "stdio.h"

#ifndef XP_MAC
#include "memory.h"
#endif

#ifdef XP_UNIX
#include "string.h"
#endif

#include "car_md.h"
#include "oobj.h"

#ifdef XP_MAC
#include "io_md.h"
#include <string.h>
#endif

/*
** @(#)car_md.c
**
** CAR file reader code
**
** Author: Kipp E.B. Hickman
*/

#define SWAPB(a,b) ((t) = (a), (a) = (b), (b) = (t))

static PRBool ReadLong(CARFile *f, int64 *ip)
{
    union {
	unsigned char c[8];
	int64 ll;
    } u;

    if (fread(u.c, 1, 8, f->file) != 8) {
	f->ioerror = 1;
	return PR_FALSE;
    }
#ifdef IS_LITTLE_ENDIAN
    {
	unsigned char t;
	SWAPB(u.c[0], u.c[7]);
	SWAPB(u.c[1], u.c[6]);
	SWAPB(u.c[2], u.c[5]);
	SWAPB(u.c[3], u.c[4]);
    }
#endif
    *ip = u.ll;
    return PR_TRUE;
}

static PRBool ReadInt(CARFile *f, int32 *ip)
{
    union {
	unsigned char c[4];
	int32 i;
    } u;

    if (fread(u.c, 1, 4, f->file) != 4) {
	f->ioerror = 1;
	return PR_FALSE;
    }
#ifdef IS_LITTLE_ENDIAN
    {
	unsigned char t;
	SWAPB(u.c[0], u.c[3]);
	SWAPB(u.c[1], u.c[2]);
    }
#endif
    *ip = u.i;
    return PR_TRUE;
}

/*
** Hash a utf string. The identical hash function is folded into the
** ReadUTF code below.
*/
static uint32 HashUTF(uint8 *s)
{
    uint32 hash;

    hash = 0;
    for (;;) {
	int c = *s++;
	hash = (hash >> 28) ^ (hash << 4) ^ c;
	if (c == 0)
	    break;
    }
    return hash;
}

/*
** Read in a null terminated utf encoded name. Don't de-encoded it
** because we will be comparing utf names against utf names. While we are
** doing the i/o, compute a hash of the name for faster searching.
*/
static PRBool ReadUTF(CARFile *f, uint8 **ip, uint32 *hp)
{
    uint8 b[32];
    uint8 *bp, *rv;
    uint8 bix, blen;
    uint32 hash;

    bix = 0;
    blen = sizeof(b);
    bp = b;
    hash = 0;
    for (;;) {
	int c = getc(f->file);
	if (c == EOF) {
	    f->ioerror = 1;
	    if (bp != b) {
		free(bp);
	    }
	    return PR_FALSE;
	}
	if (bix == blen) {
	    /* Grow bp */
	    blen *= 2;
	    if (bp == b) {
		bp = (uint8*) malloc(blen);
		if (!bp) {
		    return PR_FALSE;
		}
		memcpy(bp, b, sizeof(b));
	    } else {
		bp = (uint8*) realloc(bp, blen);
		if (!bp) {
		    return PR_FALSE;
		}
	    }
	}
	bp[bix++] = c;
	hash = (hash >> 28) ^ (hash << 4) ^ c;
	if (c == 0)
	    break;
    }

    rv = (uint8*) strdup((char*) bp);
    if (bp != b) {
	free(bp);
    }
    if (!rv) {
	return PR_FALSE;
    }
    *ip = rv;
    *hp = hash;
    return PR_TRUE;
}

/*
** Read the next TOCEntry from the car file
*/
static PRBool ReadTOCEntry(CARFile *f, TOCEntry *te)
{
    if (!ReadLong(f, &te->lastModified) ||
	!ReadLong(f, &te->offset) ||
	!ReadLong(f, &te->length) ||
	!ReadUTF(f, &te->name, &te->nameHash)) {
	return PR_FALSE;
    }
    return PR_TRUE;
}

/*
** Read the entire TOC
*/
static PRBool ReadTOC(CARFile *f)
{
    int32 i, n;
    TOCEntry *te;
    TOCEntry **tep;
    uint32 bucket;

    n = f->entries;
    te = f->toc;
    for (i = 0; i < n; i++, te++) {
	if (!ReadTOCEntry(f, te))
	    return PR_FALSE;

	/* Install entry in the hash buckets */
	bucket = te->nameHash & (f->numBuckets - 1);
	tep = &f->buckets[bucket];
	te->next = *tep;
	*tep = te;
    }
    return PR_TRUE;
}

/*
** Read in a CAR file. Actually all we read is the header and the TOC
*/
static PRBool ReadCar(CARFile *f)
{
    int32 magic;
    int32 version;
    int64 tocsize;
    int32 numBuckets;

    if (!ReadInt(f, &magic) || (magic != CAR_MAGIC) ||
	!ReadInt(f, &version) || (version != CAR_VERSION) ||
	!ReadInt(f, &f->entries) || !ReadLong(f, &tocsize)) {
	return PR_FALSE;
    }
    f->toc = (TOCEntry*) calloc(sizeof(TOCEntry), (size_t)f->entries);
    if (!f->toc) {
	return PR_FALSE;
    }
    numBuckets = PR_CeilingLog2(f->entries >> 1);
    if (!numBuckets) numBuckets = 1;
    f->numBuckets = numBuckets;
    f->buckets = (TOCEntry**) calloc(sizeof(TOCEntry*), (size_t)numBuckets);
    if (!f->buckets) {
	return PR_FALSE;
    }
    if (!ReadTOC(f)) {
	return PR_FALSE;
    }
    return PR_TRUE;
}

/*
** Shut de door
*/
static void CloseCar(CARFile *f)
{
    if (f) {
	if (f->file) {
	    fclose(f->file);
	    f->file = 0;
	}
    }
}

void CAR_Destroy(CARFile *f)
{
    if (f) {
	CloseCar(f);
	if (f->name) {
	    free(f->name);
	    f->name = 0;
	}
	if (f->buckets) {
	    free(f->buckets);
	    f->buckets = 0;
	}
	if (f->toc) {
	    TOCEntry *tp = f->toc;
	    TOCEntry *etp = tp + f->entries;
	    for (; tp < etp; tp++) {
		if (tp->name) {
		    free(tp->name);
		    tp->name = 0;
		}
	    }
	    free(f->toc);
	    f->toc = 0;
	}
	free(f);
    }
}

/*
** Hop on in
*/
CARFile *CAR_Open(const char *carname)
{
    FILE *fp;
    CARFile *f;

    fp = fopen(carname, "rb");
    if (!fp) return 0;
    f = (CARFile*) calloc(1, sizeof(CARFile));
    if (!f) {
	fclose(fp);
	return 0;
    }
    f->name = strdup(carname);
    f->file = fp;
    if (!ReadCar(f)) {
	CAR_Destroy(f);
	f = 0;
    }
    return f;
}

/*
** Look in the car for a given class name (look under the seats if we have
** to :-)
*/
TOCEntry **CAR_Lookup(CARFile *f, uint8 *name)
{
    uint32 hash = HashUTF(name);
    uint32 bucket = hash & (f->numBuckets - 1);
    TOCEntry **tep = &f->buckets[bucket];
    TOCEntry *te;

    while ((te = *tep) != 0) {
	if (te->nameHash == hash) {
	    if (strcmp((char*) te->name, (char*) name) == 0) {
		return tep;
	    }
	}
	tep = &te->next;
    }
    return 0;
}