static char *sccsid = "@(#)usq.c        1.8u (UCF) 83/09/02";
/*
 *   	 THIS IS THE SOURCE CODE FOR USQ.C.  
 *   									 
 *   	 THE ROUTINE  "SQ.EXE", ON "DISKETTE B", SQUEEZES A MODULE  TO   
 *   	 CONSERVE DISKETTE SPACE.					 
 *   	 THE ROUTINE USQ.EXE, ALSO ON "DISKETTE B", UNSQUEEZES MODULES   
 *   	 SQUEEZED BY SQ.EXE.						 
 *
 *	usq.c -	CP/M compatible	file unsqueezer	utility
 *
 *	compile	as follows:
 *	cc [-DVAX] -O usq.c -o usq
 *	   (define VAX only if running on VAX)
 */

/*	modified by J. Chappell	02-02-84 to check if ferror occurred on	write
				02-07-84 to accept wildcards in	filenames
*/


#include <stdio.h>
#define VAX 1
/* #include <signal.h> */
/* #include <ctype.h> */
#include <ctype.h>
#define	TRUE 1
#define	FALSE 0
#define	ERROR (-1)
#define	PATHLEN	312	/* Number of characters	allowed	in pathname */
#define	OK 0

#define	RECOGNIZE 0xFF76	/* unlikely pattern */
#define	DLE 0x90		/* repeat byte flag */
#define	SPEOF 256		/* special endfile token */
#define	NUMVALS	257		/* 256 data values plus	SPEOF*/
#define	LARGE 30000
/* #define	WILDCARD FALSE  */

#ifdef VAX   /*	then we	don't want 32 bit integers */

typedef	short INT;
typedef	unsigned short UNSIGNED;

#else	/*  16 bit machines  */

typedef	int INT;
typedef	unsigned UNSIGNED;

#endif

struct _sqleaf {		/* Decoding tree */
	INT _children[2];	/* left, right */
};
struct _sqleaf Dnode[NUMVALS - 1];


INT Bpos;		/* last	bit position read */
INT Curin;		/* last	byte value read	*/
INT Repct;		/* Number of times to return value */
INT Value;		/* current byte	value or EOF */

INT MakeLCPathname=TRUE;	/* translate pathname to lc if all caps	*/
INT Nlmode=FALSE;		/* zap cr's if true */
INT Inbackground = FALSE;
INT getcr(), getuhuff(), portgetw();
/* extern int fflush(),ferror(),fclose(); */
main(argc, argv)
unsigned char *argv[];
{
	register unsigned char *cp;
	register INT npats=0;
	unsigned char **patts;
	INT n, errorstat;

/*
	if (signal(SIGINT, SIG_IGN)==SIG_IGN)
		Inbackground++;
	else
		signal(SIGINT, SIG_DFL);
	signal(SIGHUP, SIG_IGN);
					*/

	errorstat=0;
	if(argc<2)
		goto usage;
	while (--argc) {
		cp = *++argv;
		if(*cp == '-') {
			while( *++cp) {
				switch(*cp) {
				case 'n':
					Nlmode=TRUE; break;
				case 'u':
					MakeLCPathname=FALSE; break;
				default:
					goto usage;
				}
			}
		}
		else if( !npats	&& argc>0) {
			if(argv[0][0]) {
				npats=argc;
				patts=argv;
			}
		}
	}
	if(npats < 1) {
usage:
		fprintf(stderr,"Usage: usq [-nu] file ...\n");
		fprintf(stderr,"\t-n Nlmode: remove carriage returns\n");
		fprintf(stderr,"\t-u preserve Uppercase pathnames\n");
#ifdef WILDCARD
		fprintf(stderr,"\twildcards are acceptable in filenames.\n");
#endif
		exit(1);
	}

#ifdef WILDCARD
	for(n=0; n<npats; ++n){
		unsigned char *dir(), *cp, *s;
		if(cp=dir(patts[n],1)){
		   for(s=cp;*s;s+=(strlen(s)+1)) errorstat |= squeeze(s);
		   free(cp);
		}
		else errorstat |= squeeze(patts[n]);
	}
#else
	for(n=0; n<npats; ++n)
		errorstat |= squeeze(patts[n]);
#endif

	exit(errorstat != 0);
}

