/* Copyright (c) 1988,1989,1991 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.
 *
 * Modified by Detlef Wuerkner for AMIGA
 * ALL Changes marked with TETISOFT (The flag FOR_AMIGA was already here!)
 * except '#ifdef DEBUG', 'size_i' instead of 'SIZE_I' and 'exit(EXIT_xxx)'
 */

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

static	char	Version[] =
"hcc: version 2.0  Copyright (c) 1988,1989,1991 by Sozobon, Limited.";

/* TETISOFT */
#ifdef FOR_AMIGA
static char Version2[] =
"\n     Amiga Version 1.1 by Detlef W\374rkner.\n";
#endif

/* ADDED BY TETISOFT */
void usage()
{
 printf("\nUsage: hcc [FLAGS] Sourcefile [Outputfile]\n\n");
 printf("Valid compiler flags (use upper case!) are:\n");
 printf("-Dsym       Define sym\n");
 printf("-Usym       Undefine sym\n");
 printf("-Idir1,dir2 Include Directorys to search, separated by ',' or ';'\n");
 printf("            (The compiler searches first in 'Include:')\n");
 printf("-P          Profiler (Currently non available)\n");
 printf("-L          'int' & 'unsigned' & function call parameters = 32 bit\n");
 printf("            (You MUST specify this flag when using CClib.library)\n");
 exit(EXIT_FAILURE);
}

/* ADDED BY TETISOFT */
int longflag;
int size_u = SIZE_U_DEF;
int size_i = SIZE_I_DEF;
/* The following structures are taken from other files. They contain the
 * sizes of int and unsigned and must be changed at runtime for the ability
 * to use 16 or 32 bit values. */
/* Taken from gsub.c */
char bsz[] = {
	SIZE_C,

/* ADDED BY TETISOFT */
	SIZE_S,

	SIZE_L, SIZE_L, SIZE_S, SIZE_U_DEF,
	SIZE_I_DEF, SIZE_C, SIZE_F, SIZE_D, 0
};
/* Taken from d2.c */
struct bt {
	char	*name;
	int	size;
	char	align;
} btbl[] = {
	{"Uchar",       SIZE_C, ALN_C},

/* ADDED BY TETISOFT */
	{"Ushort",	SIZE_S, ALN_S},

	{"Ulong",       SIZE_L, ALN_L},
	{"Long",        SIZE_L, ALN_L},
	{"Short",       SIZE_S, ALN_S},
	{"Uns",         SIZE_U_DEF, ALN_U},
	{"Int",         SIZE_I_DEF, ALN_I},
	{"Char",        SIZE_C, ALN_C},
	{"Float",       SIZE_F, ALN_F},
	{"Dbl",         SIZE_D, ALN_D},
	{"Void",        0},
};

int lineno;
int nmerrors;
int pflag = 0;			/* enable profiling */

#ifdef	DEBUG
int oflags[26];
int xflags[26];
static int anydebug;
#define debug oflags['z'-'a']
#endif

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

/* ADDED BY TETISOFT */
#ifdef FOR_AMIGA
char *outname;
int sawchip;
#endif

#if NEEDBUF
char my_ibuf[BUFSIZ];
#endif

#ifdef MINIX
#define strchr	index
#endif

NODEP cur;

#define MAXPREDEF	20

struct def {
	char *dname, *dval;
} defines[MAXPREDEF] = {
	{"MC68000"},
	{"mc68000"},
	{"SOZOBON"},
#ifdef FOR_AMIGA
	{"AMIGA"},
	{"AMIGADOS"},
#else
	{"ATARI_ST"},
#ifdef MINIX
	{"MINIX"},
#else
	{"TOS"},
#endif
#endif
};
static int npred = 5;

/* TETISOFT */
#ifndef FOR_AMIGA
char	tmpdir[128] = ".";		/* where the output goes */
#endif

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

	if (sizeof(NODE) & 3) {
		printf("sizeof NODE not mult of 4\n");
		exit(EXIT_FAILURE);
	}

/* ADDED BY TETISOFT */
#ifdef	FOR_AMIGA
	if (argc == 0)			/* Run from Workbench */
		exit(EXIT_FAILURE);	/* Maybe we have no output window */
#endif

	/*
	 * Parse the INCLUDE environment variable, if present.
	 */
	if ((p = getenv("INCLUDE")) != NULL)
		doincl(p);

/* CHANGED BY TETISOFT, was 0 */
	shownames = 1;

#ifndef FOR_AMIGA
	if (isatty(0)) {
#ifdef TOS
		write(1, "\33v", 2);
#endif
		setbuf(stdout, NULL);
	}
#endif
/* put author here */

