/*
 * tc2null - read output from troffcvt and do nothing with it.
 *
 * Actually, it's not quite true that nothing is done: unrecognized
 * tokens are flagged, so tc2null in a way serves as a diagnostic
 * program.
 *
 * The main use of this "translator" is to serve as a starting point
 * for building troffcvt postprocessors.
 *
 * The table-handling machinery doesn't actually generate any output, but
 * it does extract table information from the input stream and keep track
 * of the current position within the table.  As such, this machinery
 * serves as an example for how other postprocessors might process tables.  
 * Some modifications will likely be necessary, of course.  See the exiting
 * postprocessors to get an idea of the kind of modifications that were
 * necessary for them.
 *
 * 20 Apr 92	Paul DuBois	dubois@primate.wisc.edu
 *
 * 20 Apr 92 V1.00
 * - Created.
 * 20 May 97
 * - Added tblcvt support.
 */

# ifdef	THINK_C
# include	<console.h>
# endif /* THINK_C */
# include	<stdio.h>
# include	<ctype.h>

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

# include	"tcread.h"
# include	"tctbl.h"


static void	ReadFile (void);
static void	ControlLine (void);
static void	PlainText (void);
static void	SpecialText (void);
static void	CenterFillAdjust (void);

static void	TableBegin (short argc, char **argv);
static void	TableEnd (void);
static void	TableColumnInfo (short argc, char **argv);
static void	TableRowBegin (short argc, char **argv);
static void	TableRowEnd (void);
static void	TableRowLine (short argc, char **argv);
static void	TableCellInfo (short argc, char **argv);
static void	TableCellBegin (short argc, char **argv);
static void	TableCellEnd (void);
static void	TableEmptyCell (void);
static void	TableSpannedCell (void);
static void	TableCellLine (short argc, char **argv);
static void	CellHSpanInit (void);


int	echo = 0;


/*
 * Per-table values
 */

static short	tblCols = 0;		/* number of columns in table */
static short	tblRows = 0;		/* number of rows in table */
static short	centerTbl= 0;		/* non-zero if table is centered */

/*
 * Per-column values
 */

static long	colWidth[tblMaxCol];	/* column width */
static long	colSep[tblMaxCol];	/* column separation */
static short	colEqWidth[tblMaxCol];	/* column is equal-width? */

/*
 * Per-cell values.  For the span stuff, the flag is set when in a span
 * (a set of > 1 cells that are merged together), the size is the number
 * of cells that the span comprises, and the idx is the index of the current
 * cell within the span (range is 0..size-1).
 */

static char	cellType[tblMaxCol];		/* cell type */
static short	cellVSpanFlag[tblMaxCol];	/* cell vspan flag */
static short	cellVSpanSize[tblMaxCol];	/* cell vspan extent */
static short	cellVSpanIdx[tblMaxCol];	/* cell vspan counter */
static short	cellHSpan[tblMaxCol];		/* cell hspan extent */
static short	cellHSpanFlag;			/* cell hspan flag */
static short	cellHSpanSize;			/* cell hspan extent */
static short	cellHSpanIdx;			/* cell hspan counter */
static char	cellVAdjust[tblMaxCol];		/* cell vertical adjust */
static short	cellBorder[tblMaxCol];		/* cell borders */

/*
 * Counters for keeping track of position within table.
 * tblColIdx counts horizontally during processing of \table-column-info
 * lines, during \table-cell-info lines, and while processing each row of
 * table data.  tblRowIdx counts rows of table data.
 */

static short	tblColIdx = 0;
static short	tblRowIdx = 0;


int
main (int argc, char *argv[])
{
	ETMInit (NULL);

# ifdef	THINK_C
	argc = ccommand (&argv);
# endif /* THINK_C */

	--argc;
	++argv;
	while (argc > 0 && argv[0][0] == '-')
	{
		if (strcmp (argv[0], "-E") == 0)
			echo = 1;
		else
			ETMPanic ("Unknown option: %s", argv[0]);
		--argc;
		++argv;
	}

	TCRInit ();

	if (argc == 0)		/* stdin */
		ReadFile ();
	else while (argc > 0)
	{
		if (freopen (argv[0], "r", stdin) == (FILE *) NULL)
			ETMMsg ("Cannot open: %s", argv[0]);
		else
			ReadFile ();
		--argc;
		++argv;
	}

	ETMEnd ();
	exit (0);
	/*NOTREACHED*/
}


static void
ReadFile (void)
{
int	i;

	while (TCRGetToken () != tcrEOF)
	{
		if (echo)
		{
			printf ("class %d maj %d min %d <%s>\n",
				tcrClass, tcrMajor, tcrMinor, tcrArgv[0]);
			if (tcrClass == tcrControl || tcrClass == tcrSText)
			{
				for (i = 1; i < tcrArgc; i++)
					printf ("\t<%s>\n", tcrArgv[i]);
			}
		}
		switch (tcrClass)
		{
		case tcrControl:	ControlLine (); break;
		case tcrText:		PlainText (); break;
		case tcrSText:		SpecialText (); break;
		default:	ETMPanic ("ReadFile: unknown class %d",
							tcrClass);
		}
	}
}


