/*
 * File: gensn.c
 *
 * Program to generate tables for handling special names in IMP80 compiler
 *
 * Bob Eager   August 2002
 *
 */

/*
 * Compile time options
 *
 * Define exactly one target language name to be 1; all the rest should be 0.
 *
 */

#define	C	1			/* C language */
#define	IMP	0			/* IMP80 language */


#ifdef	__IBMC__
#pragma	strings(readonly)
#endif

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#define	FALSE		0
#define	TRUE		1

#define	MAXCC		132		/* Max length of line buffer */
#define	MAXNAME		20		/* Max length of a special name */
#define	MAXPARAM	40		/* Max length of parameter text */
#define	MAXSN		80		/* Max number of special names */
#define	MAXTYPE		25		/* Max length of type text */

/* Type definitions */

typedef	void		VOID, *PVOID;
typedef	int		BOOL, *PBOOL;
typedef	char		CHAR, *PCHAR;
typedef	unsigned char	UCHAR, *PUCHAR;
typedef	int		INT, *PINT;
typedef	unsigned int	UINT, *PUINT;
typedef	long		LONG, *PLONG;
typedef	unsigned long	ULONG, *PULONG;

/* Forward references */

static	VOID	do_comments(VOID);
static	VOID	do_names(VOID);
static	VOID	do_tables(VOID);
static	FILE	*openio(PUCHAR, PUCHAR);
static	VOID	readline(VOID);
static	VOID	usage(VOID);

/* Local data */

static	UCHAR	cc[MAXCC];		/* Input line buffer */
static	FILE	*infp;			/* File pointer for input file */
static	FILE	*comfp;			/* File pointer for comment file */
static	FILE	*namefp;		/* File pointer for name file */
static	INT	num = 0;		/* Number of names */
static	PUCHAR	progname;		/* Program name */
static	INT	ptype[MAXSN];		/* Special name type */
static	UCHAR	sname[MAXSN][MAXNAME];	/* Special name text */
static	INT	sninfo[MAXSN];		/* Special name information */
static	UCHAR	sparam[MAXSN][MAXPARAM];/* Special name parameter text */
static	UCHAR	stype[MAXSN][MAXTYPE];	/* Special name textual type info */
static	FILE	*tabfp;			/* File pointer for table file */


INT main(INT argc, PUCHAR argv[])
{	INT i;
	time_t now;			/* Binary date and time */
	UCHAR s_now[26];		/* String date and time */
	UCHAR temp[MAXCC];		/* Temporary input buffer */
	PUCHAR p;

	progname = argv[0];
	p = strrchr(progname, '\\');
	if(p != (PUCHAR) NULL) progname = ++p;
	for(i = 0; progname[i] != '\0'; i++)
		progname[i] = (UCHAR) tolower(progname[i]);

	if(argc != 5) usage();

	infp = openio(argv[1], "r");
	tabfp = openio(argv[2], "w");
	namefp = openio(argv[3], "w");
	comfp = openio(argv[4], "w");

	now = time((time_t *) NULL);	/* Get binary date and time */
	strcpy(s_now, ctime(&now));	/* Copy string version */
	s_now[24] = '\0';		/* Truncate to remove newline */

	/* Main input loop */

	for(;;) {
		if(num >= MAXSN) {
			fprintf(stderr, "%s: too many names\n", progname);
			exit(EXIT_FAILURE);
		}

		readline();

		if(cc[0] == '\0') break;
					/* Finished */

		if(cc[0] == '!') continue;
					/* Comment line - ignore */

		sscanf(cc, "%x %x %s %[^\n]",
			&ptype[num], &sninfo[num], stype[num], temp);
					/* Read and decompose line */
		p = strpbrk(temp, "(");	/* See if any parameters */
		if(p != (unsigned char *) NULL) {
			(VOID) strncpy(sname[num], temp, (UINT) (p-temp));
			(VOID) strcpy(sparam[num], p+1);
		} else {
			(VOID) strcpy(sname[num], temp);
			sparam[num][0] = '\0';
		}
		num++;			/* Count of items read */
	}
	fclose(infp);

	/* Generate the tables file */

	do_tables();

	do_names();

	do_comments();

	return(EXIT_SUCCESS);
}


/*
 * Function to generate a comment line framed by asterisks, and write
 * it to the comment file.
 *
 */

