about summary refs log tree commit diff stats
path: root/src/TRSTable.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/TRSTable.c')
-rw-r--r--src/TRSTable.c1335
1 files changed, 1335 insertions, 0 deletions
diff --git a/src/TRSTable.c b/src/TRSTable.c
new file mode 100644
index 00000000..3545fdb8
--- /dev/null
+++ b/src/TRSTable.c
@@ -0,0 +1,1335 @@
+/*		Simple table object
+**		===================
+** Authors
+**	KW	Klaus Weide <kweide@enteract.com>
+** History:
+**   2 Jul 1999	KW	Created.
+*/
+
+#include <HTUtils.h>
+#include <HTStyle.h>		/* for HT_LEFT, HT_CENTER, HT_RIGHT */
+#include <LYCurses.h>
+#include <TRSTable.h>
+
+#include <LYLeaks.h>
+
+#define CELLS_GROWBY 2
+#define ROWS_GROWBY 2
+#define MAX_STBL_POS (LYcols-1)
+
+typedef enum {
+    CS_invalid = -1,
+    CS_new     =  0,
+    CS__0,			/* new, at BOL */
+    CS__0eb,			/* starts at BOL, empty, break */
+    CS__eb,			/* empty, break */
+    CS__0cb,			/* starts at BOL, content, break */
+    CS__cb,			/* content, break */
+    CS__0f,			/* starts at BOL, finished */
+    CS__ef,			/* empty, finished */
+    CS__0cf,			/* starts at BOL, content, finished */
+    CS__cf,			/* content, finished */
+    CS__ebc,			/* empty, break, more content */
+    CS__cbc			/* content, break, more content */
+} cellstate_t;
+
+typedef struct _STable_states {
+    cellstate_t	prev_state;
+    cellstate_t	state;
+    int		lineno;		/* last line no. looked at */
+    int		icell_core;	/* first/best 'core' cell in row so far */
+    int		x_td;		/* x pos of currently open cell or -1 */
+    int		pending_len;	/* if state is CS__0?[ec]b (??) */
+} STable_states;
+
+
+typedef struct _STable_cellinfo {
+	int	Line;		/* lineno in doc (zero-based) */
+	int	pos;		/* column where cell starts */
+	int	len;		/* number of character positions */
+	int	colspan;	/* number of columns to span */
+	short	alignment;	/* one of HT_LEFT, HT_CENTER, HT_RIGHT */
+} STable_cellinfo;
+
+typedef struct _STable_rowinfo {
+	int	Line;		/* lineno in doc (zero-based) */
+	int	ncells;		/* number of table cells */
+/*	int	pending_skip;*/	/* skip this many after finishing open cell */
+	BOOL	fixed_line;	/* if we have a 'core' line of cells */
+	int	allocated;	/* number of table cells allocated */
+	STable_cellinfo * cells;
+	short	alignment;	/* global align attribute for this row */
+} STable_rowinfo;
+
+struct _STable_info {
+	int	startline;	/* lineno where table starts (zero-based) */
+	int	nrows;		/* number of rows */
+	int	ncols;		/* number of rows */
+	int	maxlen;		/* sum of max. cell lengths of any row */
+	int	maxpos;		/* max. of max. cell pos's of any row */
+	int	allocated_rows; /* number of rows allocated */
+	int	allocated_sumcols;	/* number of sumcols allocated */
+	STable_cellinfo * sumcols; /* for summary (max len/pos) col info */
+	STable_rowinfo * rows;
+	short	alignment;	/* global align attribute for this table */
+	STable_states s;
+};
+
+/*
+**  Functions and structures in this source file keep track of positions.
+**  They don't know about the character data in those lines, or about
+**  the HText and HTLine structures.  GridText.c doesn't know about our
+**  structures.  It should stay that way.
+**
+**  The basic idea: we let the code in HTML.c/GridText.c produce and format
+**  output "as usual", i.e. as without Simple Table support.  We keep track
+**  of the positions in the generated output where cells and rows start (or
+**  end).  If all goes well, that preliminary output (stored in HText/HTLine
+**  structures) can be fixed up when the TABLE end tag is processed, by just
+**  inserting spaces in the right places (and possibly changing alignment).
+**  If all goes not well, we already have a safe fallback.
+**
+**  Note that positions passed to and from these functions should be
+**  in terms of screen positions, not just byte counts in a HTLine.data
+**  (cf. line->data vs. HText_TrueLineSize).
+**
+**  Memory is allocated dynamically, so we can have tables of arbitrary
+**  length.  On allocation error we just return and error indication
+**  instead of outofmem(), so caller can give up table tracking and maybe
+**  recover memory.
+**
+**  Implemented:
+**  - ALIGN={left,right,center,justify} applied to individual table cells
+**    ("justify" is treated as "left")
+**  - Inheritance of horizontal alignment according to HTML 4.0 (with the
+**    exception of COLGROUP/COL)
+**  - COLSPAN >1 (nearly untested)
+**  - Line breaks at start of first cell or at end of last cell are treated
+**    as if they were not part of the cell and row.  This allows us to
+**    cooperate with one way in which tables have been made friendly to
+**    browsers without any table support.
+**  Missing, but can be added:
+**  - Support for COLGROUP/COL
+**  - ROWSPAN >1 (reserving cells in following rows)
+**  - Tables wider than display.  The limitation is not here but in GridText.c
+**    etc.  If horizontal scrolling were implemented there, the mechanisms
+**    here coudl deal with wide tables (just change MAX_STBL_POS code).
+**  Missing, unlikely to add:
+**  - Support for non-LTR directionality.  A general problem, support is
+**    lacking throughout the lynx code.
+**  - Support for most other table-related attributes.  Most of them are
+**    for decorative purposes.
+**  Impossible or very unlikely (because it doesn't fit the model):
+**  - Any cell contents of more than one line, line breaks within cells.
+**    Anything that requires handling cell contents as paragraphs (block
+**    elements), like reflowing.  Vertical alignment.
+*/
+PRIVATE int Stbl_finishCellInRow PARAMS((
+    STable_rowinfo *	me,
+    STable_states *	s,
+    BOOL		certain,
+    int			lineno,
+    int			pos));
+PRIVATE int Stbl_finishRowInTable PARAMS((
+    STable_info *	me));
+
+
+PUBLIC struct _STable_info * Stbl_startTABLE ARGS1(
+    short,		alignment)
+{
+    STable_info *me = (STable_info *) calloc(1, sizeof(STable_info));
+    if (me) {
+	me->alignment = alignment;
+	me->s.x_td = -1;
+	me->s.icell_core = -1;
+    }
+    return me;
+}
+
+PRIVATE void free_rowinfo ARGS1(
+    STable_rowinfo *,	me)
+{
+    if (me && me->allocated) {
+	FREE(me->cells);
+    }
+}
+
+PUBLIC void Stbl_free ARGS1(
+    STable_info *,	me)
+{
+    if (me && me->allocated_rows && me->rows) {
+	int i;
+	for (i = 0; i <= me->nrows; i++)
+	    free_rowinfo(me->rows + i);
+	free(me->rows);
+    }
+    if (me)
+	FREE(me->sumcols);
+    FREE(me);
+}
+
+/*
+ * Returns -1 on error, otherwise index of just-added table cell.
+ */
+PRIVATE int Stbl_addCellToRow ARGS7(
+    STable_rowinfo *,	me,
+    STable_states *,	s,
+    int,		colspan,
+    short,		alignment,
+    BOOL,		isheader,
+    int,		lineno,
+    int *,		ppos)
+{
+    STable_cellinfo *cells;
+    int i;
+    int last_colspan = me->ncells ?
+	me->cells[me->ncells - 1].colspan : 1;
+    cellstate_t newstate;
+
+    if (me->ncells == 0)
+	s->prev_state = CS_invalid;
+    else if (s->prev_state == CS_invalid ||
+	     (/*s->state != CS_new && */ s->state != CS__0 &&
+	      s->state != CS__ef && s->state != CS__0f))
+	s->prev_state = s->state;
+
+    if (me->ncells == 0 || *ppos == 0)
+	newstate = CS__0;
+    else
+	newstate = CS_new;
+
+    if (me->ncells > 0 && s->pending_len > 0) {
+	if (s->prev_state != CS__cbc)
+	    me->cells[me->ncells - 1].len = s->pending_len;
+	s->pending_len = 0;
+    }
+    s->x_td = *ppos;
+
+    if (lineno != s->lineno) {
+	if (!me->fixed_line) {
+	    if (me->ncells == 0 || *ppos == 0) {
+		switch (s->prev_state) {
+		case CS_invalid:
+		case CS__0:
+		case CS__0eb:
+		case CS__0cb:
+		case CS__0f:
+		case CS__0cf:
+		    if (me->ncells > 0)
+			for (i = me->ncells + last_colspan - 2;
+			     i >= me->ncells - 1; i--) {
+			    me->cells[i].pos = *ppos;
+			    me->cells[i].Line = lineno;
+			}
+		    me->Line = lineno;
+		    /* s->lineno = lineno; */
+		    break;
+		case CS_new:
+		case CS__eb:
+		case CS__ef:
+		case CS__cf:
+		default:
+		    break;
+		case CS__cb:
+		    *ppos = me->cells[me->ncells - 1].pos +
+			me->cells[me->ncells - 1].len;
+		}
+	    } else {
+		switch (s->prev_state) {
+		case CS__0:
+		case CS__0eb:
+		case CS__0f:
+		    break;
+		case CS__cb:
+		    return -1;
+		case CS__cf:
+/*		    HTAlert("foo woo!!"); */
+		    return -1;
+		case CS__0cb:
+		case CS__0cf:
+		    if (*ppos > me->cells[0].pos)
+			me->Line = lineno;
+		    me->fixed_line = YES;
+		    break;
+		case CS_new:
+		case CS__eb:
+		case CS__ef:
+		default:
+		    me->fixed_line = YES;
+		    break;
+		case CS__cbc:
+		    return -1;
+		}
+	    }
+	}
+	if (me->fixed_line && lineno != me->Line) {
+	    switch (s->prev_state) {
+	    case CS__cb:
+	    case CS__cf:
+		if (*ppos > 0)
+		    return -1;
+		else
+		    *ppos = me->cells[me->ncells - 1].pos /* == 0 */ +
+			me->cells[me->ncells - 1].len;
+		break;
+	    case CS__0cf:
+	    case CS__0cb:
+		if (*ppos == 0 || *ppos <= me->cells[0].pos)
+		    *ppos = me->cells[me->ncells - 1].pos /* == 0 */ +
+			me->cells[me->ncells - 1].len;
+		break;
+	    case CS__0:
+	    case CS__0f:
+	    case CS__0eb:
+/*		me->Line = lineno; */
+		break;
+	    case CS_new:
+	    case CS__eb:
+	    case CS__ef:
+	    default:
+		*ppos = me->cells[me->ncells - 1].pos;	break;
+	    case CS__cbc:
+/*		*ppos = me->cells[me->ncells - 1].pos +
+		    me->cells[me->ncells - 1].len;
+		if (*ppos > 0)
+		    return -1; */
+		break;
+	    case CS_invalid:
+		break;
+	    }
+	}
+	s->lineno = lineno;
+    } else {			/* lineno == s->lineno: */
+	switch (s->prev_state) {
+	case CS_invalid:
+	case CS__0:
+	case CS__0eb:		/* cannot happen */
+	case CS__0cb:		/* cannot happen */
+	case CS__0f:
+	case CS__0cf:		/* ##302?? set icell_core? or only in finish? */
+	    break;
+	case CS__eb:		/* cannot happen */
+	case CS__cb:		/* cannot happen */
+	case CS__ef:
+	    break;
+	case CS__ebc:		/* should have done smth in finish */
+	case CS__cbc:		/* should have done smth in finish */
+	    break;
+	case CS_new:
+	case CS__cf:
+	    if (me->fixed_line && me->Line != lineno) {
+		return -1;
+	    } else {
+		me->fixed_line = YES;
+		me->Line = lineno;
+	    }
+	}
+    }
+
+#if 0				/* MEGA_COMMENTOUT */
+    if (lineno != me->Line) {
+	if (!me->fixed_line) {
+	    if (me->ncells == 0 ||
+		(*ppos == 0 && me->cells[me->ncells - 1].pos == 0)) {
+		if (me->ncells > 0)
+		    for (i = me->ncells + last_colspan - 2;
+			 i >= 0; i--) {
+			me->cells[i].pos = *ppos;
+			me->cells[i].Line = lineno;
+		    }
+		me->Line = lineno;
+		s->state = CS__0;
+	    }
+	    if (*ppos > 0 && me->ncells > 0 &&
+		(me->cells[me->ncells - 1].pos > 0 ||
+		 me->cells[me->ncells - 1].len > 0)) {
+		me->fixed_line = YES;
+
+	    }
+	}
+	if (me->fixed_line && lineno != me->Line) {
+	    if (me->cells[me->ncells - 1].pos > 0 &&
+		me->cells[me->ncells - 1].len > 0) {
+		return -1;
+	    } else if (me->cells[me->ncells - 1].pos == 0 &&
+		       me->cells[me->ncells - 1].len > 0) {
+		if (*ppos > 0 && *ppos > me->cells[0].pos)
+		    me->Line = lineno;
+		else
+		    *ppos = me->cells[me->ncells - 1].pos; /* == 0 */
+	    } else /* if (me->cells[me->ncells - 1].pos == 0 &&
+		       me->cells[me->ncells - 1].len <= 0) {
+		me->Line = lineno;
+	    } else */ {
+		*ppos = me->cells[me->ncells - 1].pos;
+	    }
+	}
+#if 0
+	for (i = 0; i < me->ncells; i++) {
+	    if (me->cells[i].Line == lineno) {
+		break;
+	    } else if (me->cells[i].len <= 0) {
+		me->cells[i].Line = lineno;
+		/* @@@ reset its pos too ?? */
+	    } else {
+		break;
+	    }
+	}
+	if (i < me->ncells && me->cells[i].Line != lineno)
+	    return -1;
+	me->Line = lineno;
+#endif
+    }
+#endif /* MEGA_COMMENTOUT */
+    s->state = newstate;
+
+    if (me->ncells > 0 && me->cells[me->ncells - 1].colspan > 1) {
+	me->ncells += me->cells[me->ncells-1].colspan - 1;
+    }
+    {
+	int growby = 0;
+	while (me->ncells + colspan + 1 > me->allocated + growby)
+	    growby += CELLS_GROWBY;
+	if (growby) {
+	    if (me->allocated == 0 && !me->cells) {
+		cells = calloc(growby, sizeof(STable_cellinfo));
+	    } else {
+		cells = realloc(me->cells,
+				  (me->allocated + growby)
+				  * sizeof(STable_cellinfo));
+	    }
+	    if (cells) {
+		me->allocated += growby;
+		me->cells = cells;
+	    } else {
+		return -1;
+	    }
+	}
+    }
+
+    me->cells[me->ncells].Line = lineno;
+    me->cells[me->ncells].pos = *ppos;
+    me->cells[me->ncells].len = -1;
+    me->cells[me->ncells].colspan = colspan;
+    me->cells[me->ncells].alignment =
+	(alignment==HT_ALIGN_NONE) ? me->alignment : alignment;
+    if (me->cells[me->ncells].alignment==HT_ALIGN_NONE)
+	me->cells[me->ncells].alignment = isheader ? HT_CENTER : HT_LEFT;
+    for (i = me->ncells + 1; i < me->ncells + colspan; i++) {
+	me->cells[i].Line = lineno;
+	me->cells[i].pos = *ppos;
+	me->cells[i].len = -1;
+	me->cells[i].colspan = 0;
+	me->cells[i].alignment = HT_LEFT;
+    }
+    me->cells[me->ncells + colspan].pos = -1; /* not yet used */
+    me->ncells++;
+    return (me->ncells - 1);
+}
+
+PRIVATE int Stbl_finishCellInRow ARGS5(
+    STable_rowinfo *,	me,
+    STable_states *,	s,
+    BOOL,		certain,
+    int,		lineno,
+    int,		pos)
+{
+    STable_cellinfo *lastcell;
+    cellstate_t newstate = CS_invalid;
+    BOOL broken = NO, empty;
+
+    if (me->ncells <= 0)
+	return -1;
+    lastcell = me->cells + (me->ncells - 1);
+    broken = (lineno != lastcell->Line || lineno != s->lineno);
+    empty = broken ? (pos == 0) : (pos <= s->x_td);
+    if (broken) {
+	if (!certain) {
+	    switch (s->state) {
+	    case CS_invalid:
+		newstate = empty ? CS_invalid : CS__cbc;
+		break;
+	    case CS__0:
+		newstate = empty ? CS__0eb : CS__0cb;
+		break;
+	    case CS__0eb:
+		newstate = empty ? CS__0eb : CS__ebc;
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : -1;
+		} else {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : 0;
+		}
+		newstate = empty ? CS__0eb : CS__ebc;
+		break;
+	    case CS__0cb:
+		if (!me->fixed_line) {
+		    if (empty) { /* ##462_return_0 */
+/*			if (s->icell_core == -1)
+			    s->icell_core = lastcell->Line; */ /* we don't know yet */
+			/* lastcell->Line = lineno; */
+		    } else {	/* !empty */
+			if (s->icell_core == -1)
+			    me->Line = -1;
+		    }
+		}
+		if (s->pending_len && empty) { /* ##470_why_that?? */
+		    if ((me->fixed_line && me->Line == lastcell->Line) ||
+			s->icell_core == me->ncells - 1)
+			lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		} /* @@@ for empty do smth. about ->Line / ->icell_core !! */
+		newstate = empty ? CS__0cb : CS__cbc; /* ##474_needs_len!=-1? */
+		break;
+	    case CS__0f:
+	    case CS__0cf:
+		break;
+	    case CS_new:
+		newstate = empty ? CS__eb : CS__cb;
+		break;
+	    case CS__eb:	/* ##484_set_pending_ret_0_if_empty? */
+		newstate = empty ? CS__eb : CS__ebc;
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : -1;
+		} else {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : -1;
+		}
+		newstate = empty ? CS__eb : CS__ebc;
+		break;
+	    case CS__cb:
+		if (s->pending_len && empty) { /* ##496: */
+		    lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		} /* @@@ for empty do smth. about ->Line / ->icell_core !! */
+		if (empty) {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;
+			me->Line = lastcell->Line; /* should've happened in break */
+		    } else {
+			if (me->Line != lastcell->Line)
+			    return -1;
+		    }
+		} else {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;
+			me->Line = lastcell->Line; /* should've happened in break */
+		    }
+		    s->state = CS__cbc;
+		    return -1;
+		}
+		newstate = empty ? CS__cb : CS__cbc;
+		break;
+	    case CS__ef:
+		return 0;
+	    case CS__cf:
+		return lastcell->len; /* ##523_change_state? */
+	    case CS__cbc:
+		if (!me->fixed_line) {
+		    if (empty) {
+			if (s->icell_core == -1) /* ##528??: */
+			    me->Line = lineno;
+			/* lastcell->Line = lineno; */
+		    } else {	/* !empty */
+			if (s->icell_core == -1)
+			    me->Line = -1;
+		    }
+		}
+		s->pending_len = 0;
+		newstate = empty ? CS_invalid : CS__cbc;
+		break;
+	    default:
+		break;
+	    }
+	} else {		/* broken, certain: */
+	    s->x_td = -1;
+	    switch (s->state) {
+	    case CS_invalid:
+		/* ##540_return_-1_for_invalid_if_len!: */
+		if (!empty && lastcell->len > 0) {
+		    newstate = CS__0cf;
+		    s->state = newstate;
+		    return -1;
+		}
+				/* ##541_set_len_0_Line_-1_sometimes: */
+		lastcell->len = 0;
+		lastcell->Line = -1;
+		 /* fall thru ##546 really fall thru??: */
+		newstate = empty ? CS_invalid : CS__cbc;	break;
+	    case CS__0:
+		newstate = empty ? CS__0f  : CS__0cf;	break;
+	    case CS__0eb:
+		newstate = empty ? CS__0f  : CS__0cf;		/* ebc?? */
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : -1;
+		} else {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : 0;
+		}
+		newstate = empty ? CS__0f  : CS__0cf;	break; /* ebc?? */
+	    case CS__0cb:
+		if (s->pending_len) {
+		    if (empty)
+			lastcell->len = s->pending_len;
+		    else
+			lastcell->len = 0;
+		    s->pending_len = 0;
+		}
+		if (!me->fixed_line) {
+		    if (empty) {
+			if (s->icell_core == -1)
+			    s->icell_core = me->ncells - 1;
+			/* lastcell->Line = lineno; */
+		    } else {	/* !empty */
+			if (s->icell_core == -1)
+			    me->Line = -1;
+		    }
+		}
+		if (s->pending_len && empty) {
+		    lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		} /* @@@ for empty do smth. about ->Line / ->icell_core !! */
+		newstate = empty ? CS__0cf : CS__cbc;	break;
+	    case CS__0f:
+		newstate = CS__0f;
+	    case CS__0cf:
+		break;
+	    case CS_new:
+		newstate = empty ? CS__ef  : CS__cf;	break;
+	    case CS__eb:
+		newstate = empty ? CS__ef  : CS__ef; /* ##579??? !!!!! */
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : -1;
+		} else {
+		    if (empty)
+			return lastcell->len <= 0 ? 0 : lastcell->len;
+		    else
+			return lastcell->len <= 0 ? 0 : -1;
+		}
+		newstate = empty ? CS__ef  : CS__ef;	break;
+	    case CS__cb:
+		if (s->pending_len && empty) {
+		    lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		}
+		if (empty) {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;
+			me->Line = lastcell->Line; /* should've happened in break */
+		    } else {
+			if (me->Line != lastcell->Line)
+			    return -1;
+		    }
+		} else {
+		    return -1;
+		}
+		newstate = empty ? CS__cf  : CS__cbc;	break;
+	    case CS__ef:		/* ignored error */
+	    case CS__cf:		/* ignored error */
+		break;
+	    case CS__ebc:	/* ##540_handle_ebc: */
+		lastcell->len = 0;
+		if (!me->fixed_line) {
+		    if (!empty) {
+			if (s->icell_core == -1)
+			    lastcell->Line = -1;
+		    }
+		}
+		s->pending_len = 0;
+		newstate = empty ? CS_invalid : CS__cbc;	break;
+	    case CS__cbc:	/* ##586 */
+		lastcell->len = 0; /* ##613 */
+		if (me->fixed_line && me->Line == lastcell->Line)
+		    return -1;
+		if (!me->fixed_line) {
+		    if (empty) {
+			if (s->icell_core == -1)
+			    me->Line = lineno;
+			/* lastcell->Line = lineno; */
+#if 0	/* ?? */
+		    } else {	/* !empty */
+			if (s->icell_core == -1)
+			    me->Line = -1;
+#endif
+		    }
+		}
+		s->pending_len = 0; /* ##629 v */
+		newstate = empty ? CS_invalid : CS__cbc;	break;
+	    }
+	}
+    } else {			/* (!broken) */
+	if (!certain) {
+	    switch (s->state) {
+	    case CS_invalid:
+	    case CS__0:
+		s->pending_len = empty ? 0 : pos - lastcell->pos;
+		newstate = empty ? CS__0eb : CS__0cb;
+		s->state = newstate;
+		return 0; /* or 0 for xlen to s->pending_len?? */
+	    case CS__0eb:	/* cannot happen */
+		newstate = CS__eb;
+	    case CS__0cb:	/* cannot happen */
+		newstate = CS__cb;			break;
+	    case CS__0f:
+	    case CS__0cf:
+		break;
+	    case CS_new:
+		if (!empty && s->prev_state == CS__cbc)	/* ##609: */
+		    return -1;
+		if (!empty) {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;
+			me->Line = lineno;
+		    } else {
+			if (me->Line != lineno)
+			    return -1;
+		    }
+		}
+		newstate = empty ? CS__eb : CS__cb;
+		s->state = newstate;
+		if (!me->fixed_line) {
+		    s->pending_len = empty ? 0 : pos - lastcell->pos;
+		    return 0;
+		} else {
+		    s->pending_len = 0;
+		    lastcell->len = empty ? 0 : pos - lastcell->pos;
+		    return lastcell->len;
+		}
+	    case CS__eb:	/* cannot happen */
+		newstate = empty ? CS__eb : CS__ebc;	break;
+	    case CS__cb:	/* cannot happen */
+		newstate = empty ? CS__cb : CS__cbc;	break;
+	    case CS__ef:
+		return 0;
+	    case CS__cf:
+		return lastcell->len;
+	    case CS__cbc:	/* ??? */
+		break;
+	    default:
+		break;
+	    }
+	} else {		/* !broken, certain: */
+	    s->x_td = -1;
+	    switch (s->state) {
+	    case CS_invalid:	/* ##691_no_lastcell_len_for_invalid: */
+		if (!(me->fixed_line && me->Line == lastcell->Line))
+		    lastcell->len = 0;
+	    case CS__0:
+		newstate = empty ? CS__0f  : CS__0cf;	break; /* ##630 */
+	    case CS__0eb:
+		newstate = empty ? CS__0f : CS__0f;	break; /* ??? */
+	    case CS__0cb:
+		newstate = empty ? CS__0cf : CS__cbc;	break; /* ??? */
+	    case CS__0f:
+		newstate = CS__0f;			break; /* ??? */
+	    case CS__0cf:
+		break;		/* ??? */
+	    case CS_new:
+		if (!empty && s->prev_state == CS__cbc)
+		    return -1;
+		if (!empty) { /* ##642_set_fixed!: */
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;
+			me->Line = lineno;
+		    } else {
+			if (me->Line != lineno)
+			    return -1;
+		    }
+		}
+		if (lastcell->len < 0)
+		    lastcell->len = empty ? 0 : pos - lastcell->pos;
+		newstate = empty ? CS__ef  : CS__cf;
+		s->state = newstate;
+		return (me->fixed_line && lineno != me->Line) ? -1 : lastcell->len;
+	    case CS__eb:
+		newstate = empty ? CS__ef  : CS__cf;	break; /* ??? */
+	    case CS__cb:
+		newstate = empty ? CS__cf  : CS__cf;	break; /* ??? */
+	    case CS__ef:		/* ignored error */
+	    case CS__cf:		/* ignored error */
+	    default:
+		break;
+	    }
+	    lastcell->len = pos - lastcell->pos;
+	} /* if (!certain) ... else */
+    } /* if (broken) ... else */
+
+#if 0				/* MEGA_COMMENTOUT */
+    if (lineno != me->cells[0].Line) {
+#if 0
+	int i;
+	for (i = ncells - 1; i >= 0; i--) {
+	    if (!(me->cells[i].len == 0 || me->cells[i].colspan == 0))
+		break;
+	}
+#endif
+	if (lineno >= lastcell->Line) {
+	    if (me->fixed_line) {
+		if (pos == 0) {
+		    if (lastcell->len <= 0) {
+			return 0;
+		    } else {
+			return lastcell->len;
+		    }
+		} else {	/* pos != 0 */
+		    if (lastcell->len <= 0 && lineno > lastcell->Line && lastcell->Line <= me->Line) {
+			return 0;
+		    } else {
+			return -1;
+		    }
+		}
+	    } else {	/* not me->fixed_line */
+		if (pos == 0) {
+		    if (lastcell->len <= 0) {
+			return 0;
+		    } else {
+			return lastcell->len;
+		    }
+		} else {	/* pos != 0 */
+		    if (lastcell->len <= 0) {
+			return 0;
+		    } else {
+			if (me->ncells == 1 || lastcell->pos == 0) {
+			    return 0;
+			} else
+			    return -1;
+		    }
+		}
+	    }
+	}
+    }
+#endif /* MEGA_COMMENTOUT */
+    s->state = newstate;
+/*    lastcell->len = pos - lastcell->pos; */
+    return (lastcell->len);
+}
+
+/*
+ * Returns -1 on error, otherwise index of just-added table row.
+ */
+PUBLIC int Stbl_addRowToTable ARGS3(
+    STable_info *,	me,
+    short,		alignment,
+    int,		lineno)
+{
+    STable_rowinfo *rows, *row;
+    STable_states * s = &me->s;
+    if (me->nrows > 0 && me->rows[me->nrows-1].ncells > 0) {
+	if (s->pending_len > 0)
+	    me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].len = s->pending_len;
+	s->pending_len = 0;
+/*	if (me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].len >= 0 &&
+	    me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].Line == lineno)
+	    Stbl_finishCellInTable(me, YES,
+				   lineno,
+		me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].pos +
+		me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].len); */
+    }
+#if 0
+    s->prev_state = s->state = CS_invalid;
+    s->lineno = -1;
+    s->icell_core = -1;
+#endif
+    Stbl_finishRowInTable(me);
+    if (me->nrows > 0 && me->rows[me->nrows-1].Line == lineno)
+	me->rows[me->nrows-1].Line = -1;
+    s->pending_len = 0;
+    s->x_td = -1;
+
+    {
+	int i;
+	int growby = 0;
+	while (me->nrows + 2 > me->allocated_rows + growby)
+	    growby += ROWS_GROWBY;
+	if (growby) {
+	    if (me->allocated_rows == 0 && !me->rows) {
+		rows = calloc(growby, sizeof(STable_rowinfo));
+	    } else {
+		rows = realloc(me->rows,
+				  (me->allocated_rows + growby)
+				  * sizeof(STable_rowinfo));
+		for (i = 0; rows && i < growby; i++) {
+		    row = rows + me->allocated_rows + i;
+		    row->allocated = 0;
+		    row->ncells = 0;
+		    row->fixed_line = NO;
+		    row->cells = NULL;
+		    row->alignment = HT_ALIGN_NONE;
+		}
+	    }
+	    if (rows) {
+		me->allocated_rows += growby;
+		me->rows = rows;
+	    } else {
+		return -1;
+	    }
+	}
+    }
+
+    me->rows[me->nrows].Line = lineno;
+    if (me->nrows == 0)
+	me->startline = lineno;
+    me->rows[me->nrows].alignment =
+	(alignment==HT_ALIGN_NONE) ? me->alignment : alignment;
+    me->nrows++;
+    me->rows[me->nrows].Line = -1; /* not yet used */
+    return (me->nrows - 1);
+}
+
+/*
+ * Returns -1 on error, otherwise current number of rows.
+ */
+PRIVATE int Stbl_finishRowInTable ARGS1(
+    STable_info *,	me)
+{
+    STable_rowinfo *lastrow;
+    STable_states * s = &me->s;
+    int ncells;
+    if (!me->rows || !me->nrows)
+	return -1;		/* no row started! */
+    lastrow = me->rows + (me->nrows - 1);
+    ncells = lastrow->ncells;
+    if (lastrow->ncells > 0) {
+	if (s->pending_len > 0)
+	    lastrow->cells[lastrow->ncells - 1].len = s->pending_len;
+	s->pending_len = 0;
+    }
+    s->prev_state = s->state = CS_invalid;
+    s->lineno = -1;
+
+#if 0
+    if (lastrow->Line == -1 && s->icell_core >= 0)
+#endif
+    if (s->icell_core >= 0 && !lastrow->fixed_line &&
+	lastrow->cells[s->icell_core].Line >= 0)
+	lastrow->Line = lastrow->cells[s->icell_core].Line;
+    s->icell_core = -1;
+    return (me->nrows);
+}
+
+PRIVATE void update_sumcols0 ARGS7(
+    STable_cellinfo *,	sumcols,
+    STable_rowinfo *,	lastrow,
+    int,		pos,
+    int,		len,
+    int,		icell,
+    int,		ispan,
+    int,		allocated_sumcols)
+{
+    int i;
+    if (len > 0) {
+	int sumpos = pos;
+	int prevsumpos = sumcols[icell + ispan].pos;
+	int advance;
+	if (ispan > 0) {
+	    if (lastrow->cells[icell].pos + len > sumpos)
+		sumpos = lastrow->cells[icell].pos + len;
+	    if (sumcols[icell+ispan-1].pos + sumcols[icell+ispan-1].len > sumpos)
+		sumpos = sumcols[icell+ispan-1].pos + sumcols[icell+ispan-1].len;
+	}
+	advance = sumpos - prevsumpos;
+	if (advance > 0) {
+	    for (i = icell + ispan; i < allocated_sumcols; i++) {
+		if (ispan > 0 && sumcols[i].colspan < -1) {
+		    if (i + sumcols[i].colspan < icell + ispan) {
+			advance = sumpos - sumcols[i].pos;
+			if (i > 0)
+			    advance = HTMAX(advance,
+					    sumcols[i-1].pos + sumcols[i-1].len
+					    - (sumcols[i].pos));
+			if (advance <= 0)
+			    break;
+		    }
+		}
+		if (sumcols[i].pos >= 0)
+		    sumcols[i].pos += advance;
+		else {
+		    sumcols[i].pos = sumpos;
+		    break;
+		}
+	    }
+	}
+    }
+}
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+PUBLIC int Stbl_addCellToTable ARGS6(
+    STable_info *,	me,
+    int,		colspan,
+    short,		alignment,
+    BOOL,		isheader,
+    int,		lineno,
+    int,		pos)
+{
+    STable_states * s = &me->s;
+    STable_rowinfo *lastrow;
+    STable_cellinfo *sumcols, *sumcol;
+    int i, icell, ncells, sumpos;
+#if 0
+    int prevsumpos, advance;
+#endif
+
+    if (!me->rows || !me->nrows)
+	return -1;		/* no row started! */
+				/* ##850_fail_if_fail?? */
+    Stbl_finishCellInTable(me, YES,
+			   lineno, pos);
+    lastrow = me->rows + (me->nrows - 1);
+    ncells = lastrow->ncells;	/* remember what it was before adding cell. */
+    icell = Stbl_addCellToRow(lastrow, s,
+			      colspan, alignment, isheader,
+			      lineno, &pos);
+    if (icell < 0)
+	return icell;
+    if (me->nrows == 1 && me->startline < lastrow->Line)
+	me->startline = lastrow->Line;
+
+    {
+	int growby = 0;
+	while (icell + colspan + 1 > me->allocated_sumcols + growby)
+	    growby += CELLS_GROWBY;
+	if (growby) {
+	    if (me->allocated_sumcols == 0 && !me->sumcols) {
+		sumcols = calloc(growby, sizeof(STable_cellinfo));
+	    } else {
+		sumcols = realloc(me->sumcols,
+				  (me->allocated_sumcols + growby)
+				  * sizeof(STable_cellinfo));
+		for (i = 0; sumcols && i < growby; i++) {
+		    sumcol = sumcols + me->allocated_sumcols + i;
+		    sumcol->pos = sumcols[me->allocated_sumcols-1].pos;
+		    sumcol->len = 0;
+		    sumcol->colspan = 0;
+		}
+	    }
+	    if (sumcols) {
+		me->allocated_sumcols += growby;
+		me->sumcols = sumcols;
+	    } else {
+		return -1;
+	    }
+	}
+    }
+#if 0
+    if (icell + colspan > me->ncols) {
+	me->sumcols[icell + colspan].pos = -1; /* not yet used @@@ ??? */
+    }
+#endif
+    if (icell + 1 > me->ncols) {
+	me->ncols = icell + 1;
+    }
+    if (colspan > 1 && colspan + me->sumcols[icell + colspan].colspan > 0)
+	me->sumcols[icell + colspan].colspan = -colspan;
+    sumpos = pos;
+    if (ncells > 0)
+	sumpos += me->sumcols[ncells-1].pos - lastrow->cells[ncells-1].pos;
+    update_sumcols0(me->sumcols, lastrow, sumpos,
+		    sumpos - (ncells > 0 ? me->sumcols[icell].pos : me->sumcols[icell].pos),
+		    icell, 0, me->allocated_sumcols);
+#if 0
+    prevsumpos = me->sumcols[icell].pos;
+    advance = sumpos - prevsumpos;
+    if (advance > 0) {
+	for (i = icell; i < me->allocated_sumcols; i++) {
+	    if (me->sumcols[i].pos >= 0)
+		me->sumcols[i].pos += advance;
+	    else {
+		me->sumcols[i].pos = sumpos;
+		break;
+	    }
+	}
+    }
+#endif
+
+
+#if 0
+	int prevopos = (ncells > 0 ? lastrow->cells[ncells-1].pos : 0);
+	int prevnpos = (ncells > 0 ? me->sumcols[ncells-1].pos : 0);
+#endif
+#if 0
+    if (pos > me->maxpos) {
+	me->maxpos = pos;
+	if (me->maxpos > /* @@@ max. line length we can accept */ MAX_STBL_POS)
+	    return -1;
+    }
+#endif
+    me->maxpos = me->sumcols[me->allocated_sumcols-1].pos;
+    if (me->maxpos > /* @@@ max. line length we can accept */ MAX_STBL_POS)
+	return -1;
+    return 0;
+}
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+PUBLIC int Stbl_finishCellInTable ARGS4(
+    STable_info *,	me,
+    BOOL,		certain,
+    int,		lineno,
+    int,		pos)
+{
+    STable_states * s = &me->s;
+    STable_rowinfo *lastrow;
+    int len, xlen, icell;
+    int i;
+    if (me->nrows == 0)
+	return -1;
+    lastrow = me->rows + (me->nrows - 1);
+    icell = lastrow->ncells - 1;
+    if (icell < 0)
+	return icell;
+    if (s->x_td == -1)
+	return certain ? -1 : 0;
+    len = Stbl_finishCellInRow(lastrow, s, certain, lineno, pos);
+    if (len == -1)
+	return len;
+    xlen = (len > 0) ? len : s->pending_len; /* ##890 use xlen if fixed_line?: */
+    if (lastrow->fixed_line && lastrow->Line == lineno)
+	len = xlen;
+    if (lastrow->cells[icell].colspan > 1) {
+	/*
+	 * @@@ This is all a too-complicated mess; do we need
+	 * sumcols len at all, or is pos enough??
+	 * Answer: sumcols len is at least used for center/right
+	 * alignment, and should probably continue to be used there;
+	 * all other uses are probably not necessary.
+	 */
+	int spanlen = 0, spanlend = 0;
+	for (i = icell; i < icell + lastrow->cells[icell].colspan; i++) {
+	    if (me->sumcols[i].len > 0) {
+		spanlen += me->sumcols[i].len;
+		if (i > icell)
+		    spanlen++;
+	    }
+	    spanlend = HTMAX(spanlend,
+			     me->sumcols[i+1].pos - me->sumcols[icell].pos);
+	}
+	if (spanlend)
+	    spanlend--;
+	if (spanlend > spanlen)
+	    spanlen = spanlend;
+	/* @@@ could overcount? */
+	if (len > spanlen)
+	    me->maxlen += (len - spanlen);
+#if 0	/* this is all quite bogus! */
+	if (me->sumcols[icell].colspan > 1)
+	    me->sumcols[icell+me->sumcols[icell].colspan].pos =
+		HTMAX(me->sumcols[icell].pos + len,
+		      me->sumcols[icell+me->sumcols[icell].colspan].pos);
+	if (lastrow->cells[icell].colspan > me->sumcols[icell].colspan) {
+	    me->sumcols[icell].colspan = lastrow->cells[icell].colspan;
+	    if (me->sumcols[icell].colspan > 1)
+		me->sumcols[icell+me->sumcols[icell].colspan].pos =
+		    HTMAX(me->sumcols[icell].pos + len,
+			  me->sumcols[icell+me->sumcols[icell].colspan].pos);
+	}
+#endif
+    } else if (len > me->sumcols[icell].len) {
+	if (me->sumcols[icell + 1].colspan >= -1)
+	    me->maxlen += (len - me->sumcols[icell].len);
+	me->sumcols[icell].len = len;
+    }
+
+    if (len > 0) {
+	update_sumcols0(me->sumcols, lastrow, pos, len,
+			icell, lastrow->cells[icell].colspan,
+			me->allocated_sumcols);
+	me->maxpos = me->sumcols[me->allocated_sumcols-1].pos;
+    }
+#if 0
+    if (len > 0) {
+	int sumpos = pos;
+	int ispan = lastrow->cells[icell].colspan;
+	int prevsumpos = me->sumcols[icell + ispan].pos;
+	int advance;
+	if (lastrow->cells[icell].pos + len > sumpos)
+	    sumpos = lastrow->cells[icell].pos + len;
+	if (me->sumcols[icell+ispan-1].pos + me->sumcols[icell+ispan-1].len > sumpos)
+	    sumpos = me->sumcols[icell+ispan-1].pos + me->sumcols[icell+ispan-1].len;
+	advance = sumpos - prevsumpos;
+	if (advance > 0) {
+	    for (i = icell + ispan; i < me->allocated_sumcols; i++) {
+		if (me->sumcols[i].colspan < -1) {
+		    if (i + me->sumcols[i].colspan < icell + ispan) {
+			advance = sumpos - me->sumcols[i].pos;
+			if (i > 0)
+			    advance = HTMAX(advance,
+					    me->sumcols[i-1].pos + me->sumcols[i-1].len
+					    - (me->sumcols[i].pos));
+			if (advance <= 0)
+			    break;
+		    }
+		}
+		if (me->sumcols[i].pos >= 0)
+		    me->sumcols[i].pos += advance;
+		else {
+		    me->sumcols[i].pos = sumpos;
+		    break;
+		}
+	    }
+	}
+	me->maxpos = me->sumcols[me->allocated_sumcols-1].pos;
+    }
+#endif
+
+    if (me->maxlen + (xlen - len) > MAX_STBL_POS)
+	return -1;
+    if (me->maxpos > /* @@@ max. line length we can accept */ MAX_STBL_POS)
+	return -1;
+
+    if (lineno != lastrow->Line) {
+	/* @@@ Do something here?  Or is it taken care of in
+	   Stbl_finishCellInRow ? */
+    }
+
+    return 0;
+}
+
+PUBLIC int Stbl_finishTABLE ARGS1(
+    STable_info *,	me)
+{
+    STable_states * s = &me->s;
+    int i;
+    int curpos = 0;
+
+    if (!me || me->nrows <= 0 || me->ncols <= 0) {
+	return -1;
+    }
+    if (me->nrows > 0 && me->rows[me->nrows-1].ncells > 0) {
+	if (s->pending_len > 0)
+	    me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].len = s->pending_len;
+	s->pending_len = 0;
+/*	if (me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].len >= 0)
+	    Stbl_finishCellInTable(me, YES,
+		me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].Line,
+		me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].pos +
+		me->rows[me->nrows-1].cells[me->rows[me->nrows-1].ncells - 1].len); */
+    }
+    Stbl_finishRowInTable(me);
+    for (i = 0; i < me->ncols; i++) {
+	if (me->sumcols[i].pos < curpos) {
+	    me->sumcols[i].pos = curpos;
+	} else {
+	    curpos = me->sumcols[i].pos;
+	}
+	if (me->sumcols[i].len > 0) {
+	    curpos += me->sumcols[i].len;
+	}
+    }
+#if 0				/* ??? */
+    for (j = 0; j < me->nrows; j++) {
+	STable_rowinfo *row = me->rows + i;
+	for (i = 0; i < row->ncells; i++) {
+	}
+    }
+#endif
+    return me->ncols;
+}
+
+PUBLIC short Stbl_getAlignment ARGS1(
+    STable_info *,	me)
+{
+    return me ? me->alignment : HT_ALIGN_NONE;
+}
+
+PRIVATE int get_fixup_positions ARGS4(
+    STable_rowinfo *,	me,
+    int *,		oldpos,
+    int *,		newpos,
+    STable_cellinfo *,	sumcols)
+{
+    int i = 0, ip = 0;
+    int next_i, newlen;
+    int ninserts;
+
+    if (!me)
+	return -1;
+    while (i < me->ncells) {
+	next_i = i + me->cells[i].colspan;
+	if (me->cells[i].Line != me->Line) {
+	    if (me->cells[i].Line > me->Line)
+		break;
+	    i = next_i;
+	    continue;
+	}
+	oldpos[ip] = me->cells[i].pos;
+	newpos[ip] = sumcols[i].pos;
+	if ((me->cells[i].alignment == HT_CENTER ||
+	     me->cells[i].alignment == HT_RIGHT) &&
+	    me->cells[i].len > 0) {
+	    newlen = sumcols[next_i].pos - newpos[ip] - 1;
+	    newlen = HTMAX(newlen, sumcols[i].len);
+	    if (me->cells[i].len < newlen) {
+		if (me->cells[i].alignment == HT_RIGHT) {
+		    newpos[i] += newlen - me->cells[i].len;
+		} else {
+		    newpos[i] += (newlen - me->cells[i].len) / 2;
+		}
+	    }
+	}
+	ip++;
+	i = next_i;
+    }
+    ninserts = ip;
+    return ninserts;
+}
+
+/*
+ *  Returns -1 if we have no row for this lineno, or for other error,
+ *           0 or greater (number of oldpos/newpos pairs) if we have
+ *             a table row.
+ */
+PUBLIC int Stbl_getFixupPositions ARGS4(
+    STable_info *,	me,
+    int,		lineno,
+    int *,		oldpos,
+    int *,		newpos)
+{
+    STable_rowinfo * row;
+    int j;
+    int ninserts = -1;
+    if (!me || !me->nrows)
+	return -1;
+    for (j = 0; j < me->nrows; j++) {
+	row = me->rows + j;
+	if (row->Line == lineno) {
+	    ninserts = get_fixup_positions(row, oldpos, newpos,
+					   me->sumcols);
+	    break;
+	}
+    }
+    return ninserts;
+}
+
+PUBLIC int Stbl_getStartLine ARGS1(
+    STable_info *,	me)
+{
+    if (!me)
+	return -1;
+    else
+	return me->startline;
+}