home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Black Box 4
/
BlackBox.cdr
/
archiver
/
zip10ex.arj
/
ZIPFILE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-03
|
21KB
|
664 lines
/*
Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
Permission is granted to any individual or institution to use, copy, or
redistribute this software so long as all of the original files are included
unmodified, that it is not sold for profit, and that this copyright notice
is retained.
*/
/*
* zipfile.c by Mark Adler.
*/
#include "zip.h"
/* Macros for converting integers in little-endian to machine format */
#define SH(a) (((ush)(uch)(a)[0]) | (((ush)(uch)(a)[1]) << 8))
#define LG(a) ((ulg)SH(a) | ((ulg)SH((a)+2) << 16))
/* Macros for writing machine integers to little-endian format */
#define PUTSH(a,f) {putc((char)(a),(f)); putc((char)((a) >> 8),(f));}
#define PUTLG(a,f) {PUTSH(a,f) PUTSH((a) >> 16,f)}
/* -- Structure of a ZIP file -- */
/* Signatures for zip file information headers */
#define LOCSIG 0x04034b50L
#define CENSIG 0x02014b50L
#define ENDSIG 0x06054b50L
/* Offsets of values in headers */
#define LOCVER 0 /* version needed to extract */
#define LOCFLG 2 /* encrypt, implosion flags */
#define LOCHOW 4 /* compression method */
#define LOCTIM 6 /* last modified file time, DOS format */
#define LOCDAT 8 /* last modified file date, DOS format */
#define LOCCRC 10 /* uncompressed crc-32 for file */
#define LOCSIZ 14 /* compressed size in zip file */
#define LOCLEN 18 /* uncompressed size */
#define LOCNAM 22 /* length of filename */
#define LOCEXT 24 /* length of extra field */
#define CENVEM 0 /* version made by */
#define CENVER 2 /* version needed to extract */
#define CENFLG 4 /* encrypt, implosion flags */
#define CENHOW 6 /* compression method */
#define CENTIM 8 /* last modified file time, DOS format */
#define CENDAT 10 /* last modified file date, DOS format */
#define CENCRC 12 /* uncompressed crc-32 for file */
#define CENSIZ 16 /* compressed size in zip file */
#define CENLEN 20 /* uncompressed size */
#define CENNAM 24 /* length of filename */
#define CENEXT 26 /* length of extra field */
#define CENCOM 28 /* file comment length */
#define CENDSK 30 /* disk number start */
#define CENATT 32 /* internal file attributes */
#define CENATX 34 /* external file attributes */
#define CENOFF 38 /* relative offset of local header */
#define ENDDSK 0 /* number of this disk */
#define ENDBEG 2 /* number of the starting disk */
#define ENDSUB 4 /* entries on this disk */
#define ENDTOT 6 /* total number of entries */
#define ENDSIZ 8 /* size of entire central directory */
#define ENDOFF 12 /* offset of central on starting disk */
#define ENDCOM 16 /* length of zip file comment */
/* Local functions */
#ifdef PROTO
local int zqcmp(voidp *, voidp *);
# ifndef UTIL
local int zbcmp(voidp *, voidp far *);
local char *cutpath(char *);
# endif /* !UTIL */
#endif /* PROTO */
local int zqcmp(a, b)
voidp *a, *b; /* pointers to pointers to zip entries */
/* Used by qsort() to compare entries in the zfile list. */
{
return strcmp((*(struct zlist far **)a)->zname,
(*(struct zlist far **)b)->zname);
}
#ifndef UTIL
local int zbcmp(n, z)
voidp *n; /* string to search for */
voidp far *z; /* pointer to a pointer to a zip entry */
/* Used by search() to compare a target to an entry in the zfile list. */
{
#ifdef OS2
return stricmp((char *)n, ((struct zlist far *)z)->zname);
#else /* !OS2 */
return strcmp((char *)n, ((struct zlist far *)z)->zname);
#endif /* ?OS2 */
}
struct zlist far *zsearch(n)
char *n; /* name to find */
/* Return a pointer to the entry in zfile with the name n, or NULL if
not found. */
{
voidp far **p; /* result of search() */
if (zcount && (p = search(n, (voidp far **)zsort, zcount, zbcmp)) != NULL)
return *(struct zlist far **)p;
else
return NULL;
}
#endif /* !UTIL */
#ifdef VMS
# define PATHCUT ']'
#else /* !VMS */
# define PATHCUT '/'
#endif /* VMS */
char *ziptyp(s)
char *s; /* file name to force to zip */
/* If the file name *s has a dot (other than the first char), then return
the name, otherwise append .zip to the name. Allocate the space for
the name in either case. Return a pointer to the new name, or NULL
if malloc() fails. */
{
char *q; /* temporary pointer */
char *t; /* pointer to malloc'ed string */
if ((t = malloc(strlen(s) + 5)) == NULL)
return NULL;
strcpy(t, s);
#ifdef MSDOS
for (q = t; *q; q++)
if (*q == '\\')
*q = '/';
#endif /* MSDOS */
if (strrchr((q = strrchr(t, PATHCUT)) == NULL ? t : q + 1, '.') == NULL)
strcat(t, ".zip");
#ifdef MSDOS
#ifndef OS2
strupr(t);
#endif /* !OS2 */
#endif /* MSDOS */
return t;
}
int readzipfile()
/*
Make first pass through zip file, reading information from local file
headers and then verifying that information with the central file
headers. Any deviation from the expected zip file format returns an
error. At the end, a sorted list of file names in the zip file is made
to facilitate searching by name.
The name of the zip file is pointed to by the global "zipfile". The
globals zfiles, zcount, zcomlen, zcomment, and zsort are filled in.
Return an error code in the ZE_ class.
*/
{
char b[CENHEAD]; /* buffer for central headers */
FILE *f; /* zip file */
int m; /* mismatch flag */
extent n; /* length of name */
ulg p; /* current file offset */
char r; /* holds reserved bits during memcmp() */
ulg s; /* size of data, start of central */
char *t; /* temporary variable */
char far *u; /* temporary variable */
struct zlist far * far *x; /* pointer last entry's link */
struct zlist far *z; /* current zip entry structure */
/* Initialize zip file info */
zipbeg = 0;
zfiles = NULL; /* Points to first header */
zcomlen = 0; /* zip file comment length */
/* If zip file exists, read headers and check structure */
if ((f = fopen(zipfile, FOPR)) != NULL)
{
x = &zfiles; /* first link */
p = 0; /* starting file offset */
zcount = 0; /* number of files */
/* Find start of zip structures */
while (fread(b, 4, 1, f) == 1 && (s = LG(b)) != LOCSIG && s != ENDSIG)
if (fseek(f, -3L, SEEK_CUR))
return ferror(f) ? ZE_READ : ZE_EOF;
else
p++;
zipbeg = p;
/* Read local headers */
while (LG(b) == LOCSIG)
{
/* Read local header raw to compare later with central header
(this requires that the offest of ext in the zlist structure
be greater than or equal to LOCHEAD) */
if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
return ZE_MEM;
if (fread(b, LOCHEAD, 1, f) != 1)
return ferror(f) ? ZE_READ : ZE_EOF;
t = b; u = (char far *)z; n = LOCHEAD;
do {
*u++ = *t++;
} while (--n);
/* Link into list */
*x = z;
z->nxt = NULL;
x = &z->nxt;
/* Read file name and extra field and skip data */
n = SH(LOCNAM + (uch far *)z);
z->ext = SH(LOCEXT + (uch far *)z);
s = LG(LOCSIZ + (uch far *)z);
if (n == 0)
{
sprintf(errbuf, "%d", zcount + 1);
warn("zero-length name for entry #", errbuf);
return ZE_FORM;
}
if ((z->zname = malloc(n+1)) == NULL ||
(z->ext && (z->extra = malloc(z->ext)) == NULL))
return ZE_MEM;
if (fread(z->zname, n, 1, f) != 1 ||
(z->ext && fread(z->extra, z->ext, 1, f) != 1) ||
fseek(f, (long)s, SEEK_CUR))
return ferror(f) ? ZE_READ : ZE_EOF;
z->zname[n] = 0; /* terminate name */
#ifdef UTIL
z->name = z->zname;
#else /* !UTIL */
z->name = in2ex(z->zname); /* convert to external name */
if (z->name == NULL)
return ZE_MEM;
#endif /* ?UTIL */
/* Save offset, update for next header */
z->off = p;
p += 4 + LOCHEAD + n + z->ext + s;
zcount++;
/* Read next signature */
if (fread(b, 4, 1, f) != 1)
return ferror(f) ? ZE_READ : ZE_EOF;
}
/* Point to start of header list and read central headers */
z = zfiles;
s = p; /* save start of central */
while (LG(b) == CENSIG)
{
if (z == NULL)
{
warn("extraneous central header signature", "");
return ZE_FORM;
}
/* Read central header */
if (fread(b, CENHEAD, 1, f) != 1)
return ferror(f) ? ZE_READ : ZE_EOF;
/* Compare local header with that part of central header (except
for the reserved bits in the general purpose flags and except
for length of extra fields--authentication can make these
different in central and local headers) */
z->lflg = SH(LOCFLG + (uch far *)z); /* Save reserved bits */
r = b[CENFLG+1];
((uch far *)z)[LOCFLG+1] &= 0x1f; /* Zero out reserved bits */
b[CENFLG+1] &= 0x1f;
for (m = 0, u = (char far *)z, n = 0; n < LOCHEAD - 2; n++)
if (u[n] != b[n+2])
{
if (!m)
warn("local and central headers differ for ", z->zname);
m = 1;
sprintf(errbuf, " offset %d--local = %02x, central = %02x",
n, (uch)u[n], (uch)b[n+2]);
warn(errbuf, "");
}
if (m)
return ZE_FORM;
b[CENFLG+1] = r; /* Restore reserved bits */
/* Overwrite local header with translated central header */
z->vem = SH(CENVEM + b);
z->ver = SH(CENVER + b);
z->flg = SH(CENFLG + b); /* may be different from z->lflg */
z->how = SH(CENHOW + b);
z->tim = LG(CENTIM + b); /* time and date into one long */
z->crc = LG(CENCRC + b);
z->siz = LG(CENSIZ + b);
z->len = LG(CENLEN + b);
z->nam = SH(CENNAM + b);
z->cext = SH(CENEXT + b); /* may be different from z->ext */
z->com = SH(CENCOM + b);
z->dsk = SH(CENDSK + b);
z->att = SH(CENATT + b);
z->atx = LG(CENATX + b);
if (z->off != LG(CENOFF + b))
{
warn("local offset in central header incorrect for ", z->zname);
return ZE_FORM;
}
/* Compare name and extra fields and read comment field */
if ((t = malloc(z->nam)) == NULL)
return ZE_MEM;
if (fread(t, z->nam, 1, f) != 1)
{
free((voidp *)t);
return ferror(f) ? ZE_READ : ZE_EOF;
}
if (memcmp(t, z->zname, z->nam))
{
free((voidp *)t);
warn("names in local and central differ for ", z->zname);
return ZE_FORM;
}
free((voidp *)t);
if (z->cext)
{
if ((z->cextra = malloc(z->cext)) == NULL)
return ZE_MEM;
if (fread(z->cextra, z->cext, 1, f) != 1)
{
free((voidp *)(z->cextra));
return ferror(f) ? ZE_READ : ZE_EOF;
}
if (z->ext == z->cext && memcmp(z->extra, z->cextra, z->ext) == 0)
{
free((voidp *)(z->cextra));
z->cextra = z->extra;
}
}
if (z->com)
{
if ((z->comment = malloc(z->com)) == NULL)
return ZE_MEM;
if (fread(z->comment, z->com, 1, f) != 1)
{
free((voidp *)(z->comment));
return ferror(f) ? ZE_READ : ZE_EOF;
}
}
/* Note oddities */
if (verbose)
{
if (z->vem != 10 && z->vem != 11 &&
(n = z->vem >> 8) != 3 && n != 2 && n != 6)
{
sprintf(errbuf, "made by version %d.%d on system type %d: ",
(z->vem & 0xff) / 10, (z->vem & 0xff) % 10, z->vem >> 8);
warn(errbuf, z->zname);
}
if (z->ver != 10 && z->ver != 11)
{
sprintf(errbuf, "needs unzip %d.%d on system type %d: ",
(z->ver & 0xff) / 10, (z->ver & 0xff) % 10, z->ver >> 8);
warn(errbuf, z->zname);
}
if (z->flg != z->lflg)
{
sprintf(errbuf, "local flags = 0x%04x, central = 0x%04x: ",
z->lflg, z->flg);
warn(errbuf, z->zname);
}
else if (z->flg & ~7)
{
sprintf(errbuf, "undefined bits used in flags = 0x%04x: ", z->flg);
warn(errbuf, z->zname);
}
if (z->how > IMPLODE)
{
sprintf(errbuf, "unknown compression method %u: ", z->how);
warn(errbuf, z->zname);
}
if (z->dsk)
{
sprintf(errbuf, "starts on disk %u: ", z->dsk);
warn(errbuf, z->zname);
}
if (z->att & ~1)
{
sprintf(errbuf, "unknown internal attributes = 0x%04x: ", z->att);
warn(errbuf, z->zname);
}
if (((n = z->vem >> 8) != 3) && n != 2 && z->atx & ~0xffL)
{
sprintf(errbuf, "unknown external attributes = 0x%08lx: ", z->atx);
warn(errbuf, z->zname);
}
if (z->ext || z->cext)
if (z->ext == z->cext && z->extra == z->cextra)
{
sprintf(errbuf, "has %d bytes of extra data: ", z->ext);
warn(errbuf, z->zname);
}
else
{
sprintf(errbuf,
"local extra (%d bytes) != central extra (%d bytes): ",
z->ext, z->cext);
warn(errbuf, z->zname);
}
}
/* Clear actions */
z->mark = 0;
z->trash = 0;
/* Update file offset */
p += 4 + CENHEAD + z->nam + z->cext + z->com;
/* Advance to next header structure */
z = z->nxt;
/* Read next signature */
if (fread(b, 4, 1, f) != 1)
return ferror(f) ? ZE_READ : ZE_EOF;
}
/* Read end header */
if (z != NULL || LG(b) != ENDSIG)
{
warn("missing end signature--probably not a zip file (did you", "");
warn("remember to use binary mode when you transferred it?)", "");
return ZE_FORM;
}
if (fread(b, ENDHEAD, 1, f) != 1)
return ferror(f) ? ZE_READ : ZE_EOF;
if (SH(ENDDSK + b) || SH(ENDBEG + b) ||
SH(ENDSUB + b) != SH(ENDTOT + b))
warn("multiple disk information ignored", "");
if (zcount != SH(ENDSUB + b))
{
warn("count in end of central directory incorrect", "");
return ZE_FORM;
}
if (LG(ENDSIZ + b) != p - s)
{
warn("central directory size is incorrect", "");
return ZE_FORM;
}
if (LG(ENDOFF + b) != s)
{
warn("central directory start is incorrect", "");
return ZE_FORM;
}
cenbeg = s;
zcomlen = SH(ENDCOM + b);
if (zcomlen)
{
if ((zcomment = malloc(zcomlen)) == NULL)
return ZE_MEM;
if (fread(zcomment, zcomlen, 1, f) != 1)
{
free((voidp *)zcomment);
return ferror(f) ? ZE_READ : ZE_EOF;
}
}
if (zipbeg)
{
sprintf(errbuf, " has a preamble of %ld bytes", zipbeg);
warn(zipfile, errbuf);
}
if (getc(f) != EOF)
warn("garbage at end of zip file ignored", "");
/* Done with zip file for now */
fclose(f);
/* If one or more files, sort by name */
if (zcount)
{
if ((x = zsort =
(struct zlist far **)malloc(zcount * sizeof(struct zlist far *))) ==
NULL)
return ZE_MEM;
for (z = zfiles; z != NULL; z = z->nxt)
*x++ = z;
qsort((char *)zsort, zcount, sizeof(struct zlist far *), zqcmp);
}
}
return ZE_OK;
}
int putlocal(z, f)
struct zlist far *z; /* zip entry to write local header for */
FILE *f; /* file to write to */
/* Write a local header described by *z to file *f. Return an error code
in the ZE_ class. */
{
PUTLG(LOCSIG, f);
PUTSH(z->ver, f);
PUTSH(z->lflg, f);
PUTSH(z->how, f);
PUTLG(z->tim, f);
PUTLG(z->crc, f);
PUTLG(z->siz, f);
PUTLG(z->len, f);
PUTSH(z->nam, f);
PUTSH(z->ext, f);
if (fwrite(z->zname, 1, z->nam, f) != z->nam ||
z->ext && fwrite(z->extra, 1, z->ext, f) != z->ext)
return ZE_TEMP;
return ZE_OK;
}
int putcentral(z, f)
struct zlist far *z; /* zip entry to write central header for */
FILE *f; /* file to write to */
/* Write a central header described by *z to file *f. Return an error code
in the ZE_ class. */
{
PUTLG(CENSIG, f);
PUTSH(z->vem, f);
PUTSH(z->ver, f);
PUTSH(z->flg, f);
PUTSH(z->how, f);
PUTLG(z->tim, f);
PUTLG(z->crc, f);
PUTLG(z->siz, f);
PUTLG(z->len, f);
PUTSH(z->nam, f);
PUTSH(z->cext, f);
PUTSH(z->com, f);
PUTSH(z->dsk, f);
PUTSH(z->att, f);
PUTLG(z->atx, f);
PUTLG(z->off, f);
if (fwrite(z->zname, 1, z->nam, f) != z->nam ||
z->cext && fwrite(z->cextra, 1, z->cext, f) != z->cext ||
z->com && fwrite(z->comment, 1, z->com, f) != z->com)
return ZE_TEMP;
return ZE_OK;
}
int putend(n, s, c, m, z, f)
int n; /* number of entries in central directory */
ulg s, c; /* size and offset of central directory */
extent m; /* length of zip file comment (0 if none) */
char *z; /* zip file comment if m != 0 */
FILE *f; /* file to write to */
/* Write the end of central directory data to file *f. Return an error code
in the ZE_ class. */
{
PUTLG(ENDSIG, f);
PUTSH(0, f);
PUTSH(0, f);
PUTSH(n, f);
PUTSH(n, f);
PUTLG(s, f);
PUTLG(c, f);
PUTSH(m, f);
if (m && fwrite(z, 1, m, f) != m)
return ZE_TEMP;
return ZE_OK;
}
#ifndef UTIL
local char *cutpath(p)
char *p; /* path string */
/* Cut the last path component off the name *p in place. Return p. */
{
char *r; /* pointer to last path delimiter */
#ifdef VMS /* change [w.x.y]z to [w.x]y.DIR */
if ((r = strrchr(p, ']')) != NULL)
{
*r = 0;
if ((r = strrchr(p, '.')) != NULL)
{
*r = ']';
strcat(r, ".DIR"); /* this assumes a little padding--see PAD */
}
else
*p = 0;
}
else
*p = 0;
#else /* !VMS */ /* change w/x/y/z to w/x/y */
if ((r = strrchr(p, '/')) != NULL)
*r = 0;
else
*p = 0;
#endif /* ?VMS */
return p;
}
int trash()
/* Delete the compressed files and the directories that contained the deleted
files, if empty. Return an error code in the ZE_ class. Failure of
destroy() or deletedir() is ignored. */
{
extent i; /* counter on deleted names */
extent k; /* number of deleted directories this pass */
extent n; /* number of deleted names left to handle */
struct zlist far **s; /* table of zip entries to handle, sorted */
struct zlist far *z; /* current zip entry */
/* Count and delete marked names */
n = 0;
for (z = zfiles; z != NULL; z = z->nxt)
if (z->mark || z->trash)
{
z->mark = 1;
n++;
if (verbose)
printf("zip diagnostic: trashing file %s\n", z->name);
destroy(z->name);
}
/* Try to delete all paths that lead up to marked names */
if (n)
{
if ((s = (struct zlist far **)malloc((n+1)*sizeof(struct zlist far *))) ==
NULL ||
(s[0] = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
return ZE_MEM;
s[0]->name = "";
s++;
do {
n = k = 0;
for (z = zfiles; z != NULL; z = z->nxt)
if (z->mark)
s[n++] = z;
qsort((char *)s, n, sizeof(struct zlist far *), zqcmp);
for (i = 0; i < n; i++)
if (*cutpath(s[i]->name) && strcmp(s[i]->name, s[i-1]->name))
{
if (verbose)
printf("zip diagnostic: trashing directory %s\n", s[i]->name);
deletedir(s[i]->name);
k++;
}
else
s[i]->mark = 0;
} while (k);
farfree((voidp far *)((--s)[0]));
free((voidp *)s);
}
return ZE_OK;
}
#endif /* !UTIL */