/*
 * Probably should install a simpler scanner.
 *
 * reader.c -- Tokenizes input stream, which is assumed to have been
 *	produced by troffcvt.
 *
 * tcrArgv[] points into cstBuf or ptBuf and its elements should
 * be considered READ-ONLY.
 *
 * The reader turns off the tokenscan escaping mechanism, so any application
 * using both the reader and tokenscan routines itself should be aware of
 * that and modify/restore the scanner accordingly.
 */

#include	<stdio.h>
#include	<ctype.h>

#include	"etm.h"
#include	"memmgr.h"
#include	"tokenscan.h"

#include	"tcread.h"

# define	maxArg	10


static void	LookupInit (void);
static void	Lookup (char *s, int class);
static int	Hash (char *s);


static int	prevChar = '\n';


int	tcrClass;
int	tcrMajor;
int	tcrMinor;
char	*tcrArgv[maxArg];
int	tcrArgc = 0;

long	tcrLineNum = 0;

/*
 * Control and special text lines are read into this buffer, and
 * tcrArgv[] is set up to point at the words in the line.
 * ptBuf is used for single-char plain text tokens.
 */

static char	cstBuf[bufSiz];
static char	ptBuf[2];


/*
 * Initialize the reader.  This may be called multiple times,
 * to read multiple files.  Each one is assumed open on stdin.
 */

void
TCRInit (void)
{
TSScanner	scanner;
char		*scanEscape;

	tcrClass = tcrEOF;
	tcrArgc = 0;
	tcrArgv[0] = (char *) NULL;

	tcrLineNum = 0;

	/* initialize lookup tables */

	LookupInit ();

	/*
	 * Turn off backslash escape mechanism while parsing
	 * input.  Perhaps it should be restored later, but where?
	 * (There's no TCREnd() call.)
	 */

	TSGetScanner (&scanner);
	scanEscape = scanner.scanEscape;
	scanner.scanEscape = "";
	TSSetScanner (&scanner);
}


/*
 * Read input stream to find next token.  If stream is at
 * beginning of line (prevChar = '\n') look for control or special
 * text line, read the whole thing in one swoop and parse it apart
 * to set the tcrArgv[] vector.  Otherwise the character is just
 * plain text, which becomes its own token.
 */

int
TCRGetToken (void)
{
int	c;
char	*p;

	if ((c = getchar ()) == EOF)
		return (tcrClass = tcrEOF);

	if (prevChar != '\n' || (c != '\\' && c != '@'))	/* plain text */
	{
		ptBuf[0] = c;
		ptBuf[1] = '\0';
		tcrArgv[tcrArgc = 0] = ptBuf;
		tcrMajor = prevChar = c;
		return (tcrClass = tcrText);
	}

	/* control or special text */
	if (c == '\\')
		tcrClass = tcrControl;
	else
		tcrClass = tcrSText;

	cstBuf[0] = c;
	if (!TCRGetLine (cstBuf+1, (int) sizeof (cstBuf)-1, stdin))
		cstBuf[1] = '\0';
	prevChar = '\n';
	TSScanInit (cstBuf);
	tcrArgc = 0;
	while ((p = TSScan ()) != (char *) NULL)
	{
		/* \comment and \pass are special cases */
		if (tcrArgc == 0
			&& (strcmp (p, "\\comment") == 0
			    || strcmp (p, "\\pass") == 0))
		{
			tcrArgv[tcrArgc++] = p;
			tcrArgv[tcrArgc++] = TSGetScanPos ();
			break;
		}
		if (tcrArgc + 1 >= maxArg)
		{
			ETMMsg ("too many arguments on %s line", tcrArgv[0]);
			break;
		}
		tcrArgv[tcrArgc++] = p;
	}
	tcrArgv[tcrArgc] = (char *) NULL;
	if (tcrArgc == 0)
		ETMPanic ("trash line found");
	Lookup (tcrArgv[0], tcrClass);
	return (tcrClass);
}


/*
 * Read a line from a file, strip any cr, lf or crlf from the
 * end.  Return zero for EOF, non-zero otherwise.
 */

