
/*
 * usq.c
 *
 * By Marcel J.E. Mol           duteca!marcel
 *                              marcel@duteca.tudelft.nl
 *
 * I hereby place this program into public domain ...
 *  ...  all those all those if and whats etc....
 * I'm completely unresposible for any damage this program may cause you.
 *
 * Usq unsqueezes a file squeezed with the apple sq3 program.
 * Files extracted from a binary 2 file (using unblu for example)
 * with a .QQ or .Q extensing are probably squeezed files.
 *
 * Usage: The program reads from a file or stdin, and outputs to
 *        the filename stored in the squeezed file.
 *        So use the program as follows:
 *
 *           usq  file.QQ 
 *
 * Remember that the newline character on apple is different from Unix.
 * Thus files extracted with unblu, and possibly unsqueezed with usq
 * may have an ^M character instead of a \n character at the end of each line.
 * To create a proper Unix file use the following 'tr' command:
 *
 *		tr '\015'  '\012' < applefile > unixfile
 *
 * This program is based on the unsqueeze program in the unix/PC 
 * ARC utility.
 */

/* 
 * Squeezed file format:
 *     2 bytes MAGIC
 *     2 bytes dummy ???
 *     filename ended by \0
 *     2 bytes node count
 *     node count node values, each 2 bytes
 *     squeezed data per byte
 */

#include <stdio.h>
#if defined(MSDOS)
# include <fcntl.h>
#endif

#define BUFSIZE		128
#define MAGIC		0xff76	       /* Squeezed file magic */
#define DLE		0x90           /* repeat byte flag */
#define NOHIST		0              /* no relevant history */
#define INREP		1              /* sending a repeated value */
#define SPEOF		256            /* special endfile token */
#define NUMVALS		257            /* 256 data values plus SPEOF */

char * copyright = "@(#) usq.c  2.1 18/06/88  (c) M.J.E. Mol";

/*
 * Global variable declarations
 */
char *progname;
char *sfn;			       /* squeezed file name */
struct nd {                            /* decoding tree */
    int child[2];                      /* left, right */
}   node[NUMVALS];                     /* use large buffer */
int state;                             /* repeat unpacking state */
int bpos;                              /* last bit position read */
int curin;                             /* last byte value read */
int numnodes;                          /* number of nodes in decode tree */


/*
 * Functions declarations
 */
void main      ();
int  unsqueeze ();
void putc_ncr  ();
int  get_int   ();
int  init_usq  ();
int  getc_usq  ();



