/*
 *	QRESET - Q/C Compiler V3.2 Customization Program
 *			11/25/84
 */

#include <qstdio.h>

char	signon[] = "Q/C Compiler ",
	version[] = "V3.2",
	v_Z80[] = "(Z80)",
	v_8080[] = "(8080)";

int	flavor, 	/* 'z' - Z80, '8' - 8080 */
	chgflag = FALSE, /* record changes */
	optflag = FALSE; /* record -O request */

#define READ	0
#define WRITE	1
#define BUFSIZE 1024
#define CTLC	0x3	/* ^C */

/* Offsets in CC.COM to modify */

#define PSYMSIZE  0x0		/* symbol table size */
#define PMEMSIZE  PSYMSIZE+1	/* member table size */
#define PTYPESIZE PMEMSIZE+1	/* type table size */
#define PSWQSIZE  PTYPESIZE+1	/* switch/loop table size */
#define PCASSIZE  PSWQSIZE+1	/* case table size */
#define PLITSIZE  PCASSIZE+1	/* macro (#define) pool size */
#define PMACSIZE  PLITSIZE+1	/* literal (string) pool size */
#define PAUSECNT  PMACSIZE+1	/* error pause count */
#define PVERBOSE  PAUSECNT+1	/* terse/verbose */
#define PINITSW   PVERBOSE+1	/* large array initialization */
#define PREDIRECT PINITSW+1	/* redirection */
#define PCODESW   PREDIRECT+1	/* default assembler */
#define PCODEEXT  PCODESW+1	/* default file extension */

main(argc, argv)
int argc;
char *argv[];
	{
	static unsigned
		bdos;		/* used to compute CP/M size */
	int
		*pglobal,	/* start of global variables */
		fd;		/* file descriptor for compiler */
	char
		buffer[BUFSIZE],/* buffer for reading CC.COM */
		ccname[15],	/* complete CP/M filename of compiler */
		*findglb(),
		*strcat(),
		*strcpy();

	printf("\nQRESET customization program for %s%s ",
		signon, version);

/* Was a drive or alternate filename specified for CC.COM? */

	if (argc > 1 && *argv[1] != '-') { /* compiler name will be first */
		strcpy(ccname, *++argv);
		--argc;
		if (ccname[1] == ':' && ccname[2] == '\0')
			strcat(ccname, "CC.COM"); /* only a drive given */
		else if (!index(ccname, '.'))
			strcat(ccname, ".COM"); /* no file extension */
		}
	else			/* use the standard name */
		strcpy(ccname, "CC.COM");

/* Check for command line options */

	while (--argc > 0) {
		if (**++argv == '-') {
			switch (chlower(*++*argv)) {
			case 'o':	/* ask about Z80 AO optimizer */
				optflag = TRUE;
				break;
			case 'v':	/* version override (undocumented */
				strncpy(version, *argv, sizeof(version)-1);
				printf("[override %s] ", version);
				break;
				}
			}
		}

/* Get part of CC.COM to change */

	if ((fd = open(ccname, READ)) == -1) {
		printf("\n\nCan't open: %s\n",ccname);
		_exit();
		}
	if(read(fd, buffer, BUFSIZE) == 0) {
		printf("\n\nError reading %s", ccname);
		_exit();
		}
	close(fd);

/* Check for good file and locate the global flags */

	pglobal = (int *) findglb(buffer, ccname);

/* Find true CP/M size */

	bdos = peek(0x7);	/* high order byte of bdos address */
	printf("\nYour CP/M TPA size is: %uK\n", bdos/4);

/* Report old settings and get new settings */

	setswitch(pglobal);
	settables(pglobal);

/* Rewrite the changed part of CC.COM */

	if (chgflag == FALSE)
		nochg();		/* no changes made */
	if ((fd = open(ccname, WRITE)) == -1 ||
	    write(fd, buffer, BUFSIZE) < BUFSIZE) {
		printf("\nError writing %s", ccname);
		_exit();
		}
	printf("\n%s has been changed", ccname);
	close(fd);
	}