static void
ControlLine (void)
{
	switch (tcrMajor)
	{
	default:
		ETMMsg ("ControlLine: bad control major code: %d <%s>",
						tcrMajor, tcrArgv[0]);
		break;
	case tcrCUnknown:
		ETMMsg ("ControlLine: unknown control token: <%s>",
						tcrArgv[0]);
		break;
	case tcrCFA:
		CenterFillAdjust ();
		break;
	case tcrComment:
	case tcrSetupBegin:
	case tcrSetupEnd:
	case tcrPass:
	case tcrInputLine:
	case tcrOther:
	case tcrResolution:
	case tcrBreak:
	case tcrFont:
	case tcrPointSize:
	case tcrSpacing:
	case tcrLineSpacing:
	case tcrOffset:
	case tcrIndent:
	case tcrTempIndent:
	case tcrLineLength:
	case tcrPageLength:
	case tcrPageNumber:
	case tcrTitleLength:
	case tcrTitleBegin:
	case tcrTitleEnd:
	case tcrSpace:
	case tcrUnderline:
	case tcrCUnderline:
	case tcrNoUnderline:
	case tcrULineFont:
	case tcrBracketBegin:
	case tcrBracketEnd:
	case tcrBreakSpread:
	case tcrExtraSpace:
	case tcrLine:
	case tcrMark:
	case tcrMotion:
	case tcrBeginOverstrike:
	case tcrOverstrikeEnd:
	case tcrBeginPage:
	case tcrZeroWidth:
	case tcrSpaceSize:
	case tcrConstantWidth:
	case tcrNoConstantWidth:
	case tcrNeed:
	case tcrEmbolden:
	case tcrSEmbolden:
	case tcrResetTabs:
	case tcrFirstTab:
	case tcrNextTab:
	case tcrHyphenate:
	case tcrDiversionBegin:
	case tcrDiversionAppend:
	case tcrDiversionEnd:
	case tcrTabChar:
	case tcrLeaderChar:
		break;
	case tcrTableBegin:
		TableBegin (tcrArgc - 1, &tcrArgv[1]);
		break;
	case tcrTableEnd:
		TableEnd ();
		break;
	case tcrColumnInfo:
		TableColumnInfo (tcrArgc - 1, &tcrArgv[1]);
		break;
	case tcrRowBegin:
		TableRowBegin (tcrArgc - 1, &tcrArgv[1]);
		break;
	case tcrRowEnd:
		TableRowEnd ();
		break;
	case tcrRowLine:
		TableRowLine (tcrArgc - 1, &tcrArgv[1]);
		break;
	case tcrCellInfo:
		TableCellInfo (tcrArgc - 1, &tcrArgv[1]);
		break;
	case tcrCellBegin:
		TableCellBegin (tcrArgc - 1, &tcrArgv[1]);
		break;
	case tcrCellEnd:
		TableCellEnd ();
		break;
	case tcrCellLine:
		TableCellLine (tcrArgc - 1, &tcrArgv[1]);
		break;
	case tcrEmptyCell:
		TableEmptyCell ();
		break;
	case tcrSpannedCell:
		TableSpannedCell ();
		break;
	}
}


static void
PlainText (void)
{
	if (tcrMajor != '\n' && tcrMajor != '\r')
	{
		/* do something with character */
	}
}


/*
 * The switch below lists all the known builtin special characters.
 * Those that aren't builtin will have a major code of tcrSTUnknown;
 * this will include those characters listed in the actions file.
 * It may be just as easy to skip the switch and handle all special
 * characters uniformly, e.g., by looking up their name in a translation
 * table for an output sequence.
 */

static void
SpecialText (void)
{
	switch (tcrMajor)
	{
	default:
		printf ("bad special text major code: %d <%s>\n",
						tcrMajor, tcrArgv[0]);
		break;
	case tcrSTUnknown:
		/* handle non-builtin special characters here */
		break;
	case tcrBackslash:
	case tcrAtSign:
	case tcrLSglQuote:
	case tcrRSglQuote:
	case tcrLDblQuote:
	case tcrRDblQuote:
	case tcrZeroSpace:
	case tcrTwelfthSpace:
	case tcrSixthSpace:
	case tcrDigitSpace:
	case tcrHardSpace:
	case tcrMinus:
	case tcrGraveAccent:
	case tcrAcuteAccent:
	case tcrBackspace:
	case tcrOptHyphen:
	case tcrTab:
	case tcrLeader:
	case tcrFieldBegin:
	case tcrFieldEnd:
	case tcrFieldPad:
		break;
	}
}