#if	C
static VOID com(PUCHAR s)
{	INT i;

	fprintf(comfp, " *      %s", s);
	for(i = 0; i < 71 - strlen(s); i++) fputc(' ', comfp);
	fputs("*\n", comfp);
}
#endif

	
/*
 * Function to generate a comment line framed by asterisks, and write
 * it to the comment file.
 *
 */

#if	IMP
static VOID com(PUCHAR s)
{	int i;

	fprintf(comfp, "!*      %s", s);
	for(i = 0; i < 71 - strlen(s); i++) fputc(' ', comfp);
	fputs("*\n", comfp);
}
#endif

	
/*
 * Function to generate comment block for head of CSNAME
 *
 */

#if	C
static VOID do_comments()
{	INT i;
	UCHAR line[MAXCC+1];

	fputc('/', comfp);
	for(i = 0; i < 79; i++) fputc('*', comfp);
	fputc('\n', comfp);
	com("");
	com("Compile a special name - 'ptype' = 1006 (=%routine %label)");
	com("Their true 'ptype' is in global array 'tsname'.");
	com("'sninfo' has a four byte record for each name:");
	com("   2 byte extra information");
	com("   1 byte pointer (various uses)");
	com("   1 byte flags:");
	com("      Bits 0-2  -  number of parameters");
	com("      Bit  3    -  set if first parameter is of %name type");
	com("      Bit  4    -  set if ad-hoc code planted by this routine");
	com("      Bit  5    -  set for built in mapping functions");
	com("      Bit  6    -  set for 'iocp' call");
	com("      Bit  7    -  set for implicitly specified items");
	com("");

	for(i = 0; i < num; i++) {
		sprintf(line, "%2d  =  %s %s%s%s",
			i, stype[i], sname[i], sparam[i][0] == '\0' ? "" : "(",
			sparam[i]);
		com(line);
	}

	com("");
	fputc(' ', comfp);
	for(i = 0; i < 77; i++) fputc('*', comfp);
	fputs("*/\n", comfp);
}
#endif


/*
 * Function to generate comment block for head of CSNAME
 *
 */

#if	IMP
static VOID do_comments()
{	INT i;
	UCHAR line[MAXCC+1];

	fputc('!', comfp);
	for(i = 0; i < 79; i++) fputc('*', comfp);
	fputc('\n', comfp);
	com("");
	com("Compile a special name - 'ptype' = 1006 (=%routine %label)");
	com("Their true 'ptype' is in global array 'tsname'.");
	com("'sninfo' has a four byte record for each name:");
	com("   2 byte extra information");
	com("   1 byte pointer (various uses)");
	com("   1 byte flags:");
	com("      Bits 0-2  -  number of parameters");
	com("      Bit  3    -  set if first parameter is of %name type");
	com("      Bit  4    -  set if ad-hoc code planted by this routine");
	com("      Bit  5    -  set for built in mapping functions");
	com("      Bit  6    -  set for 'iocp' call");
	com("      Bit  7    -  set for implicitly specified items");
	com("");

	for(i = 0; i < num; i++) {
		sprintf(line, "%2d  =  %s %s%s%s",
			i, stype[i], sname[i], sparam[i] == '\0' ? "" : "(",
			sparam[i]);
		com(line);
	}

	com("");
	fputc('!', comfp);
	for(i = 0; i < 79; i++) fputc('*', comfp);
	fputc('\n', comfp);
}
#endif


/*
 * Function to generate name table
 *
 */

#if	C
static VOID do_names()
{	INT i, j;
	UINT nmax = 0;
	INT online = 0;
	PUCHAR s;

	/* Work out total length required */

	for(i = 0; i < num; i++) nmax += (strlen(sname[i]) + 1);

	fprintf(namefp, "const\tunsigned char\tilett[%d] = {\n", nmax+1);

	for(i = 0; i < num; i++) {
		s = sname[i];

		for(j = 0; j <= strlen(s); j++) {
			if(online == 16) {
				fputc('\n', namefp);
				online = 0;
			}
			if(j == 0) fprintf(namefp, "%3d, ", strlen(s));
			else fprintf(namefp, "'%c', ", toupper(s[j-1]));
			online++;
		}
	}
 	if(online++ == 16) {
		fputc('\n', namefp);
		online = 1;
	}
	fprintf(namefp, "%3d\n};\n", 255);
}
#endif


/*
 * Function to generate name table
 *
 */

