/* * 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); } } }