/* Copyright (c) 1988 by Sozobon, Limited.  Author: Johann Ruegg
 *
 * Permission is granted to anyone to use this software for any purpose
 * on any computer system, and to redistribute it freely, with the
 * following restrictions:
 * 1) No charge may be made other than reasonable charges for reproduction.
 * 2) Modified versions must be clearly marked as such.
 * 3) The authors are not responsible for any harmful consequences
 *    of using this software, even if they result from defects in it.
 *
 *	main.c
 *
 *	Main routine, error handling, keyword lookup.
 *
 *
 *   Revised: Dec 1988	Joe Montgomery
 *
 *   Revised main.c to use Amiga File System Naming Conventions
 *	Added ?,C,F switches. ? help
 *			      C force data,bss into Chip memory
 *			      F force data,bss into Fast memory
 *	To be added -o switch to specify assembly output
 *
 *     other modules:
 *   Revised out.c to use MOTOROLA assembly directives in order
 *	to be compatible with C.Gibbs a68k assembler & blink
 *	Added END statement
 *	Changed .comm label,size to label DC.x 0
 *   Revised d2.c so that externs are declared as XREF -----
 *   Revised g2.c & gen.c to declare all called functions XREF
 *     (will need to change this to declare only external functions)
 *
 *
 *   All changes labeled JMM
 */

#include <stdio.h>
#include "param.h"
#include "nodes.h"
#include "tok.h"

extern short usechipmemory,usefastmemory;
int lineno;
int nmerrors;
int oflags[26];
int xflags[26];
int pflag = 0;			/* enable profiling */
static int anydebug;
#define debug oflags['z'-'a']


FILE *input;
FILE *output;
#if CC68
FILE *fopenb();
#define fopen fopenb
#endif
char *inname;

#if NEEDBUF
char my_ibuf[BUFSIZ];
#endif

NODEP cur;

/* JMM changed defines to be compatible with AMIGA */
static	char *defines[] = {
	"MC68000",
	"mc68000",
	"SOZOBON",
	"MCH_AMIGA",
	"AmigaDOS",
	NULL
};

static	char	Version[] =
"zc: Amiga Version 1.01  Copyright (c) 1988 by Sozobon, Limited.\n";

static char Version2[] =
"    modified by J.Montgomery. Now generates Motorola compatible \n";
static char Version3[] =
"   assembly code.\n";
extern char *outfilename,*errorfile;


main(argc, argv)
char **argv;
{
	char	*p, *getenv();
	int shownames;
	int i;

/* JMM added switches to force data,bss into chip or fast memory */
	usefastmemory = 0;
	usechipmemory = 0;  /* don't force data into either chip or fast*/
	outfilename = (char *) NULL;

/* JMM force hcc to always print out version */
	printf(Version);
	if (sizeof(NODE) & 3) {
		printf("sizeof NODE not mult of 4\n");
		exit(1);
	}

	/*
	 * Define the "built-in" macros
	 */
	for (i=0; defines[i] != NULL; i++)
		optdef(defines[i]);

	/*
	 * Parse the INCLUDE environment variable, if present.
	 */
	if ((p = getenv("INCLUDE")) != NULL){
		if( doincl(p) == 1 )exit(0);
	}
	shownames = 0;
	if (isatty(0)) {
		write(1, "\33v", 2);
		setbuf(stdout, NULL);
	}
/* put author here */
	while (argc-- > 1) {
		argv++;
		if(argv[0][0] == '?') {
			doopt(&argv[0][0]);
			exit(1);
		}
		if(argv[0][0] == '-')
		      doopt(&argv[0][1]);
#if CC68
		else if (argv[0][0] == '+') {
			upstr(&argv[0][1]);
			doopt(&argv[0][1]);
		}
#endif
		else {
			if (argc > 1 || shownames) {
				shownames++;
				printf("%s:\n", argv[0]);
			}
			if (input != NULL)
				fclose(input);
			input = fopen(argv[0], ROPEN);
			if (input == NULL) {
				printf("Cant open %s\n", argv[0]);
				exit(1);
			}
#if NEEDBUF
			setbuf(input, my_ibuf);
#endif
			inname = argv[0];
			dofile();
		}
	}
	if (input == NULL) {
		input = stdin;
		output = stdout;
		inname = "<STDIN>";
		dofile();
	}
	exit(0);
}

