/*
** Small-C Compiler -- Part 2 -- Front End and Miscellaneous.
** Copyright 1982, 1983, 1985, 1988 J. E. Hendrix
** Copyright 1998 H T Walheim
** All rights reserved.
*/

#include <stdio.h>
#include "cc.h"

extern char
*symtab, *macn, *macq, *pline, *mline, optimize,
    alarm, *glbptr, *line, *lptr, *cptr, *cptr2, *cptr3,
    *locptr, msname[NAMESIZE], pause, quote[2];

extern int
*wq, ccode, ch, csp, eof, errflag, iflevel,
    input, input2, listfp, macptr, nch,
    nxtlab, op[16], opindex, opsize, output, pptr, skiplevel, *wqptr;

/********************** input functions **********************/

preprocess()
{
    int k;
    char c;
    if (ccode) {
	line = mline;
	ifline();
	if (eof)
	    return;
    } else {
	zinline();
	return;
    }
    pptr = -1;
    while (ch != NEWLINE && ch) {
	if (white()) {
	    keepch(' ');
	    while (white())
		gch();
	} else if (ch == '"') {
	    keepch(ch);
	    gch();
	    while (ch != '"' || (*(lptr - 1) == 92 && *(lptr - 2) != 92)) {
		if (ch == NULL) {
		    error("no quote");
		    break;
		}
		keepch(gch());
	    }
	    gch();
	    keepch('"');
	} else if (ch == 39) {
	    keepch(39);
	    gch();
	    while (ch != 39 || (*(lptr - 1) == 92 && *(lptr - 2) != 92)) {
		if (ch == NULL) {
		    error("no apostrophe");
		    break;
		}
		keepch(gch());
	    }
	    gch();
	    keepch(39);
	} else if (ch == '/' && nch == '*') {
	    bump(2);
	    while ((ch == '*' && nch == '/') == 0) {
		if (ch)
		    bump(1);
		else {
		    ifline();
		    if (eof)
			break;
		}
	    }
	    bump(2);
	} else if (ch == '/' && nch == '/') {
	    bump(2);
	    while (ch != NEWLINE) {
		if (ch)
		    bump(1);
		else {
		    if (eof)
			break;
		}
	    }
	    bump(1);
	} else if (an(ch)) {
	    k = 0;
	    while (an(ch) && k < NAMEMAX) {
		msname[k++] = ch;
		gch();
	    }
	    msname[k] = NULL;
	    if (search(msname, macn, NAMESIZE + 2, MACNEND, MACNBR, 0)) {
		k = getint(cptr + NAMESIZE, 2 /*INTSIZE*/);
		while (c = macq[k++])
		    keepch(c);
		while (an(ch))
		    gch();
	    } else {
		k = 0;
		while (c = msname[k++])
		    keepch(c);
	    }
	} else
	    keepch(gch());
    }
    if (pptr >= LINEMAX)
	error("line too long");
    keepch(NULL);
    line = pline;
    bump(0);
}

keepch(c)
char c;
{
    if (pptr < LINEMAX)
	pline[++pptr] = c;
}

ifline()
{
    while (1) {
	zinline();
	if (eof)
	    return;
	if (match("#ifdef")) {
	    ++iflevel;
	    if (skiplevel)
		continue;
	    symname(msname);
	    if (search(msname, macn, NAMESIZE + 2, MACNEND, MACNBR, 0) ==
		0)
		skiplevel = iflevel;
	    continue;
	}
	if (match("#ifndef")) {
	    ++iflevel;
	    if (skiplevel)
		continue;
	    symname(msname);
	    if (search(msname, macn, NAMESIZE + 2, MACNEND, MACNBR, 0))
		skiplevel = iflevel;
	    continue;
	}
	if (match("#else")) {
	    if (iflevel) {
		if (skiplevel == iflevel)
		    skiplevel = 0;
		else if (skiplevel == 0)
		    skiplevel = iflevel;
	    } else
		noiferr();
	    continue;
	}
	if (match("#endif")) {
	    if (iflevel) {
		if (skiplevel == iflevel)
		    skiplevel = 0;
		--iflevel;
	    } else
		noiferr();
	    continue;
	}
	if (skiplevel)
	    continue;
	if (ch == 0)
	    continue;
	break;
    }
}

