#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <dos.h>
#include <dir.h>
#include "intercep.h"

#define MAXFILESIZE 20000		/* maximum template file size */
#define MAXLINELEN 200			/* maximum output line length */
#define AVGLINELEN 40			/* average line length */

char template_text[ MAXFILESIZE ];		/* file text */
char *templates[ MAXFILESIZE/AVGLINELEN ];	/* pointers to each line */
int ntemplates;		/* number of templates */

/* copy template to dest, changing regname to regval throughout.
 * assumes dest is big enough to hold result.
 */
void
subst(char *regname, unsigned rval, char *template, char *dest)
{
	char regval[5];
	if (regname[1] == 'H' || regname[1] == 'L')
		sprintf(regval, "%02X", rval);
	else
		sprintf(regval, "%04X", rval);
	while (*dest = *template) { /* copy through '\0' at end of string. */
		if (template[0] == regname[0] && template[1] == regname[1]) {
			dest = stpcpy(dest, regval);
			template += 2;	/* all regnames are 2 chars long */
		}
		else {
			template++;
			dest++;
		}
	}
}

/* Read template file into buffer, setting pointers to
 * beginning of lines.
 * Sets template_text, templates and ntemplates.
 * Pads out interrupt IDs to 6 characters.
 * Returns number of lines.
 */
int
read_template_file(char *filename)
{
	FILE *ifile;
	char inline[ MAXLINELEN ];
	char **nextline = templates;
	char *dest = template_text;
	char *q = template_text;

	if (! (ifile = fopen(filename, "rt")))
		return 0;
	while (fgets(inline, MAXLINELEN, ifile)) {
		char *p = inline;
		if (! isxdigit(inline[0]))
			continue;	/* handle comment lines */
		*nextline++ = dest = q;
		while (isxdigit(*p))
			*q++ = *p++;	/* copy initial hex string */
		while (q < dest+6)
			*q++ = ' ';		/* pad out to 6 characters */
		*p = ' ';			/* convert tab (or whatever) to space */
		while (*q++ = *p++)
			;				/* copy rest of line through null */
		while (q[-3] == '\\' && q[-2] == '\n') {	/* continuation lines */
			if (p = fgets(inline, MAXLINELEN, ifile)) {
				q -= 3;
				*q++ = '\n';
				while (*q++ = *p++)
					;
			}
			else
				goto on_eof;
		}
	}
on_eof:
	*nextline = NULL;
	return (ntemplates = nextline - templates);
}

int compare_templates(char **t1, char **t2)
{
	return(strnicmp(*t1, *t2, 6));
}

/* find a matching template given interrupt number and ax value
 * works from the most specific (all 6 characters)
 * back to the most general (just the 2 characters of the interrupt number).
 */
char *find_template(int intnum, unsigned axval)
{
	char proposed[ 7 ];
	char *key = proposed;
	char **found;
	char *ignoring = proposed + 6;

	sprintf(proposed, "%02X%04X", intnum, axval);

	while (ignoring > proposed) {
		if (found = bsearch(&key, templates, ntemplates,
			sizeof(char *), compare_templates))
				return(*found);
		/* else ignore 2 more characters */
		ignoring -= 2;
		ignoring[0] = ignoring[1] = ' ';
	}
	return NULL;
}

#define HI(u)	(((u)&0xff00)>>8)
#define LO(u)	((u)&0x00ff)

/* read Swi_info records from input file
 * interpret them, and output to output file.
 */
