/********************************************************/
/*							*/
/*		Q/C Compiler Version 3.2		*/
/*			(Part 8)			*/
/*							*/
/*     Copyright (c) 1984 Quality Computer Systems	*/
/*							*/
/*			11/12/84			*/
/********************************************************/

#include "stdio.h"
#include "cstddef.h"
#include "cglbdecl.c"


/* This section handles I/O and preprocessor commands */

/* Get the next input file */
getinfil()
{
    FILE           *fopen();
    char           *strcpy();

    if (ninfils--)
    {				/* any more input files? */
	getfilename(*nextfil++, infil, "C");
	if ((input = fopen(infil, "r")) == NULL)
	    cantopen(infil);
	setbuf(input, inbuf);
#ifndef PORTABLE
	setbsize(input, iobufsize);
#endif
	lineno = 0;
	if (verbose)
	    qprintf("*** Read    %s\n", infil);
    } else
	eof = TRUE;
}

/* Open output file */
getoutfil()
{
    FILE           *fopen();

    if (output == stderr)	/* output to console */
	return;
    if ((output = fopen(outfil, "w")) == NULL)
	cantopen(outfil);
    setbuf(output, outbuf);
#ifndef PORTABLE
    setbsize(output, iobufsize);
#endif
    header();
}

/* Close the output file */
closeout()
{
    trailer();
    if (output != stderr && fclose(output) == EOF)
	out_err();
}

out_err()
{
    puts("Can't close output file");
    exit(1);
}

/* Get file name, optionally append extension */
getfilename(s, name, ext)
    char           *s, *name, *ext;
{
    register        i;
    register char  *p;
    char           *index();

    p = name;
    i = 0;
    while (*s && ++i <= FILNAMSIZE)
	*p++ = *s++;
    *p = '\0';
    if (index(name, '.') == NULL && ++i < FILNAMSIZE)
    {
	*p++ = '.';		/* no extension given */
	while (*ext && ++i <= FILNAMSIZE)	/* use .EXT */
	    *p++ = *ext++;
	*p = '\0';
    }
}

/* Get the next line of input */
inline()
{
    register        c;

    chkabort();
    for (;;)
    {				/* find a non-empty line or EOF */
	if (eof)
	    return;
	kill_line();		/* clear the input line */
	++lineno;
	while ((c = getc(input)) != EOF)
	{
	    if (c == '\n' || lptr - line >= LINEMAX)
		break;
	    *lptr++ = c;
	}
	*lptr = '\0';
	if (lptr != line)
	{			/* anything on this line? */
	    if (fullist && cmode)
	    {
		textline = TRUE;
		comment();	/* print C text */
		outstr(line);
		nl();
		textline = FALSE;
	    }
	    lptr = line;	/* reset line to beginning */
	    return;
	} else if (c == EOF)
	{
	    fclose(input);
	    getinfil();		/* get the next input file */
	}
    }
}


/* Write a character to the requested output location */
outbyte(c)
    register        c;
{
    testflag =
	    testdone = FALSE;	/* turn off when anything written */
    if (peepflag && !textline)	/* is a peephole pattern pending? */
	dumpeep();		/* dump it out, this is not part of it */
    if (genflag || textline)
    {
	midline = (c != '\n');
	if (putc(c, output) == EOF)
	    out_err();
    }
}



/* Output a string */

outstr(p)
    char           *p;
{
    chkabort();			/* did user type ^C? */
    testflag =
	    testdone = FALSE;	/* turn off when anything written */
    if (peepflag && !textline)	/* is a peephole pattern pending? */
	dumpeep();		/* dump it out, this is not part of it */
    if (genflag || textline)
    {
	midline = TRUE;
	fputs(p, output);
	if (ferror(output))
	    out_err();
    }
}


/* Start a new line if we're not at the beginning */
newline()
{
    if (midline)
	nl();
}

/* Print a string with a leading TAB */
ot(s)
    char           *s;
{
    outbyte('\t');
    outstr(s);
}

/* Print a line with a leading TAB */
ol(s)
    char           *s;
{
    ot(s);
    nl();
}

nl()
{
    outbyte('\n');
}

/* Print an erroneous line with a pointer (^) to	*/
/*	the location where the error was discovered	*/
/*	and a message describing the error		*/
error(mesg)
    char           *mesg;
{
    errfile();
    qprintf("%s\n", mesg);
    errptr();
    errpause();			/* time to pause for full screen? */
}

errname(mesg, name)
    char           *mesg, *name;
{
    errfile();
    qprintf("%s: %s\n", mesg, name);
    errptr();
    errpause();
}

/* Print the name of the file which contains the error */
errfile()
{
    qprintf("<%s> @ %d: ", infil, lineno);
}

/* Print the input line with a pointer (^) under the error */
errptr()
{
    register char  *p;

    p = line;
    if (*p == '\0')
	return;			/* empty line or EOF */
    qprintf("%s\n", p);		/* print erroneous line */
    while (p < lptr)		/* get to error location */
	putchar((*p++ == '\t') ? '\t' : ' ');
    puts("^");			/* print error pointer ^ */
}

/* If screen is full, pause for keyboard response */
errpause()
{
    ++errcnt;
    if ((errcnt % pausecnt == 0) || verbose)
    {
	qprintf("Press any key to continue or ^C to abort:");
	getchar();
	/*********************************************
	 * This should be here in original instead of
	 * above line:
	 *
	 * while (chkabort() == EOF) ;
	 *********************************************/
	/* wait for response */

	putchar('\n');
    }
}