zinline()
{				/* numerous revisions */
    int k, unit;

    if (input == EOF)
	openfile();
    if (eof)
	return;
    if ((unit = input2) == EOF)
	unit = input;
    if (fgets(line, LINEMAX, unit) == NULL) {
	fclose(unit);
	if (input2 != EOF)
	    input2 = EOF;
	else
	    input = EOF;
	*line = NULL;
    } else if (listfp) {
	if (listfp == output)
	    fputc(';', output);
	fputs(line, listfp);
    }
#ifdef __GNUC__
    else {
	fputc(';', output);
	fputs(line, output);
    }
#endif
    bump(0);
}

inbyte()
{
    while (ch == 0) {
	if (eof)
	    return 0;
	preprocess();
    }
    return gch();
}

/********************* scanning functions ********************/

/*
** test if next input string is legal symbol name
*/
symname(sname)
char *sname;
{
    int k;
    char c;
    blanks();
    if (alpha(ch) == 0)
	return (*sname = 0);
    k = 0;
    while (an(ch)) {
	sname[k] = gch();
	if (k < NAMEMAX)
	    ++k;
    }
    sname[k] = 0;
    return 1;
}

need(str)
char *str;
{
    if (match(str) == 0)
	error("missing token");
}

ns()
{
    if (match(";") == 0)
	error("no semicolon");
    else
	errflag = 0;
}

match(lit)
char *lit;
{
    int k;
    blanks();
    if (k = streq(lptr, lit)) {
	bump(k);
	return 1;
    }
    return 0;
}

streq(str1, str2)
char str1[], str2[];
{
    int k;
    k = 0;
    while (str2[k]) {
	if (str1[k] != str2[k])
	    return 0;
	++k;
    }
    return k;
}

amatch(lit, len)
char *lit;
int len;
{
    int k;
    blanks();
    if (k = astreq(lptr, lit, len)) {
	bump(k);
	return 1;
    }
    return 0;
}

astreq(str1, str2, len)
char str1[], str2[];
int len;
{
    int k;
    k = 0;
    while (k < len) {
	if (str1[k] != str2[k])
	    break;
	/*
	   ** must detect end of symbol table names terminated by
	   ** symbol length in binary
	 */
	if (str2[k] < ' ')
	    break;
	if (str1[k] < ' ')
	    break;
	++k;
    }
    if (an(str1[k]) || an(str2[k]))
	return 0;
    return k;
}

nextop(list)
char *list;
{
    char op[4];
    opindex = 0;
    blanks();
    while (1) {
	opsize = 0;
	while (*list > ' ')
	    op[opsize++] = *list++;
	op[opsize] = 0;
	if (opsize = streq(lptr, op))
	    if (*(lptr + opsize) != '=' &&
		*(lptr + opsize) != *(lptr + opsize - 1))
		return 1;
	if (*list) {
	    ++list;
	    ++opindex;
	} else
	    return 0;
    }
}

blanks()
{
    while (1) {
	while (ch) {
	    if (white())
		gch();
	    else
		return;
	}
	if (line == mline)
	    return;
	preprocess();
	if (eof)
	    break;
    }
}

white()
{
    return (*lptr <= ' ' && *lptr);
}

gch()
{
    int c;
    if (c = ch)
	bump(1);
    return c;
}

bump(n)
int n;
{
    if (n)
	lptr += n;
    else
	lptr = line;
    if (ch = nch = *lptr)
	nch = *(lptr + 1);
}

kill()
{
    *line = 0;
    bump(0);
}

skip()
{
    if (an(inbyte()))
	while (an(ch))
	    gch();
    else
	while (an(ch) == 0) {
	    if (ch == 0)
		break;
	    gch();
	}
    blanks();
}

endst()
{
    blanks();
    return (streq(lptr, ";") || ch == 0);
}

/*********** symbol table management functions ***********/

addsym(sname, id, type, size, value, lgpp, class)
char *sname, id, type;
int size, value, *lgpp, class;
{
    if (lgpp == &glbptr) {
	if (cptr2 = findglb(sname))
	    return cptr2;
	if (cptr == 0) {
	    error("global symbol table overflow");
	    return 0;
	}
    } else {
	if (locptr > (ENDLOC - SYMMAX)) {
	    error("local symbol table overflow");
	    exit(ERRCODE);
	}
	cptr = *lgpp;
    }
    cptr[IDENT] = id;
    cptr[TYPE] = type;
    cptr[CLASS] = class;
    putint(size, cptr + SIZE, INTSIZE);
    putint(value, cptr + OFFSET, INTSIZE);
    cptr3 = cptr2 = cptr + NAME;
    while (an(*sname))
	*cptr2++ = *sname++;

    if (lgpp == &locptr) {
	*cptr2 = cptr2 - cptr3;	/* set length */
	*lgpp = ++cptr2;
    }
    return cptr;
}

