This is untar.c in view mode; [Download] [Up]
/* untar.c */
/* DESCRIPTION:
* Untar extracts files from an uncompressed tar archive, or one which
* has been compressed with gzip. Usually such archives will have file
* names that end with ".tar" or ".tgz" respectively, although untar
* doesn't depend on any naming conventions.
*
* HOW TO COMPILE:
* Untar doesn't require any special libraries or compile-time flags.
* A simple "cc untar.c -o untar" (or the local equivelent) is
* sufficient. Even "make untar" works, without needing a Makefile.
* For Microsoft Visual C++, the command is either "cl untar.c"
* (for 32 bit compilers) or "cl /F 1400 untar.c" (for 16-bit).
* IF YOU SEE COMPILER WARNINGS, THAT'S NORMAL; you can ignore them.
*
* PORTABILITY:
* Untar uses only the <stdio.h> header. It uses old-style function
* definitions. It opens all files in binary mode. Taken together,
* this means that untar should compile & run on just about anything
* ... though your compiler may give a lot of warning messages.
*
* If your system supports the POSIX chmod(2) and utime(2) calls, then
* you may wish to compile with -D_POSIX_SOURCE, which will enable untar
* to use those system calls to restore the timestamp and permissions of
* the extracted files. (For Linux, _POSIX_SOURCE is always defined.)
*
* SELF-EXTRACTING ARCHIVES:
* You can create a self-extracting archive by compiling untar.c for the
* target system, and then appending the gzipped tar archive onto the end
* of the executable. When the resulting executable is executed with "-s"
* it will locate the data inside itself, and extract it. An example of
* this under UNIX:
* cc untar.c -o mystuff
* tar cvfz - datafiles... >>mystuff
* mystuff -s -t
*
* AUTHOR & COPYRIGHT INFO:
* Written by Steve Kirkendall, kirkenda@cs.pdx.edu
* Placed in public domain, 6 October 1995
*
* Portions derived from inflate.c -- Not copyrighted 1992 by Mark Adler
* version c10p1, 10 January 1993
*/
#include <stdio.h>
#ifndef SEEK_SET
# define SEEK_SET 0
#endif
#ifdef _POSIX_SOURCE
# include <sys/types.h>
# include <sys/stat.h>
# include <utime.h>
#endif
#define WSIZE 32768 /* size of decompression buffer */
#define TSIZE 512 /* size of a "tape" block */
#define CR 13 /* carriage-return character */
#define LF 10 /* line-feed character */
typedef unsigned char uchar_t;
typedef unsigned short ushort_t;
typedef unsigned long ulong_t;
typedef struct
{
uchar_t magic[2]; /* magic: 0x1F, 0x8b */
uchar_t compression; /* compression method: 8=deflated */
uchar_t flags; /* content flags: 0x08 bit -> name present */
uchar_t mtime[4]; /* time_t when archive created */
uchar_t extraflags; /* ? */
uchar_t os; /* operating system: 3=UNIX */
/* if flags&0x08, then original file name goes here, '\0'-terminated */
} gzhdr_t;
#define MAGIC0 0x1f
#define MAGIC1 0x8b
#define DEFLATE 0x08
#define NAME 0x08
typedef struct
{
char filename[100]; /* 0 name of next file */
char mode[8]; /* 100 Permissions and type (octal digits) */
char owner[8]; /* 108 Owner ID (ignored) */
char group[8]; /* 116 Group ID (ignored) */
char size[12]; /* 124 Bytes in file (octal digits) */
char mtime[12]; /* 136 Modification time stamp (octal digits)*/
char checksum[8]; /* 148 Header checksum (ignored) */
char type; /* 156 File type (see below) */
char linkto[100]; /* 157 Linked-to name (ignored) */
char brand[8]; /* 257 Identifies tar version (ignored) */
char ownername[32]; /* 265 Name of owner (ignored) */
char groupname[32]; /* 297 Name of group (ignored) */
char devmajor[8]; /* 329 Device major number (ignored) */
char defminor[8]; /* 337 Device minor number (ignored) */
char prefix[155]; /* 345 Prefix of name (optional) */
char RESERVED[12]; /* 500 Pad header size to 512 bytes */
} tar_t;
#define ISREGULAR(hdr) ((hdr).type < '1' || (hdr).type > '6')
typedef struct huft {
uchar_t e; /* number of extra bits or operation */
uchar_t b; /* number of bits in this code or subcode */
union {
ushort_t n; /* literal, length base, or distance base */
struct huft *t; /* pointer to next level of table */
} v;
} huft_t;
int wp; /* output counter */
uchar_t slide[WSIZE];
#define error(desc) {fprintf(stderr, "%s:%s", inname, (desc)); exit(1);}
/* 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_t 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_t 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_t 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_t 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};
char *inname;/* name of input archive */
FILE *infp; /* input byte stream */
FILE *outfp; /* output stream, for file currently being extracted */
FILE *tarfp; /* usually NULL; else file for writing gunzipped data */
int maketar;/* -u: 1 to gunzip only, 0 to gunzip and extract tar files */
int listing;/* -t: 1 if listing, 0 if extracting */
int quiet; /* -q: 1 to write nothing to stderr, 0 for normal chatter */
int force; /* -f: 1 to overwrite existing files, 0 to skip them */
int convert;/* -c: 1 to convert newlines, 0 to leave unchanged */
int selfext;/* -s: 1 to skip data preceding a GZIP archive */
int nskips; /* number of files skipped in absence of "force" option */
char **only; /* array of filenames to extract/list */
int nonlys; /* number of filenames in "only" array; 0=extract all */
ulong_t bb; /* bit buffer */
unsigned bk; /* bits in bit buffer */
ushort_t mask_bits[] = {
0x0000,
0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
};
#define NEXTBYTE() (uchar_t)getc(infp)
#define NEEDBITS(n) {while(k<(n)){b|=((ulong_t)NEXTBYTE())<<k;k+=8;}}
#define DUMPBITS(n) {b>>=(n);k-=(n);}
int lbits = 9; /* bits in base literal/length lookup table */
int dbits = 6; /* bits in base distance lookup table */
/* If BMAX needs to be larger than 16, then h and x[] should be ulong_t. */
#define BMAX 16 /* maximum bit length of any code (16 for explode) */
#define N_MAX 288 /* maximum number of codes in any set */
unsigned hufts; /* track memory usage */
/*----------------------------------------------------------------------------*/
/* create a file for writing. If necessary, create the directories leading up
* to that file as well.
*/
static FILE *createpath(name)
char *name; /* pathname of file to create */
{
FILE *fp;
int i;
/* if we aren't allowed to overwrite and this file exists, return NULL */
if (!force && access(name, 0) == 0)
{
fprintf(stderr, "%s: exists, will not overwrite without -f\n", name);
return NULL;
}
/* first try creating it the easy way */
fp = fopen(name, convert ? "w" : "wb");
if (fp)
return fp;
/* Else try making all of its directories, and then try creating
* the file again.
*/
for (i = 0; name[i]; i++)
{
/* If this is a slash, then temporarily replace the '/'
* with a '\0' and do a mkdir() on the resulting string.
* Ignore errors for now.
*/
if (name[i] == '/')
{
name[i] = '\0';
(void)mkdir(name, 0777);
name[i] = '/';
}
}
fp = fopen(name, convert ? "w" : "wb");
if (!fp)
perror(name);
return fp;
}
/* This calls fwrite(), possibly after converting CR-LF to LF */
static void cvtwrite(blk, size, fp)
uchar_t *blk; /* the block to be written */
ulong_t size; /* number of characters to be written */
FILE *fp; /* file to write to */
{
int i, j;
static uchar_t mod[TSIZE];
if (convert)
{
for (i = j = 0; i < size; i++)
{
/* convert LF to local newline convention */
if (blk[i] == LF)
mod[j++] = '\n';
/* If CR-LF pair, then delete the CR */
else if (blk[i] == CR && (i+1 >= size || blk[i+1] == LF))
;
/* other characters copied literally */
else
mod[j++] = blk[i];
}
size = j;
blk = mod;
}
fwrite(blk, (size_t)size, sizeof(uchar_t), fp);
}
/* list files in an archive, and optionally extract them as well */
static void untar(blk)
uchar_t *blk; /* a tape block */
{
static char nbuf[256];/* storage space for prefix+name, combined */
static char *name; /* prefix and name, combined */
static ulong_t size; /* number of bytes in file */
int i;
#ifdef _POSIX_SOURCE
static mode_t mode; /* file permissions */
static struct utimbuf timestamp; /* file timestamp */
#endif
/* process each type of tape block differently */
if (size > TSIZE)
{
/* data block, but not the last one */
if (outfp)
cvtwrite(blk, (ulong_t)TSIZE, outfp);
size -= TSIZE;
}
else if (size > 0)
{
/* last data block of current file */
if (outfp)
{
cvtwrite(blk, size, outfp);
fclose(outfp);
#ifdef _POSIX_SOURCE
utime(name, ×tamp);
chmod(name, mode);
#endif
}
size = 0;
}
else if (((tar_t *)blk)->filename[0] == '\0')
{
/* end-of-archive marker */
exit(0);
}
else
{
/* file header */
/* half-assed verification -- does it look like header? */
if (((tar_t *)blk)->filename[99] != '\0'
|| (((tar_t *)blk)->size[0] < '0'
&& ((tar_t *)blk)->size[0] != ' ')
|| ((tar_t *)blk)->size[0] > '9')
{
fprintf(stderr, "%s: not a valid tar file\n", inname);
exit(2);
}
/* combine prefix and filename */
memset(nbuf, 0, sizeof nbuf);
name = nbuf;
if (((tar_t *)blk)->prefix[0])
{
strncpy(name, ((tar_t *)blk)->prefix, sizeof ((tar_t *)blk)->prefix);
strcat(name, "/");
strncat(name + strlen(name), ((tar_t *)blk)->filename, sizeof ((tar_t *)blk)->filename);
}
else
{
strncpy(name, ((tar_t *)blk)->filename, sizeof ((tar_t *)blk)->filename);
}
/* strip off leading '/' characters */
while (*name == '/')
name++;
/* if last character of name is '/' then assume directory */
if (*name && name[strlen(name) - 1] == '/')
((tar_t *)blk)->type = '5';
/* convert file size */
for (size = 0L, i = 0; i < sizeof(((tar_t *)blk)->size); i++)
{
if (((tar_t *)blk)->size[i] >= '0' && ((tar_t *)blk)->size[i] <= '7')
size = size * 8 + ((tar_t *)blk)->size[i] - '0';
}
#ifdef _POSIX_SOURCE
/* convert file timestamp */
for (timestamp.modtime = 0L, i = 0; i < sizeof(((tar_t *)blk)->mtime); i++)
{
if (((tar_t *)blk)->mtime[i] >= '0' && ((tar_t *)blk)->mtime[i] <= '7')
timestamp.modtime = timestamp.modtime * 8 + ((tar_t *)blk)->mtime[i] - '0';
}
timestamp.actime = timestamp.modtime;
/* convert file permissions */
for (mode = i = 0; i < sizeof(((tar_t *)blk)->mode); i++)
{
if (((tar_t *)blk)->mode[i] >= '0' && ((tar_t *)blk)->mode[i] <= '7')
mode = mode * 8 + ((tar_t *)blk)->mode[i] - '0';
}
#endif
/* If we have an "only" list, and this file isn't in it,
* then skip it.
*/
if (nonlys > 0)
{
for (i = 0;
i < nonlys
&& strcmp(only[i], name)
&& (strncmp(only[i], name, strlen(only[i]))
|| name[strlen(only[i])] != '/');
i++)
{
}
if (i >= nonlys)
{
outfp = NULL;
return;
}
}
/* list the file */
if (!quiet)
{
printf("%c %s",
ISREGULAR(*(tar_t *)blk) ? '-' : ("Llcbdp"[((tar_t *)blk)->type - '1']),
name);
}
/* if not a regular file, then skip it */
if (!ISREGULAR(*(tar_t *)blk))
{
if (!quiet)
printf("\n");
return;
}
/* print file statistics */
if (!quiet)
{
printf(" (%ld byte%s, %ld tape block%s)\n",
size,
size == 1 ? "" : "s",
(size + TSIZE - 1) / TSIZE,
(size > 0 && size <= TSIZE) ? "" : "s");
}
/* if extracting, then try to create the file */
if (!listing)
outfp = createpath(name);
else
outfp = NULL;
/* if file is 0 bytes long, then we're done already! */
if (size == 0 && outfp)
{
fclose(outfp);
#ifdef _POSIX_SOURCE
utime(name, ×tamp);
chmod(name, mode);
#endif
}
}
}
/* send decompressed tape blocks to untar() */
void flush_output(w)
unsigned w; /* number of bytes in slide[] */
{
unsigned i;
if (tarfp)
{
cvtwrite(slide, (ulong_t)w, tarfp);
}
else
{
/* send each block to untar() */
for (i = 0; i < w; i += TSIZE)
{
untar(&slide[i]);
}
}
}
/*----------------------------------------------------------------------------*/
/* 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.
*/
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_t *d; /* list of base values for non-simple codes */
ushort_t *e; /* list of extra bits for non-simple codes */
huft_t **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 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 l; /* bits per table (returned in m) */
register unsigned *p; /* pointer into c[], b[], or v[] */
register huft_t *q; /* points to current table */
huft_t r; /* table entry for structure assignment */
huft_t *u[BMAX]; /* table stack */
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 */
memset(c, 0, sizeof(c));
p = b; i = n;
do {
c[*p++]++; /* assume all entries <= BMAX */
} while (--i);
if (c[0] == n) /* null input--all zero length codes */
{
*t = (huft_t *)NULL;
*m = 0;
return 0;
}
/* Find minimum and maximum length, bound *m by those */
l = *m;
for (j = 1; j <= BMAX; j++)
if (c[j])
break;
k = j; /* minimum code length */
if ((unsigned)l < j)
l = j;
for (i = BMAX; i; i--)
if (c[i])
break;
g = i; /* maximum code length */
if ((unsigned)l > i)
l = i;
*m = l;
/* 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; /* bits decoded == (l * h) */
u[0] = (huft_t *)NULL; /* just to keep compilers happy */
q = (huft_t *)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; /* previous table always l bits */
/* compute minimum size table less than or equal to l bits */
z = (z = g - w) > (unsigned)l ? l : z; /* upper limit on table size */
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 */
}
}
z = 1 << j; /* table entries for j-bit table */
/* allocate and link in new table */
q = (huft_t *)malloc((z + 1)*sizeof(huft_t));
hufts += z + 1; /* track memory usage */
*t = q + 1; /* link to list for huft_free() */
*(t = &(q->v.t)) = (huft_t *)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_t)l; /* bits to dump before this table */
r.e = (uchar_t)(16 + j);/* bits in this table */
r.v.t = q; /* pointer to this table */
j = i >> (w - l); /* (get around Turbo C bug) */
u[h-1][j] = r; /* connect to last table */
}
}
/* set up table entry in r */
r.b = (uchar_t)(k - w);
if (p >= v + n)
r.e = 99;/* out of values--invalid code */
else if (*p < s)
{
r.e = (uchar_t)(*p < 256 ? 16 : 15);/* 256 is end-of-block code */
r.v.n = *p++; /* simple code is just the value */
}
else
{
r.e = (uchar_t)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])
{
h--; /* don't need to update q */
w -= l;
}
}
}
/* Return true (1) if we were given an incomplete table */
return y != 0 && g != 1;
}
/* 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.
*/
int huft_free(t)
huft_t *t; /* table to free */
{
register huft_t *p, *q;
/* Go through linked list, freeing from the malloced (t[-1]) address. */
p = t;
while (p != (huft_t *)NULL)
{
q = (--p)->v.t;
free(p);
p = q;
}
return 0;
}
/* Inflate (decompress) the codes in a deflated (compressed) block.
* Return an error code or zero if it all goes ok.
*/
int inflate_codes(tl, td, bl, bd)
huft_t *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 */
huft_t *t; /* pointer to table entry */
unsigned ml, md; /* masks for bl and bd bits */
register ulong_t 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_bits[bl]; /* precompute masks for speed */
md = mask_bits[bd];
for (;;) /* 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_bits[e]))->e) > 16);
DUMPBITS(t->b)
if (e == 16) /* then it's a literal */
{
slide[w++] = (uchar_t)t->v.n;
if (w == WSIZE)
{
flush_output(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_bits[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_bits[e]))->e) > 16);
DUMPBITS(t->b)
NEEDBITS(e)
d = w - t->v.n - ((unsigned)b & mask_bits[e]);
DUMPBITS(e)
/* do the copy */
do
{
n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
#if !defined(NOMEMCPY) && !defined(DEBUG)
if (w - d >= e) /* (this test assumes unsigned comparison) */
{
memcpy(slide + w, slide + d, e);
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_output(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;
}
/* "decompress" an inflated type 0 (stored) block. */
int inflate_stored()
{
unsigned n; /* number of bytes in block */
unsigned w; /* current window position */
register ulong_t 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 */
/* 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_t)b;
if (w == WSIZE)
{
flush_output(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;
}
/* 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.
*/
int inflate_fixed()
{
int i; /* temporary variable */
huft_t *tl; /* literal/length code table */
huft_t *td; /* distance code table */
int bl; /* lookup bits for tl */
int bd; /* lookup bits for td */
unsigned l[288]; /* length list for huft_build */
/* set up 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;
bl = 7;
if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
return i;
/* set up distance table */
for (i = 0; i < 30; i++) /* make an incomplete code set */
l[i] = 5;
bd = 5;
if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
{
huft_free(tl);
return i;
}
/* decompress until an end-of-block code */
if (inflate_codes(tl, td, bl, bd))
return 1;
/* free the decoding tables, return */
huft_free(tl);
huft_free(td);
return 0;
}
/* decompress an inflated type 2 (dynamic Huffman codes) block. */
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 */
huft_t *tl; /* literal/length code table */
huft_t *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 */
unsigned ll[286+30];/* literal/length and distance code lengths */
register ulong_t b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
/* make local bit buffer */
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)
if (nl > 286 || nd > 30)
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);
return i; /* incomplete code set */
}
/* read in literal and distance code lengths */
n = nl + nd;
m = mask_bits[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)
{
if (i == 1) {
error(" incomplete literal 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)
{
if (i == 1) {
error(" incomplete distance tree\n");
huft_free(td);
}
huft_free(tl);
return i; /* incomplete code set */
}
/* decompress until an end-of-block code */
if (inflate_codes(tl, td, bl, bd))
return 1;
/* free the decoding tables, return */
huft_free(tl);
huft_free(td);
return 0;
}
/* decompress an inflated block */
int inflate_block(e)
int *e; /* last block flag */
{
unsigned t; /* block type */
register ulong_t b; /* bit buffer */
register unsigned k; /* number of bits in bit buffer */
/* make local bit buffer */
b = bb;
k = bk;
/* read in last block bit */
NEEDBITS(1)
*e = (int)b & 1;
DUMPBITS(1)
/* read in block type */
NEEDBITS(2)
t = (unsigned)b & 3;
DUMPBITS(2)
/* 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();
/* bad block type */
return 2;
}
/* decompress an inflated entry */
int inflate()
{
int e; /* last block flag */
int r; /* result code */
unsigned h; /* maximum huft_t's malloc'ed */
/* initialize window, bit buffer */
wp = 0;
bk = 0;
bb = 0;
/* decompress until the last block */
h = 0;
do
{
hufts = 0;
if ((r = inflate_block(&e)) != 0)
return r;
if (hufts > h)
h = hufts;
} while (!e);
/* Undo too much lookahead. The next read will be byte aligned so we
* can discard unused bits in the last meaningful byte.
*/
while (bk >= 8)
{
bk -= 8;
/* inptr--;*/
}
/* flush out slide */
flush_output(wp);
/* return success */
return 0;
}
/* Process an archive file. This involves reading the blocks one at a time
* (uncompressing if necessary as it goes along) and passing them to a untar()
* function.
*/
static void doarchive(filename)
char *filename; /* name of the archive file */
{
char gunzipname[300];
int ch, len;
long pos, datapos;
/* open the archive */
inname = filename;
infp = fopen(filename, "rb");
if (!infp)
{
perror(filename);
return;
}
/* if self-extracting, then skip to GZIP data */
if (selfext)
{
for (len = 0; len < 3; )
{
pos = ftell(infp);
ch = getc(infp);
if (ch == EOF)
{
fprintf(stderr, "No data in %s\n", filename);
return;
}
else if (ch == MAGIC0)
{
datapos = pos;
len = 1;
}
else if (ch == MAGIC1 && len == 1)
{
len = 2;
}
else if (ch == DEFLATE && len == 2)
{
len = 3;
}
else
{
len = 0;
}
}
fseek(infp, datapos, SEEK_SET);
}
/* read the first few bytes, so we can determine whether to decompress */
fread(slide, 1, sizeof(gzhdr_t), infp);
if (((gzhdr_t *)slide)->magic[0] == MAGIC0
&& ((gzhdr_t *)slide)->magic[1] == MAGIC1)
{
/* COMPRESSED WITH GZIP */
/* Check for unsupported compression types */
if (((gzhdr_t *)slide)->compression != DEFLATE)
{
fprintf(stderr, "Unsupported compression type\n");
exit(0);
}
/* If original file name present, skip it */
if (((gzhdr_t *)slide)->flags & NAME)
{
for (len = 0; (ch = getc(infp)) != '\0'; len++)
{
gunzipname[len] = ch;
}
gunzipname[len] = '\0';
}
else if (maketar)
{
/* we need to make up a name */
strcpy(gunzipname, filename);
len = strlen(filename);
if (len > 3 && (!strcmp(filename + len - 3, ".gz")
|| !strcmp(filename + len - 3, ".GZ")))
{
gunzipname[len - 3] = '\0';
}
else if (len > 2 && (!strcmp(filename + len - 2, ".z")
|| !strcmp(filename + len - 2, ".Z")))
{
gunzipname[len - 2] = '\0';
}
else if (len > 4 && (!strcmp(filename + len - 4, ".tgz")
|| !strcmp(filename + len - 4, ".TGZ")))
{
strcpy(&gunzipname[len - 4], ".tar");
}
else
{
strcpy(gunzipname, "untar.out");
}
}
/* if we're writing the gunzip output, then create the output file */
if (maketar)
{
if (!quiet && listing)
{
printf("%s: would be gunzipped to %s\n", filename, gunzipname);
fclose(infp);
return;
}
/* if not allowed to overwrite and file exists, complain */
if (!force && access(gunzipname, 0) == 0)
{
fprintf(stderr, "%s: exists, will not overwrite without -f\n", gunzipname);
exit(2);
}
tarfp = fopen(gunzipname, convert ? "w" : "wb");
if (!tarfp)
{
perror(gunzipname);
exit(2);
}
}
/* inflate the blocks */
inflate();
}
else
{
/* UNCOMPRESSED */
/* if we were supposed to just uncompress, complain */
if (maketar)
{
fprintf(stderr, "%s: isn't gzipped\n", filename);
fclose(infp);
return;
}
/* read the rest of the first block */
fread(&slide[sizeof(gzhdr_t)], 1, TSIZE - sizeof(gzhdr_t), infp);
/* send each block to the untar() function */
do
{
untar(slide);
} while (fread(slide, 1, TSIZE, infp) > 0);
}
/* close the archive file. */
fclose(infp);
if (tarfp)
{
fclose(tarfp);
tarfp = NULL;
if (!quiet)
{
printf("%s: gunzipped to %s\n", filename, gunzipname);
}
}
}
static void usage(argv0)
char *argv0; /* name of program */
{
/* Give a usage message and exit */
fprintf(stderr, "Usage: %s [options] archive.tgz [filename]...\n", argv0);
fprintf(stderr, " or: %s [options] -s [filename]...\n", argv0);
fprintf(stderr, " or: %s [options] -u filename...\n", argv0);
fprintf(stderr, "\n");
fprintf(stderr, "Options -t test -- list contents but don't extract\n");
fprintf(stderr, " -f force -- allow existing files to be overwritten\n");
fprintf(stderr, " -q quiet -- suppress normal chatter\n");
fprintf(stderr, " -c convert -- convert files to local text format\n");
fprintf(stderr, " -s self extracting -- gzipped archive data is appended to %s\n", argv0);
fprintf(stderr, " -u uncompress -- perform \"gunzip\" but not \"tar x\"\n");
fprintf(stderr, "+------------------------------------%.*s---------------------------+\n", strlen(argv0), "--------------------------------------");
fprintf(stderr, "| FOR SELF-EXTRACTING ARCHIVES, USE \"%s -s\" TO EXTRACT THE FILES. |\n", argv0);
fprintf(stderr, "+------------------------------------%.*s---------------------------+\n", strlen(argv0), "--------------------------------------");
fprintf(stderr, "This program lists/extracts files from a \"*.tar\" or \"*.tgz\" archive. You can\n");
fprintf(stderr, "optionally specify certain files or directories to list/extract; otherwise it\n");
#ifdef _POSIX_SOURCE
fprintf(stderr, "will list/extract them all. File attributes are preserved. This program\n");
#else
fprintf(stderr, "will list/extract them all. File attributes are NOT preserved. This program\n");
#endif
fprintf(stderr, "can also be used (with -u) to gunzip non-tar files.\n");
fprintf(stderr, "\n");
fprintf(stderr, "THIS PROGRAM IS IN THE PUBLIC DOMAIN, AND IS FREELY REDISTRIBUTABLE.\n");
exit(1);
}
/* parse command-line arguments, and process each file */
main(argc, argv)
int argc;
char **argv;
{
int exitcode = 0; /* value passed to exit() */
int i, j;
/* listing? extracting? */
for (i = 1; i < argc && argv[i][0] == '-'; i++)
{
if (!argv[i][1])
usage(argv[0]);
for (j = 1; argv[i][j]; j++)
{
switch (argv[i][j])
{
case 'u': maketar = 1; break;
case 't': listing = 1; break;
case 'f': force = 1; break;
case 'q': quiet = 1; break;
case 'c': convert = 1; break;
case 's': selfext = 1; break;
default:
usage(argv[0]);
}
}
}
if (i >= argc && !selfext)
{
usage(argv[0]);
}
if (maketar)
{
/* uncompress each argument */
for (; i < argc; i++)
{
doarchive(argv[i]);
}
}
else if (selfext)
{
/* detect whether we have an extraction list */
if (i < argc)
{
only = &argv[i];
nonlys = argc - i;
}
else
{
nonlys = 0;
}
/* list/extract files from self */
doarchive(argv[0]);
}
else
{
/* detect whether we have an extraction list */
if (i + 1 < argc)
{
only = &argv[i + 1];
nonlys = argc - (i + 1);
}
else
{
nonlys = 0;
}
/* list/extract files from archive */
doarchive(argv[i]);
}
exit(0);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.