doincl(s)
char	*s;
{
	char	*malloc(), *strcpy();
	char	buf[256];
	char	dir[128];
	register char	*p;


	strcpy(buf, s);
	/*
	 * Convert ',' and ';' to nulls
	 */
	for (p=buf; *p != '\0' ;p++)
		if (*p == ',' || *p == ';')
			*p = '\0';
	p[1] = '\0';                    /* double null terminated */

	/*
	 * Grab each directory, make sure it ends with a slash,
	 * and add it to the directory list.
	 */
	for (p=buf; *p != '\0' ;p++) {
		strcpy(dir, p);
      /* JMM use Amiga file naming conventions */
		if (dir[strlen(dir)-1] != '/' && dir[strlen(dir)-1] != ':')
			strcat(dir, "/");

		optincl( strcpy(malloc((unsigned) (strlen(dir) + 1)), dir) );

		while (*p != '\0')
			p++;
	}
}


extern int nodesmade, nodesavail;
extern NODEP deflist[], symtab[], tagtab;
extern NODEP strsave;
extern int level;
dofile()
{
	char *scopy();
	int i;

	out_start(inname);
	inname = scopy(inname);
	lineno = 1;
	nmerrors = 0;
	advnode();

	level = 0;
	program();
	dumpstrs(strsave);

	out_end();
	if (cur && cur->e_token == EOFTOK)
		freenode(cur);
	sfree(inname);
	for (i=0; i<NHASH; i++) {
		if (debug>1 && deflist[i]) {
			printf("defines[%d]", i);
			printlist(deflist[i]);
		}
		freenode(deflist[i]);
		deflist[i] = NULL;
		if (debug && symtab[i]) {
			printf("gsyms[%d]", i);
			printlist(symtab[i]);
		}
		freenode(symtab[i]);
		symtab[i] = NULL;
	}
	if (debug) {
		printf("structs");
		printlist(tagtab);
	}
	freenode(tagtab);
	tagtab = NULL;
	freenode(strsave);
	strsave = NULL;
	if (nmerrors) {
		printf("%d errors\n", nmerrors);
		exit(1);
	}
	if (nodesmade != nodesavail) {
		printf("lost %d nodes!!!\n", nodesmade-nodesavail);
		exit(1);
	}
/*
	printf("Space = %ldK\n", ((long)nodesavail*sizeof(NODE))/1024);
*/
}

dooutfile(s)
char	*s;
{
     char    *malloc(), *strcpy();

     outfilename = strcpy(malloc((unsigned)(strlen(s) + 1)), s );
}

doerrorfile(s)
char *s;
{
     char  *malloc(),*strcpy();

     errorfile = strcpy(malloc((unsigned)(strlen(s) + 1)), s);
}

doopt(s)
char *s;
{
	register char c;

	while ((c = *s++)) {
#ifdef	DEBUG
		if (c >= 'a' && c <='z') {
			oflags[c-'a']++;
			anydebug++;
		} else
#endif
		if ( (c >= 'A' && c <= 'Z') || c == '?') {
			switch (c) {
			case 'D':
				optdef(s);
				return;
			case 'U':
				optundef(s);
				return;
			case 'I':
				doincl(s);
				return;
			case 'P':
				pflag = 1;
				continue;
			case 'V':
				printf("%s %s",Version2,Version3);
				continue;
/* JMM added ?,C,F,O,E	switches */
			case 'E': /* specify error file */
				doerrorfile(s);
				return(1);
			case 'O':
				dooutfile(s);
				return(1);
			case 'C':
				if(usefastmemory){
				   printf(" Can't use both Chip & Fast memory\n");
				   return(1);
				}
				usechipmemory = 1;
				continue;
			 case 'F':
				if(usechipmemory){
				   printf(" Can't use both Chip & Fast memory\n");
				   return(1);
				}
				usefastmemory = 1;
				continue;
			case '?':
				printf("%s %s",Version2,Version3);
				printf("    The Correct Syntax is \n");
				printf("zc [FLAGS] SOURCEFILE \n");
				printf("    The valid compiler flags are : \n");
				printf("\n  -Dxxxx   Define xxxx\n  -Uxxxx   Undefine xxxx\n");
				printf("  -Ixxxx   Include Directory = xxxx\n  -P   profiler\n");
				printf("  -Oxxxx   outputfile name = xxxx\n");
				printf("  -V   display compiler version\n  -?   Help\n");
				printf("  -C   force Data,Bss into Chip memory \n");
				printf("  -F   force Data,Bss into Fast memory \n");
				return(1);
				continue;
			}
#ifdef	DEBUG
			xflags[c-'A']++;
			anydebug++;
#endif
		}
	}
return(0);
}

