ZipArchive.st
author Claus Gittinger <cg@exept.de>
Wed, 07 May 2003 16:12:34 +0200
changeset 1199 105887070e89
parent 1194 c5f793d52c3c
child 1418 0bfc19132acd
permissions -rw-r--r--
method category rename

"
 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 C_CENTRALHEADERSIGNATURE
		C_LOCALHEADERSIGNATURE C_CENTRALENDSIGNATURE
		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 dataStart 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.
    f exists ifFalse:[^ nil].

    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.

    C_LOCALHEADERSIGNATURE := 16r04034b50.
    C_CENTRALHEADERSIGNATURE := 16r02014b50.
    C_CENTRALENDSIGNATURE := 16r06054b50.

    "/ 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 isNil ifTrue:[
        ZipFileFormatErrorSignal := Error 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 position0Based:(zmemb fileStart).
    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 ~~ #write 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
    |fn|

    file isNil ifTrue:[
        fn := archiveName asFilename.
        mode ~~ #write ifTrue:[
            file := fn readStream.
        ] ifFalse:[
            file := fn writeStream
        ].
        file checkNilFileStream.
        file 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 raiseErrorString:' - zipfile too short'.
    ].

    foundPK := false.
    file position0Based:(pos0 := size - ECREC_SIZE - 4).
    ((file next ~~ ($P asciiValue))
    or:[file next ~~ ($K asciiValue)
    or:[file next ~~ 8r005
    or:[file next ~~ 8r006]]]) ifTrue:[
        "/ search for PK ...
        file reset. "/(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 position0Based - 4.
            ]
        ].
        foundPK ifTrue:[
            'ZipArchive [warning]: funny format; resynchronized'.
        ] ifFalse:[
            ^ ZipFileFormatErrorSignal raiseErrorString:' - invalid 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 position0Based:(pos0 - dir_size ).

    file signalAtEnd:true.

    0 to:(count_in-1) do:[:i |
        |zipd filename_length s sig extraFieldLength fileCommentLength relative_offset_local_header
         pos extra|

        zipd := ZipMember new.
        "/ file skip:(4+C_COMPRESSED_SIZE-4-2-2-2).
        sig := file nextLongMSB:false.          "/ signature
        file nextUnsignedShortMSB:false. "/ versionMade
        file nextUnsignedShortMSB:false. "/ versionExtract
        file nextUnsignedShortMSB:false. "/ flags
        
        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)).
        extraFieldLength := (file nextUnsignedShortMSB:false).   "/ extraFieldLength
        fileCommentLength := (file nextUnsignedShortMSB:false).   "/ fileCommentLength
        (file nextUnsignedShortMSB:false).   "/ diskNumberStart
        (file nextUnsignedShortMSB:false).   "/ internalFileAttribute
        (file nextLongMSB:false).            "/ externalFileAttribute
        relative_offset_local_header := file nextLongMSB:false.

        sig ~= C_CENTRALHEADERSIGNATURE ifTrue:[
            self warn:'file format error - bad centralHeaderSignature in:' , (file pathName ? 'inStream').
            ^ self.
        ].

        pos := file position.
        file position0Based:(relative_offset_local_header + 28).
        extra := file nextUnsignedShortMSB:false.
        zipd dataStart:(relative_offset_local_header + "C_SIZEOFLOCALHEADER" 30 + filename_length + extra ).
        file position:pos.

        zipd name:(s := String new:filename_length).
        file nextBytes:filename_length into:s.

        file skip:(extraFieldLength + fileCommentLength).
        self addMember:zipd.
    ]

    "
     ZipArchive flush.
     ZipArchive oldFileNamed:'/usr/lib/jdk1.1.7/lib/classes.zip'
     ZipArchive oldFileNamed:'/usr/lib/jdk1.1.8/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"
!

dataStart
    "return the value of the instance variable 'dataStart' (automatically generated)"

    ^ dataStart

    "Created: / 29.3.1998 / 18:28:40 / cg"
!

dataStart:something
    "set the value of the instance variable 'dataStart' (automatically generated)"

    dataStart := something.

    "Created: / 29.3.1998 / 18:28:40 / 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"
!

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
    ^ dataStart
    "/ ^ 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.43 2003-05-07 14:12:34 cg Exp $'
! !

ZipArchive initialize!