#ifndef lint
static char sccsid[] = "@(#)uncompact.c	4.7 (Berkeley) 8/25/84";
#endif

/*
 *  Uncompact adaptive Huffman code input to output
 *
 *  On - line algorithm
 *
 *  Input file does not contain decoding tree
 *
 *  Written by Colin L. Mc Master (UCB) February 14, 1979
 */
#include "compact.h"
#include <strings.h>

union	cio c;
union	cio d;
char	*infname;			/* input file's name */
char	fname[MAXPATHLEN+1];		/* output file's name */
struct	stat status;			/* compacted file status */

int	verbose = 0;

main(argc, argv)
	int argc;
	char *argv[];
{
	register short j;

	argc--, argv++;
	if (argc > 0 && strcmp(*argv, "-v") == 0) {
		verbose++;
		argc--, argv++;
	}
	dir[513].next = NULL;
	for (head = dir + (j = 513); j--; ) {
		dirp = head--;
		head->next = dirp;
	}
	bottom = dirp->pt = dict;
	dict[0].sons[LEFT].top = dict[0].sons[RIGHT].top = dirp;
	dirq = dirp->next;
	in[EF].flags = FBIT | SEEN;
	if (argc == 0)
		exit(uncompact("-"));
	for (j = 0; j < argc; j++) {
		if (uncompact(argv[j]))
			exit(1);
		if (verbose && argc > 0)
			printf("%s uncompacted to %s\n", argv[j], fname);
	}
	exit(0);
}

uncompact(file)
	char *file;
{
	int ignore;
	FILE *setup();

	bottom->sons[RIGHT].top->next = flist;
	bottom->sons[RIGHT].top = dirp;
	flist = dirq;
	uncfp = cfp = NULL;
	if (strcmp(file, "-") != 0) {
		char *cp;

		strcpy(fname, file);
		cp = rindex(fname, '.');
		if (cp == 0 || strcmp(cp, ".C") != 0) {
			fprintf(stderr,
			    "uncompact: %s: File must have .C suffix.\n", file);
			goto bad;
		}
		*cp = '\0';
		cfp = fopen(file, "r");
		if (cfp == NULL) {
			fprintf(stderr, "uncompact: "), perror(file);
			goto bad;
		}
		(void) fstat(fileno(cfp), &status);
	} else
		cfp = stdin;
	infname = file;
	uncfp = setup(cfp, &ignore);
	if (uncfp == NULL) {
		if (ignore)
			goto done;
		goto bad;
	}
	decompress(cfp, uncfp);
	fflush(uncfp);
	if (ferror(uncfp) || ferror(cfp)) {
		fprintf(stderr, "uncompact: ");
		if (uncfp != stdout) {
			if (ferror(uncfp))
				perror(fname);
			else
				perror(infname);
			(void) unlink(fname);
		} else
			fprintf(stderr,
	    "Unsuccessful uncompact of standard input to standard output.\n");
		goto bad;
	}
	if (uncfp != stdout && unlink(infname) < 0)
		fprintf(stderr, "uncompact: "), perror(infname);
done:
	if (uncfp != NULL && uncfp != stdout)
		fclose(uncfp);
	if (cfp != NULL)
		fclose(cfp);
	return (0);
bad:
	if (cfp != NULL)
		fclose(cfp);
	return (1);
}

decompress(cfp, uncfp)
	register FILE *cfp, *uncfp;
{
	register struct node *p;
	register short j;
	register int m;
	register struct cio *dp = &d;
	char b;

	p = dict;
	while ((c.integ = getc (cfp)) != EOF) {
		for (m = 0200; m; ) {
			b = (m & c.integ ? 1 : 0);
			m >>= 1;
			if (p->fath.flags & (b ? RLEAF : LLEAF)) {
				dp->integ = p->sons[b].sp.ch;
				if (dp->integ == EF)
					break;
				if (dp->integ == NC) {
					uptree(NC);
					dp->integ = 0;
					for (j = 8; j--; m >>= 1) {
						if (m == 0) {
							c.integ = getc(cfp);
							m = 0200;
						}
						dp->integ <<= 1;
						if (m & c.integ)
							dp->integ++;
					}
					insert(dp->integ);
				}
				uptree(dp->integ);
				putc(dp->chars.lob, uncfp);
				p = dict;
			} else
				p = p->sons[b].sp.p;
		}
	}
}

FILE *
setup(cfp, ignore)
	FILE *cfp;
	int *ignore;
{
	FILE *uncfp = NULL;
	register union cio *dp = &d;
	register union cio *cp = &c;

	*ignore = 0;
	dp->integ = getc(cfp);
	if (dp->integ != EOF) {	
		cp->integ = getc(cfp);
		if (cp->integ != EOF)
			dp->chars.hib = cp->integ & 0377;
	} else
		dp->integ = 0;
	if ((dp->integ &= 0177777) != COMPACTED) {
		fprintf(stderr, "uncompact: ");
		if (dp->integ == PACKED)
			fprintf(stderr, "%s: File is packed, use unpack.\n",
			    infname);
		else
			fprintf(stderr, "%s: Not a compacted file.\n", infname);
		*ignore = 1;
		goto bad;
	}
	if (strcmp(infname, "-") != 0) {
		uncfp = fopen(fname, "w");
		if (uncfp == NULL)
			goto bad2;
		(void) fchmod(fileno(uncfp), status.st_mode);
	} else
		uncfp = stdout;
	cp->integ = getc(cfp);
	if (cp->integ == EOF)
		goto bad2;
	putc(cp->chars.lob, uncfp);

	in[NC].fp = in[EF].fp = dict[0].sons[LEFT].sp.p = bottom = dict + 1;
	bottom->sons[LEFT].count = bottom->sons[RIGHT].count =
	    dict[0].sons[RIGHT].count = 1;
	dirp->next = dict[0].sons[RIGHT].top = bottom->sons[LEFT].top =
	    bottom->sons[RIGHT].top = dirq = NEW;
	dirq->next = NULL;
	dict[0].fath.fp = NULL;
	dirq->pt = bottom->fath.fp = in[cp->integ].fp = dict;
	in[cp->integ].flags = (FBIT | SEEN);
	in[NC].flags = SEEN;
	dict[0].fath.flags = RLEAF;
	bottom->fath.flags = (LLEAF | RLEAF);
	dict[0].sons[LEFT].count = 2;

	dict[0].sons[RIGHT].sp.ch = cp->integ;
	bottom->sons[LEFT].sp.ch = NC;
	bottom->sons[RIGHT].sp.ch = EF;
	return (uncfp);
bad2:
	fprintf(stderr, "uncompact: ");
	perror(fname);
bad:
	if (uncfp && uncfp != stdout) {
		(void) unlink(fname);
		fclose(uncfp);
	}
	return (NULL);
}