void
interpret_file(FILE *ifp, FILE *ofp, char *progname, int longmode)
{
	Swi_info rec;
	char s1[ MAXLINELEN ];
	char s2[ MAXLINELEN ];
	char *templ;

	if (progname != NULL)
		fprintf (ofp, "INTERCEPT/INTERPRET by Ned Konz 08/02/87\n"
			"dump of DOS/BIOS calls from program \"%s\"\n",
			progname);
	if (longmode)
		fprintf(ofp,
			"  CS:IP   INT  AX    BX   CX   DX    DS   ES   SI   DI   BP\n");

	while (fread(&rec, sizeof(Swi_info), 1, ifp)) {
		if (templ = find_template(rec.intnum, rec.regs.ovl.old[2])) {
			subst("AX", rec.regs.ovl.old[2],  templ, s1);
			subst("AH", HI(rec.regs.ovl.old[2]), s1, s2);
			subst("AL", LO(rec.regs.ovl.old[2]), s2, s1);

			subst("BX", rec.regs.ovl.old[1],     s1, s2);
			subst("BH", HI(rec.regs.ovl.old[1]), s2, s1);
			subst("BL", LO(rec.regs.ovl.old[1]), s1, s2);

			subst("CX", rec.regs.ovl.old[0],     s2, s1);
			subst("CH", HI(rec.regs.ovl.old[0]), s1, s2);
			subst("CL", LO(rec.regs.ovl.old[0]), s2, s1);

			subst("DX", rec.regs.dx,         s1, s2);
			subst("DH", HI(rec.regs.dx),     s2, s1);
			subst("DL", LO(rec.regs.dx),     s1, s2);

			subst("BP", rec.regs.bp,         s2, s1);
			subst("DI", rec.regs.di,         s1, s2);
			subst("SI", rec.regs.si,         s2, s1);
			subst("DS", rec.regs.ds,         s1, s2);
			subst("ES", rec.regs.es,         s2, s1);

			subst("CS", FP_SEG(rec.caller.ipcs),       s1, s2);
			subst("IP", FP_OFF(rec.caller.ipcs),       s2, s1);
			fprintf(ofp, "%Fp  %s",
				(char far *)rec.caller.ipcs - 2, s1);
		}

		if ((templ==NULL) || longmode) {
		  fprintf(ofp,
			"%Fp  %2x %04x  "
			"%04x %04x %04x  "
			"%04x %04x %04x %04x %04x\n",
			(char far *)rec.caller.ipcs - 2, rec.intnum,
			rec.regs.ovl.old[2], /*AX*/
			rec.regs.ovl.old[1] /*BX*/, rec.regs.ovl.old[0],/*CX*/ rec.regs.dx,
			rec.regs.ds, rec.regs.es, rec.regs.si, rec.regs.di,
			rec.regs.bp);
		}
	} /*while*/

	return;
}

int
main(int argc, char *argv[])
{
	int longmode = 0;
	char *templfile = TFILENAME;
	char *progname = argv[0];
	FILE *ifp, *ofp;

	if (argc < 3) {
		fprintf(stderr, "INTERCEPT/INTERPRET by Ned Konz 08/02/87\n"
			"usage: %s [-l] [-t templ_file] "
			"infile outfile [progname]\n",
			argv[0]);
		exit(1);
	}
	argv++;

	while (argv[0][0] == '-') {
		if (! stricmp(argv[0], "-l")) { longmode++; }
		else if (! stricmp(argv[0], "-t")) { templfile = argv[1]; argv++; }
		else {
			fprintf(stderr, "%s: unknown switch \"%s\"\n", progname, argv[0]);
			exit(4);
		}
		argv++;
	}

	if (! read_template_file( searchpath(templfile) )) {
		fprintf(stderr, "%s: error during read of \"%s\"\n",
			progname, templfile);
		exit(5);
	}
	qsort(templates, ntemplates, sizeof(char *), compare_templates);
	if (! (ifp = fopen(argv[0], "rb"))) {
		fprintf(stderr, "%s: can't open input file \"%s\"\n",
			progname, argv[0]);
		exit(2);
	}
	if (! (ofp = fopen(argv[1], "wt"))) {
		fprintf(stderr, "%s: can't open output file \"%s\"\n",
			progname, argv[1]);
		exit(3);
	}
	interpret_file(ifp, ofp, argv[2], longmode);
	fclose(ifp);
	fclose(ofp);
	exit(0);
}