/* Check for good CC.COM file and locate the global flags */
char *findglb(buffer, ccname)
char *buffer, *ccname;
	{
	register char *p;
	register i, found, length;

	found = FALSE;
	length = strlen(signon);	/* locate sign-on message */
	for (p = buffer, i = 0; i < BUFSIZE-length; ++p, ++i) {
		if (strcmp(p, signon) == 0) {
			found = TRUE;
			break;
			}
		}
	p += length + 1;		/* version number comes next */
	if (!found || *p != 'V')
		badfile(ccname);
	length = strlen(version);
	if (strncmp(p, version, length) != 0)
		wrongversion();
	else {
		p += length;
		i += length;
		}
	flavor = ' ';
	for ( ; i < BUFSIZE; ++p, ++i) {
	    if (strcmp(p, v_Z80) == 0) { /* check flavor: Z80 or 8080 */
		printf("%s\n", v_Z80);
		flavor = 'z';
		p += strlen(v_Z80) + 1;
		break;
		}
	    else if (strcmp(p, v_8080) == 0) {
		printf("%s\n", v_8080);
		flavor = '8';
		p += strlen(v_8080) + 1;
		break;
		}
	    }
	if (flavor == ' ') {
		printf("\nCan't tell whether this is for 8080 or Z80\n");
		nochg();
		}
	return (p);			/* beginning of globals */
	}

/* Report that QRESET and Q/C are not the same version */
wrongversion()
	{
	printf("\nThis version of QRESET only works with Q/C %s\n",
		version);
	nochg();
	}

/* Report that CC.COM doesn't look good */
badfile(ccname)
char *ccname;
	{
	printf("\n%s does not look like a Q/C Compiler -\n", ccname);
	printf("  either the file is damaged, or you have changed\n");
	printf("  the order of global variables in CGLBDEF.C\n");
	nochg();
	}

/* Confirm that no changes will be made and quit */
nochg()
	{
	printf("\nNo changes made");
	_exit();
	}

/* Get response from user and check for request to quit */
getanswer()
	{
	register c;

	c = chlower(getchar());
	if (c == CTLC) {
		printf("^C");
		_exit();
		}
	return c;
	}

/* Do all compiler switch settings */
setswitch(pglobal)
register *pglobal;
	{
	register
		*pc,		/* pointer to compiler setting */
		newsize,	/* new size setting */
		newsw;		/* hold area for new switch setting */
	register char
		*file_ext;	/* pointer to default output file extension */
	char
		*strcpy(),
		num[7]; 	/* hold area for numeric answers */

/* ASSEMBLER CODE/FILE EXTENSION SWITCH */

	if (flavor == '8') {	/* 8080 choices */
	    pc = pglobal + (PCODESW);
	    printf("\nCurrently compiler will generate code for %s",
		(*pc == 'a') ? "RMAC" : "M80");
	    do {
		printf("\n    Enter A (RMAC), M (M80) or <CR> for no change: ");
		newsw = getanswer();
		} while (newsw != 'a' && newsw != 'm' && newsw != '\n');
	    if (newsw != '\n') {
		*pc = newsw;
		strcpy(pglobal+PCODEEXT, (newsw == 'a') ? "ASM": "MAC");
		printf("\n    Compiler changed to generate code for %s\n",
			(newsw == 'a') ? "RMAC" : "M80");
		chgflag = TRUE;
		}
	    }
	else if (optflag) {	/* ask about AO Z80 optimizer */
	    file_ext = (char *) (pglobal + (PCODEEXT));
	    printf("\nCurrently compiler will generate code for %s",
		(*file_ext == 'M') ? "M80" : "AO");
	    do {
		printf("\n    Enter M (M80), O (AO) or <CR> for no change: ");
		newsw = getanswer();
		} while (newsw != 'm' && newsw != 'o' && newsw != '\n');
	    if (newsw != '\n') {
		strcpy(file_ext, (newsw == 'm') ? "MAC": "CAS");
		printf("\n    Compiler changed to generate code for %s\n",
			(newsw == 'm') ? "M80" : "AO");
		chgflag = TRUE;
		}
	    }

/* TERSE/VERBOSE SWITCH */

	pc = pglobal + (PVERBOSE);
	printf("\nCurrently compiler is in %s mode",
		(*pc) ? "verbose" : "terse");
	do {
		printf("\n    Enter T (terse), V (verbose) or <CR> for no change: ");
		newsw = getanswer();
		} while (newsw != 't' && newsw != 'v' && newsw != '\n');
	if (newsw != '\n') {
		*pc = (newsw == 'v');
		printf("\n    Compiler changed to %s mode\n",
			(*pc) ? "verbose" : "terse");
		chgflag = TRUE;
		}

/* ERROR PAUSE COUNT */

	pc = pglobal + (PAUSECNT);
	printf("\nCurrently compiler pauses after %d error%s\n",
		*pc, (*pc == 1) ? "": "s");
	printf("    Enter new size or <CR> for no change: ");
	if (newsize = atoi(gets(num))) {
		printf("   New compiler pause count = %d\n", newsize);
		*pc = newsize;
		chgflag = TRUE;
		}
/* LARGE ARRAY INITIALIZATION SWITCH */

	pc = pglobal + (PINITSW);
	printf("\nCurrently compiler %s large arrays",
		(*pc) ? "initializes": "does not initialize");
	do {
		printf("\n    Enter I (initialize), N (do not initialize)\
 or <CR> for no change: ");
		newsw = getanswer();
		} while (newsw != 'i' && newsw != 'n' && newsw != '\n');
	if (newsw != '\n') {
		*pc = (newsw == 'i');
		printf("\n    Compiler changed to %s large arrays\n",
			(*pc) ? "initialize": "skip initialization of");
		chgflag = TRUE;
		}
/* REDIRECTION SWITCH */

	pc = pglobal + (PREDIRECT);
	printf("\nCurrently compiler %s redirection",
		(*pc) ? "includes": "excludes");
	do {
		printf("\n    Enter R (redirect), N (do not redirect)\
 or <CR> for no change: ");
		newsw = getanswer();
		} while (newsw != 'r' && newsw != 'n' && newsw != '\n');
	if (newsw != '\n') {
		*pc = (newsw == 'r');
		printf("\n    Compiler changed to %s redirection\n",
			(*pc) ? "include": "exclude");
		chgflag = TRUE;
		}
	}

