/* unhexify.c - input hex file, output binary */
/*
 * laugh also: hexify.c, hexsum.c, coagulate.c
 */
/*
 * a hex file has some bytes, each represented as a pair of hex digits (hits)
 * to %02x format.
 * any whitespace may occur between bytes, but whitespace is forbidden
 * between the hits of a byte.
 * an odd number of hits in a file is silly.
 */
/*
 * after reading 'recsiz' bytes, we output a files-11 record of that many bytes
 * if the last record output is shorter than the rest: so be it.
 * because magtapes etc want minimum record sizes, we allow padding the last
 * (possibly short) record with 'pad' bytes up to 'recsiz'
 */

#include <stdio.h>
#include <algol68.h>

#define BUFSIZ	4096	/* maximum output record size (bytes)		*/

helpless()
BEGIN
	fprintf(stderr,"\7usage: UNHEXIFY recsiz {pad}\n");
	fprintf(stderr,"std input is a hex file.\n");
	fprintf(stderr,"std output is binary records\n");
	fprintf(stderr,"every recsiz bytes we make a new record\n");
	fprintf(stderr,"the last record is padded to recsiz with pad bytes\n");
	fprintf(stderr,"pad is hexadecimal\n");
	fprintf(stderr,"the maximum record size for this edition is %d.\n",BUFSIZ);
	exit();
END

int	recsiz;		/* number of bytes per output record		*/
FILE *	winge;		/* where to send error messages			*/
int	line;		/* 0-org line number last read from input	*/
int	got1st;		/* TRUE if seen 1st hit of a byte & not 2nd	*/
char	buf[BUFSIZ];	/* output record built here			*/
char *	ptr;		/* tracks buf[]					*/
int	hexbyte;	/* build up a byte here				*/
int	pad;		/* what to pad last record with	-1==don't pad	*/

main(argc,argv)
int	argc;
char * * argv;
BEGIN
int	c;		/* suspicious character				*/

	winge = stderr;
	IF	(argc<2 || argc>3)
	THEN	helpless();
	FI
	sscanf(argv[1],"%d",&recsiz);
	IF	(recsiz<=0 || recsiz>BUFSIZ)
	THEN	helpless();
	FI
	IF	(argc==3)
	THEN	sscanf(argv[2],"%x",&pad);
		pad &= 0xFF;
	ELSE	pad = -1;
	FI
	line = 0;
	got1st = FALSE;
	ptr = buf;
	while	((c=getchar())>=0)
	{
		if	(!iswhite(c))
		{	hexacc(c);
		}else{	if	(got1st)
			{	fprintf(winge,"whitespace within hexadecimal digits of byte");
				panic();
			};
		};
		if	(c=='\n')
		{	line++;
		};
	};
	if	(got1st)
	{	fprintf(winge,"odd number of hex digits");
		panic();
	};
	IF	(pad>-1)
	THEN	WHILE	(ptr < buf+recsiz)
		DO	* ptr ++ = pad;
		OD
	FI
	fart();
}

hexacc(c)
int	c;
{
int	h;

	if	((h=hexconv(c))>=0)
	{	if	(got1st)
		{	hexbyte = (hexbyte<<4) | h;
			IF	(ptr >= buf+recsiz)
			THEN	fart();
			FI
			*ptr ++ = hexbyte;
		}else{	hexbyte = h;
		};
		got1st = ! got1st;
	}else{	fprintf(winge,"char=%xx invalid",c);
		panic();
	};
}

fart()
BEGIN
	fput(buf,ptr-buf,stdout);
	IF	(ferror(stdout))
	THEN	fprintf(winge,"output error $$ferr=%oo\7\n",$$ferr);
	FI
	ptr = buf;
END

panic()
{
	fprintf(winge,"\n\7line=%d.\n",line);
	exit(42);
};

int	hexconv(c)
int	c;
{
	c = tolower(c);
	if	(isdigit(c))
	{	c = c - '0';
	}else{	if	(c>='a' && c<='f')
		{	c = c-'a' + 10;
		}else{	c = -1;
		};
	};
	return(c);
}

iswhite(c)
int	c;
{
int	r;
char * index();

	r=index(" \t\n\r\b\f",c);
	return(r);
}

int	isdigit(c)
int	c;
{
int	r;
char * index();

	r=index("0123456789",c);
	return(r);
}

int	tolower(c)
int	c;
{
	if	(c<='Z' && c>='A')
	{	c = c-'Z'+'z';
	};
	return(c);
}

char * index(s,c)
char	*s;
char	c;
{
	while	(*s)
	{	if	( (*s++)==c )
		{	return(--s);
		};
	};
	return(0);
}