/* If a ^C was typed abort the run */
chkabort()
{
    static char     mesg[] = "\nCompilation aborted";
    int             c;

#ifndef PORTABLE

    if ((c = getkey()) == 0x3)
    {
	outstr(mesg);
	puts(mesg);
	exit(1);
    }
    return c;

#else
    return (0);
#endif

}

/*	Common error messages		*/
matcherr()
{
    error("Must match a #if command");
}

scerr()
{
    error("Invalid storage class");
}

typerr()
{
    error("Invalid type");
}

illname()
{
    error("Illegal symbol name");
    junk();
}

notavail(feature)
    char           *feature;
{
    errname("Not implemented", feature);
}

multidef(name)
    char           *name;
{
    errname("Already defined", name);
}

tagerr(tag)
    char           *tag;
{
    errname("Undeclared tag", tag);
}

needpunc(c)
{
    static char     s[2];

    if (!matchc(s[0] = c))
	errname("Missing punctuation -- assumed present", s);
}

keyerr(keyword)
    char           *keyword;
{
    errname("Illegal use of a keyword", keyword);
}

sizerr()
{
    error("Need explicit array size");
}

initerr()
{
    error("Initializer must be constant expression");
}

cexperr()
{
    error("Must be constant expression");
}

/* Test parse line to see if lit matches */
/*	the next sequence of characters */
/*	and skip over them if it does	*/
match(lit)
    char           *lit;
{
    register        k;

    blanks();			/* skip white space */
    if (k = streq(lptr, lit))
    {
	lptr += k;
	return TRUE;
    }
    return FALSE;
}

/* Test parse line to see if c matches */
/*	the next character and skip it */
matchc(c)
{
    blanks();			/* skip white space */
    if (*lptr == c)
    {
	++lptr;
	return TRUE;
    }
    return FALSE;
}

/* Test parse line to see if 'c' is the next char */
nextc(c)
{
    blanks();
    return (*lptr == c);
}

/* Test parse line to see if lit matches */
/*	the next token and skip over it */
/*	if a match is found		*/
amatch(lit)
    char           *lit;
{
    blanks();
    if (chks(lit))
    {
	skip();
	return TRUE;
    }
    return FALSE;
}

/* Skip white space in parse line */
blanks()
{
    register        c;

    for (;;)
    {
	if (isspace(c = ch()))
	    ++lptr;
	else if (c == '\0')
	{			/* at eol? */
	    if (stopeol)
		return;
	    inline();		/* get next line */
	    if (eof)
		return;
	    preprocess();
	} else
	    return;
    }
}



#ifndef PORTABLE
#include "cc8asm.c"
#else

/* Match string against next token in parsing buffer */
chks(s)
    char           *s;
{
    return (astreq(lptr, s, strlen(s)));
}

/* Test two strings for equality up to	*/
/*	the end of the second string	*/
streq(s1, s2)
    char           *s1, *s2;
{
    register        len;

    len = 0;
    while (*s2)
    {
	if (*s1++ != *s2++)
	    return 0;
	++len;
    }
    return len;
}

/* Test two strings for equality of	*/
/*	contents up to len (<256)	*/
astreq(s1, s2, len)
    char           *s1, *s2;
{
    register        cnt;

    for (cnt = 0; cnt < len; ++cnt)
    {
	if (*s2 == '\0')
	    break;
	if (*s1++ != *s2++)
	    return 0;
    }
    return (isletnum(*s1)) ? 0 : cnt;
}

/* Test for valid C symbol name characters */
isletnum(c)
{
    return (isletter(c) || isdigit(c));
}

isletter(c)
{
    return (isalpha(c) || c == '_');
}


/* Skip token in input line */
skip()
{
    while (isletnum(*lptr))	/* skip all a/n chars */
	++lptr;
}




#endif


/* Throw away garbage in input line */
junk()
{
    while (!isletnum(ch()))
    {
	gch();			/* flush non-a/n characters */
	if (endst())		/* up to EOL or ';' */
	    break;
    }
}

/* Test for end of statement */
endst()
{
    blanks();			/* skip white space */
    return (ch() == ';' || ch() == '\0');
}

/* Look at current character in parsing buffer */
/* Possibly replaced by macro. */
#ifndef ch
ch()
{
    return *lptr;
}

#endif

/* Look at next character in parsing buffer */
nch()
{
    return (*lptr ? *(lptr + 1) : 0);
}

/* Get current character in parsing buffer */
gch()
{
    return (*lptr ? *lptr++ : 0);
}

/* Put a character back in the parsing buffer */
putback(c)
{
    if (lptr > line)
	*--lptr = c;
}

/* Clear current line from parsing buffer */
kill_line()
{
    lptr = line;
    *lptr = '\0';
}

/* Check for #preprocessor commands */
preprocess()
{
    int             val;

    if (!matchc('#'))
    {				/* if it's not a #command... */
	lptr = line;		/* start at beginning of line */
    } else
    {				/* do a #preprocessor command */
	stopeol = TRUE;		/* look only on this line */
	if (amatch("asm"))
	    doasm();		/* copy assembler thru */
	else
	{
	    amatch("line");	/* optional */
	    if (!decimal(&val))	/* new line number? */
		error("#line number must be decimal");
	    else
	    {
		lineno = val;
		if (!endst())	/* optional filename? */
		    getfilename(lptr, infil, "C");
	    }
	}
	stopeol = FALSE;	/* don't stop at end-of-line */
	kill_line();
    }
}


/* Output an integer in decimal */
outdec(n)
{
    char           *itob(), s[7];

    outstr(itob(n, s, -10));
}

/* end of CC8.C */