static void
CenterFillAdjust (void)
{
	switch (tcrMinor)
	{
	default:
		ETMMsg ("CenterFillAdjust: bad minor code %d <%s>",
						tcrMinor, tcrArgv[0]);
		break;
	case tcrCenter:
	case tcrNofill:
	case tcrAdjFull:
	case tcrAdjLeft:
	case tcrAdjRight:
	case tcrAdjCenter:
		break;
	}
}


/*
 * Table element routines
 */

static void
TableBegin (short argc, char **argv)
{
	if (argc != 8)
		ETMPanic ("TableBegin: wrong number of args: %hd", argc);
	tblCols = StrToShort (argv[tblColsArg]);
	tblRows = StrToShort (argv[tblRowsArg]);

	if (tblCols == 0)
		ETMPanic ("TableBegin: zero column table");
	if (tblCols > tblMaxCol)
		ETMPanic ("TableBegin: too many table columns (%hd), max = %d",
						tblCols, tblMaxCol);

	/* determine whether or not to center the table */
	centerTbl = argv[tblAlignArg][0] == 'C'? 1 : 0;

	tblColIdx = 0;
	tblRowIdx = 0;
}


static void
TableEnd (void)
{
}


static void
TableColumnInfo (short argc, char **argv)
{
	if (argc != 3)
		ETMPanic ("TableColumnInfo: wrong number of args: %hd", argc);
	if (tblColIdx >= tblMaxCol)
		ETMPanic ("TableColumnInfo: logic error");

	/*
	 * For separation and width, 0 means "none specified".
	 */
	colSep[tblColIdx] = StrToLong (argv[colSepArg]);
	colWidth[tblColIdx] = StrToLong (argv[colWidthArg]);

	colEqWidth[tblColIdx] = (argv[colEqWidthArg][0] == 'y' ? 1 : 0);

	++tblColIdx;
}


/*
 * Begin a table row.
 */

static void
TableRowBegin (short argc, char **argv)
{
	/*
	 * Reset tblColIdx for use by \table-cell-info controls
	 */
	tblColIdx = 0;
}


static void
TableRowEnd (void)
{
	++tblRowIdx;	/* done with row, bump counter */
}


static void
TableRowLine (short argc, char **argv)
{
	++tblRowIdx;	/* done with row, bump counter */
}


static void
TableCellInfo (short argc, char **argv)
{
char	buf[bufSiz];
short	vspan;

	if (argc != 5)
		ETMPanic ("TableCellInfo: wrong number of args: %hd", argc);
	if (tblColIdx >= tblMaxCol)
		ETMPanic ("TableCellInfo: logic error");

	cellType[tblColIdx] = argv[cellTypeArg][0];

	vspan = StrToShort (argv[cellVSpanArg]);
	if (vspan == 1)			/* not part of a vspan */
	{
		cellVSpanFlag[tblColIdx] = 0;
	}
	else if (vspan > 1)		/* part of a vspan -- first cell */
	{
		cellVSpanFlag[tblColIdx] = 1;
		cellVSpanSize[tblColIdx] = vspan;
		cellVSpanIdx[tblColIdx] = 0;
	}
	else				/* part of a vspan -- not first cell */
	{
		++cellVSpanIdx[tblColIdx];
	}

	cellHSpan[tblColIdx] = StrToShort (argv[cellHSpanArg]);
	cellVAdjust[tblColIdx] = StrToShort (argv[cellVAdjustArg]);
	cellBorder[tblColIdx] = StrToShort (argv[cellBorderArg]);

	/*
	 * Bump cell index.
	 * Reset if we've reached the end, so it'll be correct for
	 * \table-cell-begin, \table-empty-cell, \table-spanned-cell,
	 * \table-cell-line.
	 */

	++tblColIdx;
	if (tblColIdx >= tblCols)
		tblColIdx = 0;
}


static void
TableCellBegin (short argc, char **argv)
{
	CellHSpanInit ();
}


static void
TableCellEnd (void)
{
	++tblColIdx;
}


static void
TableEmptyCell (void)
{
	CellHSpanInit ();
	++tblColIdx;
}


static void
TableSpannedCell (void)
{
	CellHSpanInit ();
	++tblColIdx;
}


static void
TableCellLine (short argc, char **argv)
{
	CellHSpanInit ();
	++tblColIdx;
}


static void
CellHSpanInit (void)
{
short	hspan;

	hspan = cellHSpan[tblColIdx];
	if (hspan == 1)			/* not part of a hspan */
	{
		cellHSpanFlag = 0;
	}
	else if (hspan > 1)		/* part of a hspan -- first cell */
	{
		cellHSpanFlag = 1;
		cellHSpanSize = hspan;
		cellHSpanIdx = 0;
	}
	else				/* part of a hspan -- not first cell */
	{
		++cellHSpanIdx;
	}
}
