/* dsfile.c */

/*
 *  Includes
 */
#include <stdio.h>
#include <string.h>

#ifndef	VOID
#define	VOID	void
#endif


/*
 * OS/Machine/Compiler Specific Configuration
 */

/*
 * DECUS C predefined symbols:
 *
 *	decus		DECUS C Compiler
 *	rt11		RT-11 Compiler
 */

#ifdef	decus
#define	READ_BIN	"rn"
#define	WRIT_BIN	"wn"
#endif


/*
 * Default Definitions
 */

#ifndef	READ_BIN
#define	READ_BIN	"rb"
#endif

#ifndef	WRIT_BIN
#define	WRIT_BIN	"wb"
#endif

/*
 * Define Version and Date
 */

#define	VERSION	"   V01.0  18-Sep-1999"

static char *vrstxt[] = {
       "",
       "_DSFILE", VERSION,
       "",
	0
};

/*
 * Versions
 *
 * V01.0	 18-Sep-1999
 */

/*
 * Encoded Tape Format
 *
 *
 *	The Data Files processed by this program are encoded
 *	'Magnetic Tape' images:
 *
 *	(1)	EOT Tape Mark is when all data has been read
 *		from the Data File.
 *
 *	(2)	EOF Tape Marks are encoded as a
 *		32-Bit integer (4-bytes) of value 0.
 *
 *	(3)	Tape Records are encoded as:
 *
 *		(a)	Tape Record length as a 32-Bit integer (4-bytes).
 *		(b)	Tape Record (length bytes)
 *		(c)	Tape Record length as a 32-Bit integer (4-bytes).
 *
 */

/*
 *  enable inclusion of debugging code
 */

#define	DEBUGOPTION	 1
#define DBUGTRUE	-1

#define FALSE		 0
#define TRUE		 1

/*
 * Tape Record Types
 */
#define	UNDEFINED	-1
#define	LIST		 0
#define	HISTO		 1

/*
 * Tape Record Errors
 */
#define	TFR_OK		0
#define	TFR_INV		1
#define	TFR_TOBIG	2
#define	TFR_EOF		3
#define	TFR_EOT		4

/*
 * Debugging options
 *
 *	bit 0	(0x01)	getwrd()
 *	bit 1	(0x02)	getcnt()
 *	bit 2	(0x04)	getrec()
 *	bit 3	(0x08)	r50toa()
 */


char *usetxt[] = {
       "",
#if	DEBUGOPTION
       "dsfile [?] [-d level] [-f filename] [-uvx]",
       "	?		List this Help Text and Exit dsfile",
       "	d  level	Debug Level",
       "		1 -	getwrd()",
       "		2 -	getcnt()",
       "		4 -	getrec()",
       "		8 -	r50toa()",
#else
       "dsfile [?] [-f filename] [-uvx]",
       "	?		List this Help Text and Exit dsfile",
#endif
       "	f  filename	Input Data File in Encoded Tape Format",
       "	u		Log UICs",
       "	v		Verbose Mode On",
       "	x  maj  min	Extract Files [maj, min]",
       "",
       "	DOS-11 File Extractor.",
	0
	};


int	debug = 0,		/* debugging flag */
	uflag = FALSE,		/* Log UICs flag */
	vflag = FALSE,		/* Verbose Mode flag */
	xflag = FALSE;		/* File Extraction flag */

int	hdrs = 0;		/* Header Records */
long	trec = 0;		/* Total Records */

int	xmajuic = 0;		/* Extract Major UIC */
int	xminuic = 0;		/* Extract Minor UIC */

int	majuic = 0;		/* Major UIC */
int	minuic = 0;		/* Minor UIC */

int	umajuic = 0;		/* Log Major UIC */
int	uminuic = 0;		/* Log Minor UIC */

char	*inputfile = NULL;	/* Input File Specification */

FILE	*inpfp = NULL;		/* Input File Handle */
FILE	*outfp = NULL;		/* Output File Handle */

/*
 * BUFFERS must be as large as the largest
 * tape record expected to be read.
 */

#define BUFFERS		8192	/* size of buffer */

char	*xs;			/* buffer space for tape record pointer */
char	xscnt[4];		/* buffer for count */
char	file_nam[8];		/* file name */
char	file_ext[4];		/* file extension */
char	str[80];		/* temporary string */


unsigned int
getwrd(buf)
char *buf;
{
	unsigned int d;

	d  = (*buf++ & 0xFF);
	d += (*buf++ & 0xFF) << 8;

#if	DEBUGOPTION
	if (debug & 0x01) {
		printf("getwrd(): %5.5u\r\n", d);
	}
#endif

	return(d);
}