/*
	The following code is primarily	from typesq.c and utr.c.  Typesq
is a modification of USQ by Dick Greenlaw.  Those modifications	(usq
to typesq) were	made by	Bob Mathias, I am responsible for the butchery
done to	make it	work with cat.

*/

FILE *in, *out;
squeeze(fname)
unsigned char *fname;
{
	register INT i,	c;
	register unsigned char *p;
	register INT numnodes;			/* size	of decoding tree */
	register UNSIGNED crc;
	UNSIGNED filecrc;
	unsigned char origname[PATHLEN];	/* Original file name without
						    drive */

	init_cr(); init_huff();	crc=0;

	if((in=fopen( fname, "rb"))==NULL) {
		fprintf(stderr,	"usq: can't open %s\n", fname);
		return ERROR;
	}
	if(portgetw(in)	!= (INT) RECOGNIZE) {/*	Process	header */
		fprintf(stderr,	"usq: %s is not a SQueezed file\n", fname);
		return(ERROR);
	}
	filecrc	= (UNSIGNED) portgetw(in);	/* checksum */
	p = origname;				/* Get original	file name */
	do {					/* send	it to array */
		*p = getc(in);
	} while(*p++ !=	'\0');

	numnodes = portgetw(in);
	if(numnodes < 0	|| numnodes >= NUMVALS)	{
		fprintf(stderr,	"usq: %s has invalid decode tree\n", fname);
		fclose(in);
		return(ERROR);
	}
	/* Initialize for possible empty tree (SPEOF only) */
	Dnode[0]._children[0] =	-(SPEOF	+ 1);
	Dnode[0]._children[1] =	-(SPEOF	+ 1);

	for(i =	0; i < numnodes; ++i) {	/* Get decoding	tree from file */
		Dnode[i]._children[0] =	portgetw(in);
		Dnode[i]._children[1] =	portgetw(in);
	}
	/* Get translated output bytes and write file */
	if(MakeLCPathname && !IsAnyLower(origname))
		uncaps(origname);
	for(p=origname;	*p; ++p)		/* change / to _ */
		if( *p == '/')
			*p = '_';
	if (!Inbackground)
		fprintf(stderr,	"usq: %s -> %s\n",fname,origname);
	if((out=fopen(origname,	"wb"))==NULL) {
		fprintf(stderr,	"usq: can't create %s\n", origname);
	}
	while ((c = getcr()) !=	EOF) {
		crc += (UNSIGNED) c;
		if ( c == '\r' && Nlmode)
			continue;
		if(fputc(c,out)!=c){
			fclose(in);
			abort("usq: error in writing file: %s\n",origname);
		}
	}
	fclose(in);

	if(fflush(out)<0 || ferror(out)<0)
		abort("usq: Error writing file: %s\n",origname);
	if(fclose(out)<0)
		abort("usq: Error closing file: %s\n",origname);

	if( crc	!= filecrc ) {
		fprintf(stderr,	"usq: bad checksum in %s\n", fname);
		fflush(stdout);
		return(ERROR);
	}
	return(OK);
}
/*** from utr.c	- */
/* initialize decoding functions */

init_cr()
{
	Repct =	0;
}

init_huff()
{
	Bpos = 99;	/* force initial read */
}

/* Get bytes with decoding - this decodes repetition,
 * calls getuhuff to decode file stream	into byte
 * level code with only	repetition encoding.
 *
 * The code is simple passing through of bytes except
 * that	DLE is encoded as DLE-zero and other values
 * repeated more than twice are	encoded	as value-DLE-count.
 */

INT
getcr()
{
	register INT c;

	if(Repct > 0) {
		/* Expanding a repeated	char */
		--Repct;
		return(Value);
	} else {
		/* Nothing unusual */
		if((c =	getuhuff()) != DLE) {
			/* It's not the special delimiter */
			Value =	c;
			if(Value == EOF)
				Repct =	LARGE;
			return(Value);
		} else {
			/* Special token */
			if((Repct = getuhuff())	== 0)
				/* DLE,	zero represents	DLE */
				return(DLE);
			else {
				/* Begin expanding repetition */
				Repct -= 2;	/* 2nd time */
				return(Value);
			}
		}
	}
}
/* Decode file stream into a byte level	code with only
 * repetition encoding remaining.
 */