int
TCRGetLine (char *buf, int size, FILE *f)
{
int	len;

	if (fgets (buf, size, f) == (char *) NULL)
		return (0);
	if ((len = strlen (buf)) > 0 && buf[len-1] == '\n')
		buf[len-1] = '\0';
	if ((len = strlen (buf)) > 0 && buf[len-1] == '\r')
		buf[len-1] = '\0';
	return (1);
}


/* ---------------------------------------------------------------------- */

/*
 * Symbol lookup routines
 */


typedef struct TCRKey	TCRKey;

struct TCRKey
{
	int	major;	/* major number */
	int	minor;	/* minor number */
	char	*str;	/* symbol name */
	int	hash;	/* symbol name hash value */
};


/*
 * A minor number of -1 means the token has no minor number
 * (all valid minor numbers are >= 0).
 */


static TCRKey	tcrControlKey[] =
{
	tcrComment,	-1,		"comment",		0,
	tcrSetupBegin,	-1,		"setup-begin",		0,
	tcrSetupEnd,	-1,		"setup-end",		0,
	tcrResolution,	-1,		"resolution",		0,
	tcrPass,	-1,		"pass",			0,
	tcrInputLine,	-1,		"line",			0,
	tcrOther,	-1,		"other",		0,
	tcrBreak,	-1,		"break",		0,
	tcrFont,	-1,		"font",			0,
	tcrPointSize,	-1,		"point-size",		0,
	tcrSpacing,	-1,		"spacing",		0,
	tcrLineSpacing,	-1,		"line-spacing",		0,
	tcrOffset,	-1,		"offset",		0,
	tcrIndent,	-1,		"indent",		0,
	tcrTempIndent,	-1,		"temp-indent",		0,
	tcrLineLength,	-1,		"line-length",		0,
	tcrPageLength,	-1,		"page-length",		0,
	tcrPageNumber,	-1,		"page-number",		0,
	tcrSpace,	-1,		"space",		0,
	tcrCFA,		tcrCenter,	"center",		0,
	tcrCFA,		tcrNofill,	"nofill",		0,
	tcrCFA,		tcrAdjFull,	"adjust-full",		0,
	tcrCFA,		tcrAdjLeft,	"adjust-left",		0,
	tcrCFA,		tcrAdjRight,	"adjust-right",		0,
	tcrCFA,		tcrAdjCenter,	"adjust-center",	0,
	tcrUnderline,	-1,		"underline",		0,
	tcrCUnderline,	-1,		"cunderline",		0,
	tcrNoUnderline,	-1,		"nounderline",		0,
	tcrULineFont,	-1,		"underline-font",	0,
	tcrBreakSpread,	-1,		"break-spread",		0,
	tcrExtraSpace,	-1,		"extra-space",		0,
	tcrLine,	-1,		"line",			0,
	tcrMark,	-1,		"mark",			0,
	tcrMotion,	-1,		"motion",		0,
	tcrBracketBegin, -1,		"bracket-begin",	0,
	tcrBracketEnd,	-1,		"bracket-end",		0,
	tcrBeginOverstrike, -1,		"overstrike-begin",	0,
	tcrOverstrikeEnd, -1,		"overstrike-end",	0,
	tcrBeginPage,	-1,		"begin-page",		0,
	tcrZeroWidth,	-1,		"zero-width",		0,
	tcrSpaceSize,	-1,		"space-size",		0,
	tcrConstantWidth, -1,		"constant-width",	0,
	tcrNoConstantWidth, -1,		"noconstant-width",	0,
	tcrNeed,	-1,		"need",			0,
	tcrEmbolden,	-1,		"embolden",		0,
	tcrSEmbolden,	-1,		"embolden-special",	0,
	tcrResetTabs,	-1,		"reset-tabs",		0,
	tcrFirstTab,	-1,		"first-tab",		0,
	tcrNextTab,	-1,		"next-tab",		0,
	tcrHyphenate,	-1,		"hyphenate",		0,
	tcrDiversionBegin, -1,		"diversion-begin",	0,
	tcrDiversionEnd, -1,		"diversion-end",	0,
	tcrDiversionAppend, -1,		"diversion-append",	0,
	tcrTabChar,	-1,		"tab-char",		0,
	tcrLeaderChar,	-1,		"leader-char",		0,
	tcrTitleLength,	-1,		"title-length",		0,
	tcrTitleBegin,	-1,		"title-begin",		0,
	tcrTitleEnd,	-1,		"title-end",		0,
	tcrTableBegin,	-1,		"table-begin",		0,
	tcrTableEnd,	-1,		"table-end",		0,
	tcrColumnInfo,	-1,		"table-column-info",	0,
	tcrRowBegin,	-1,		"table-row-begin",	0,
	tcrRowEnd,	-1,		"table-row-end",	0,
	tcrRowLine,	-1,		"table-row-line",	0,
	tcrCellInfo,	-1,		"table-cell-info",	0,
	tcrCellBegin,	-1,		"table-cell-begin",	0,
	tcrCellEnd,	-1,		"table-cell-end",	0,
	tcrCellLine,	-1,		"table-cell-line",	0,
	tcrEmptyCell,	-1,		"table-empty-cell",	0,
	tcrSpannedCell,	-1,		"table-spanned-cell",	0,

	0,		-1,		(char *) NULL,		0
};