#if	IMP
static VOID do_names()
{	INT i, j;
	INT nmax = 0;
	INT online = 0;
	PUCHAR s;

	/* Work out total length required */

	for(i = 0; i < num; i++) nmax += (strlen(sname[i]) + 1);

	fprintf(namefp, "%%constantbyteintegerarray ilett(0:%d) = %%c\n", nmax);

	for(i = 0; i < num; i++) {
		s = sname[i];

		for(j = 0; j <= strlen(s); j++) {
			if(online == 16) {
				fputc('\n', namefp);
				online = 0;
			}
			if(j == 0) fprintf(namefp, "%3d, ", strlen(s));
			else fprintf(namefp, "'%c',", toupper(s[j-1]));
			online++;
		}
	}
 	if(online++ == 16) {
		fputc('\n', namefp);
		online = 1;
	}
	fprintf(namefp, "%3d;\n", 255);
}
#endif


/*
 * Function to generate the subsidiary tables
 *
 */

#if	C
static VOID do_tables()
{	INT i;

	/* First the PTYPE table */

	fprintf(tabfp, "#define\tNOOFSNS\t\t%d\n\n", num);
	fputs("const\tunsigned short\ttsname[NOOFSNS] = {\n", tabfp);

	for(i = 0; i < num; i++) {
		fprintf(tabfp, "0x%04x%s", ptype[i], (i == num - 1) ? "" : ",");
		if((i+1)%8 == 0) fputc('\n', tabfp);
	}		
	if(num%8 != 0) fputc('\n', tabfp);
	fputs("};\n", tabfp);

	/* Now the special name information table */

	fputs("\nconst\tunsigned int\tsninfo[NOOFSNS] = {\n", tabfp);

	for(i = 0; i < num; i++) {
		fprintf(tabfp, "0x%08x%s",
			sninfo[i], (i == num - 1) ? "" : ",");
		if((i+1)%4 == 0) fputc('\n', tabfp);
	}		
	if(num%4 != 0) fputc('\n', tabfp);
	fputs("};\n", tabfp);
}
#endif


/*
 * Function to generate the subsidiary tables
 *
 */

#if	IMP
static VOID do_tables()
{	INT i;

	/* First the PTYPE table */

	fprintf(tabfp, "%%constantinteger no of sns = %d\n", num-1);
	fputs("%constanthalfintegerarray tsname(0:no of sns) = %c\n", tabfp);

	for(i = 0; i < num; i++) {
		fprintf(tabfp, "x'%04x'%c", ptype[i],
			(i == num - 1) ? ';' : ',');
		if((i+1)%8 == 0) fputc('\n', tabfp);
	}		
	if(num%8 != 0) fputc('\n', tabfp);

	/* Now the special name information table */

	fputs("!\n%constantintegerarray sninfo(0:no of sns) = %c\n", tabfp);

	for(i = 0; i < num; i++) {
		fprintf(tabfp, "x'%08x'%c", sninfo[i],
			(i == num - 1) ? ';' : ',');
		if((i+1)%4 == 0) fputc('\n', tabfp);
	}		
	if(num%4 != 0) fputc('\n', tabfp);
}
#endif


/*
 * Function to open a file in a specified mode
 * Does not return if there is a failure, but outputs an error
 * message and exits
 *
 */

static FILE *openio(PUCHAR file, PUCHAR mode)
{	FILE *fp;

	fp = fopen(file, mode);
	if(fp == (FILE *) NULL) {
		fprintf(stderr, "%s: cannot open '%s'\n", progname, file);
		exit(EXIT_FAILURE);
	}
	return(fp);
}


/*
 * Routine to read a line from the input file into 'cc'.
 * Blank lines are ignored.
 *
 * On exit, 'cc' contains the string, null terminated.
 *
 */

static VOID readline(VOID)
{	INT i = 0;
	INT ch;

	do {
		for(;;) {
			ch = fgetc(infp);
			if(ch == EOF) break;
			if(ch == '\n') break;

			cc[i++] = (unsigned char) ch;
		}
		cc[i] = '\0';
	} while((cc[0] == '\0') && (ch == '\n'));
	if(ch == EOF) cc[0] = '\0';
}


/*
 * Routine to output brief usage information, then exit.
 *
 */

static VOID usage()
{	fprintf(stderr, "Usage: %s input tables names comments\n", progname);
	exit(EXIT_SUCCESS);
}

/*
 * End of file: gensn.c
 *
 */