/* Check for table size changes */
settables(pglobal)
register *pglobal;
	{
	register *pc;

/* SYMBOL TABLE */

	pc = pglobal + (PSYMSIZE);
	*pc = getsize("SYMBOL TABLE size", *pc, "entries");

/* MEMBER TABLE */

	pc = pglobal + (PMEMSIZE);
	*pc = getsize("MEMBER TABLE size", *pc, "entries");

/* TYPE TABLE */

	pc = pglobal + (PTYPESIZE);
	*pc = getsize("TYPE TABLE size", *pc, "types");

/* LITERAL (string) POOL */

	pc = pglobal + (PLITSIZE);
	*pc = getsize("LITERAL (string) POOL size", *pc, "characters");

/* MACRO (#define) POOL */

	pc = pglobal + (PMACSIZE);
	*pc = getsize("MACRO (#define) POOL size", *pc, "characters");

/* SWITCH/LOOP QUEUE */

	pc = pglobal + (PSWQSIZE);
	*pc = getsize("SWITCH/LOOP nesting depth", *pc, "levels");

/* SWITCH/CASE TABLE */

	pc = pglobal + (PCASSIZE);
	*pc = getsize("SWITCH/CASE TABLE size", *pc, "cases");
	}

/* Report a table size and ask for a change */
getsize(table, size, units)
char *table, *units;
int size;
	{
	char	num[7]; 	/* hold area for numeric answers */
	int	newsize;

	printf("\n%s is: %d %s\n", table, size, units);
	printf("    Enter new size or <CR> for no change: ");
	if (newsize = atoi(gets(num))) {
		printf("    New %s is: %d %s\n", table, newsize, units);
		chgflag = TRUE;
		return newsize;
		}
	else
		return size;
	}
/* Console only version of buffered I/O to save space */
#define CONIN	1
#define CONOUT	2
printf(nargs)
	{
	register char **arg, *fmt, *p;
	register c, base;
	char s[7], *itob();

	arg = (char **)&nargs + nargs;
	fmt = *arg;
	while (c = *fmt++) {
		if (c != '%') {
			putchar(c);
			continue;
			}
		switch (c = *fmt++) {
		case 'c':
			putchar(*--arg);
			continue;
		case 'd':
			base = -10;
			goto prt;
		case 'o':
			base = 8;
			goto prt;
		case 'u':
			base = 10;
			goto prt;
		case 'x':
			base = 16;
		prt:
			p = itob(*--arg, s, base);
			goto puts;
		case 's':
			p = *--arg;
		puts:
			while (*p)
				putchar(*p++);
			continue;
		default:
			putchar(c);
			continue;
			}
		}
	}
putchar(c)
	{
	if (c == '\n')
		bdos1(CONOUT, '\r');
	bdos1(CONOUT, c);
	}
getchar()
	{
	register c;

	if ((c = bdos1(CONIN, 0)) == '\r')
		bdos1(CONOUT, c = '\n');
	return c;
	}
getc()	{}
/* end of QRESET.C */
