libzip reorganization

--HG--
branch : HEAD
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..e20b4c9
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,5 @@
+noinst_LIBRARIES=libzip.a
+noinst_HEADERS=zip.h \
+	zipint.h
+
+libzip_a_SOURCES=zip_open.c
diff --git a/lib/zip.h b/lib/zip.h
new file mode 100644
index 0000000..6fa7feb
--- /dev/null
+++ b/lib/zip.h
@@ -0,0 +1,122 @@
+#ifndef _HAD_ZIP_H
+#define _HAD_ZIP_H
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <zlib.h>
+
+enum zip_state { Z_UNCHANGED, Z_DELETED, Z_REPLACED, Z_ADDED, Z_RENAMED };
+
+/* flags for zip_open */
+#define ZIP_CREATE           1
+#define ZIP_EXCL             2
+#define ZIP_CHECKCONS        4
+
+int zip_err; /* global variable for errors returned by the low-level
+		library */
+
+/* 0 is no error */
+#define ZERR_MULTIDISK        1
+#define ZERR_RENAME           2
+#define ZERR_CLOSE            3
+#define ZERR_SEEK             4
+#define ZERR_READ             5
+#define ZERR_WRITE            6
+#define ZERR_CRC              7
+#define ZERR_ZIPCLOSED        8
+#define ZERR_FILEEXISTS       9
+#define ZERR_FILENEXISTS     10
+#define ZERR_OPEN            11
+
+extern char *zip_err_str[];
+
+/* zip file */
+
+struct zf {
+    char *zn;
+    FILE *zp;
+    unsigned short comlen, changes;
+    unsigned int cd_size, cd_offset;
+    char *com;
+    int nentry, nentry_alloc;
+    struct zf_entry *entry;
+    int nfile, nfile_alloc;
+    struct zf_file **file;
+};
+
+/* file in zip file */
+
+struct zf_file {
+    struct zf *zf;
+    char *name;
+    int flags; /* -1: eof, >0: error */
+
+    int method;
+    /* position within zip file (fread/fwrite) */
+    long fpos;
+    /* no of bytes left to read */
+    unsigned long bytes_left;
+    /* no of bytes of compressed data left */
+    unsigned long cbytes_left;
+    /* crc so far */
+    unsigned long crc, crc_orig;
+    
+    char *buffer;
+    z_stream *zstr;
+};
+
+/* entry in zip file directory */
+
+struct zf_entry {
+    unsigned short version_made, version_need, bitflags, comp_meth,
+	lmtime, lmdate, fnlen, eflen, fcomlen, disknrstart, intatt;
+    unsigned int crc, comp_size, uncomp_size, extatt, local_offset;
+    enum zip_state state;
+    char *fn, *ef, *fcom;
+    char *fn_old;
+    /* only use one of the following three for supplying new data
+       listed in order of priority, if more than one is set */
+    struct zf *ch_data_zf;
+    char *ch_data_buf;
+    FILE *ch_data_fp;
+    /* offset & len of new data in ch_data_fp or ch_data_buf */
+    unsigned int ch_data_offset, ch_data_len;
+    /* if source is another zipfile, number of file in zipfile */
+    unsigned int ch_data_zf_fileno;
+};
+
+
+
+/* opening/closing zip files */
+
+struct zf *zip_open(char *fn, int checkp);
+int zip_close(struct zf *zf);
+
+int zip_name_locate(struct zf *zf, char *fn, int case_sens);
+
+/* read access to files in zip file */
+
+struct zf_file *zff_open(struct zf *zf, char *fn, int case_sens);
+struct zf_file *zff_open_index(struct zf *zf, int fileno);
+int zff_read(struct zf_file *zff, char *outbuf, int toread);
+int zff_close(struct zf_file *zff);
+
+/* high level routines to modify zip file */
+
+int zip_unchange(struct zf *zf, int idx);
+int zip_rename(struct zf *zf, int idx, char *name);
+int zip_add_file(struct zf *zf, char *name, FILE *file, int start, int len);
+int zip_add_data(struct zf *zf, char *name, char *buf, int start, int len);
+int zip_add_zip(struct zf *zf, char *name, struct zf *srczf, int srcidx,
+		int start, int len);
+int zip_replace_file(struct zf *zf, int idx, char *name, FILE *file,
+		     int start, int len);
+int zip_replace_data(struct zf *zf, int idx, char *name, char *buf,
+		     int start, int len);
+int zip_replace_zip(struct zf *zf, int idx, char *name,
+		    struct zf *srczf, int srcidx, int start, int len);
+
+int zip_delete(struct zf *zf, int idx);
+
+
+#endif /* _HAD_ZIP_H */
diff --git a/lib/zip_close.c b/lib/zip_close.c
new file mode 100644
index 0000000..48be564
--- /dev/null
+++ b/lib/zip_close.c
@@ -0,0 +1,522 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "zip.h"
+#include "zipint.h"
+
+static int _zip_entry_copy(struct zf *dest, struct zf *src,
+			  int entry_no, char *name);
+static int _zip_entry_add(struct zf *dest, struct zf *src, int entry_no);
+static int writecdir(struct zf *zfp)
+static void write2(FILE *fp, int i);
+static void write4(FILE *fp, int i);
+static void writestr(FILE *fp, char *str, int len);
+static int writecdentry(FILE *fp, struct zf_entry *zfe, int localp);
+static int _zip_create_entry(struct zf *dest, struct zf_entry *src_entry,
+			     char *name);
+
+
+
+/* zip_close:
+   Tries to commit all changes and close the zipfile; if it fails,
+   zip_err (and errno) are set and *zf is unchanged, except for
+   problems in zf_free. */ 
+
+int
+zip_close(struct zf *zf)
+{
+    int i, count, tfd;
+    char *temp;
+    FILE *tfp;
+    struct zf *tzf;
+
+    if (zf->changes == 0)
+	return zf_free(zf);
+
+    /* look if there really are any changes */
+    count = 0;
+    for (i=0; i<zf->nentry; i++) {
+	if (zf->entry[i].state != Z_UNCHANGED) {
+	    count = 1;
+	    break;
+	}
+    }
+
+    /* no changes, all has been unchanged */
+    if (count == 0)
+	return zf_free(zf);
+    
+    temp = (char *)xmalloc(strlen(zf->zn)+8);
+    sprintf(temp, "%s.XXXXXX", zf->zn);
+
+    tfd = mkstemp(temp);
+
+    if ((tfp=fdopen(tfd, "r+b")) == NULL) {
+	free(temp);
+	close(tfd);
+	return -1;
+    }
+
+    tzf = zf_new();
+    tzf->zp = tfp;
+    tzf->zn = temp;
+    tzf->nentry = 0;
+    tzf->comlen = zf->comlen;
+    tzf->cd_size = tzf->cd_offset = 0;
+    tzf->com = (unsigned char *)memdup(zf->com, zf->comlen);
+    tzf->entry = (struct zf_entry *)xmalloc(sizeof(struct
+						   zf_entry)*ALLOC_SIZE);
+    tzf->nentry_alloc = ALLOC_SIZE;
+    
+    count = 0;
+    if (zf->entry) {
+	for (i=0; i<zf->nentry; i++) {
+	    switch (zf->entry[i].state) {
+	    case Z_UNCHANGED:
+		if (zip_entry_copy(tzf, zf, i, NULL))
+		    myerror(ERRFILE, "zip_entry_copy failed");
+		break;
+	    case Z_DELETED:
+		break;
+	    case Z_REPLACED:
+		/* fallthrough */
+	    case Z_ADDED:
+		if (zf->entry[i].ch_data_zf) {
+		    if (zip_entry_copy(tzf, zf->entry[i].ch_data_zf,
+				       zf->entry[i].ch_data_zf_fileno,
+				       zf->entry[i].fn))
+			myerror(ERRFILE, "zip_entry_copy failed");
+		} else if (zf->entry[i].ch_data_buf) {
+#if 0
+		    zip_entry_add(tzf, zf, i);
+#endif /* 0 */
+		} else if (zf->entry[i].ch_data_fp) {
+#if 0
+		    zip_entry_add(tzf, zf, i);
+#endif /* 0 */
+		} else {
+		    /* XXX: ? */
+		    myerror(ERRFILE, "Z_ADDED: no data");
+		    break;
+		}
+		break;
+	    case Z_RENAMED:
+		if (zip_entry_copy(tzf, zf, i, NULL))
+		    myerror(ERRFILE, "zip_entry_copy failed");
+		break;
+	    default:
+		/* can't happen */
+		break;
+	    }
+	}
+    }
+
+    writecdir(tzf);
+    
+    if (fclose(tzf->zp)==0) {
+	if (rename(tzf->zn, zf->zn) != 0) {
+	    zip_err = ZERR_RENAME;
+	    zf_free(tzf);
+	    return -1;
+	}
+    }
+
+    free(temp);
+    zf_free(zf);
+
+    return 0;
+}
+
+
+
+static int
+_zip_entry_copy(struct zf *dest, struct zf *src, int entry_no, char *name)
+{
+    char buf[BUFSIZE];
+    unsigned int len, remainder;
+    unsigned char *null;
+    struct zf_entry tempzfe;
+
+    null = NULL;
+
+    zip_create_entry(dest, src->entry+entry_no, name);
+
+    if (fseek(src->zp, src->entry[entry_no].local_offset, SEEK_SET) != 0) {
+	zip_err = ZERR_SEEK;
+	return -1;
+    }
+
+    if (_zip_readcdentry(src->zp, &tempzfe, &null, 0, 1, 1) != 0) {
+	zip_err = ZERR_READ;
+	return -1;
+    }
+    
+    free(tempzfe.fn);
+    tempzfe.fn = xstrdup(dest->entry[dest->nentry-1].fn);
+    tempzfe.fnlen = dest->entry[dest->nentry-1].fnlen;
+
+    if (writecdentry(dest->zp, &tempzfe, 1) != 0) {
+	zip_err = ZERR_WRITE;
+	return -1;
+    }
+    
+    remainder = src->entry[entry_no].comp_size;
+    len = BUFSIZE;
+    while (remainder) {
+	if (len > remainder)
+	    len = remainder;
+	if (fread(buf, 1, len, src->zp)!=len) {
+	    zip_err = ZERR_READ;
+	    return -1;
+	}
+	if (fwrite(buf, 1, len, dest->zp)!=len) {
+	    zip_err = ZERR_WRITE;
+	    return -1;
+	}
+	remainder -= len;
+    }
+
+    return 0;
+}
+
+
+
+static int
+_zip_entry_add(struct zf *dest, struct zf *src, int entry_no)
+{
+    z_stream *zstr;
+    char *outbuf;
+    int ret, wrote;
+    
+    char buf[BUFSIZE];
+    unsigned int len, remainder;
+    unsigned char *null;
+
+    null = NULL;
+
+    if (!src)
+	return -1;
+    
+    zip_create_entry(dest, NULL, src->entry[entry_no].fn);
+
+    if (writecdentry(dest->zp, dest->entry+dest->nentry, 1) != 0) {
+	zip_err = ZERR_WRITE;
+	return -1;
+    }
+
+    if (src->entry[entry_no].ch_data_fp) {
+	if (fseek(src->entry[entry_no].ch_data_fp,
+		  src->entry[entry_no].ch_data_offset, SEEK_SET) != 0) {
+	    zip_err = ZERR_SEEK;
+	    return -1;
+	}
+    } else if (src->entry[entry_no].ch_data_buf) {
+	if (src->entry[entry_no].ch_data_offset)
+	    src->entry[entry_no].ch_data_buf +=
+		src->entry[entry_no].ch_data_offset;
+	
+	if (!src->entry[entry_no].ch_data_len) {
+	    /* obviously no data */
+	    dest->nentry++;
+	    return 0;
+	}
+    } else 
+	return 1;
+
+    zstr->zalloc = Z_NULL;
+    zstr->zfree = Z_NULL;
+    zstr->opaque = NULL;
+    zstr->avail_in = 0;
+    zstr->avail_out = 0;
+
+    /* -15: undocumented feature of zlib to _not_ write a zlib header */
+    deflateInit2(zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 9,
+		 Z_DEFAULT_STRATEGY);
+
+    if (src->entry[entry_no].ch_data_buf) {
+	outbuf = (char *)xmalloc(src->entry[entry_no].ch_data_len*1.01+12);
+	zstr->next_in = src->entry[entry_no].ch_data_buf;
+	zstr->avail_in = src->entry[entry_no].ch_data_len;
+	zstr->next_out = outbuf;
+	zstr->avail_out = src->entry[entry_no].ch_data_len*1.01+12;
+
+	ret = deflate(zstr, Z_FINISH);
+
+	switch(ret) {
+	case Z_STREAM_END:
+	    break;
+	default:
+	    myerror(ERRDEF, "zlib error while deflating buffer: %s",
+		    zstr->msg);
+	    return -1;
+	}
+	dest->entry[dest->nentry-1].crc =
+	    crc32(dest->entry[dest->nentry-1].crc,
+		  src->entry[entry_no].ch_data_buf,
+		  src->entry[entry_no].ch_data_len);
+	dest->entry[dest->nentry-1].uncomp_size =
+	    src->entry[entry_no].ch_data_len;
+	dest->entry[dest->nentry-1].comp_size = zstr->total_out;
+
+	wrote = 0;
+	if ((ret = fwrite(outbuf+wrote, 1, zstr->total_out-wrote, dest->zp))
+	    < zstr->total_out-wrote) {
+	    if (ferror(dest->zp)) {
+		zip_err = ZERR_WRITE;
+		return -1;
+	    }
+	    wrote += ret;
+	}
+
+	fseek(dest->zp, dest->entry[dest->nentry-1].local_offset,
+	      SEEK_SET);
+	if (ferror(dest->zp)) {
+	    zip_err = ZERR_SEEK;
+	    return -1;
+	}
+
+	if (writecdentry(dest->zp, dest->entry+dest->nentry, 1) != 0) {
+	    zip_err = ZERR_WRITE;
+	    return -1;
+	}
+
+	fseek(dest->zp, 0, SEEK_END);
+	if (ferror(dest->zp)) {
+	    zip_err = ZERR_SEEK;
+	    return -1;
+	}
+	
+	dest->nentry++;
+	return 0;
+    }
+
+    /* missing: fp, partial zf */
+    /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+#if 0	
+	/* XXX: ignore below  */
+	remainder = src->entry[entry_no].comp_size;
+	len = BUFSIZE;
+	while (remainder) {
+	    if (len > remainder)
+		len = remainder;
+	    if (fread(buf, 1, len, src->zp)!=len) {
+		zip_err = ZERR_READ;
+		return -1;
+	    }
+	    if (fwrite(buf, 1, len, dest->zp)!=len) {
+		zip_err = ZERR_WRITE;
+		return -1;
+	    }
+	    remainder -= len;
+	}
+
+	fseek(dest->zp, 0, SEEK_END);
+	dest->nentry++;
+	return 0;
+#endif
+	return 1;
+}
+
+
+
+static int
+writecdir(struct zf *zfp)
+{
+    int i;
+    long cd_offset, cd_size;
+
+    cd_offset = ftell(zfp->zp);
+
+    for (i=0; i<zfp->nentry; i++) {
+	if (writecdentry(zfp->zp, zfp->entry+i, 0) != 0) {
+	    zip_err = ZERR_WRITE;
+	    return -1;
+	}
+    }
+
+    cd_size = ftell(zfp->zp) - cd_offset;
+    
+    clearerr(zfp->zp);
+    fprintf(zfp->zp, EOCD_MAGIC);
+    fprintf(zfp->zp, "%c%c%c%c", 0, 0, 0, 0);
+    write2(zfp->zp, zfp->nentry);
+    write2(zfp->zp, zfp->nentry);
+    write4(zfp->zp, cd_size);
+    write4(zfp->zp, cd_offset);
+    write2(zfp->zp, zfp->comlen);
+    writestr(zfp->zp, zfp->com, zfp->comlen);
+
+    return 0;
+}
+
+
+
+static void
+write2(FILE *fp, int i)
+{
+    putc(i&0xff, fp);
+    putc((i>>8)&0xff, fp);
+
+    return;
+}
+
+
+
+static void
+write4(FILE *fp, int i)
+{
+    putc(i&0xff, fp);
+    putc((i>>8)&0xff, fp);
+    putc((i>>16)&0xff, fp);
+    putc((i>>24)&0xff, fp);
+    
+    return;
+}
+
+
+
+static void
+writestr(FILE *fp, char *str, int len)
+{
+#if WIZ
+    int i;
+    for (i=0; i<len; i++)
+	putc(str[i], fp);
+#else
+    fwrite(str, 1, len, fp);
+#endif
+    
+    return;
+}
+
+
+
+/* writecdentry:
+   if localp, writes local header for zfe to zf->zp,
+   else write central directory entry for zfe to zf->zp.
+   if after writing ferror(fp), return -1, else return 0.*/
+   
+static int
+writecdentry(FILE *fp, struct zf_entry *zfe, int localp)
+{
+    fprintf(fp, "%s", localp?LOCAL_MAGIC:CENTRAL_MAGIC);
+    
+    if (!localp)
+	write2(fp, zfe->version_made);
+    write2(fp, zfe->version_need);
+    write2(fp, zfe->bitflags);
+    write2(fp, zfe->comp_meth);
+    write2(fp, zfe->lmtime);
+    write2(fp, zfe->lmdate);
+
+    write4(fp, zfe->crc);
+    write4(fp, zfe->comp_size);
+    write4(fp, zfe->uncomp_size);
+    
+    write2(fp, zfe->fnlen);
+    write2(fp, zfe->eflen);
+    if (!localp) {
+	write2(fp, zfe->fcomlen);
+	write2(fp, zfe->disknrstart);
+	write2(fp, zfe->intatt);
+
+	write4(fp, zfe->extatt);
+	write4(fp, zfe->local_offset);
+    }
+    
+    if (zfe->fnlen)
+	writestr(fp, zfe->fn, zfe->fnlen);
+    if (zfe->eflen)
+	writestr(fp, zfe->ef, zfe->eflen);
+    if (zfe->fcomlen)
+	writestr(fp, zfe->fcom, zfe->fcomlen);
+
+    if (ferror(fp))
+	return -1;
+    
+    return 0;
+}
+
+
+
+static int
+_zip_create_entry(struct zf *dest, struct zf_entry *src_entry, char *name)
+{
+    time_t now_t;
+    struct tm *now;
+
+    if (!dest)
+	return -1;
+    
+    zip_new_entry(dest);
+
+    if (!src_entry) {
+	/* set default values for central directory entry */
+	dest->entry[dest->nentry-1].version_made = 20;
+	dest->entry[dest->nentry-1].version_need = 20;
+	/* maximum compression */
+	dest->entry[dest->nentry-1].bitflags = 2;
+	/* deflate algorithm */
+	dest->entry[dest->nentry-1].comp_meth = 8;
+	/* standard MS-DOS format time & date of compression start --
+	   thanks Info-Zip! (+1 for rounding) */
+	now_t = time(NULL)+1;
+	now = localtime(&now_t);
+	dest->entry[dest->nentry-1].lmtime = ((now->tm_year+1900-1980)<<9)+
+	    ((now->tm_mon+1)<<5) + now->tm_mday;
+	dest->entry[dest->nentry-1].lmdate = ((now->tm_hour)<<11)+
+	    ((now->tm_min)<<5) + ((now->tm_sec)>>1);
+	dest->entry[dest->nentry-1].fcomlen = 0;
+	dest->entry[dest->nentry-1].eflen = 0;
+	dest->entry[dest->nentry-1].disknrstart = 0;
+	/* binary data */
+	dest->entry[dest->nentry-1].intatt = 0;
+	/* XXX: init CRC-32, compressed and uncompressed size --
+	   will be updated later */
+	dest->entry[dest->nentry-1].crc = crc32(0, 0, 0);
+	dest->entry[dest->nentry-1].comp_size = 0;
+	dest->entry[dest->nentry-1].uncomp_size = 0;
+	dest->entry[dest->nentry-1].extatt = 0;
+	dest->entry[dest->nentry-1].ef = NULL;
+	dest->entry[dest->nentry-1].fcom = NULL;
+    } else {
+	/* copy values from original zf_entry */
+	dest->entry[dest->nentry-1].version_made = src_entry->version_made;
+	dest->entry[dest->nentry-1].version_need = src_entry->version_need;
+	dest->entry[dest->nentry-1].bitflags = src_entry->bitflags;
+	dest->entry[dest->nentry-1].comp_meth = src_entry->comp_meth;
+	dest->entry[dest->nentry-1].lmtime = src_entry->lmtime;
+	dest->entry[dest->nentry-1].lmdate = src_entry->lmdate;
+	dest->entry[dest->nentry-1].fcomlen = src_entry->fcomlen;
+	dest->entry[dest->nentry-1].eflen = src_entry->eflen;
+	dest->entry[dest->nentry-1].disknrstart = src_entry->disknrstart;
+	dest->entry[dest->nentry-1].intatt = src_entry->intatt;
+	dest->entry[dest->nentry-1].crc = src_entry->crc;
+	dest->entry[dest->nentry-1].comp_size = src_entry->comp_size;
+	dest->entry[dest->nentry-1].uncomp_size = src_entry->uncomp_size;
+	dest->entry[dest->nentry-1].extatt = src_entry->extatt;
+	dest->entry[dest->nentry-1].ef = (char *)memdup(src_entry->ef,
+						      src_entry->eflen);
+	dest->entry[dest->nentry-1].fcom = (char *)memdup(src_entry->fcom,
+							src_entry->fcomlen);
+    }
+
+    dest->entry[dest->nentry-1].local_offset = ftell(dest->zp);
+
+    if (name) {
+	dest->entry[dest->nentry-1].fn = xstrdup(name);
+	dest->entry[dest->nentry-1].fnlen = strlen(name);
+    } else if (src_entry && src_entry->fn) {
+	dest->entry[dest->nentry-1].fn = xstrdup(src_entry->fn);
+	dest->entry[dest->nentry-1].fnlen = src_entry->fnlen;
+    } else {
+	dest->entry[dest->nentry-1].fn = xstrdup("-");
+	dest->entry[dest->nentry-1].fnlen = 1;
+    }
+
+    return 0;
+}
diff --git a/lib/zip_name_locate.c b/lib/zip_name_locate.c
new file mode 100644
index 0000000..e6e708d
--- /dev/null
+++ b/lib/zip_name_locate.c
@@ -0,0 +1,21 @@
+#include <string.h>
+
+#include "zip.h"
+#include "zipint.h"
+
+
+
+int
+zip_name_locate(struct zf *zf, char *fname, int case_sens)
+{
+    int i;
+
+    if (case_sens == 0)
+	case_sens = 1 /* XXX: os default */;
+
+    for (i=0; i<zf->nentry; i++)
+	if ((case_sens ? strcmp : strcasecmp)(fname, zf->entry[i].fn) == 0)
+	    return i;
+
+    return -1;
+}
diff --git a/lib/zip_open.c b/lib/zip_open.c
new file mode 100644
index 0000000..b42cc56
--- /dev/null
+++ b/lib/zip_open.c
@@ -0,0 +1,487 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "zip.h"
+#include "zipint.h"
+
+static struct zf *readcdir(FILE *fp, unsigned char *buf,
+			   unsigned char *eocd, int buflen);
+static int read2(unsigned char **a);
+static int read4(unsigned char **a);
+static char *readstr(unsigned char **buf, int len, int nulp);
+static char *readfpstr(FILE *fp, int len, int nulp);
+static int checkcons(FILE *fp, struct zf *zf);
+static int headercomp(struct zf_entry *h1, int local1p, struct zf_entry *h2,
+		      int local2p);
+
+
+
+/* zip_open:
+   Tries to open the file 'fn' as a zipfile. If flags & ZIP_CHECKCONS,
+   also does some consistency checks (comparing local headers to
+   central directory entries). If flags & ZIP_CREATE, make a new file
+   (if flags & ZIP_EXCL, error if already existing).  Returns a
+   zipfile struct, or NULL if unsuccessful, setting zip_err. */
+
+struct zf *
+zip_open(char *fn, int flags)
+{
+    FILE *fp;
+    unsigned char *buf, *match;
+    int a, i, buflen, best;
+    struct zf *cdir, *cdirnew;
+    long len;
+    struct stat st;
+
+    if (fn == NULL)
+	return NULL;
+    
+    if (stat(fn, &st) != 0) {
+	if (flags & ZIP_CREATE) {
+	    cdir = zf_new();
+	    cdir->zn = xstrdup(fn);
+	    return cdir;
+	} else {
+	    zip_err = ZERR_FILENEXISTS;
+	    return NULL;
+	}
+    } else if ((flags & ZIP_EXCL)) {
+	zip_err = ZERR_FILEEXISTS;
+	return NULL;
+    }
+    /* ZIP_CREAT gets ignored if file exists and not ZIP_EXCL,
+       just like open() */
+    
+    if ((fp=fopen(fn, "rb"))==NULL) {
+	zip_err = ZERR_OPEN;
+	return NULL;
+    }
+    
+    clearerr(fp);
+    fseek(fp, 0, SEEK_END);
+    len = ftell(fp);
+    i = fseek(fp, -(len < BUFSIZE ? len : BUFSIZE), SEEK_END);
+    if (i == -1 && errno != EFBIG) {
+	/* seek before start of file on my machine */
+	fclose(fp);
+	return NULL;
+    }
+
+    buf = (unsigned char *)xmalloc(BUFSIZE);
+
+    clearerr(fp);
+    buflen = fread(buf, 1, BUFSIZE, fp);
+
+    if (ferror(fp)) {
+	/* read error */
+	free(buf);
+	fclose(fp);
+	return NULL;
+    }
+    
+    best = -1;
+    cdir = NULL;
+    match = buf;
+    while ((match=memmem(match, buflen-(match-buf)-18, EOCD_MAGIC, 4))!=NULL) {
+	/* found match -- check, if good */
+	/* to avoid finding the same match all over again */
+	match++;
+	if ((cdirnew=readcdir(fp, buf, match-1, buflen)) == NULL)
+	    continue;	    
+
+	if (cdir) {
+	    if (best <= 0)
+		best = checkcons(fp, cdir);
+	    a = checkcons(fp, cdirnew);
+	    if (best < a) {
+		zf_free(cdir);
+		cdir = cdirnew;
+		best = a;
+	    }
+	    else
+		zf_free(cdirnew);
+	}
+	else {
+	    cdir = cdirnew;
+	    if (flags & ZIP_CHECKCONS)
+		best = checkcons(fp, cdir);
+	    else
+		best = 0;
+	}
+	cdirnew = NULL;
+    }
+
+    if (best < 0) {
+	/* no consistent eocd found */
+	free(buf);
+	zf_free(cdir);
+	fclose(fp);
+	return NULL;
+    }
+
+    free(buf);
+
+    cdir->zn = xstrdup(fn);
+    cdir->zp = fp;
+    
+    return cdir;
+}
+
+
+
+/* readcdir:
+   tries to find a valid end-of-central-directory at the beginning of
+   buf, and then the corresponding central directory entries.
+   Returns a zipfile struct which contains the central directory 
+   entries, or NULL if unsuccessful. */
+
+static struct zf *
+readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen)
+{
+    struct zf *zf;
+    unsigned char *cdp;
+    int i, comlen, readp;
+
+    comlen = buf + buflen - eocd - EOCDLEN;
+    if (comlen < 0) {
+	/* not enough bytes left for comment */
+	return NULL;
+    }
+
+    /* check for end-of-central-dir magic */
+    if (memcmp(eocd, EOCD_MAGIC, 4) != 0)
+	return NULL;
+
+    if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) {
+	zip_err = ZERR_MULTIDISK;
+	return NULL;
+    }
+
+    zf = zf_new();
+
+    cdp = eocd + 8;
+    /* number of cdir-entries on this disk */
+    i = read2(&cdp);
+    /* number of cdir-entries */
+    zf->nentry = zf->nentry_alloc = read2(&cdp);
+    zf->cd_size = read4(&cdp);
+    zf->cd_offset = read4(&cdp);
+    zf->comlen = read2(&cdp);
+    zf->entry = NULL;
+
+    if ((zf->comlen != comlen) || (zf->nentry != i)) {
+	/* comment size wrong -- too few or too many left after central dir */
+	/* or number of cdir-entries on this disk != number of cdir-entries */
+	zf_free(zf);
+	return NULL;
+    }
+
+    zf->com = (unsigned char *)memdup(eocd+EOCDLEN, zf->comlen);
+
+    cdp = eocd;
+    if (zf->cd_size < eocd-buf) {
+	/* if buffer already read in, use it */
+	readp = 0;
+	cdp = eocd - zf->cd_size;
+    }
+    else {
+	/* go to start of cdir and read it entry by entry */
+	readp = 1;
+	clearerr(fp);
+	fseek(fp, -(zf->cd_size+zf->comlen+EOCDLEN), SEEK_END);
+	if (ferror(fp) || (ftell(fp) != zf->cd_offset)) {
+	    /* seek error or offset of cdir wrong */
+	    zf_free(zf);
+	    return NULL;
+	}
+    }
+
+    zf->entry = (struct zf_entry *)xmalloc(sizeof(struct zf_entry)
+					   *zf->nentry);
+    for (i=0; i<zf->nentry; i++) {
+	zf->entry[i].fn = NULL;
+	zf->entry[i].ef = NULL;
+	zf->entry[i].fcom = NULL;
+	zip_entry_init(zf, i);
+    }
+    
+    for (i=0; i<zf->nentry; i++) {
+	if ((_zip_readcdentry(fp, zf->entry+i, &cdp, eocd-cdp, readp, 0)) < 0) {
+	    /* i entries have already been filled, tell zf_free
+	       how many to free */
+	    zf_free(zf);
+	    return NULL;
+	}
+    }
+    
+    return zf;
+}
+
+
+
+/* _zip_readcdentry:
+   fills the zipfile entry zfe with data from the buffer *cdpp, not reading
+   more than 'left' bytes from it; if readp != 0, it also reads more data
+   from fp, if necessary. If localp != 0, it reads a local header instead
+   of a central directory entry. Returns 0 if successful, -1 if not,
+   advancing *cdpp for each byte read. */
+
+int
+_zip_readcdentry(FILE *fp, struct zf_entry *zfe, unsigned char **cdpp, 
+		 int left, int readp, int localp)
+{
+    unsigned char buf[CDENTRYSIZE];
+    unsigned char *cur;
+    int size;
+
+    if (localp)
+	size = LENTRYSIZE;
+    else
+	size = CDENTRYSIZE;
+    
+    if (readp) {
+	/* read entry from disk */
+	if ((fread(buf, 1, size, fp)<size))
+	    return -1;
+	left = size;
+	cur = buf;
+    }
+    else {
+	cur = *cdpp;
+	if (left < size)
+	    return -1;
+    }
+
+    if (localp) {
+	if (memcmp(cur, LOCAL_MAGIC, 4)!=0)
+	    return -1;
+    }
+    else
+	if (memcmp(cur, CENTRAL_MAGIC, 4)!=0)
+	    return -1;
+
+    cur += 4;
+
+    /* convert buffercontents to zf_entry */
+    if (!localp)
+	zfe->version_made = read2(&cur);
+    else
+	zfe->version_made = 0;
+    zfe->version_need = read2(&cur);
+    zfe->bitflags = read2(&cur);
+    zfe->comp_meth = read2(&cur);
+    zfe->lmtime = read2(&cur);
+    zfe->lmdate = read2(&cur);
+
+    zfe->crc = read4(&cur);
+    zfe->comp_size = read4(&cur);
+    zfe->uncomp_size = read4(&cur);
+    
+    zfe->fnlen = read2(&cur);
+    zfe->eflen = read2(&cur);
+    if (!localp) {
+	zfe->fcomlen = read2(&cur);
+	zfe->disknrstart = read2(&cur);
+	zfe->intatt = read2(&cur);
+
+	zfe->extatt = read4(&cur);
+	zfe->local_offset = read4(&cur);
+    }
+    else {
+	zfe->fcomlen = zfe->disknrstart = zfe->intatt = 0;
+	zfe->extatt = zfe->local_offset = 0;
+    }
+    
+    if (left < CDENTRYSIZE+zfe->fnlen+zfe->eflen+zfe->fcomlen) {
+	if (readp) {
+	    if (zfe->fnlen)
+		zfe->fn = readfpstr(fp, zfe->fnlen, 1);
+	    else
+		zfe->fn = xstrdup("");
+	    if (zfe->eflen)
+		zfe->ef = readfpstr(fp, zfe->eflen, 0);
+	    /* XXX: really null-terminate comment? */
+	    if (zfe->fcomlen)
+		zfe->fcom = readfpstr(fp, zfe->fcomlen, 1);
+	}
+	else {
+	    /* can't get more bytes if not allowed to read */
+	    return -1;
+	}
+    }
+    else {
+        if (zfe->fnlen)
+	    zfe->fn = readstr(&cur, zfe->fnlen, 1);
+        if (zfe->eflen)
+	    zfe->ef = readstr(&cur, zfe->eflen, 0);
+        if (zfe->fcomlen)
+	    zfe->fcom = readstr(&cur, zfe->fcomlen, 1);
+    }
+    if (!readp)
+      *cdpp = cur;
+
+    /* XXX */
+    zfe->ch_data_fp = NULL;
+    zfe->ch_data_buf = NULL;
+    zfe->ch_data_offset = 0;
+    zfe->ch_data_len = 0;
+
+    return 0;
+}
+
+
+
+static int
+read2(unsigned char **a)
+{
+    int ret;
+
+    ret = (*a)[0]+((*a)[1]<<8);
+    *a += 2;
+
+    return ret;
+}
+
+
+
+static int
+read4(unsigned char **a)
+{
+    int ret;
+
+    ret = ((((((*a)[3]<<8)+(*a)[2])<<8)+(*a)[1])<<8)+(*a)[0];
+    *a += 4;
+
+    return ret;
+}
+
+
+
+static char *
+readstr(unsigned char **buf, int len, int nulp)
+{
+    char *r;
+
+    r = (char *)xmalloc(nulp?len+1:len);
+    memcpy(r, *buf, len);
+    *buf += len;
+
+    if (nulp)
+	r[len] = 0;
+
+    return r;
+}
+
+
+
+static char *
+readfpstr(FILE *fp, int len, int nullp)
+{
+    char *r;
+
+    r = (char *)xmalloc(nullp?len+1:len);
+    if (fread(r, 1, len, fp)<len) {
+	free(r);
+	return NULL;
+    }
+
+    if (nullp)
+	r[len] = 0;
+    
+    return r;
+}
+
+
+
+/* checkcons:
+   Checks the consistency of the central directory by comparing central
+   directory entries with local headers and checking for plausible
+   file and header offsets. Returns -1 if not plausible, else the
+   difference between the lowest and the highest fileposition reached */
+
+static int
+checkcons(FILE *fp, struct zf *zf)
+{
+    int min, max, i, j;
+    struct zf_entry temp;
+    unsigned char *buf;
+
+    buf = NULL;
+
+    if (zf->nentry) {
+	max = zf->entry[0].local_offset;
+	min = zf->entry[0].local_offset;
+    }
+
+    for (i=0; i<zf->nentry; i++) {
+	if (zf->entry[i].local_offset < min)
+	    min = zf->entry[i].local_offset;
+	if (min < 0)
+	    return -1;
+
+	j = zf->entry[i].local_offset + zf->entry[i].comp_size
+	    + zf->entry[i].fnlen + zf->entry[i].eflen
+	    + zf->entry[i].fcomlen + LENTRYSIZE;
+	if (j > max)
+	    max = j;
+	if (max > zf->cd_offset)
+	    return -1;
+
+	if (fseek(fp, zf->entry[i].local_offset, SEEK_SET) != 0) {
+	    zip_err = ZERR_SEEK;
+	    return -1;
+	}
+	_zip_readcdentry(fp, &temp, &buf, 0, 1, 1);
+	if (headercomp(zf->entry+i, 0, &temp, 1) != 0)
+	    return -1;
+    }
+
+    return max - min;
+}
+
+
+
+/* headercomp:
+   compares two headers h1 and h2; if they are local headers, set
+   local1p or local2p respectively to 1, else 0. Return 0 if they
+   are identical, -1 if not. */
+
+static int
+headercomp(struct zf_entry *h1, int local1p, struct zf_entry *h2,
+	   int local2p)
+{
+    if ((h1->version_need != h2->version_need)
+	|| (h1->bitflags != h2->bitflags)
+	|| (h1->comp_meth != h2->comp_meth)
+	|| (h1->lmtime != h2->lmtime)
+	|| (h1->lmdate != h2->lmdate)
+	|| (h1->fnlen != h2->fnlen)
+	|| (h1->crc != h2->crc)
+	|| (h1->comp_size != h2->comp_size)
+	|| (h1->uncomp_size != h2->uncomp_size)
+	|| (h1->fnlen && memcmp(h1->fn, h2->fn, h1->fnlen)))
+	return -1;
+
+    /* if they are different type, nothing more to check */
+    if (local1p != local2p)
+	return 0;
+
+    if ((h1->version_made != h2->version_made)
+	|| (h1->disknrstart != h2->disknrstart)
+	|| (h1->intatt != h2->intatt)
+	|| (h1->extatt != h2->extatt)
+	|| (h1->local_offset != h2->local_offset)
+	|| (h1->eflen != h2->eflen)
+	|| (h1->eflen && memcmp(h1->fn, h2->fn, h1->fnlen))
+	|| (h1->fcomlen != h2->fcomlen)
+	|| (h1->fcomlen && memcmp(h1->fcom, h2->fcom, h1->fcomlen))) {
+	return -1;
+    }
+
+    return 0;
+}
diff --git a/lib/zipint.h b/lib/zipint.h
new file mode 100644
index 0000000..9a8bcf4
--- /dev/null
+++ b/lib/zipint.h
@@ -0,0 +1,20 @@
+#ifndef _HAD_ZIPINT_H
+#define _HAD_ZIPINT_H
+
+
+#define MAXCOMLEN        65536
+#define EOCDLEN             22
+#define BUFSIZE       (MAXCOMLEN+EOCDLEN)
+#define LOCAL_MAGIC   "PK\3\4"
+#define CENTRAL_MAGIC "PK\1\2"
+#define EOCD_MAGIC    "PK\5\6"
+#define DATADES_MAGIC "PK\7\8"
+#define CDENTRYSIZE         46
+#define LENTRYSIZE          30
+
+struct zf *_zip_zf_new(void);
+int _zip_zf_free(struct zf *zf);
+int _zip_readcdentry(FILE *fp, struct zf_entry *zfe, unsigned char **cdpp, 
+		     int left, int readp, int localp);
+
+#endif /* zipint.h */
diff --git a/zf_new_free.c b/zf_new_free.c
new file mode 100644
index 0000000..c9fa182
--- /dev/null
+++ b/zf_new_free.c
@@ -0,0 +1,81 @@
+#include <stdlib.h>
+
+#include "zip.h"
+#include "zipint.h"
+
+
+
+/* _zip_zf_new:
+   creates a new zipfile struct, and sets the contents to zero; returns
+   the new struct. */
+
+struct zf *
+_zip_zf_new(void)
+{
+    struct zf *zf;
+
+    zf = (struct zf *)xmalloc(sizeof(struct zf));
+
+    zf->zn = NULL;
+    zf->zp = NULL;
+    zf->comlen = zf->changes = 0;
+    zf->nentry = zf->nentry_alloc = zf->cd_size = zf->cd_offset = 0;
+    zf->nfile = zf->nfile_alloc = 0;
+    zf->com = NULL;
+    zf->entry = NULL;
+    zf->file = NULL;
+    
+    return zf;
+}
+
+
+
+/* _zip_zf_free:
+   frees the space allocated to a zipfile struct, and closes the
+   corresponding file. Returns 0 if successful, the error returned
+   by fclose if not. */
+
+int
+_zip_zf_free(struct zf *zf)
+{
+    int i, ret;
+
+    if (zf == NULL)
+	return 0;
+
+    if (zf->zn)
+	free(zf->zn);
+
+    if (zf->zp)
+	ret = fclose(zf->zp);
+
+    if (zf->com)
+	free(zf->com);
+
+    if (zf->entry) {
+	for (i=0; i<zf->nentry; i++) {
+	    free(zf->entry[i].fn);
+	    free(zf->entry[i].ef);
+	    free(zf->entry[i].fcom);
+	    free(zf->entry[i].fn_old);
+	    if (zf->entry[i].ch_data_fp)
+		(void)fclose(zf->entry[i].ch_data_fp);
+	}
+	free (zf->entry);
+    }
+
+    for (i=0; i<zf->nfile; i++) {
+	zf->file[i]->flags = ZERR_ZIPCLOSED;
+	zf->file[i]->zf = NULL;
+	zf->file[i]->name = NULL;
+    }
+
+    free(zf->file);
+    
+    free(zf);
+
+    if (ret)
+	zip_err = ZERR_CLOSE;
+    
+    return ret;
+}
diff --git a/zff.c b/zff.c
new file mode 100644
index 0000000..d02def9
--- /dev/null
+++ b/zff.c
@@ -0,0 +1,265 @@
+#include <stdlib.h>
+#include "error.h"
+#include "xmalloc.h"
+#include "ziplow.h"
+#include "zip.h"
+
+struct zf_file *
+zff_new(struct zf *zf)
+{
+    struct zf_file *zff;
+
+    if (zf->nfile >= zf->nfile_alloc-1) {
+	zf->nfile_alloc += 10;
+	zf->file = (struct zf_file **)xrealloc(zf->file, zf->nfile_alloc
+					      *sizeof(struct zf_file *));
+    }
+
+    zff = (struct zf_file *)xmalloc(sizeof(struct zf_file));
+    zf->file[zf->nfile++] = zff;
+
+    zff->zf = zf;
+    zff->flags = 0;
+    zff->crc = crc32(0L, Z_NULL, 0);
+    zff->crc_orig = 0;
+    zff->method = -1;
+    zff->bytes_left = zff->cbytes_left = 0;
+    zff->fpos = 0;
+    zff->buffer = NULL;
+    zff->zstr = NULL;
+
+    return zff;
+}
+
+
+
+int
+zff_fillbuf(char *buf, int buflen, struct zf_file *zff)
+{
+    int i, j;
+
+    if (zff->flags != 0)
+	return -1;
+    if (zff->cbytes_left <= 0 || buflen <= 0)
+	return 0;
+    
+    fseek(zff->zf->zp, zff->fpos, SEEK_SET);
+    if (buflen < zff->cbytes_left)
+	i = buflen;
+    else
+	i = zff->cbytes_left;
+
+    j = fread(buf, 1, i, zff->zf->zp);
+    if (j == 0) 
+	zff->flags = ZERR_SEEK;
+    else if (j < 0)
+	zff->flags = ZERR_READ;
+    else {
+	zff->fpos += j;
+	zff->cbytes_left -= j;
+    }
+
+    return j;	
+}
+
+
+
+int
+zff_close(struct zf_file *zff)
+{
+    int i, ret;
+    
+    if (zff->zstr)
+	inflateEnd(zff->zstr);
+    free(zff->buffer);
+    free(zff->zstr);
+
+    for (i=0; i<zff->zf->nfile; i++) {
+	if (zff->zf->file[i] == zff) {
+	    zff->zf->file[i] = zff->zf->file[zff->zf->nfile-1];
+	    zff->zf->nfile--;
+	    break;
+	}
+    }
+
+    /* EOF is ok */
+    ret = (zff->flags == -1 ? 0 : zff->flags);
+    if (!ret)
+	ret = (zff->crc_orig == zff->crc);
+
+    free(zff);
+    return ret;
+}
+
+
+
+struct zf_file *
+zff_open(struct zf *zf, char *fname, int case_sens)
+{
+    int idx;
+
+    if ((idx=zip_name_locate(zf, fname, case_sens)) < 0)
+	return NULL;
+
+    return zff_open_index(zf, idx);
+}
+
+
+
+struct zf_file *
+zff_open_index(struct zf *zf, int fileno)
+{
+    unsigned char buf[4], *c;
+    int len;
+    struct zf_file *zff;
+
+    if ((fileno < 0) || (fileno >= zf->nentry))
+	return NULL;
+
+    if (zf->entry[fileno].state != Z_UNCHANGED
+	&& zf->entry[fileno].state != Z_RENAMED)
+	return NULL;
+
+    if ((zf->entry[fileno].comp_meth != 0)
+	&& (zf->entry[fileno].comp_meth != 8)) {
+	myerror(ERRFILE, "unsupported compression method %d",
+		zf->entry[fileno].comp_meth);
+	return NULL;
+    }
+
+    zff = zff_new(zf);
+
+    zff->name = zf->entry[fileno].fn;
+    zff->method = zf->entry[fileno].comp_meth;
+    zff->bytes_left = zf->entry[fileno].uncomp_size;
+    zff->cbytes_left = zf->entry[fileno].comp_size;
+    zff->crc_orig = zf->entry[fileno].crc;
+
+    /* go to start of actual data */
+    fseek (zf->zp, zf->entry[fileno].local_offset+LENTRYSIZE-4, SEEK_SET);
+    len = fread(buf, 1, 4, zf->zp);
+    if (len != 4) {
+	myerror (ERRSTR, "can't read local header");
+	return NULL;
+    }
+    c = buf;
+    zff->fpos = zf->entry[fileno].local_offset+LENTRYSIZE;
+    zff->fpos += read2(&c);
+    zff->fpos += read2(&c);
+	
+    if (zf->entry[fileno].comp_meth == 0)
+	return zff;
+    
+    zff->buffer = (char *)xmalloc(BUFSIZE);
+
+    len = zff_fillbuf (zff->buffer, BUFSIZE, zff);
+    if (len <= 0) {
+	/* XXX: error handling */
+	zff_close(zff);
+	return NULL;
+    }
+
+    zff->zstr = (z_stream *)xmalloc(sizeof(z_stream));
+    zff->zstr->zalloc = Z_NULL;
+    zff->zstr->zfree = Z_NULL;
+    zff->zstr->opaque = NULL;
+    zff->zstr->next_in = zff->buffer;
+    zff->zstr->avail_in = len;
+
+    /* negative value to tell zlib that there is no header */
+    if (inflateInit2(zff->zstr, -MAX_WBITS) != Z_OK) {
+	/* XXX: error here 
+	   myerror(ERRFILE, zff->zstr->msg);
+	*/
+	zff_close(zff);
+	return NULL;
+    }
+    
+    return zff;
+}
+
+
+
+
+int
+zff_read(struct zf_file *zff, char *outbuf, int toread)
+{
+    int len, out_before, ret;
+
+    if (!zff)
+	return -1;
+
+    if (zff->flags != 0)
+	return -1;
+
+    if (toread == 0)
+	return 0;
+
+    if (zff->bytes_left == 0) {
+	zff->flags = -1;
+	if (zff->crc != zff->crc_orig) {
+	    zff->flags = ZERR_CRC;
+	    return -1;
+	}
+	return 0;
+    }
+    
+    if (zff->method == 0) {
+	ret = zff_fillbuf(outbuf, toread, zff);
+	if (ret > 0) {
+	    zff->crc = crc32(zff->crc, outbuf, ret);
+	    zff->bytes_left -= ret;
+	}
+	return ret;
+    }
+    
+    zff->zstr->next_out = outbuf;
+    zff->zstr->avail_out = toread;
+    out_before = zff->zstr->total_out;
+    
+    /* endless loop until something has been accomplished */
+    for (;;) {
+	ret = inflate(zff->zstr, Z_SYNC_FLUSH);
+
+	switch (ret) {
+	case Z_OK:
+	case Z_STREAM_END:
+	    /* all ok */
+	    /* XXX: STREAM_END probably won't happen, since we didn't
+	       have a header */
+	    len = zff->zstr->total_out - out_before;
+	    if (len >= zff->bytes_left || len >= toread) {
+		zff->crc = crc32(zff->crc, outbuf, len);
+		zff->bytes_left -= len;
+	        return(len);
+	    }
+	    break;
+
+	case Z_BUF_ERROR:
+	    if (zff->zstr->avail_in == 0) {
+		/* read some more bytes */
+		len = zff_fillbuf(zff->buffer, BUFSIZE, zff);
+		if (len <= 0) {
+		    /* XXX: error
+		       myerror (ERRSTR, "read error");
+		    */
+		    return -1;
+		}
+		zff->zstr->next_in = zff->buffer;
+		zff->zstr->avail_in = len;
+		continue;
+	    }
+	    /* XXX: set error */
+	    myerror(ERRFILE, "zlib error: buf_err: %s", zff->zstr->msg);
+	    return -1;
+	case Z_NEED_DICT:
+	case Z_DATA_ERROR:
+	case Z_STREAM_ERROR:
+	case Z_MEM_ERROR:
+	    /* XXX: set error */
+	    myerror(ERRFILE, "zlib error: %s", zff->zstr->msg);
+	    return -1;
+	}
+    }
+}
+
diff --git a/zip.c b/zip.c
new file mode 100644
index 0000000..a344680
--- /dev/null
+++ b/zip.c
@@ -0,0 +1,268 @@
+#include <stdlib.h>
+
+#include "zip.h"
+#include "xmalloc.h"
+
+static int zip_unchange_data(struct zf *zf, int idx);
+static int zip_set_name(struct zf *zf, int idx, char *name);
+
+/* XXX: place in internal header file. */
+void zip_new_entry(struct zf *zf);
+void zip_entry_init(struct zf *zf, int idx);
+
+
+
+int
+zip_rename(struct zf *zf, int idx, char *name)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    if (zf->entry[idx].state == Z_UNCHANGED) 
+	zf->entry[idx].state = Z_RENAMED;
+    zf->changes = 1;
+
+    zip_set_name(zf, idx, name);
+
+    return idx;
+}
+
+
+
+int
+zip_add_file(struct zf *zf, char *name, FILE *file,
+	int start, int len)
+{
+    zip_new_entry(zf);
+
+    zf->changes = 1;
+    zip_set_name(zf, zf->nentry-1, name ? name : "-");
+    zf->entry[zf->nentry-1].ch_data_fp = file;
+    zf->entry[zf->nentry-1].ch_data_offset = start;
+    zf->entry[zf->nentry-1].ch_data_len = len;
+
+    return zf->nentry-1;
+}
+
+
+
+int
+zip_add_data(struct zf *zf, char *name, char *buf,
+	int start, int len)
+{
+    zip_new_entry(zf);
+
+    zf->changes = 1;
+    zip_set_name(zf, zf->nentry-1, name ? name : "-");
+    zf->entry[zf->nentry-1].ch_data_buf = buf;
+    zf->entry[zf->nentry-1].ch_data_offset = start;
+    zf->entry[zf->nentry-1].ch_data_len = len;
+
+    return zf->nentry-1;
+}
+
+
+
+int
+zip_add_zip(struct zf *zf, char *name, struct zf *srczf, int srcidx,
+	    int start, int len)
+{
+    if (srcidx >= srczf->nentry || srcidx < 0)
+	return -1;
+
+    zip_new_entry(zf);
+
+    zf->changes = 1;
+    zip_set_name(zf, zf->nentry-1, name ? name : srczf->entry[srcidx].fn);
+    zf->entry[zf->nentry-1].ch_data_zf = srczf;
+    zf->entry[zf->nentry-1].ch_data_zf_fileno = srcidx;
+    zf->entry[zf->nentry-1].ch_data_offset = start;
+    zf->entry[zf->nentry-1].ch_data_len = len;
+
+    return zf->nentry-1;
+}
+
+
+
+int
+zip_replace_file(struct zf *zf, int idx, char *name, FILE *file,
+	    int start, int len)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    zip_unchange_data(zf, idx);
+
+    zf->changes = 1;
+    zf->entry[idx].state = Z_REPLACED;
+    zip_set_name(zf, idx, name);
+    
+    zf->entry[idx].ch_data_fp = file;
+    zf->entry[idx].ch_data_offset = start;
+    zf->entry[idx].ch_data_len = len;
+
+    return idx;
+}
+
+
+
+int
+zip_replace_data(struct zf *zf, int idx, char *name, char *buf,
+	    int start, int len)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    zip_unchange_data(zf, idx);
+
+    zf->changes = 1;
+    zf->entry[idx].state = Z_REPLACED;
+    zip_set_name(zf, idx, name);
+    
+    zf->entry[idx].ch_data_buf = buf;
+    zf->entry[idx].ch_data_offset = start;
+    zf->entry[idx].ch_data_len = len;
+
+    return idx;
+}
+
+
+
+int
+zip_replace_zip(struct zf *zf, int idx, char *name, struct zf *srczf,
+		int srcidx, int start, int len)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    if (srcidx >= srczf->nentry || srcidx < 0)
+	return -1;
+
+    zip_unchange_data(zf, idx);
+
+    zf->changes = 1;
+    zf->entry[idx].state = Z_REPLACED;
+    zip_set_name(zf, idx, name);
+    
+    zf->entry[idx].ch_data_zf = srczf;
+    zf->entry[idx].ch_data_zf_fileno = srcidx;
+    zf->entry[idx].ch_data_offset = start;
+    zf->entry[idx].ch_data_len = len;
+
+    return idx;
+}
+
+
+
+int
+zip_delete(struct zf *zf, int idx)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    zip_unchange(zf, idx);
+
+    zf->changes = 1;
+    zf->entry[idx].state = Z_DELETED;
+
+    return idx;
+}
+
+
+
+int
+zip_unchange(struct zf *zf, int idx)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    if (zf->entry[idx].fn_old) {
+	free(zf->entry[idx].fn);
+	zf->entry[idx].fn = zf->entry[idx].fn_old;
+	zf->entry[idx].fn_old = NULL;
+	zf->entry[idx].fnlen = strlen(zf->entry[idx].fn);
+    }
+
+    return zip_unchange_data(zf, idx);
+}
+
+
+
+static int
+zip_unchange_data(struct zf *zf, int idx)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    if (zf->entry[idx].ch_data_fp) {
+	fclose(zf->entry[idx].ch_data_fp);
+	zf->entry[idx].ch_data_fp = NULL;
+    }
+    if (zf->entry[idx].ch_data_buf) {
+	free(zf->entry[idx].ch_data_buf);
+	zf->entry[idx].ch_data_buf = NULL;
+    }
+
+    zf->entry[idx].ch_data_zf = NULL;
+    zf->entry[idx].ch_data_zf_fileno = 0;
+    zf->entry[idx].ch_data_offset = 0;
+    zf->entry[idx].ch_data_len = 0;
+
+    zf->entry[idx].state = zf->entry[idx].fn_old ? Z_RENAMED : Z_UNCHANGED;
+
+    return idx;
+}
+
+
+
+static int
+zip_set_name(struct zf *zf, int idx, char *name)
+{
+    if (idx >= zf->nentry || idx < 0)
+	return -1;
+
+    if (name != NULL) {
+	if (zf->entry[idx].fn_old == NULL)
+	    zf->entry[idx].fn_old = zf->entry[idx].fn;
+	else
+	    free(zf->entry[idx].fn);
+	zf->entry[idx].fn = xstrdup(name);
+	zf->entry[idx].fnlen = strlen(name);
+    }
+
+    return idx;
+}
+
+
+
+void
+zip_entry_init(struct zf *zf, int idx)
+{
+    zf->entry[idx].fn = zf->entry[idx].ef = zf->entry[idx].fcom =
+	zf->entry[idx].fn_old = NULL;
+    zf->entry[idx].state = Z_UNCHANGED;
+    zf->entry[idx].ch_data_zf = NULL;
+    zf->entry[idx].ch_data_buf = NULL;
+    zf->entry[idx].ch_data_fp = NULL;
+    zf->entry[idx].ch_data_offset = zf->entry[idx].ch_data_len = 0;
+    zf->entry[idx].ch_data_zf_fileno = 0;
+}
+
+
+
+void
+zip_new_entry(struct zf *zf)
+{
+    if (zf->nentry >= zf->nentry_alloc-1) {
+	zf->nentry_alloc += 16;
+	zf->entry = (struct zf_entry *)xrealloc(zf->entry,
+						 sizeof(struct zf_entry)
+						 * zf->nentry_alloc);
+    }
+    zip_entry_init(zf, zf->nentry);
+
+    zf->entry[zf->nentry].fn_old = NULL;
+    zf->entry[zf->nentry].state = Z_ADDED;
+
+    zf->nentry++;
+}
diff --git a/zip_err_str.c b/zip_err_str.c
new file mode 100644
index 0000000..d2c803c
--- /dev/null
+++ b/zip_err_str.c
@@ -0,0 +1,18 @@
+#include "zip.h"
+
+
+
+char * zip_err_str[]={
+    "no error",
+    "multi-disk zip-files not supported",
+    "replacing zipfile with tempfile failed",
+    "closing zipfile failed",
+    "seek error",
+    "read error",
+    "write error",
+    "CRC error",
+    "zip file closed without closing this file",
+    "file does already exist",
+    "file doesn't exist",
+    "can't open file"    
+};
diff --git a/ziplow.h b/ziplow.h
new file mode 100644
index 0000000..6bda41d
--- /dev/null
+++ b/ziplow.h
@@ -0,0 +1,34 @@
+#ifndef _HAD_ZIPLOW_H
+#define _HAD_ZIPLOW_H
+
+#include "zip.h"
+
+#define MAXCOMLEN        65536
+#define EOCDLEN             22
+#define BUFSIZE       (MAXCOMLEN+EOCDLEN)
+#define LOCAL_MAGIC   "PK\3\4"
+#define CENTRAL_MAGIC "PK\1\2"
+#define EOCD_MAGIC    "PK\5\6"
+#define DATADES_MAGIC "PK\7\8"
+#define CDENTRYSIZE         46
+#define LENTRYSIZE          30
+
+struct zf *readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, 
+		    int buflen);
+int writecdir(struct zf *zfp);
+struct zf *zf_new(void);
+int zf_free(struct zf *zf);
+char *readstr(unsigned char **buf, int len, int nullp);
+char *readfpstr(FILE *fp, int len, int nullp);
+int read2(unsigned char **a);
+int read4(unsigned char **a);
+int readcdentry(FILE *fp, struct zf_entry *zfe, unsigned char **cdpp, 
+		int left, int readp, int localp);
+int writecdentry(FILE *fp, struct zf_entry *zfe, int localp);
+int checkcons(FILE *fp, struct zf *zf);
+int headercomp(struct zf_entry *h1, int local1p, struct zf_entry *h2,
+	       int local2p);
+int zip_entry_copy(struct zf *dest, struct zf *src, int entry_no,
+		   char *name);
+
+#endif /* _HAD_ZIPLOW_H */