V10/cmd/pack/unpack.c
/*
* Huffman decompressor
* Usage: pcat filename...
* or unpack filename...
*/
static char unpackvers[] = "@(#)unpack.c 1.10";
#include <stdio.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#define VOID (void)
jmp_buf env;
struct stat status;
char *argv0, *argvk;
short errorm;
#define NAMELEN 80
#define SUF0 '.'
#define SUF1 'z'
#define US 037
#define RS 036
#define BLKSIZE BUFSIZ
/* variables associated with i/o */
char filename[NAMELEN+2];
short infile;
short outfile;
short inleft;
char *inp;
char *outp;
char inbuff[BUFSIZ];
char outbuff[BUFSIZ];
/* the dictionary */
long origsize;
short maxlev;
short intnodes[25];
char *tree[25];
char characters[256];
char *eof;
/* read in the dictionary portion and build decoding structures */
/* return 1 if successful, 0 otherwise */
getdict ()
{
register int c, i, nchildren;
/*
* check two-byte header
* get size of original file,
* get number of levels in maxlev,
* get number of leaves on level i in intnodes[i],
* set tree[i] to point to leaves for level i
*/
eof = &characters[0];
inbuff[6] = 25;
inleft = read (infile, &inbuff[0], BUFSIZ);
if (inleft < 0) {
eprintf (".z: read error");
return (0);
}
if (inbuff[0] != US)
goto goof;
if (inbuff[1] == US) { /* oldstyle packing */
if (setjmp (env))
return (0);
expand ();
return (1);
}
if (inbuff[1] != RS)
goto goof;
inp = &inbuff[2];
origsize = 0;
for (i=0; i<4; i++)
origsize = origsize*256 + ((*inp++) & 0377);
maxlev = *inp++ & 0377;
if (maxlev > 24) {
goof: eprintf (".z: not in packed format");
return (0);
}
for (i=1; i<=maxlev; i++)
intnodes[i] = *inp++ & 0377;
for (i=1; i<=maxlev; i++) {
tree[i] = eof;
for (c=intnodes[i]; c>0; c--) {
if (eof >= &characters[255])
goto goof;
*eof++ = *inp++;
}
}
*eof++ = *inp++;
intnodes[maxlev] += 2;
inleft -= inp - &inbuff[0];
if (inleft < 0)
goto goof;
/*
* convert intnodes[i] to be number of
* internal nodes possessed by level i
*/
nchildren = 0;
for (i=maxlev; i>=1; i--) {
c = intnodes[i];
intnodes[i] = nchildren /= 2;
nchildren += c;
}
return (decode ());
}
/* unpack the file */
/* return 1 if successful, 0 otherwise */
decode ()
{
register int bitsleft, c, i;
int j, lev;
char *p;
outp = &outbuff[0];
lev = 1;
i = 0;
while (1) {
if (inleft <= 0) {
inleft = read (infile, inp = &inbuff[0], BUFSIZ);
if (inleft < 0) {
eprintf (".z: read error");
return (0);
}
}
if (--inleft < 0) {
uggh: if(origsize == 0) return 1;
eprintf (".z: unpacking error");
return (0);
}
c = *inp++;
bitsleft = 8;
while (--bitsleft >= 0) {
i *= 2;
if (c & 0200)
i++;
c <<= 1;
if ((j = i - intnodes[lev]) >= 0) {
p = &tree[lev][j];
if (p == eof) {
c = outp - &outbuff[0];
if (write (outfile, &outbuff[0], c) != c) {
wrerr: eprintf (": write error");
return (0);
}
origsize -= c;
if (origsize != 0)
goto uggh;
return (1);
}
*outp++ = *p;
if (outp == &outbuff[BUFSIZ]) {
if (write (outfile, outp = &outbuff[0], BUFSIZ) != BUFSIZ)
goto wrerr;
origsize -= BUFSIZ;
}
lev = 1;
i = 0;
} else
lev++;
}
}
}
main (argc, argv)
char *argv[];
{
register i, k;
int sep, pcat = 0;
register char *p1, *cp;
int fcount = 0; /* failure count */
p1 = *argv;
while(*p1++); /* Point p1 to end of argv[0] string */
while(--p1 >= *argv)
if(*p1 == '/')break;
*argv = p1 + 1;
argv0 = argv[0];
if(**argv == 'p')pcat++; /* User entered pcat (or /xx/xx/pcat) */
if(pcat && argc == 1){
infile = 0;
outfile = 1;
return getdict();
}
for (k=1; k<argc; k++) {
errorm = 0;
sep = -1;
cp = filename;
argvk = argv[k];
for (i=0; i < (NAMELEN-3) && (*cp = argvk[i]); i++)
if (*cp++ == '/')
sep = i;
if (cp[-1] == SUF1 && cp[-2] == SUF0) {
argvk[i-2] = '\0'; /* Remove suffix and try again */
k--;
continue;
}
fcount++; /* expect the worst */
if (i >= (NAMELEN-3) || (i - sep) > 13) {
eprintf (": file name too long");
goto done;
}
*cp++ = SUF0;
*cp++ = SUF1;
*cp = '\0';
if ((infile = open (filename, 0)) == -1) {
eprintf (".z: cannot open");
goto done;
}
if (pcat)
outfile = 1; /* standard output */
else {
if (stat (argvk, &status) != -1) {
eprintf (": already exists");
goto done;
}
VOID fstat (infile, &status);
if (status.st_nlink != 1)
eprintf (".z: Warning: file has links");
if ((outfile = creat (argvk, status.st_mode&07777)) == -1) {
eprintf (": cannot create");
goto done;
}
if (chmod (argvk, status.st_mode) != 0)
printf("can't change mode to %o\n", status.st_mode);
VOID chown (argvk, status.st_uid, status.st_gid);
}
if (getdict ()) { /* unpack */
fcount--; /* success after all */
if (!pcat) {
eprintf (": unpacked");
VOID unlink (filename);
/*
* preserve acc & mod dates
*/
VOID utime (argvk, &status.st_atime);
}
}
else
if (!pcat)
VOID unlink (argvk);
done: if (errorm)
VOID fprintf (stderr, "\n");
VOID close (infile);
if (!pcat)
VOID close (outfile);
}
return (fcount);
}
eprintf (s)
char *s;
{
if (!errorm) {
errorm = 1;
VOID fprintf (stderr, "%s: %s", argv0, argvk);
}
VOID fprintf (stderr, s);
}
/*
* This code is for unpacking files that
* were packed using the previous algorithm.
*/
int Tree[1024];
expand ()
{
register tp, bit;
short word;
int keysize, i, *t;
outp = outbuff;
inp = &inbuff[2];
inleft -= 2;
origsize = ((long) (unsigned) getwd ())*256*256;
origsize += (unsigned) getwd ();
t = Tree;
for (keysize = getwd (); keysize--; ) {
if ((i = getch ()) == 0377)
*t++ = getwd ();
else
*t++ = i & 0377;
}
bit = tp = 0;
for (;;) {
if (bit <= 0) {
word = getwd ();
bit = 16;
}
tp += Tree[tp + (word<0)];
word <<= 1;
bit--;
if (Tree[tp] == 0) {
putch (Tree[tp+1]);
tp = 0;
if ((origsize -= 1) == 0) {
write (outfile, outbuff, outp - outbuff);
return;
}
}
}
}
getch ()
{
if (inleft <= 0) {
inleft = read (infile, inp = inbuff, BUFSIZ);
if (inleft < 0) {
eprintf (".z: read error");
longjmp (env, 1);
}
}
inleft--;
return (*inp++ & 0377);
}
getwd ()
{
register char c;
register d;
c = getch ();
d = getch ();
d <<= 8;
d |= c & 0377;
return (d);
}
putch (c)
char c;
{
register n;
*outp++ = c;
if (outp == &outbuff[BUFSIZ]) {
n = write (outfile, outp = outbuff, BUFSIZ);
if (n < BUFSIZ) {
eprintf (": write error");
longjmp (env, 2);
}
}
}