INT
getuhuff()
{
	register INT i;

	/* Follow bit stream in	tree to	a leaf*/
	i = 0;	/* Start at root of tree */
	do {
		if(++Bpos > 7) {
			if((Curin = getc(in)) == ERROR)
				return(ERROR);
			Bpos = 0;
			/* move	a level	deeper in tree */
			i = Dnode[i]._children[1 & Curin];
		} else
			i = Dnode[i]._children[1 & (Curin >>= 1)];
	} while(i >= 0);

	/* Decode fake node index to original data value */
	i = -(i	+ 1);
	/* Decode special endfile token	to normal EOF */
	i = (i == SPEOF) ? EOF : i;
	return(i);
}
/*
 * Machine independent getw which always gets bytes in the same	order
 *  as the CP/M	version	of SQ wrote them
 */
INT
portgetw(f)
FILE *f;
{
	register INT c;

	c = getc(f) & 0377;
	return(c | (getc(f) << 8));
}


/* make	string s lower case */
uncaps(s)
unsigned char *s;
{
	for( ; *s; ++s)
		if(isupper(*s))
			*s = tolower(*s);
}


/*
 * IsAnyLower returns TRUE if string s has lower case letters.
 */
IsAnyLower(s)
unsigned char *s;
{
	for( ; *s; ++s)
		if (islower(*s))
			return(TRUE);
	return(FALSE);
}
#ifdef WILDCARD
/*	dir: for DOS ALL

	Entry:
		1. Filename possibly containing	wildcards ? and	*
		2. Drive specifier flag

	Returns: Pointer to data area containing NULL terminated
		 list of filenames, or NULL if none found.

	Notes: User must free up data allocated	by this	function
		with free() if memory is to be restored	to heap
		after use.

	      Drive specifier: if non-zero will	drive specifiers
		will be	put on file names

	      Path names: not supported

*/

unsigned char *dir(filespec,drflag)
unsigned char *filespec;
short drflag;
{
#define	BLOCK 100		/* room	for fcb,dta,etc.. */
#define	FIRST 0x1100		/* search first	*/
#define	NEXT 0x1200		/* search next */
#define	CURRENT	0x19		/* get current drive */
#define	PARSE 0x2900		/* parse file name */
#define	SETDTA 0x1a00		/* set disk transfer address */

unsigned char *fcb,*rets,*cp,*tmp,*dta,*realloc();
unsigned short mode,pos,i;
struct regval {	int ax,bx,cx,dx,si,di,ds,es;} r;

  tmp=realloc(0,BLOCK);	fcb=realloc(0,BLOCK);
  dta=realloc(0,BLOCK);	rets=realloc(0,BLOCK);

  segread(&r.si);			/* parse file name */
  r.es=r.ds; r.si=filespec; r.di=fcb; r.ax=PARSE; sysint21(&r,&r);

  segread(&r.si);			/* set dta */
  r.dx=dta; r.ax=SETDTA; sysint21(&r,&r);

  for(pos=0,mode=FIRST;;mode=NEXT){
	segread(&r.si);	r.ax=mode; r.dx=fcb; sysint21(&r,&r);	/* search  */
	if(r.ax&0xff==0xff)break;			/* not found */
	cp=tmp;
	if(drflag){				/* put on drive	specifier */
		if(*dta==0)*dta=(bdos(CURRENT)&0xff)+1;
		*cp++=*dta+'A'-1; *cp++=':';
	}
	for(i=1;i<9;i++)if(*(dta+i)!=' ')*cp++=*(dta+i); *cp++='.';
	for(;i<12;i++)if(*(dta+i)!=' ')*cp++=*(dta+i); *cp='\0';
	rets=realloc(rets,pos+strlen(tmp)+1);	/* store file name */
	strcpy(rets+pos,tmp);
	pos+=(strlen(tmp)+1);
  }

  if(pos)rets=realloc(rets,pos+strlen(tmp)+1);
  else free(rets);
  free(fcb); free(tmp);	free(dta);
  return pos ? rets: 0;
}
#endif