/*
** search for symbol match
** on return cptr points to slot found or empty slot
*/
search(sname, buf, len, end, max, off)
char *sname, *buf, *end;
int len, max, off;
{
    cptr = cptr2 = buf + ((hash(sname) % (max - 1)) * len);
    while (*cptr != NULL) {
	if (astreq(sname, cptr + off, NAMEMAX))
	    return 1;
	if ((cptr = cptr + len) >= end)
	    cptr = buf;
	if (cptr == cptr2)
	    return (cptr = 0);
    }
    return 0;
}

hash(sname)
char *sname;
{
    int i, c;
    i = 0;
    while (c = *sname++)
	i = (i << 1) + c;
    return i;
}

findglb(sname)
char *sname;
{
    if (search(sname, STARTGLB, SYMMAX, ENDGLB, NUMGLBS, NAME))
	return cptr;
    return 0;
}

findloc(sname)
char *sname;
{
    cptr = locptr - 1;		/* search backward for block locals */
    while (cptr > STARTLOC) {
	cptr = cptr - *cptr;
	if (astreq(sname, cptr, NAMEMAX))
	    return (cptr - NAME);
	cptr = cptr - NAME - 1;
    }
    return 0;
}

nextsym(entry)
char *entry;
{
    entry = entry + NAME;
    while (*entry++ >= ' ');	/* find length byte */
    return entry;
}

/******** while queue management functions *********/

addwhile(ptr)
int ptr[];
{
    int k;
    ptr[WQSP] = csp;		/* and stk ptr */
    ptr[WQLOOP] = getlabel();	/* and looping label */
    ptr[WQEXIT] = getlabel();	/* and exit label */
    if (wqptr == WQMAX) {
	error("control statement nesting limit");
	exit(ERRCODE);
    }
    k = 0;
    while (k < WQSIZ)
	*wqptr++ = ptr[k++];
}

readwhile(ptr)
int *ptr;
{
    if (ptr <= wq) {
	error("out of context");
	return 0;
    } else
	return (ptr - WQSIZ);
}

delwhile()
{
    if (wqptr > wq)
	wqptr -= WQSIZ;
}

/****************** utility functions ********************/

/*
** test if c is alphabetic
*/
alpha(c)
char c;
{
    return (isalpha(c) || c == '_');
}

/*
** test if given character is alphanumeric
*/
an(c)
char c;
{
    return (alpha(c) || isdigit(c));
}

/*
** return next avail internal label number
*/
getlabel()
{
    return (nxtlab++);
}

/*
** get integer of length len from address addr
** (byte sequence set by "putint")
*/
getint(addr, len)
char *addr;
int len;
{
    int i;
    i = *(addr + --len);	/* high order byte sign extended */
    while (len--)
	i = (i << 8) | *(addr + len) & 255;
    return i;
}

/*
** put integer i of length len into address addr
** (low byte first)
*/
putint(i, addr, len)
char *addr;
int i, len;
{
    while (len--) {
	*addr++ = i;
	i = i >> 8;
    }
}

lout(line, fd)
char *line;
int fd;
{
    fputs(line, fd);
    fputc(NEWLINE, fd);
}

/******************* error functions *********************/

illname()
{
    error("illegal symbol");
    skip();
}

multidef(sname)
char *sname;
{
    error("already defined");
}

needlval()
{
    error("must be lvalue");
}

noiferr()
{
    error("no matching #if...");
    errflag = 0;
}

error(msg)
char msg[];
{
    if (errflag)
	return;
    else
	errflag = 1;

    lout(line, stderr);
    errout(msg, stderr);
    if (alarm)
	fputc(7, stderr);
    if (pause)
	while (fgetc(stderr) != NEWLINE);
    if (listfp > 0)
	errout(msg, listfp);
}

errout(msg, fp)
char msg[];
int fp;
{
    int k;
    k = line + 2;
    while (k++ <= lptr)
	fputc(' ', fp);
    lout("/\\", fp);
    fputs("**** ", fp);
    lout(msg, fp);
}