static TCRKey	tcrSTextKey[] =
{
	tcrBackslash,	-1,		"backslash",		0,
	tcrAtSign,	-1,		"at",			0,
	tcrLSglQuote,	-1,		"quoteleft",		0,
	tcrRSglQuote,	-1,		"quoteright",		0,
	tcrLDblQuote,	-1,		"quotedblleft",		0,
	tcrRDblQuote,	-1,		"quotedblright",	0,
	tcrZeroSpace,	-1,		"zerospace",		0,
	tcrTwelfthSpace, -1,		"twelfthspace",		0,
	tcrSixthSpace,	-1,		"sixthspace",		0,
	tcrDigitSpace,	-1,		"digitspace",		0,
	tcrHardSpace,	-1,		"hardspace",		0,
	tcrMinus,	-1,		"minus",		0,
	tcrGraveAccent,	-1,		"grave",		0,
	tcrAcuteAccent,	-1,		"acute",		0,
	tcrBackspace,	-1,		"backspace",		0,
	tcrOptHyphen,	-1,		"opthyphen",		0,
	tcrTab,		-1,		"tab",			0,
	tcrLeader,	-1,		"leader",		0,
	tcrFieldBegin,	-1,		"fieldbegin",		0,
	tcrFieldEnd,	-1,		"fieldend",		0,
	tcrFieldPad,	-1,		"fieldpad",		0,

	0,		-1,		(char *) NULL,		0
};


/*
 * Initialize lookup table hash values.  Only need to do this once.
 */

static void
LookupInit (void)
{
static int	inited = 0;
TCRKey	*rp;

	if (inited == 0)
	{
		for (rp = tcrControlKey; rp->str != (char *) NULL; rp++)
			rp->hash = Hash (rp->str);
		for (rp = tcrSTextKey; rp->str != (char *) NULL; rp++)
			rp->hash = Hash (rp->str);
		++inited;
	}
}


/*
 * Determine major and minor number of control token.  If it's
 * not found, the major number turns into tcr{C,ST}Unknown.
 */

static void
Lookup (char *s, int class)
{
TCRKey	*rp;
int	hash;

	++s;			/* skip over the leading \ character */
	hash = Hash (s);
	if (class == tcrControl)
	{
		rp = tcrControlKey;
		tcrMajor = tcrCUnknown;
	}
	else if (class == tcrSText)
	{
		rp = tcrSTextKey;
		tcrMajor = tcrSTUnknown;
	}
	else
		ETMPanic ("Lookup: class = %d", class);

	for (/* empty */; rp->str != (char *) NULL; rp++)
	{
		if (hash == rp->hash && strcmp (s, rp->str) == 0)
		{
			tcrMajor = rp->major;
			tcrMinor = rp->minor;
			break;
		}
	}
}


/*
 * Compute hash value of symbol
 */

static int
Hash (char *s)
{
char	c;
int	val = 0;

	while ((c = *s++) != '\0')
		val += (int) c;
	return (val);
}
