"
COPYRIGHT (c) 1998 by eXept Software AG
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
"{ Package: 'stx:libbasic2' }"
Object subclass:#ZipArchive
instanceVariableNames:'file mode archiveName firstEntry lastEntry
recentlyExtractedEntries'
classVariableNames:'RecentlyUsedZipArchives FlushBlock ECREC_SIZE LREC_SIZE CREC_SIZE
SIZE_CENTRAL_DIRECTORY TOTAL_ENTRIES_CENTRAL_DIR
C_COMPRESSED_SIZE C_RELATIVE_OFFSET_LOCAL_HEADER
C_FILENAME_LENGTH C_UNCOMPRESSED_SIZE ZipFileFormatErrorSignal
COMPR_STORED COMPR_SHRUNK COMPR_REDUCED1 COMPR_REDUCED2
COMPR_REDUCED3 COMPR_REDUCED4 COMPR_IMPLODED COMPR_TOKENIZED
COMPR_DEFLATED'
poolDictionaries:''
category:'System-Support-FileFormats'
!
Object subclass:#ZipMember
instanceVariableNames:'next relative_offset_local_header compressed_size
uncompressed_size name crc32 compression_method data'
classVariableNames:''
poolDictionaries:''
privateIn:ZipArchive
!
!ZipArchive primitiveDefinitions!
%{
#include <stdio.h>
#define uchar unsigned char
#define ushort unsigned short
#define ulong unsigned long
/*
* inflate definitions
*/
#define PKZIP_BUG_WORKAROUND /* PKZIP 1.93a problem--live with it */
#ifndef WSIZE /* default is 32K */
# define WSIZE 0x8000 /* window size--must be a power of two, and at least */
#endif /* 32K for zip's deflate method */
#define NEXTBYTE (*inPtr++)
#define XXXFLUSH(n) slide += (n)
#define FLUSH(n) { bcopy(slide, outPtr, (n)); outPtr += (n); }
#ifdef DEBUG
# define Trace(x) if (debugTrace) { fprintf x ; }
#else
# define Trace(x) /* nothing */
#endif
/* Huffman code lookup table entry--this entry is four bytes for machines
that have 16-bit pointers (e.g. PC's in the small or medium model).
Valid extra bits are 0..13. e == 15 is EOB (end of block), e == 16
means that v is a literal, 16 < e < 32 means that v is a pointer to
the next table, which codes e - 16 bits, and lastly e == 99 indicates
an unused code. If a code with e == 99 is looked up, this implies an
error in the data. */
struct huft {
uchar e; /* number of extra bits or operation */
uchar b; /* number of bits in this code or subcode */
union {
ushort n; /* literal, length base, or distance base */
struct huft *t; /* pointer to next level of table */
} v;
};
%}
! !
!ZipArchive primitiveVariables!
%{
static int debugTrace = 0;
/*
* inflate variables
*/
static unsigned char *inPtr;
static unsigned char *outPtr;
static unsigned char *slide;
static int qflag = 0;
/* The inflate algorithm uses a sliding 32K byte window on the uncompressed
stream to find repeated byte strings. This is implemented here as a
circular buffer. The index is updated simply by incrementing and then
and'ing with 0x7fff (32K-1). */
/* It is left to other modules to supply the 32K area. It is assumed
to be usable as if it were declared "uchar slide[32768];" or as just
"uchar *slide;" and then malloc'ed in the latter case. The definition
must be in unzip.h, included above. */
static unsigned wp; /* current position in slide */
/* Tables for deflate from PKZIP's appnote.txt. */
static unsigned border[] = { /* Order of the bit length code lengths */
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
static ushort cplens[] = { /* Copy lengths for literal codes 257..285 */
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
/* note: see note #13 above about the 258 in this list. */
static ushort cplext[] = { /* Extra bits for literal codes 257..285 */
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99}; /* 99==invalid */
static ushort cpdist[] = { /* Copy offsets for distance codes 0..29 */
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577};
static ushort cpdext[] = { /* Extra bits for distance codes */
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
12, 12, 13, 13};
/* And'ing with mask[n] masks the lower n bits */
static ushort mask[] = {
0x0000,
0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
/* Macros for inflate() bit peeking and grabbing.
The usage is:
NEEDBITS(j)
x = b & mask[j];
DUMPBITS(j)
where NEEDBITS makes sure that b has at least j bits in it, and
DUMPBITS removes the bits from b. The macros use the variable k
for the number of bits in b. Normally, b and k are register
variables for speed, and are initialized at the begining of a
routine that uses these macros from a global bit buffer and count.
In order to not ask for more bits than there are in the compressed
stream, the Huffman tables are constructed to only ask for just
enough bits to make up the end-of-block code (value 256). Then no
bytes need to be "returned" to the buffer at the end of the last
block. See the huft_build() routine.
*/
static ulong bb; /* bit buffer */
static unsigned bk; /* bits in bit buffer */
#define NEEDBITS(n) { while(k<(n)){ b |= ((ulong)NEXTBYTE)<<k; k+=8; } }
#define DUMPBITS(n) { b>>=(n); k-=(n); }
/*
Huffman code decoding is performed using a multi-level table lookup.
The fastest way to decode is to simply build a lookup table whose
size is determined by the longest code. However, the time it takes
to build this table can also be a factor if the data being decoded
is not very long. The most common codes are necessarily the
shortest codes, so those codes dominate the decoding time, and hence
the speed. The idea is you can have a shorter table that decodes the
shorter, more probable codes, and then point to subsidiary tables for
the longer codes. The time it costs to decode the longer codes is
then traded against the time it takes to make longer tables.
This results of this trade are in the variables lbits and dbits
below. lbits is the number of bits the first level table for literal/
length codes can decode in one step, and dbits is the same thing for
the distance codes. Subsequent tables are also less than or equal to
those sizes. These values may be adjusted either when all of the
codes are shorter than that, in which case the longest code length in
bits is used, or when the shortest code is *longer* than the requested
table size, in which case the length of the shortest code in bits is
used.
There are two different values for the two tables, since they code a
different number of possibilities each. The literal/length table
codes 286 possible values, or in a flat code, a little over eight
bits. The distance table codes 30 possible values, or a little less
than five bits, flat. The optimum values for speed end up being
about one bit more than those, so lbits is 8+1 and dbits is 5+1.
The optimum values may differ though from machine to machine, and
possibly even between compilers. Your mileage may vary.
*/
static int lbits = 9; /* bits in base literal/length lookup table */
static int dbits = 6; /* bits in base distance lookup table */
/* If BMAX needs to be larger than 16, then h and x[] should be ulong. */
#define BMAX 16 /* maximum bit length of any code (16 for explode) */
#define N_MAX 288 /* maximum number of codes in any set */
static unsigned hufts; /* track memory usage */
%}
! !
!ZipArchive primitiveFunctions!
%{
/*
* inflate algorithm
*/
/* Free the malloc'ed tables built by huft_build(), which makes a linked
list of the tables it made, with the links in a dummy first entry of
each table. */
static int
huft_free(t)
struct huft *t; /* table to free */
{
register struct huft *p, *q;
/* Go through linked list, freeing from the malloced (t[-1]) address. */
p = t;
while (p != (struct huft *)NULL)
{
q = (--p)->v.t;
free(p);
p = q;
}
return 0;
}
/* Given a list of code lengths and a maximum table size, make a set of
tables to decode that set of codes. Return zero on success, one if
the given code set is incomplete (the tables are still built in this
case), two if the input is invalid (all zero length codes or an
oversubscribed set of lengths), and three if not enough memory.
The code with value 256 is special, and the tables are constructed
so that no bits beyond that code are fetched when that code is
decoded. */
static int
huft_build(b, n, s, d, e, t, m)
unsigned *b; /* code lengths in bits (all assumed <= BMAX) */
unsigned n; /* number of codes (assumed <= N_MAX) */
unsigned s; /* number of simple-valued codes (0..s-1) */
ushort *d; /* list of base values for non-simple codes */
ushort *e; /* list of extra bits for non-simple codes */
struct huft **t; /* result: starting table */
int *m; /* maximum lookup bits, returns actual */
{
unsigned a; /* counter for codes of length k */
unsigned c[BMAX+1]; /* bit length count table */
unsigned el; /* length of EOB code (value 256) */
unsigned f; /* i repeats in table every f entries */
int g; /* maximum code length */
int h; /* table level */
register unsigned i; /* counter, current code */
register unsigned j; /* counter */
register int k; /* number of bits in current code */
int lx[BMAX+1]; /* memory for l[-1..BMAX-1] */
int *l = lx+1; /* stack of bits per table */
register unsigned *p; /* pointer into c[], b[], or v[] */
register struct huft *q; /* points to current table */
struct huft r; /* table entry for structure assignment */
struct huft *u[BMAX]; /* table stack */
static unsigned v[N_MAX]; /* values in order of bit length */
register int w; /* bits before this table == (l * h) */
unsigned x[BMAX+1]; /* bit offsets, then code stack */
unsigned *xp; /* pointer into x */
int y; /* number of dummy codes added */
unsigned z; /* number of entries in current table */
/* Generate counts for each bit length */
el = n > 256 ? b[256] : BMAX; /* set length of EOB code, if any */
bzero((char *)c, sizeof(c));
p = b; i = n;
do {
c[*p]++; p++; /* assume all entries <= BMAX */
} while (--i);
if (c[0] == n) /* null input--all zero length codes */
{
*t = (struct huft *)NULL;
*m = 0;
return 0;
}
/* Find minimum and maximum length, bound *m by those */
for (j = 1; j <= BMAX; j++)
if (c[j])
break;
k = j; /* minimum code length */
if ((unsigned)*m < j)
*m = j;
for (i = BMAX; i; i--)
if (c[i])
break;
g = i; /* maximum code length */
if ((unsigned)*m > i)
*m = i;
/* Adjust last length count to fill out codes, if needed */
for (y = 1 << j; j < i; j++, y <<= 1)
if ((y -= c[j]) < 0)
return 2; /* bad input: more codes than bits */
if ((y -= c[i]) < 0)
return 2;
c[i] += y;
/* Generate starting offsets into the value table for each length */
x[1] = j = 0;
p = c + 1; xp = x + 2;
while (--i) { /* note that i == g from above */
*xp++ = (j += *p++);
}
/* Make a table of values in order of bit lengths */
p = b; i = 0;
do {
if ((j = *p++) != 0)
v[x[j]++] = i;
} while (++i < n);
/* Generate the Huffman codes and for each, make the table entries */
x[0] = i = 0; /* first Huffman code is zero */
p = v; /* grab values in bit order */
h = -1; /* no tables yet--level -1 */
w = l[-1] = 0; /* no bits decoded yet */
u[0] = (struct huft *)NULL; /* just to keep compilers happy */
q = (struct huft *)NULL; /* ditto */
z = 0; /* ditto */
/* go through the bit lengths (k already is bits in shortest code) */
for (; k <= g; k++)
{
a = c[k];
while (a--)
{
/* here i is the Huffman code of length k bits for value *p */
/* make tables up to required level */
while (k > w + l[h])
{
w += l[h++]; /* add bits already decoded */
/* compute minimum size table less than or equal to *m bits */
z = (z = g - w) > (unsigned)*m ? *m : z; /* upper limit */
if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */
{ /* too few codes for k-w bit table */
f -= a + 1; /* deduct codes from patterns left */
xp = c + k;
while (++j < z) /* try smaller tables up to z bits */
{
if ((f <<= 1) <= *++xp)
break; /* enough codes to use up j bits */
f -= *xp; /* else deduct codes from patterns */
}
}
if ((unsigned)w + j > el && (unsigned)w < el)
j = el - w; /* make EOB code end at table */
z = 1 << j; /* table entries for j-bit table */
l[h] = j; /* set table size in stack */
/* allocate and link in new table */
if ((q = (struct huft *)malloc((z + 1)*sizeof(struct huft))) ==
(struct huft *)NULL)
{
if (h)
huft_free(u[0]);
return 3; /* not enough memory */
}
hufts += z + 1; /* track memory usage */
*t = q + 1; /* link to list for huft_free() */
*(t = &(q->v.t)) = (struct huft *)NULL;
u[h] = ++q; /* table starts after link */
/* connect to last table, if there is one */
if (h)
{
x[h] = i; /* save pattern for backing up */
r.b = (uchar)l[h-1]; /* bits to dump before this table */
r.e = (uchar)(16 + j); /* bits in this table */
r.v.t = q; /* pointer to this table */
j = (i & ((1 << w) - 1)) >> (w - l[h-1]);
u[h-1][j] = r; /* connect to last table */
}
}
/* set up table entry in r */
r.b = (uchar)(k - w);
if (p >= v + n)
r.e = 99; /* out of values--invalid code */
else if (*p < s)
{
r.e = (uchar)(*p < 256 ? 16 : 15); /* 256 is end-of-block code */
r.v.n = *p++; /* simple code is just the value */
}
else
{
r.e = (uchar)e[*p - s]; /* non-simple--look up in lists */
r.v.n = d[*p++ - s];
}
/* fill code-like entries with r */
f = 1 << (k - w);
for (j = i >> w; j < z; j += f)
q[j] = r;
/* backwards increment the k-bit code i */
for (j = 1 << (k - 1); i & j; j >>= 1)
i ^= j;
i ^= j;
/* backup over finished tables */
while ((i & ((1 << w) - 1)) != x[h])
w -= l[--h]; /* don't need to update q */
}
}
/* return actual size of base table */
*m = l[0];
/* Return true (1) if we were given an incomplete table */
return y != 0 && g != 1;
}
#ifdef ASM_INFLATECODES
# define inflate_codes(tl,td,bl,bd) flate_codes(tl,td,bl,bd,(uchar *)slide)
int flate_codes OF((struct huft *, struct huft *, int, int, uchar *));
#else
/* inflate (decompress) the codes in a deflated (compressed) block.
Return an error code or zero if it all goes ok. */
static int
inflate_codes(tl, td, bl, bd)
struct huft *tl, *td; /* literal/length and distance decoder tables */
int bl, bd; /* number of bits decoded by tl[] and td[] */
{
register unsigned e; /* table entry flag/number of extra bits */
unsigned n, d; /* length and index for copy */
unsigned w; /* current window position */
struct huft *t; /* pointer to table entry */
unsigned ml, md; /* masks for bl and bd bits */
register ulong b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
/* make local copies of globals */
b = bb; /* initialize bit buffer */
k = bk;
w = wp; /* initialize window position */
/* inflate the coded data */
ml = mask[bl]; /* precompute masks for speed */
md = mask[bd];
while (1) /* do until end of block */
{
NEEDBITS((unsigned)bl)
if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
do {
if (e == 99)
return 1;
DUMPBITS(t->b)
e -= 16;
NEEDBITS(e)
} while ((e = (t = t->v.t + ((unsigned)b & mask[e]))->e) > 16);
DUMPBITS(t->b)
if (e == 16) /* then it's a literal */
{
slide[w++] = (uchar)t->v.n;
if (w == WSIZE)
{
FLUSH(w);
w = 0;
}
}
else /* it's an EOB or a length */
{
/* exit if end of block */
if (e == 15)
break;
/* get length of block to copy */
NEEDBITS(e)
n = t->v.n + ((unsigned)b & mask[e]);
DUMPBITS(e);
/* decode distance of block to copy */
NEEDBITS((unsigned)bd)
if ((e = (t = td + ((unsigned)b & md))->e) > 16)
do {
if (e == 99)
return 1;
DUMPBITS(t->b)
e -= 16;
NEEDBITS(e)
} while ((e = (t = t->v.t + ((unsigned)b & mask[e]))->e) > 16);
DUMPBITS(t->b)
NEEDBITS(e)
d = w - t->v.n - ((unsigned)b & mask[e]);
DUMPBITS(e)
/* do the copy */
do {
n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
#ifndef NOMEMCPY
if (w - d >= e) /* (this test assumes unsigned comparison) */
{
# ifdef USE_MEMCPY
memcpy(slide + w, slide + d, e);
# else
bcopy(slide + d, slide + w, e);
# endif
w += e;
d += e;
}
else /* do it slow to avoid memcpy() overlap */
#endif /* !NOMEMCPY */
do {
slide[w++] = slide[d++];
} while (--e);
if (w == WSIZE)
{
FLUSH(w);
w = 0;
}
} while (n);
}
}
/* restore the globals from the locals */
wp = w; /* restore global window pointer */
bb = b; /* restore global bit buffer */
bk = k;
/* done */
return 0;
}
#endif /* ASM_INFLATECODES */
/* "decompress" an inflated type 0 (stored) block. */
static int
inflate_stored()
{
unsigned n; /* number of bytes in block */
unsigned w; /* current window position */
register ulong b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
/* make local copies of globals */
Trace((stderr, "stored block\n"));
b = bb; /* initialize bit buffer */
k = bk;
w = wp; /* initialize window position */
/* go to byte boundary */
n = k & 7;
DUMPBITS(n);
/* get the length and its complement */
NEEDBITS(16)
n = ((unsigned)b & 0xffff);
DUMPBITS(16)
NEEDBITS(16)
if (n != (unsigned)((~b) & 0xffff))
return 1; /* error in compressed data */
DUMPBITS(16)
/* read and output the compressed data */
while (n--)
{
NEEDBITS(8)
slide[w++] = (uchar)b;
if (w == WSIZE)
{
FLUSH(w);
w = 0;
}
DUMPBITS(8)
}
/* restore the globals from the locals */
wp = w; /* restore global window pointer */
bb = b; /* restore global bit buffer */
bk = k;
return 0;
}
/* Globals for literal tables (built once) */
static struct huft *fixed_tl = (struct huft *)NULL;
static struct huft *fixed_td = (struct huft *)NULL;
static int fixed_bl, fixed_bd;
/* decompress an inflated type 1 (fixed Huffman codes) block. We should
either replace this with a custom decoder, or at least precompute the
Huffman tables. */
static int
inflate_fixed()
{
/* if first time, set up tables for fixed blocks */
Trace((stderr, "fixed block\n"));
if (fixed_tl == (struct huft *)NULL)
{
int i; /* temporary variable */
static unsigned l[288]; /* length list for huft_build */
/* literal table */
for (i = 0; i < 144; i++)
l[i] = 8;
for (; i < 256; i++)
l[i] = 9;
for (; i < 280; i++)
l[i] = 7;
for (; i < 288; i++) /* make a complete, but wrong code set */
l[i] = 8;
fixed_bl = 7;
if ((i = huft_build(l, 288, 257, cplens, cplext,
&fixed_tl, &fixed_bl)) != 0)
{
Trace((stderr, "incomplete code set 1\n"));
fixed_tl = (struct huft *)NULL;
return i;
}
/* distance table */
for (i = 0; i < 30; i++) /* make an incomplete code set */
l[i] = 5;
fixed_bd = 5;
if ((i = huft_build(l, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd)) > 1)
{
Trace((stderr, "incomplete code set 2\n"));
huft_free(fixed_tl);
fixed_tl = (struct huft *)NULL;
return i;
}
}
/* decompress until an end-of-block code */
return inflate_codes(fixed_tl, fixed_td, fixed_bl, fixed_bd) != 0;
}
/* decompress an inflated type 2 (dynamic Huffman codes) block. */
static int
inflate_dynamic()
{
int i; /* temporary variables */
unsigned j;
unsigned l; /* last length */
unsigned m; /* mask for bit lengths table */
unsigned n; /* number of lengths to get */
struct huft *tl; /* literal/length code table */
struct huft *td; /* distance code table */
int bl; /* lookup bits for tl */
int bd; /* lookup bits for td */
unsigned nb; /* number of bit length codes */
unsigned nl; /* number of literal/length codes */
unsigned nd; /* number of distance codes */
#ifdef PKZIP_BUG_WORKAROUND
static unsigned ll[288+32]; /* literal/length and distance code lengths */
#else
static unsigned ll[286+30]; /* literal/length and distance code lengths */
#endif
register ulong b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
/* make local bit buffer */
Trace((stderr, "dynamic block\n"));
b = bb;
k = bk;
/* read in table lengths */
NEEDBITS(5)
nl = 257 + ((unsigned)b & 0x1f); /* number of literal/length codes */
DUMPBITS(5)
NEEDBITS(5)
nd = 1 + ((unsigned)b & 0x1f); /* number of distance codes */
DUMPBITS(5)
NEEDBITS(4)
nb = 4 + ((unsigned)b & 0xf); /* number of bit length codes */
DUMPBITS(4)
#ifdef PKZIP_BUG_WORKAROUND
if (nl > 288 || nd > 32)
#else
if (nl > 286 || nd > 30)
#endif
{
Trace((stderr, "bad length\n"));
return 1; /* bad lengths */
}
/* read in bit-length-code lengths */
for (j = 0; j < nb; j++)
{
NEEDBITS(3)
ll[border[j]] = (unsigned)b & 7;
DUMPBITS(3)
}
for (; j < 19; j++)
ll[border[j]] = 0;
/* build decoding table for trees--single level, 7 bit lookup */
bl = 7;
if ((i = huft_build(ll, 19, 19, NULL, NULL, &tl, &bl)) != 0)
{
if (i == 1)
huft_free(tl);
Trace((stderr, "incomplete code set 3\n"));
return i; /* incomplete code set */
}
/* read in literal and distance code lengths */
n = nl + nd;
m = mask[bl];
i = l = 0;
while ((unsigned)i < n)
{
NEEDBITS((unsigned)bl)
j = (td = tl + ((unsigned)b & m))->b;
DUMPBITS(j)
j = td->v.n;
if (j < 16) /* length of code in bits (0..15) */
ll[i++] = l = j; /* save last length in l */
else if (j == 16) /* repeat last length 3 to 6 times */
{
NEEDBITS(2)
j = 3 + ((unsigned)b & 3);
DUMPBITS(2)
if ((unsigned)i + j > n)
return 1;
while (j--)
ll[i++] = l;
}
else if (j == 17) /* 3 to 10 zero length codes */
{
NEEDBITS(3)
j = 3 + ((unsigned)b & 7);
DUMPBITS(3)
if ((unsigned)i + j > n)
return 1;
while (j--)
ll[i++] = 0;
l = 0;
}
else /* j == 18: 11 to 138 zero length codes */
{
NEEDBITS(7)
j = 11 + ((unsigned)b & 0x7f);
DUMPBITS(7)
if ((unsigned)i + j > n)
return 1;
while (j--)
ll[i++] = 0;
l = 0;
}
}
/* free decoding table for trees */
huft_free(tl);
/* restore the global bit buffer */
bb = b;
bk = k;
/* build the decoding tables for literal/length and distance codes */
bl = lbits;
if ((i = huft_build(ll, nl, 257, cplens, cplext, &tl, &bl)) != 0)
{
Trace((stderr, "incomplete code set 4\n"));
if (i == 1 && !qflag) {
Trace((stderr, "incomplete l-tree\n"));
huft_free(tl);
}
return i; /* incomplete code set */
}
bd = dbits;
if ((i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &td, &bd)) != 0)
{
Trace((stderr, "huft_build err\n"));
if (i == 1 && !qflag) {
Trace((stderr, "incomplete d-tree\n"));
#ifdef PKZIP_BUG_WORKAROUND
i = 0;
}
#else
huft_free(td);
}
huft_free(tl);
return i; /* incomplete code set */
#endif
}
/* decompress until an end-of-block code */
if (inflate_codes(tl, td, bl, bd)) {
Trace((stderr, "inflate_codes error\n"));
return 1;
}
/* free the decoding tables, return */
huft_free(tl);
huft_free(td);
Trace((stderr, "block ok\n"));
return 0;
}
/* decompress an inflated block */
static int
inflate_block(endPtr)
int *endPtr; /* last block flag */
{
unsigned t; /* block type */
register ulong b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
Trace((stderr, "inflate_block\n"));
/* make local bit buffer */
b = bb;
k = bk;
/* read in last block bit */
NEEDBITS(1)
*endPtr = (int)b & 1;
Trace((stderr, " end = %d\n", (int)b & 1));
DUMPBITS(1)
/* read in block type */
NEEDBITS(2)
t = (unsigned)b & 3;
DUMPBITS(2)
Trace((stderr, " type = %d\n", t));
/* restore the global bit buffer */
bb = b;
bk = k;
/* inflate that block type */
if (t == 2) {
return inflate_dynamic();
}
if (t == 0) {
return inflate_stored();
}
if (t == 1) {
return inflate_fixed();
}
Trace((stderr, "bad block type\n"));
/* bad block type */
return 2;
}
/* decompress an inflated entry */
static int
inflate()
{
int endFlag; /* last block flag */
int r; /* result code */
unsigned h; /* maximum struct huft's malloc'ed */
/* initialize window, bit buffer */
wp = 0;
bk = 0;
bb = 0;
endFlag = 0;
/* decompress until the last block */
h = 0;
do {
hufts = 0;
if ((r = inflate_block(&endFlag)) != 0) {
Trace((stderr, "inflate_block -> %d\n", r));
return r;
}
if (hufts > h)
h = hufts;
} while (!endFlag);
/* flush out slide */
FLUSH(wp);
/* return success */
Trace((stderr, "%u bytes in Huffman tables (%d/entry)\n",
h * sizeof(struct huft), sizeof(struct huft)));
return 0;
}
static int
inflate_free()
{
if (fixed_td != (struct huft *)NULL)
{
huft_free(fixed_td);
fixed_td = (struct huft *)NULL;
}
if (fixed_tl != (struct huft *)NULL)
{
huft_free(fixed_tl);
fixed_tl = (struct huft *)NULL;
}
return 0;
}
static int
stx_inflate(in, out)
char *in, *out;
{
int rslt;
inPtr = in;
outPtr = out;
slide = (char *)malloc(WSIZE+2);
if (! slide) return 1;
rslt = inflate();
inflate_free();
free(slide);
return rslt;
}
%}
! !
!ZipArchive class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1998 by eXept Software AG
All Rights Reserved
This software is furnished under a license and may be used
only in accordance with the terms of that license and with the
inclusion of the above copyright notice. This software may not
be provided or otherwise made available to, or used by, any
other person. No title to or ownership of the software is
hereby transferred.
"
!
documentation
"
provides access to a zip archive.
Caveat: only uncompressed archives are supported (for now).
[author:]
Claus Gittinger
"
!
examples
"
[exBegin]
|zip bytes|
zip := ZipArchive oldFileNamed:'foo.zip'.
bytes := zip extract:'bar'.
[exEnd]
[exBegin]
|zip bytes|
zip := ZipArchive oldFileNamed:'source/stx/libbasic2.zip'.
zip entries do:[:entry |
Transcript showCR:entry
].
[exEnd]
[exBegin]
|zip bytes|
zip := ZipArchive oldFileNamed:'source/stx/libbasic2.zip'.
bytes := zip extract:'TwoByteStr.st'.
Transcript showCR:(bytes asString).
[exEnd]
"
! !
!ZipArchive class methodsFor:'instance creation'!
newFileNamed:name
^ self new name:name mode:#write
"Created: / 29.3.1998 / 17:46:16 / cg"
!
oldFileNamed:name
|zar f fn|
RecentlyUsedZipArchives isNil ifTrue:[
RecentlyUsedZipArchives := OrderedCollection new
].
f := name asFilename.
fn := f pathName.
RecentlyUsedZipArchives keysAndValuesDo:[:i :z |
z name = fn ifTrue:[
RecentlyUsedZipArchives removeIndex:i.
RecentlyUsedZipArchives addLast:z.
self installFlushBlock.
^ z
].
].
zar := self new name:fn mode:#read.
RecentlyUsedZipArchives add:zar.
[RecentlyUsedZipArchives size > 15] whileTrue:[
RecentlyUsedZipArchives removeFirst
].
self installFlushBlock.
^ zar
"Created: / 29.3.1998 / 17:46:09 / cg"
"Modified: / 20.10.1998 / 00:30:02 / cg"
! !
!ZipArchive class methodsFor:'class initialization'!
initialize
ECREC_SIZE := 18.
LREC_SIZE := 26.
CREC_SIZE := 42.
TOTAL_ENTRIES_CENTRAL_DIR := 10.
SIZE_CENTRAL_DIRECTORY := 12.
C_COMPRESSED_SIZE := 16.
C_UNCOMPRESSED_SIZE := 20.
C_FILENAME_LENGTH := 24.
C_RELATIVE_OFFSET_LOCAL_HEADER := 38.
"/ compression methods
COMPR_STORED := 0.
COMPR_SHRUNK := 1.
COMPR_REDUCED1 := 2.
COMPR_REDUCED2 := 3.
COMPR_REDUCED3 := 4.
COMPR_REDUCED4 := 5.
COMPR_IMPLODED := 6.
COMPR_TOKENIZED := 7.
COMPR_DEFLATED := 8.
ZipFileFormatErrorSignal := ErrorSignal newSignalMayProceed:true.
ZipFileFormatErrorSignal nameClass:self message:#zipFileFormatErrorSignal.
ZipFileFormatErrorSignal notifierString:'unrecognized/bad zip file format'.
"
self initialize
"
"Modified: / 29.3.1998 / 20:17:18 / cg"
! !
!ZipArchive class methodsFor:'cleanup'!
flush
"forget about cached zipArchives"
RecentlyUsedZipArchives := nil. FlushBlock := nil.
"
self flush
"
!
installFlushBlock
"forget about cached zipArchives"
FlushBlock isNil ifTrue:[
FlushBlock := [ RecentlyUsedZipArchives := nil. FlushBlock := nil. ].
] ifFalse:[
Processor removeTimedBlock:FlushBlock.
].
Processor addTimedBlock:FlushBlock for:nil afterSeconds:60.
"
self installFlushBlock
"
"Created: / 9.4.1998 / 13:17:07 / cg"
"Modified: / 19.10.1998 / 21:02:22 / cg"
!
lowSpaceCleanup
"forget about cached zipArchives"
RecentlyUsedZipArchives := nil
"
self lowSpaceCleanup
"
"Modified: / 7.4.1998 / 17:58:57 / cg"
! !
!ZipArchive class methodsFor:'constants'!
LREC_SIZE
^ LREC_SIZE
"Created: / 29.3.1998 / 19:11:20 / cg"
! !
!ZipArchive class methodsFor:'debugging'!
debugTrace:aBoolean
%{
if (aBoolean == true) {
debugTrace = 1;
} else {
debugTrace = 0;
}
%}
! !
!ZipArchive methodsFor:'accessing'!
entries
"return a collection of fileName entries"
|names|
names := OrderedCollection new.
self zipMembersDo:[:zipd |
names add:(zipd name)
].
^ names
"
(ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') entries
"
"Modified: / 29.3.1998 / 20:08:38 / cg"
!
extract:fileName
"extract a filename entry as a byteArray;
nil on errors"
|zmemb rawContents data oldEntry|
recentlyExtractedEntries isNil ifTrue:[
recentlyExtractedEntries := OrderedCollection new
].
recentlyExtractedEntries keysAndValuesDo:[:index :entry |
(entry notNil and:[entry name = fileName]) ifTrue:[
recentlyExtractedEntries removeIndex:index.
recentlyExtractedEntries addLast:entry.
data := entry data.
data notNil ifTrue:[
^ data
]
]
].
zmemb := self findMember:fileName.
zmemb isNil ifTrue:[^ nil].
self openFile.
file position:(zmemb fileStart + 1).
rawContents := file nextBytes:(zmemb compressed_size).
self closeFile.
data := self
decode:rawContents
method:(zmemb compression_method)
size:(zmemb uncompressed_size).
data size < (32*1024) ifTrue:[
"/ zmemb data:data.
recentlyExtractedEntries addLast:zmemb.
[recentlyExtractedEntries size > 5] whileTrue:[
oldEntry := recentlyExtractedEntries removeFirst.
oldEntry data:nil.
].
].
^ data.
"
(ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') extract:'java/io/UTFDataFormatException.class'
"
"Modified: / 10.1.1999 / 17:43:25 / cg"
!
members
"return a collection of members"
|members|
members := OrderedCollection new.
self zipMembersDo:[:zipd |
members add:zipd
].
^ members
"
(ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') members
"
"Created: / 29.3.1998 / 20:09:27 / cg"
"Modified: / 29.3.1998 / 20:10:21 / cg"
!
name
"return the (file-)name of this zipArchive"
^ archiveName
"Created: / 6.4.1998 / 10:47:11 / cg"
! !
!ZipArchive methodsFor:'private'!
closeFile
file notNil ifTrue:[
file close.
file := nil.
]
"Created: / 30.3.1998 / 18:18:10 / cg"
"Modified: / 29.12.1998 / 23:08:27 / cg"
!
name:nm mode:m
archiveName := nm asFilename name.
mode := m.
mode == #read ifTrue:[
self openFile.
self readDirectory.
self closeFile.
] ifFalse:[
"/ self openFile.
]
"
ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip'
(ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') entries
(ZipArchive oldFileNamed:'/usr/lib/java/lib/classes.zip') extract:'java/io/UTFDataFormatException.class'
"
"Modified: / 30.3.1998 / 18:19:48 / cg"
!
openFile
file isNil ifTrue:[
mode == #read ifTrue:[
file := archiveName asFilename readStream binary.
] ifFalse:[
file := archiveName asFilename writeStream binary
]
].
"Created: / 30.3.1998 / 18:18:48 / cg"
"Modified: / 30.3.1998 / 18:19:26 / cg"
! !
!ZipArchive methodsFor:'private - decompression'!
decode:rawBytes method:compressionMethod size:uncompressedSize
|outBytes|
compressionMethod == COMPR_STORED ifTrue:[
"/
"/ uncompressed
"/
^ rawBytes
].
compressionMethod == COMPR_DEFLATED ifTrue:[
"/
"/ deflate/inflate algorithm
"/
outBytes := ByteArray new:uncompressedSize.
^ self inflate:rawBytes to:outBytes
].
"/
"/ the other algorithms are not (yet) supported
"/
compressionMethod == COMPR_SHRUNK ifTrue:[
self error:'unsupported compression method: SHRUNK'.
^ nil
].
compressionMethod == COMPR_REDUCED1 ifTrue:[
self error:'unsupported compression method: REDUCED1'.
^ nil
].
compressionMethod == COMPR_REDUCED2 ifTrue:[
self error:'unsupported compression method: REDUCED2'.
^ nil
].
compressionMethod == COMPR_REDUCED3 ifTrue:[
self error:'unsupported compression method: REDUCED3'.
^ nil
].
compressionMethod == COMPR_REDUCED4 ifTrue:[
self error:'unsupported compression method: REDUCED4'.
^ nil
].
compressionMethod == COMPR_IMPLODED ifTrue:[
self error:'unsupported compression method: IMPLODED'.
^ nil
].
compressionMethod == COMPR_TOKENIZED ifTrue:[
self error:'unsupported compression method: TOKENIZED'.
^ nil
].
self error:'unsupported compression method'.
^ nil
"Created: / 29.3.1998 / 20:14:45 / cg"
"Modified: / 8.4.1998 / 10:31:34 / cg"
!
inflate:inBytes to:outBytes
|inflateReturnCode|
%{ /* STACK:32768 */
if (__isByteArray(inBytes)
&& __isByteArray(outBytes)) {
char *in, *out;
int rc;
in = __ByteArrayInstPtr(inBytes)->ba_element;
out = __ByteArrayInstPtr(outBytes)->ba_element;
if ((rc = stx_inflate(in, out)) == 0) {
RETURN (outBytes);
}
inflateReturnCode = __MKSMALLINT(rc);
}
%}.
inflateReturnCode notNil ifTrue:[
"/ bad blockType 2
self error:'inflate error: ' , inflateReturnCode printString
].
^ nil.
"Created: / 8.4.1998 / 10:31:27 / cg"
! !
!ZipArchive methodsFor:'private - directory stuff'!
addMember
"add a zipMember"
|zmemb |
self addMember:(zmemb := ZipMember new).
^ zmemb.
"Created: / 29.3.1998 / 18:22:25 / cg"
"Modified: / 9.9.1998 / 20:33:32 / cg"
!
addMember:zmemb
"add a zipMember"
(firstEntry == nil) ifTrue:[
firstEntry := zmemb
] ifFalse:[
lastEntry next:zmemb.
].
lastEntry := zmemb.
^ zmemb.
"Modified: / 30.3.1998 / 17:13:20 / cg"
"Created: / 9.9.1998 / 20:33:06 / cg"
!
findMember:name
"find a zipMember by name"
self zipMembersDo:[:zipd |
(zipd name = name) ifTrue:[^ zipd].
].
^ nil
"Modified: / 30.3.1998 / 17:13:30 / cg"
!
readDirectory
"read the zip directory into a linked-list of zipMembers"
|size count_in dir_size foundPK pos0|
size := file fileSize.
(size == 0) ifTrue:[
count_in := 0.
^ self
].
(size < (ECREC_SIZE+4)) ifTrue:[
^ ZipFileFormatErrorSignal raiseWith:'zipfile too short'.
].
foundPK := false.
file position:(pos0 := size - ECREC_SIZE - 4 + 1).
((file next ~~ ($P asciiValue))
or:[file next ~~ ($K asciiValue)
or:[file next ~~ 8r005
or:[file next ~~ 8r006]]]) ifTrue:[
"/ search for PK ...
file position:1. "/(pos0 - 100).
[file atEnd not and:[foundPK not]] whileTrue:[
(file next == ($P asciiValue)
and:[file next == ($K asciiValue)
and:[file next == 8r005
and:[file next == 8r006]]]) ifTrue:[
foundPK := true.
pos0 := file position - 4.
]
].
foundPK ifTrue:[
'ZipArchive [warning]: funny format; resynchronized'.
] ifFalse:[
^ ZipFileFormatErrorSignal raiseWith:'not a valid zipfile'.
]
].
file skip: (TOTAL_ENTRIES_CENTRAL_DIR - 4).
count_in := file nextUnsignedShortMSB:false. "/ Get TOTAL_ENTRIES_CENTRAL_DIR
dir_size := file nextLongMSB:false. "/ Get SIZE_CENTRAL_DIRECTORY
file position:(pos0 - dir_size ).
file signalAtEnd:true.
0 to:(count_in-1) do:[:i |
|zipd filename_length s|
zipd := ZipMember new.
file skip:(4+C_COMPRESSED_SIZE-4-2-2-2).
zipd compression_method:(file nextUnsignedShortMSB:false). "/ Get compression method
(file nextUnsignedShortMSB:false) isNil ifTrue:[ "/ skip last_mod_file_time
self warn:'file format error or short file: ' , (file pathName ? 'inStream').
^ self.
].
(file nextUnsignedShortMSB:false). "/ skip last_mod_file_date
zipd crc32:(file nextLongMSB:false). "/ Get crc32
zipd compressed_size:(file nextLongMSB:false). "/ Get C_COMPRESSED_SIZE
zipd uncompressed_size:(file nextLongMSB:false). "/ Get C_UNCOMPRESSED_SIZE
filename_length := (file nextUnsignedShortMSB:false). "/ Get C_FILENAME_LENGTH
file skip:(C_RELATIVE_OFFSET_LOCAL_HEADER-(C_FILENAME_LENGTH+2)).
zipd relative_offset_local_header:(file nextLongMSB:false).
zipd name:(s := String new:filename_length).
file nextBytes:filename_length into:s.
self addMember:zipd.
]
"
ZipArchive oldFileNamed:'/usr/lib/jdk1.1.7/lib/classes.zip'
"
"Modified: / 19.10.1998 / 21:27:32 / cg"
!
zipMembersDo:aBlock
"evaluate aBlock for all zipMembers"
|zipd|
zipd := firstEntry.
[zipd notNil] whileTrue:[
aBlock value:zipd.
zipd := zipd next
].
"Created: / 29.3.1998 / 19:15:15 / cg"
"Modified: / 30.3.1998 / 17:13:47 / cg"
! !
!ZipArchive::ZipMember class methodsFor:'documentation'!
documentation
"
keeps some information for a single entry in a zipFile.
"
! !
!ZipArchive::ZipMember methodsFor:'accessing'!
compressed_size
"return the value of the instance variable 'compressed_size' (automatically generated)"
^ compressed_size
"Created: / 29.3.1998 / 18:28:03 / cg"
!
compressed_size:something
"set the value of the instance variable 'compressed_size' (automatically generated)"
compressed_size := something.
"Created: / 29.3.1998 / 18:28:03 / cg"
!
compression_method
"return the value of the instance variable 'compression_method' (automatically generated)"
^ compression_method
"Created: / 29.3.1998 / 20:02:57 / cg"
!
compression_method:something
"set the value of the instance variable 'compression_method' (automatically generated)"
compression_method := something.
"Created: / 29.3.1998 / 20:02:57 / cg"
!
crc32
"return the value of the instance variable 'crc32' (automatically generated)"
^ crc32
"Created: / 29.3.1998 / 20:03:00 / cg"
!
crc32:something
"set the value of the instance variable 'crc32' (automatically generated)"
crc32 := something.
"Created: / 29.3.1998 / 20:03:00 / cg"
!
data
"return the value of the instance variable 'data' (automatically generated)"
^ data
"Created: / 9.4.1998 / 13:05:03 / cg"
!
data:something
"set the value of the instance variable 'data' (automatically generated)"
data := something.
"Created: / 9.4.1998 / 13:05:03 / cg"
!
name
"return the value of the instance variable 'name' (automatically generated)"
^ name
"Created: / 29.3.1998 / 18:29:22 / cg"
!
name:something
"set the value of the instance variable 'name' (automatically generated)"
name := something.
"Created: / 29.3.1998 / 18:29:22 / cg"
!
next
"return the value of the instance variable 'next' (automatically generated)"
^ next
"Created: / 29.3.1998 / 18:29:42 / cg"
!
next:something
"set the value of the instance variable 'next' (automatically generated)"
next := something.
"Created: / 29.3.1998 / 18:29:42 / cg"
!
relative_offset_local_header
"return the value of the instance variable 'relative_offset_local_header' (automatically generated)"
^ relative_offset_local_header
"Created: / 29.3.1998 / 18:28:40 / cg"
!
relative_offset_local_header:something
"set the value of the instance variable 'relative_offset_local_header' (automatically generated)"
relative_offset_local_header := something.
"Created: / 29.3.1998 / 18:28:40 / cg"
!
uncompressed_size
"return the value of the instance variable 'uncompressed_size' (automatically generated)"
^ uncompressed_size
"Created: / 29.3.1998 / 18:28:21 / cg"
!
uncompressed_size:something
"set the value of the instance variable 'uncompressed_size' (automatically generated)"
uncompressed_size := something.
"Created: / 29.3.1998 / 18:28:21 / cg"
! !
!ZipArchive::ZipMember methodsFor:'printing & storing'!
displayString
^ 'ZipMember(' , (name ? '*nil*') , ')'
"Created: / 29.3.1998 / 20:10:07 / cg"
"Modified: / 2.4.1998 / 15:10:08 / cg"
! !
!ZipArchive::ZipMember methodsFor:'queries'!
fileStart
^ relative_offset_local_header + ZipArchive LREC_SIZE + 4 + name size
"Created: / 29.3.1998 / 19:10:57 / cg"
! !
!ZipArchive class methodsFor:'documentation'!
version
^ '$Header: /cvs/stx/stx/libbasic2/ZipArchive.st,v 1.30 2000-08-15 14:37:23 cg Exp $'
! !
ZipArchive initialize!