int
getcnt(error)
int *error;
{
	int cnt;
	char cntbuf[4];

	*error = TFR_OK;

	/*
	 * Read 4 Bytes Containing Tape Record Length
	 */
	cnt = fread(&cntbuf[0], 1, 4, inpfp);

	/*
	 * Wrong Count is End-of-Tape
	 */
	if (cnt != 4) {
		*error = TFR_EOT;
		return(0);
	}

	/*
	 * Limit Record Length to 32767
	 */
	cnt =   (cntbuf[0] & 0xFF);
	cnt += ((cntbuf[1] & 0xFF) << 8);
	if ((cnt < 0) || (cntbuf[2] & 0xFF) || (cntbuf[3] & 0xFF)) {
		cnt = 32767;
	}

#if	DEBUGOPTION
	if (debug & 0x02) {
		printf("getcnt(): byt1=%d, byt2=%d, byt3=%d, byt4=%d\r\n",
			cntbuf[0] & 0xFF, cntbuf[1] & 0xFF,
			cntbuf[2] & 0xFF, cntbuf[3] & 0xFF);
	}
#endif

	return(cnt);
}

VOID	wrtcnt(buf, cnt)
char	*buf;
int	cnt;
{
	*buf++ = cnt & 0xFF;
	*buf++ = (cnt >> 8) & 0xFF;
	*buf++ = 0;
	*buf++ = 0;
}

/*
 *	getetr()
 *
 *	Function getetr() reads a record from the
 *	Encoded Tape Record Format File with the
 *	file handle 'inpfp'.
 *
 *	The possible error codes are:
 *
 *		TFR_OK		Record OK, returns record length
 *		TFR_TOBIG	Record too long, truncated
 *		TFR_INV		Record has invalid format
 *		TFR_EOF		Record was an EOF
 *		TFR_EOT		Unexpected Errors are treated as EOT
 */

int
getetr(error)
int *error;
{
	int bytcnt, cnt, buf, i;

	*error = TFR_OK;

	/*
	 * Get Tape Record Length
	 */
	cnt = getcnt(error);
	if (*error != TFR_OK) {
		return(cnt);
	}
	if (cnt == 0) {
		*error = TFR_EOF;
		return(cnt);
	}

	/*
	 * Read The Tape Record
	 */
	if (cnt > BUFFERS) {
		bytcnt = fread(&xs[0],1,BUFFERS,inpfp);
		if (bytcnt != BUFFERS) {
			*error = TFR_EOT;
			return(bytcnt);
		}
		for (i=0; i<(cnt-BUFFERS); i++) {
			if (1 != fread(&buf,1,1,inpfp)) {
				*error = TFR_EOT;
				return(bytcnt);
			}
		}
		if (cnt != getcnt(error)) {
			*error = TFR_INV;
			return(0);
		}
		*error = TFR_TOBIG;
		return(bytcnt);
	} else {
		bytcnt = fread(&xs[0],1,cnt,inpfp);
		if (bytcnt != cnt) {
			*error = TFR_EOT;
			return(bytcnt);
		}
		if (cnt != getcnt(error)) {
			*error = TFR_INV;
			return(0);
		}
		return(bytcnt);
	}
}

/*
 *	getrec()
 *
 *	Function getrec() calls the function getetr()
 *	and reports the errors.
 */

int
getrec(error)
int *error;
{
	int cnt;

	cnt = getetr(error);

#if	DEBUGOPTION
	if (debug & 0x04) {
		switch(*error) {
		case TFR_OK:
			printf("getrec(): TFR_OK      %5.5d\r\n", cnt);
			break;

		case TFR_TOBIG:
			printf("getrec(): TFR_TOBIG   %5.5d\r\n", cnt);
			break;

		case TFR_INV:
			printf("getrec(): TFR_INV     %5.5d\r\n", cnt);
			break;

		case TFR_EOF:
			printf("getrec(): TFR_EOF     %5.5d\r\n", cnt);
			break;

		case TFR_EOT:
			printf("getrec(): TFR_EOT     %5.5d\r\n", cnt);
			break;

		default:
			break;
		}
	}
#endif

	return(cnt);
}

/*
 *	r50toa()
 *
 *	RAD50 to ASCII Conversion
 */

#if 0
char *r50asc = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$  0123456789  ";
#else
char *r50asc = " abcdefghijklmnopqrstuvwxyz$  0123456789  ";
#endif

r50toa(buf, r5str, r5cnt)
char *buf;
char *r5str;
int r5cnt;
{
	int i;
	char * p;
	unsigned int r5wrd;

	p = buf;
	for (i=0; i<r5cnt; i++) {
		r5wrd  = (*r5str++ & 0xFF);
		r5wrd += (*r5str++ & 0xFF) << 8;

		*buf++ = r50asc[r5wrd/1600];
		r5wrd = r5wrd % 1600;
		*buf++ = r50asc[r5wrd/40];
		r5wrd = r5wrd % 40;
		*buf++ = r50asc[r5wrd];
	}
	*buf++ = '\0';

#if	DEBUGOPTION
	if (debug & 0x08) {
		printf("r50toa(): [%s]\r\n", p);
	}
#endif

}