/* TETISOFT */
	printf("%s%s", Version, Version2);

	while (argc-- > 1) {
		argv++;
		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 {

/* ADDED BY TETISOFT, allow only one source file, but output file */
#ifdef FOR_AMIGA
			if (argc > 2)
				usage();
			if (argc == 2) {
				outname = argv[1];
				if (outname[0] == '-')
					usage();
				i = strlen(outname);
				if (i >= 2 && outname[i-2] == '.' &&
				    outname[i-1] == 'c')
					usage();
			}
#endif

			if (argc > 1 || shownames) {
				shownames++;
				printf("%s:\n", argv[0]);
			}
			if (input != NULL)

/* ADDED BY TETISOFT */
#ifdef FOR_AMIGA
			    if (input != stdin)
#endif

				fclose(input);
			input = fopen(argv[0], ROPEN);
			if (input == NULL) {
				printf("Cant open %s\n", argv[0]);
				exit(EXIT_FAILURE);
			}
#if NEEDBUF
			setbuf(input, my_ibuf);
#endif
			inname = argv[0];
			dofile();

/* ADDED BY TETISOFT */
#ifdef FOR_AMIGA
			break;
#endif

		}
	}
	if (input == NULL) {

/* CHANGED BY TETISOFT */
#ifndef FOR_AMIGA
		input = stdin;
		output = stdout;
		inname = "<STDIN>";
		dofile();
#else
		usage();
#endif

	}
	exit(EXIT_SUCCESS);
}

adddef(s)
char *s;
{
	char *as, *strchr();

	if (npred >= MAXPREDEF) {
		warn("too many -D 's");
		return;
	}
	if ((as = strchr(s,'=')) != NULL)
		*as++ = 0;
	else
		as = NULL;
	defines[npred].dname = s;
	defines[npred].dval = as;
	npred++;
}

subdef(s)
char *s;
{
	int i;

	for (i=0; i<npred; i++)
		if (strcmp(s, defines[i].dname) == 0)
			goto found;
	return;
found:
	while (i < npred) {
		defines[i] = defines[i+1];
		i++;
	}
	npred--;
}

dodefs()
{
	int i;
	struct def *p;

	/*
	 * Define the "built-in" macros
	 */
	p = defines;
	for (i=0; i < npred; i++,p++)
		optdef(p->dname, p->dval ? p->dval : "1");
}

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

	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);
		c = dir[strlen(dir)-1];
#ifndef FOR_AMIGA
#if MINIX || UNIX
		if (c != '/')
			strcat(dir, "/");
#else
		if (c != '\\')
			strcat(dir, "\\");
#endif
#else
		if (c != '/' && c != ':')
			strcat(dir, "/");
#endif

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

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

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

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

	level = 0;
	program();
	dumpstrs(strsave);
#ifdef OUT_AZ
	xrefs();
#endif

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

/* ADDED BY TETISOFT */
#ifdef	FOR_AMIGA
	if (sawchip) {
		printf("Remember to call Top with the -c flag set\n");
		printf("to force your data & bss to CHIP memory...\n");
		exit(EXIT_WARN);
	}
#endif

}

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') {
			switch (c) {
			case 'D':
				adddef(s);
				return;
			case 'U':
				subdef(s);
				return;
			case 'I':
				doincl(s);
				return;
			case 'P':
				pflag = 1;
				continue;

/* COMMENTED OUT BY TETISOFT, Version always printed */
			/* case 'V':
			 *	printf("%s\n", Version);
			 *	continue;
			 */

/* TETISOFT: Output file specification */
#ifndef FOR_AMIGA

			case 'T':
#if MINIX || UNIX
				strcpy(tmpdir, s);
				if (tmpdir[strlen(tmpdir)-1] == '/')
					tmpdir[strlen(tmpdir)-1] = '\0';
#else
				if (tmpdir[strlen(tmpdir)-1] == '\\')
					tmpdir[strlen(tmpdir)-1] = '\0';
#endif
				return;

#endif	/* TETISOFT */

/* ADDED BY TETISOFT, use of 32 bit ints */
			case 'L':
				longflag = 1;
				size_u = SIZE_U_OPTL;
				size_i = SIZE_I_OPTL;
				bsz[5] = SIZE_U_OPTL;
				bsz[6] = SIZE_I_OPTL;
				btbl[5].size = SIZE_U_OPTL;
				btbl[6].size = SIZE_I_OPTL;
				continue;

			}
#ifdef	DEBUG
			xflags[c-'A']++;
			anydebug++;
#endif
		}
	}
}

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(EXIT_FAILURE);
}

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(EXIT_FAILURE);
}

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

static
optnl()
{
#ifdef	DEBUG
	if (anydebug)
		putchar('\n');
#endif
}

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