void main(argc, argv)
int argc;
char ** argv;
{
    
    FILE *fp; 				/* File pointer for squeezed file */
    register int r = 0;			/* Exit value */

    progname = *argv;

    if (argc == 1) {                    /* no args; unsqueeze standard input */
#if defined(MSDOS)
        setmode(fileno(stdin), O_BINARY);
#endif
        unsqueeze(stdin);
    }
    else				/* unsqueeze each file in arglist */
        while (--argc > 0) {
	    sfn = *++argv;
#if defined(MSDOS)
            if ((fp = fopen(sfn, "rb")) == NULL) {
#else
	    if ((fp = fopen(sfn, "r")) == NULL) {
#endif
	        perror(sfn);
	        continue;
	    }
	    r |= unsqueeze(fp);
	    fclose(fp);
	}

    exit(r);

} /* main */



int unsqueeze(sfp)
FILE *sfp;
{
    register int i;
    register int c;                     /* one char of stream */
    char fname[BUFSIZE];
    int magic;
    register FILE *dfp;

    magic = get_int(sfp);
#if defined(DEBUG)
    fprintf(stderr, "Magic number: %x\n", magic);
#endif
    if (magic != MAGIC) { 		/* Check magic number */
	fprintf(stderr, "%s: %s not a squeezed file\n", progname, sfn);
	fprintf(stderr, "Wrong magic number: %x\n", magic);
	return(1);
    }
    (void) get_int(sfp);		/* two dummy bytes ??? */
    i = 0;				/* get the original file name */
    while ((fname[i++] = getc(sfp)) != NULL) 
	;

#if defined(MSDOS)                           /* open destination file */
    if ((dfp = fopen(fname, "wb")) == NULL) {
#else
    if ((dfp = fopen(fname, "w")) == NULL) {
#endif
        perror(fname);
        return(1);
    }

    state = NOHIST;                    /* initial repeat unpacking state */

    if (init_usq(sfp)) 		       /* init unsqueeze algorithm */
	return 1;
    while ((c=getc_usq(sfp)) != EOF)   /* and unsqueeze file */
         putc_ncr(c, dfp);

    return 0;                          /* file is okay */

} /* unsqueeze */




/*  putc-ncr -- decode non-repeat compression.  Bytes are passed one
 *              at a time in coded format, and are written out uncoded.
 *              The data is stored normally, except that runs of more 
 *              than two characters are represented as:
 *
 *                       <char> <DLE> <count>
 *
 *              With a special case that a count of zero indicates a DLE 
 *              as data, not as a repeat marker.
 */
void putc_ncr(c, t)                    /* put NCR coded bytes */
unsigned char c;                       /* next byte of stream */
FILE *t;                               /* file to receive data */
{
    static int lastc;         /* last character seen */

    switch (state) {                   /* action depends on our state */
        case NOHIST:                   /* no previous history */
            if (c==DLE)                /* if starting a series */
                 state = INREP;        /* then remember it next time */
            else putc(lastc=c, t);     /* else nothing unusual */
            return;

        case INREP:                    /* in a repeat */
            if (c)                     /* if count is nonzero */
                while (--c)            /* then repeatedly ... */
                    putc(lastc, t);    /* ... output the byte */
            else putc(DLE, t);         /* else output DLE as data */
            state = NOHIST;            /* back to no history */
            return;

        default:
            fprintf(stderr, "%s: bad NCR unpacking state (%d)",
				progname, state);
    }

} /* putc_ncr */




int get_int(f)                         /* get an integer */
FILE *f;                               /* file to get it from */
{
   
    return getc(f) | (getc(f) << 8);

} /* get_int */



int init_usq(f)                        /* initialize Huffman unsqueezing */
FILE *f;                               /* file containing squeezed data */
{
    register int i;                    /* node index */

    bpos = 99;                         /* force initial read */

    numnodes = get_int(f);             /* get number of nodes */

    if (numnodes<0 || numnodes>=NUMVALS) {
         fprintf(stderr, "%s: %s file has an invalid decode tree",
			progname, sfn);
	 return 1;
    }

    /* initialize for possible empty tree (SPEOF only) */

    node[0].child[0] = -(SPEOF + 1);
    node[0].child[1] = -(SPEOF + 1);

    for (i=0; i<numnodes; ++i) {        /* get decoding tree from file */
         node[i].child[0] = get_int(f);
         node[i].child[1] = get_int(f);
    }

    return 0;

} /* init_usq */




int getc_usq(f)                        /* get byte from squeezed file */
FILE *f;                               /* file containing squeezed data */
{
    register int i;                    /* tree index */

    /* follow bit stream in tree to a leaf */

    for (i=0; (i < 0x8000) && (i>=0); )/* work down(up?) from root */
    {  
	 if (++bpos > 7) {
              if ((curin=getc(f)) == EOF)
                   return(EOF);
              bpos = 0;

              /* move a level deeper in tree */
              i = node[i].child[1 & curin];
         }
         else i = node[i].child[1 & (curin >>= 1)];
    }

    /* decode fake node index to original data value */
    i = i | 0xffff0000; 	     /* Unix hack: have 32 bits instead of 16 */
    i = -(i + 1);


    /* decode special endfile token to normal EOF */
    return ((i==SPEOF) ? EOF : i);

} /* getc_usq */