errors(s,t)
char *s, *t;
{
	optnl();
	printf("error in %s on line %d: %s %s\n", inname, lineno, s,t);
	nmerrors++;
}

errorn(s,np)
char *s;
NODE *np;
{
	optnl();
	printf("error in %s on line %d: %s ", inname, lineno, s);
	put_nnm(np);
	putchar('\n');
	nmerrors++;
}

error(s)
char *s;
{
	optnl();
	printf("error in %s on line %d: %s\n", inname, lineno, s);
	nmerrors++;
}

warns(s,t)
char *s, *t;
{
	optnl();
	printf("warning in %s on line %d: %s %s\n", inname, lineno, s,t);
}

warnn(s,np)
char *s;
NODE *np;
{
	optnl();
	printf("warning in %s on line %d: %s ", inname, lineno, s);
	put_nnm(np);
	putchar('\n');
}

warn(s)
char *s;
{
	optnl();
	printf("warning in %s on line %d: %s\n", inname, lineno, s);
}

fatals(s,t)
char *s, *t;
{
	optnl();
	printf("fatal error in %s on line %d: %s %s\n", inname, lineno, s,t);
	exit(1);
}

fataln(s,np)
char *s;
NODE *np;
{
	optnl();
	printf("fatal error in %s on line %d: %s ", inname, lineno, s);
	put_nnm(np);
	putchar('\n');
	exit(1);
}

fatal(s)
char *s;
{
	optnl();
	printf("fatal error in %s on line %d: %s\n", inname, lineno, s);
	exit(1);
}

static
optnl()
{
	if (anydebug)
		putchar('\n');
}

struct kwtbl {
	char *name;
	int	kwval;
	int	kflags;
} kwtab[] = {
	/* must be sorted */
	{"asm", K_ASM},
	{"auto", K_AUTO},
	{"break", K_BREAK},
	{"case", K_CASE},
	{"char", K_CHAR},
	{"continue", K_CONTINUE},
	{"default", K_DEFAULT},
	{"do", K_DO},
	{"double", K_DOUBLE},
	{"else", K_ELSE},
	{"enum", K_ENUM},
	{"extern", K_EXTERN},
	{"float", K_FLOAT},
	{"for", K_FOR},
	{"goto", K_GOTO},
	{"if", K_IF},
	{"int", K_INT},
	{"long", K_LONG},
	{"register", K_REGISTER},
	{"return", K_RETURN},
	{"short", K_SHORT},
	{"sizeof", K_SIZEOF},
	{"static", K_STATIC},
	{"struct", K_STRUCT},
	{"switch", K_SWITCH},
	{"typedef", K_TYPEDEF},
	{"union", K_UNION},
	{"unsigned", K_UNSIGNED},
	{"void", K_VOID},
	{"while", K_WHILE},

	{0,0}
};

#define FIRST_C 'a'
#define LAST_C	'z'
struct kwtbl *kwstart[LAST_C-FIRST_C+1];

kw_init()
{
	register struct kwtbl *p;
	register c;

	for (p=kwtab; p->name; p++) {
		c = p->name[0];
		if (kwstart[c-FIRST_C] == 0)
			kwstart[c-FIRST_C] = p;
	}
}

kw_tok(tp)
NODE *tp;
{
	register struct kwtbl *kp;
	register char *nm;
	register i;
	static first = 0;

	nm = tp->n_name;
	if (first == 0) {
		kw_init();
		first = 1;
	}
	i = nm[0];
	if (i < FIRST_C || i > LAST_C)
		return;
	kp = kwstart[i-FIRST_C];
	if (kp)
	for (; kp->name; kp++) {
		i = strcmp(nm, kp->name);
		if (i == 0) {
			tp->e_token = kp->kwval;
			tp->e_flags = kp->kflags;
			return;
		} else if (i < 0)
			return;
	}
}

#if CC68
/* fix args since stupid lib makes all lower case */
upstr(s)
char *s;
{
	while (*s) {
		if (*s >= 'a' && *s <= 'z')
			*s += 'A'-'a';
		s++;
	}
}
downstr(s)
char *s;
{
	while (*s) {
		if (*s >= 'A' && *s <= 'Z')
			*s -= 'A'-'a';
		s++;
	}
}
#endif