/*
 *  printout text
 */
VOID printtxt(txt)
char **txt;
{
	register char **dp;

	for (dp = txt; *dp; dp++) {
		if (**dp == '_') {
			printf("%s", *dp + 1);
		} else {
			printf("%s\r\n", *dp);
		}
	}
}

main(argc,argv)
int argc;
char *argv[];
{
	register int i,j,k,c;
	int cnt, error;
	char *p, *q;

	/*
	 * parse arguments
	 */
	for(i=1; i<argc; i++) {
		if((argc == 1) || (argv[i][0] == '?')) {
		    printtxt(vrstxt);
		    printtxt(usetxt);
		    exit(0);
		} else
		if(argv[i][0] == '-') {
		    j = i;
		    k = 1;
		    while((c = argv[j][k]) != '\0') {
			switch(tolower(c)) {
#if	DEBUGOPTION
			case 'd':	/* debug, optional level */
				if(sscanf(argv[++i],"%d",&debug) <= 0)
					debug = TRUE;
				break;
#endif
			case 'f':	/* data filename */
				inputfile = argv[++i];
				if(inputfile[0]) {
		   		    inpfp = fopen(inputfile,READ_BIN);
		   		    if(inpfp == NULL) {
					printf(
					"Could not open data file: %s\r\n",
					inputfile);
					exit(0);
		   		    }
				}
				break;

			case 'u':	/* Log UICs */
				uflag = FALSE;
				break;

			case 'v':	/* Verbose Mode */
				vflag = TRUE;
				break;

			case 'x':	/* Extract Files */
				xflag = TRUE;
				if(sscanf(argv[++i],"%d",&xmajuic) <= 0)
					xflag = FALSE;
				if(sscanf(argv[++i],"%d",&xminuic) <= 0)
					xflag = FALSE;
				break;

			default:	/* unknown option */
				printf(
				"Unrecognized option -%c ignored\r\n", c);
				break;
			}
			k++;
		    }
		} else {
			printf(
			"Unrecognized argument -%s ignored\r\n", argv[i]);
		}
	}

	/*
	 * Input Data File Required
	 */
	if (inpfp == NULL) {
		printtxt(usetxt);
		exit(0);
	}

	hdrs = 0;		/* Header Records */
	trec = 0L;		/* Total Records */

	xs = (char *) malloc(BUFFERS);

	while (1) {
		cnt = getrec(&error);
		if (error == TFR_EOT) {
			break;
		}
		trec += 1L;

		/*
		 * EOF
		 */
		if (cnt == 0) {
			if (error != TFR_EOF) {
				break;
			}
		} else
		/*
		 * Tape Header
		 */
		if (cnt == 14) {
			hdrs += 1;
			/*
			 * Convert file name from RAD50 and strip spaces
			 */
			r50toa(file_nam, &xs[0], 2);
			for (p=q=file_nam,i=0; i<6; i++) {
				if ((*p++ = *q++) == '\040') {
					--p;
				}
			}
			*p = '\0';

			/*
			 * Convert file extension from RAD50 and strip spaces
			 */
			r50toa(file_ext, &xs[4], 1);
			for (p=q=file_ext,i=0; i<3; i++) {
				if ((*p++ = *q++) == '\040') {
					--p;
				}
			}
			*p = '\0';

			/*
			 * Get Major and Minor UIC Numbers
			 */
			minuic = xs[6] & 0x00FF;
			majuic = xs[7] & 0x00FF;

			if (outfp != NULL) {
				fclose(outfp);
			}

			sprintf(str, "%.6s.%.3s", file_nam, file_ext);

			if (uflag == TRUE) {
				if ((umajuic != majuic) || (uminuic != minuic)) {
					umajuic = majuic;
					uminuic = minuic;
					printf("UIC = [%d,%d]\r\n", majuic, minuic);
				}
			}

			if (vflag == TRUE) {
				printf("Tape = %s,  File = %s,  UIC = [%d,%d]\r\n",
					inputfile, str, majuic, minuic);
			}

			if ((xflag == TRUE) && (xmajuic == majuic) && (xminuic == minuic)) {
				outfp = fopen(str, WRIT_BIN);
			}

#if	DEBUGOPTION
	if (debug & 0x10) {
		printf("Tape File =  %s\r\n", str);
	}
#endif

		} else {
		/*
		 * Output Record
		 */
			if (outfp != NULL) {
				fwrite(&xs[0], 1, cnt, outfp);
			}
		}
	}

	printf("Tape Record Summary:\r\n");
	printf("File Headers  = %d\r\n", hdrs);
	printf("Records Read = %ld\r\n", trec);

	/*
	 * Close any open files
	 */
	if (inpfp != NULL) {
		fclose(inpfp);
	}
	if (outfp != NULL) {
		fclose(outfp);
	}
	free(xs);
}
