/************************************************************************/
/*                                                                      */
/* Copyright (c) 1988 -                                                 */
/*     The Rector and Visitors of the University of Virginia            */
/*                                                                      */
/* This code may not be distributed further without permission          */
/*      from the University of Virginia.                                */
/* This code is distributed WITHOUT ANY WARRANTY.  No claims are        */
/*      made as to whether it serves any particular purpose or even     */
/*      works at all.                                                   */
/*                                                                      */
/************************************************************************/

/*
 * This file contains the code to read characters from a Pascal
 * source file.  It keeps track of line numbers, source file names, etc.
 * It also does #include processing.  Since many error messages complain
 * about the _previous_ input symbol, the scanner (scan.c) employs
 * a two-input-symbol buffer.  To support this, we need a a two-source-
 * line buffer here.  The scanner uses the macro nextch() (nextline.h),
 * which drives the whole input process.  nextch() returns the next
 * character in the current line buffer, and calls nextline() at the
 * end of a line.
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "types.h"
#include "nextline.h"
#include "error.h"

#define MAXINCLUDES 15

extern void exit();

struct {
	char *s_name;
	FILE *s_fp;
	int s_linenum;
}
	src_stack[MAXINCLUDES],   /* stack of current #included files */
	*src_file = src_stack
;

struct src_line
	src_linebuf[2],           /* two-source-line buffer */
	*src_curline = src_linebuf,
	*src_prevline = src_linebuf
;

struct src_sy
	src_sybuf[2],             /* two-input-symbol buffer */
	*src_cursy = src_sybuf,
	*src_prevsy = src_sybuf
;

int	src_comment = 0;

char *src_cp;

/*
 * getinclname parses the #include directive and returns the
 * included file's name.
 */

char *
getinclname()
{
	register char quote, *name, *cp;
	char *strsave();

	while (isspace(*++src_cp))
		;
	for (cp = "include"; *cp; cp++, src_cp++) {
		if (*cp != (isupper(*src_cp)? tolower(*src_cp) : *src_cp)) {
			return((char *)0);
		}
	}
	while (isspace(*src_cp))
		src_cp++;
	quote = '\0';
	if (*src_cp == '"' || *src_cp == '\'') {
		quote = *src_cp;
		name = ++src_cp;
		while (*src_cp && *src_cp != quote)
			src_cp++;
	}
	if (!quote || !*src_cp)
		return((char *)0);
	*src_cp = '\0';
	name = strsave(name);
	*src_cp = quote;
	return (name);
}

/*
 * src_set gets the name of the Pascal source that we are compiling
 */

src_set(fname)
	char *fname;
{
	src_file->s_name = fname;
	c_file(fname);
}

/*
 * src_linenum returns the line number of the current input symbol.
 * If the current file is being #included, return the line number
 * where the #include directive is located in the outermost file.
 */
int
src_linenum()
{
	return(src_stack->s_linenum);
}

/*
 * nextline() fills a line buffer with the next line of the current input
 * file, returning the first character of the line.  Upon encountering a
 * #include directive, nextline reads from the included file, saving
 * the previous file's input state on a stack.  At EOF nextline pops the
 * stack to resume reading the previous file.
 */

int
nextline()
{
	static FILE *input = (FILE *)0;
	char *inclfile;
	register char *cp, *limit;
	register int c;
	static int flip = -1;

	/* If we need a new file, open it and push it on the file stack */

	if (!input) {
		if (!(input = fopen(src_file->s_name, "r"))) {
			if (src_file == src_stack) {
				fprintf(stderr, err_open, src_file->s_name);
				putc('\n', stderr);
				exit(1);
			}
			sprintf(err_text, err_open, src_file->s_name);
			err_msg(E_FATAL, E_CURLINE);
			input = (--src_file)->s_fp;
			return(nextline());
		}
		src_file->s_fp = input;
		src_file->s_linenum = 0;
	}

	/* set up the current line buffer */

	if (src_prevsy->s_line == src_curline) {
		src_prevline = src_curline;
		src_curline += (flip = (-flip));  /* switch buffer */
	}
	src_curline->s_linenum = ++(src_file->s_linenum);
	src_curline->s_name = src_file->s_name;

	/* read an input line */

	cp = src_cp = src_curline->s_line;
	limit = cp + MAXLINE-2;		/* leave room for \n\0 */
	while ((c = getc(input)) != '\n' && c != EOF) {
		if (cp < limit)
			*cp++ = c;
	}
	if (cp >= limit) {
		strcpy(err_text, "syntax error:  line too long");
		err_msg(E_FATAL, E_CURLINE);
	}
	if (c == EOF && cp == src_cp) {
		if (src_file > src_stack) {
			input = (--src_file)->s_fp;		/* pop stack */
			return(nextline());
		}
		*src_cp++ = '\n';
		*src_cp = '\0';
		return(EOF);
	}
	*cp++ = '\n';
	*cp = '\0';

	/* if #include directive, store new pathname on file stack, recurse */

	if (*src_cp == '#' && !src_comment) {
		if (inclfile = getinclname()) {
			if (++src_file < src_stack + MAXINCLUDES) {
				src_file->s_name = inclfile;
				input = (FILE *)0;
				return(nextline());
			}
			strcpy(err_text, "#include nested too deeply");
			err_msg(E_INTERNAL, E_CURLINE);
		}
		strcpy(err_text, "syntax error:  illegal #include");
		err_msg(E_FATAL, E_CURLINE);
		return(nextline());
	}

	/* return 1st character of the new line */

	return(*src_cp++);
}
