/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   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.
 */

#ifndef lint
static char rcsid[] = "$Header: compact.c 2.0 86/01/28 $";
#endif

/*
 *  Adaptive Huffman code input to output
 *
 *  On - line algorithm
 *
 *  Does not prepend decoding tree
 *
 *  Written by Colin L. Mc Master (UCB) February 28, 1979
 */


#include "compact.h"

union cio d;
int bits;
int exit_status;


main (argc, argv)
short argc;
char *argv [ ];
{
	register short i, j;
	register int m;
	union cio c;
	short l;
	longint ic, n;
	char *cp, fname [LNAME];

	exit_status = 0;
	dir [MAXDIR-1] . next = NULL;
	for (head = dir + (j = MAXDIR-1); j--; ) {
		dirp = head--;
		head -> next = dirp;
	}
	bottom = dirp -> pt = dict;
	dict [0] . top [0] = dict [0] . top [1] = dirp;
	dirq = dirp -> next;
	in [EF] . flags = FBIT | SEEN;

	for (i = 1; ; i++) {
		ic = oc =  0;
		(bottom -> top [1]) -> next = flist;
		bottom -> top [1] = dirp;
		flist = dirq;
		if (i >= argc) {
			uncfp = stdin;
			cfp = stdout;
		}
		else {
			m = -1;
			cp = fname;
			for (l = 0; l < (LNAME - 3) && (*cp = argv [i][l]); l++)
				if (*cp++ == '/') m = l;
			if (l >= (LNAME - 3) || (l - m) > MAXNAMLEN - 1) {
				fprintf (stderr, "%s: File name too long\n", argv [i]);
				exit_status++;
				if (i == argc - 1) break;
				continue;
			}
			if ((uncfp = fopen (argv [i], "r")) == NULL) {
				perror (argv [i]);
				exit_status++;
				if (i == argc - 1) break;
				continue;
			}
		}

		fstat (fileno (uncfp), &status);
		if ((status.st_mode & 040000) == 040000) {
			fprintf (stderr, "%s: Can't compact a directory\n", argv [i]);
			exit_status++;
			if (i < argc) goto closein;
			break;
		}

		if ((d . integ = getc (uncfp)) != EOF) {
			ic++;
			if ((c . integ = getc (uncfp)) != EOF) {
				d . chars . hib = c . integ & 0377;
				if ((d . integ &= 0177777) == COMPACTED) {
					fprintf (stderr, "%s: Already compacted.\n", argv [i]);
					exit_status++;
					if (i < argc) goto closein;
					break;
				}
				if (d . integ == PACKED) {
					fprintf (stderr, "%s: Already packed using program pack.  Use unpack.\n", argv [i]);
					exit_status++;
					if (i < argc) goto closein;
					break;
				}
				if (i < argc) {
					*cp++ = '.'; *cp++ = 'C'; *cp = '\0';
					if ((cfp = fopen (fname, "w")) == NULL) {
						perror (fname);
						exit_status++;
						goto closein;
					}
					chmod (fname, status.st_mode);
				}
				c . integ = COMPACTED;
				putc (c . chars . lob, cfp);
				putc (c . chars . hib, cfp);
				if (ferror (cfp))
					if (i < argc) {
						exit_status++;
						perror (fname);
						unlink (fname);
						goto closeboth;
					}
					else {
						 exit_status++;
						 goto fail;
					}
				bits = 8;
				oc = 2;
				c . integ = d . integ & 0377;
	
				in [NC] . fp = in [EF] . fp = dict [0] . sp [0] . p = bottom = dict + 1;
				bottom -> count [0] = bottom -> count [1] = dict [0] . count [1] = 1;
				dirq = NEW;
				if (dirq == NULL) {
					fprintf(stderr,"compact: storage overflow.\n");
					exit(1);
				}
				dirp -> next = dict [0] . top [1] = bottom -> top [0] = bottom -> top [1] = dirq;
				dirq -> next = NULL;
				dict [0] . fath . fp = NULL;
				dirq -> pt = bottom -> fath . fp = in [c . integ] . fp = dict;
				in [c . integ] . flags = (FBIT | SEEN);
				in [NC] . flags = SEEN;
				dict [0] . fath . flags = RLEAF;
				bottom -> fath . flags = (LLEAF | RLEAF);
				dict [0] . count [0] = 2;
	
				dict [0] . sp [1] . ch = c . integ;
				bottom -> sp [0] . ch = NC;
				bottom -> sp [1] . ch = EF;
	
				for (c . integ = ((d . integ >> 8) & 0377); c . integ != EOF; c . integ = getc (uncfp)) {
					ic++;
					if (in [c . integ] . flags & SEEN) encode (c . integ);
					else {
						encode (NC);
						uptree (NC);
						insert (c . integ);
	
						m = 0200;
						for (j = 8; j--; m >>= 1) {
							d . integ <<= 1;
							if (m & c . integ) d . integ++;
							++bits;
							if ((bits &= 017) == 0) {
								putc (d . chars . hib, cfp);
								putc (d . chars . lob, cfp);
								oc += 2;
							}
						}
					}
					if (ferror (cfp))
						if (i < argc) {
							exit_status++;
							perror (fname);
							unlink (fname);
							goto closeboth;
						}
						else {
							exit_status++;
							goto fail;
						}
					uptree (c . integ);
	
				}

				if (ferror (uncfp))
					if (i < argc) {
						exit_status++;
						perror (argv [i]);
						unlink (fname);
						goto closeboth;
					}
					else {
						exit_status++;
						goto fail;
					}
	
				encode (EF);
	
				if (bits) {
					d . integ <<= (16 - bits);
					oc++;
					putc (d . chars . hib, cfp);
					if (bits > 8) {
						oc++;
						putc (d . chars . lob, cfp);
					}
					bits = 0;
				}
			}
			else oc = ic;
		}

		if (ferror (uncfp) || ferror (cfp)) {
			exit_status++;
			if (i < argc) {
				if (ferror (cfp))
					perror (fname);
				else
					perror (argv [i]);
				if (oc > 1) {
					unlink (fname);
					goto closeboth;
				}
				goto closein;
			}
			else goto fail;
		}
		else {
			if (oc >= ic) {
				if (i < argc) fprintf (stderr, "%s: ", argv [i]);
				if (i < argc) fprintf (stderr, "Not compacted.  ");
				fprintf (stderr, "Does not save bytes.\n");
				if (i < argc) {
					if (oc > 1) {
						unlink (fname);
						goto closeboth;
					}
					goto closein;
				}
				else break;
			}
			while ((ic - oc) > 21474) {
				ic /= 10;
				oc /= 10;
			}
			n = 100000 * (ic - oc) / ic + 5;
			m = (n % 1000) / 10;
			bits = m % 10 + '0';
			c . integ = m / 10 + '0';
			if (i < argc) fprintf (stderr, "%s:  ", argv [i]);
			fprintf (stderr, "Compression : %4ld.%c%c%%\n", n / 1000, c . chars . lob, bits);
		}

			    if (i >= argc)  break;
		            fclose (cfp);
			    unlink (argv [i]);
			    goto closein;
		closeboth : fclose (cfp);
		closein   : fclose (uncfp);
			    if (i == argc - 1) break;
			    for (j = 256; j--; ) in [j] . flags = 0;
			    continue;
		fail 	  : fprintf (stderr, "Unsuccessful compact of standard input to standard output.\n");
		            break;
	}
	exit(exit_status);
}

encode (ch)
int ch;
{

	register struct node *pp;
	register char j;
	union cio c;
	int stack [17], stp, stbits;

	c . integ = ch;
	stack [stp = 0] = in [c . integ] . flags & FBIT;
	stbits = 1;
	pp = in [c . integ] . fp;

	while (pp -> fath . fp) {
		stack [stp] <<= 1;
		if (pp -> fath . flags & FBIT) stack [stp]++;
		stbits++;
		if ((stbits &= 017) == 0) stp++;
		pp = pp ->  fath . fp;
	}

	/* pop the output stack */

	for (stp++; stp--; ) {
		for (j = 0; j < stbits; j++) {
			d . integ <<= 1;
			if (stack [stp] & 01) d . integ++;
			++bits;
			if ((bits &= 017) == 0) {
				putc (d . chars . hib, cfp);
				putc (d . chars . lob, cfp);
				if (ferror (cfp)) return;
				oc += 2;
			}
			stack [stp] >>= 1;
		}
		stbits = 16;
	}
}
