summaryrefslogtreecommitdiffstats
path: root/src/TRSTable.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/TRSTable.c2017
1 files changed, 2017 insertions, 0 deletions
diff --git a/src/TRSTable.c b/src/TRSTable.c
new file mode 100644
index 0000000..2a9461c
--- /dev/null
+++ b/src/TRSTable.c
@@ -0,0 +1,2017 @@
+/*
+ * $LynxId: TRSTable.c,v 1.39 2021/10/24 18:05:05 tom Exp $
+ * 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 <LYGlobalDefs.h>
+
+#include <LYLeaks.h>
+
+#ifdef SAVE_TIME_NOT_SPACE
+#define CELLS_GROWBY 16
+#define ROWS_GROWBY 16
+#else
+#define CELLS_GROWBY 2
+#define ROWS_GROWBY 2
+#endif
+
+#ifdef USE_CURSES_PADS
+# define MAX_STBL_POS (LYwideLines ? MAX_COLS - 1 : LYcolLimit)
+#else
+# define MAX_STBL_POS (LYcolLimit)
+#endif
+
+/* must be different from HT_ALIGN_NONE and HT_LEFT, HT_CENTER etc.: */
+#define RESERVEDCELL (-2) /* cell's alignment field is overloaded, this
+ value means cell was reserved by ROWSPAN */
+#define EOCOLG (-2) /* sumcols' Line field isn't used for line info, this
+ special value means end of COLGROUP */
+#ifndef NO_AGGRESSIVE_NEWROW
+# define NO_AGGRESSIVE_NEWROW 0
+#endif
+
+typedef enum {
+ CS_invalid = -1, /* cell "before the first",
+ or empty lines after [ce]bc,
+ or TRST aborted */
+ CS__new = 0,
+ CS__0new, /* 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__0ef, /* starts at BOL, empty, finished */
+ CS__ef, /* empty, finished */
+ CS__0cf, /* starts at BOL, content, finished */
+ CS__cf, /* content, finished */
+ CS__ebc, /* empty, break, more content (maybe @BOL) */
+ CS__cbc /* content, break, more content (maybe @BOL) */
+} cellstate_t;
+
+typedef struct _STable_states {
+ cellstate_t prev_state; /* Contents type of the previous cell */
+ cellstate_t state; /* Contents type of the worked-on cell */
+ int lineno; /* Start line of the current cell */
+ int icell_core; /* -1 or the 1st cell with <BR></TD> on row */
+ int x_td; /* x start pos of the current cell or -1 */
+ int pending_len; /* For multiline cells, the length of
+ the part on the first line (if
+ state is CS__0?[ec]b) (??), or 0 */
+} STable_states;
+
+typedef struct _STable_cellinfo {
+ int cLine; /* lineno in doc (zero-based): -1 for
+ contentless cells (and cells we do
+ not want to measure and count?),
+ line-of-the-start otherwise. */
+ int pos; /* column where cell starts */
+ int len; /* number of character positions */
+ int colspan; /* number of columns to span */
+ int alignment; /* one of HT_LEFT, HT_CENTER, HT_RIGHT,
+ or RESERVEDCELL */
+} STable_cellinfo;
+
+enum ended_state {
+ ROW_not_ended,
+ ROW_ended_by_endtr,
+ ROW_ended_by_splitline
+};
+
+#define HAS_END_OF_CELL 1
+#define HAS_BEG_OF_CELL 2
+#define IS_CONTINUATION_OF_CELL 4
+#define OFFSET_IS_VALID 8
+#define OFFSET_IS_VALID_LAST_CELL 0x10
+#define BELIEVE_OFFSET 0x20
+
+typedef struct _STable_rowinfo {
+ /* Each row may be displayed on many display lines, but we fix up
+ positions of cells on this display line only: */
+ int Line; /* lineno in doc (zero-based) */
+ int ncells; /* number of table cells */
+
+ /* What is the meaning of this?! It is set if:
+ [search for def of fixed_line below]
+
+ a1) a non-last cell is not at BOL,
+ a2) a non-last cell has something on the first line,
+ b) a >=3-lines-cell not at BOL, the first row non-empty, the 2nd empty;
+ c) a multiline cell not at BOL, the first row non-empty, the rest empty;
+ d) a multiline cell not at BOL, the first row non-empty;
+ e) a singleline non-empty cell not at BOL;
+
+ Summary: have seen a cell which is one of:
+ (Notation: B: at BOL; L: last; E: the first row is non-empty)
+
+ bcde: !B && !E
+ a1: !L && !B
+ a2: !L && !E
+
+ Or: has at least two of !B, !L, !E, or: has at most one of B,L,E.
+
+ REMARK: If this variable is not set, but icell_core is, Line is
+ reset to the line of icell_core.
+ */
+ BOOL fixed_line; /* if we have a 'core' line of cells */
+ enum ended_state ended; /* if we saw </tr> etc */
+ int content; /* Whether contains end-of-cell etc */
+ int offset; /* >=0 after line break in a multiline cell */
+ int allocated; /* number of table cells allocated */
+ STable_cellinfo *cells;
+ int alignment; /* global align attribute for this row */
+} STable_rowinfo;
+
+struct _STable_info {
+#ifdef EXP_NESTED_TABLES
+ struct _STable_info *enclosing; /* The table which contain us */
+ struct _TextAnchor *enclosing_last_anchor_before_stbl;
+#endif
+ 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 */
+ int ncolinfo; /* number of COL info collected */
+ STable_cellinfo *sumcols; /* for summary (max len/pos) col info */
+ STable_rowinfo *rows;
+ STable_rowinfo rowspans2eog;
+ short alignment; /* global align attribute for this table */
+ short rowgroup_align; /* align default for current group of rows */
+ short pending_colgroup_align;
+ int pending_colgroup_next;
+ 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
+ * - COLSPAN >1 (may work incorrectly for some tables?)
+ * - ROWSPAN >1 (reserving cells in following rows)
+ * - 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
+ * - Tables wider than display. The limitation is not here but in GridText.c
+ * etc. If horizontal scrolling were implemented there, the mechanisms
+ * here could 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.
+ */
+static int Stbl_finishRowInTable(STable_info *me);
+
+static const char *cellstate_s(cellstate_t state)
+{
+ const char *result = "?";
+ /* *INDENT-OFF* */
+ switch (state) {
+ case CS_invalid: result = "CS_invalid"; break;
+ case CS__new: result = "CS__new"; break;
+ case CS__0new: result = "CS__0new"; break;
+ case CS__0eb: result = "CS__0eb"; break;
+ case CS__eb: result = "CS__eb"; break;
+ case CS__0cb: result = "CS__0cb"; break;
+ case CS__cb: result = "CS__cb"; break;
+ case CS__0ef: result = "CS__0ef"; break;
+ case CS__ef: result = "CS__ef"; break;
+ case CS__0cf: result = "CS__0cf"; break;
+ case CS__cf: result = "CS__cf"; break;
+ case CS__ebc: result = "CS__ebc"; break;
+ case CS__cbc: result = "CS__cbc"; break;
+ }
+ /* *INDENT-ON* */
+
+ return result;
+}
+
+struct _STable_info *Stbl_startTABLE(int alignment)
+{
+ STable_info *me = typecalloc(STable_info);
+
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_startTABLE(align=%d)\n", (int) alignment));
+ if (me) {
+ me->alignment = (short) alignment;
+ me->rowgroup_align = HT_ALIGN_NONE;
+ me->pending_colgroup_align = HT_ALIGN_NONE;
+ me->s.x_td = -1;
+ me->s.icell_core = -1;
+#ifdef EXP_NESTED_TABLES
+ if (nested_tables)
+ me->enclosing = 0;
+#endif
+ }
+ return me;
+}
+
+static void free_rowinfo(STable_rowinfo *me)
+{
+ if (me && me->allocated) {
+ FREE(me->cells);
+ }
+}
+
+void Stbl_free(STable_info *me)
+{
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_free()\n"));
+ if (me && me->allocated_rows && me->rows) {
+ int i;
+
+ for (i = 0; i < me->allocated_rows; i++)
+ free_rowinfo(me->rows + i);
+ FREE(me->rows);
+ }
+ free_rowinfo(&me->rowspans2eog);
+ if (me)
+ FREE(me->sumcols);
+ FREE(me);
+}
+
+/*
+ * Returns -1 on error, otherwise index of just-added table cell.
+ */
+static int Stbl_addCellToRow(STable_rowinfo *me, STable_cellinfo *colinfo, int ncolinfo,
+ STable_states *s,
+ int colspan,
+ int alignment,
+ int 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;
+ int ret;
+
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_addCellToRow, line=%d, pos=%d, colspan=%d\n",
+ lineno, *ppos, colspan));
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ " ncells=%d, stateLine=%d, pending_len=%d, pstate=%s, state=%s\n",
+ me->ncells, s->lineno, s->pending_len,
+ cellstate_s(s->prev_state), cellstate_s(s->state)));
+ if (me->ncells == 0)
+ s->prev_state = CS_invalid;
+ else if (s->prev_state == CS_invalid ||
+ (s->state != CS__0new &&
+ s->state != CS__ef && s->state != CS__0ef))
+ s->prev_state = s->state;
+
+ if (me->ncells == 0 || *ppos == 0)
+ newstate = CS__0new;
+ 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__0new:
+ case CS__0eb:
+ case CS__0cb:
+ case CS__0ef:
+ 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].cLine = lineno;
+ }
+ me->Line = 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 { /* last cell multiline, ncells != 0, pos != 0 */
+ switch (s->prev_state) {
+ case CS__0new:
+ case CS__0eb:
+ case CS__0ef:
+ /* Do not fail, but do not set fixed_line either */
+ break;
+ case CS__cb:
+ goto trace_and_fail;
+ case CS__cf:
+ goto trace_and_fail;
+ case CS__0cb:
+ case CS__0cf:
+ if (*ppos > me->cells[0].pos)
+ me->Line = lineno;
+ me->fixed_line = YES; /* type=a def of fixed_line i */
+ break;
+ case CS__new:
+ case CS__eb:
+ case CS__ef:
+ default:
+ me->fixed_line = YES; /* type=e def of fixed_line ii */
+ break;
+ case CS__cbc:
+ goto trace_and_fail;
+ }
+ }
+ }
+ if (me->fixed_line && lineno != me->Line) {
+ switch (s->prev_state) {
+ case CS__cb:
+ case CS__cf:
+ if (*ppos > 0)
+ goto trace_and_fail;
+ 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__0new:
+ case CS__0ef:
+ case CS__0eb:
+ break;
+ case CS__new:
+ case CS__eb:
+ case CS__ef:
+ default:
+ *ppos = me->cells[me->ncells - 1].pos;
+ break;
+ case CS__cbc:
+ break;
+ case CS_invalid:
+ break;
+ }
+ }
+ s->lineno = lineno;
+ } else { /* lineno == s->lineno: */
+ switch (s->prev_state) {
+ case CS_invalid:
+ case CS__0new:
+ case CS__0eb: /* cannot happen */
+ case CS__0cb: /* cannot happen */
+ case CS__0ef:
+ 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) {
+ goto trace_and_fail;
+ } else {
+ me->fixed_line = YES;
+ me->Line = lineno;
+ }
+ }
+ }
+
+ s->state = newstate;
+
+ if (me->ncells > 0 && me->cells[me->ncells - 1].colspan > 1) {
+ me->ncells += me->cells[me->ncells - 1].colspan - 1;
+ }
+ while (me->ncells < me->allocated &&
+ me->cells[me->ncells].alignment == RESERVEDCELL) {
+ me->ncells++;
+ }
+ {
+ int growby = 0;
+
+ while (me->ncells + colspan + 1 >= me->allocated + growby)
+ growby += CELLS_GROWBY;
+ if (growby) {
+ if (me->allocated == 0 && !me->cells) {
+ cells = typecallocn(STable_cellinfo, (unsigned) growby);
+ } else {
+ cells = typeRealloc(STable_cellinfo, me->cells,
+ (unsigned) (me->allocated + growby));
+
+ for (i = 0; cells && i < growby; i++) {
+ cells[me->allocated + i].alignment = HT_ALIGN_NONE;
+ }
+ }
+ if (cells) {
+ me->allocated += growby;
+ me->cells = cells;
+ } else {
+ goto trace_and_fail;
+ }
+ }
+ }
+
+ me->cells[me->ncells].cLine = lineno;
+ me->cells[me->ncells].pos = *ppos;
+ me->cells[me->ncells].len = -1;
+ me->cells[me->ncells].colspan = colspan;
+
+ if (alignment != HT_ALIGN_NONE)
+ me->cells[me->ncells].alignment = alignment;
+ else {
+ if (ncolinfo >= me->ncells + 1)
+ me->cells[me->ncells].alignment = colinfo[me->ncells].alignment;
+ else
+ me->cells[me->ncells].alignment = me->alignment;
+ if (me->cells[me->ncells].alignment == HT_ALIGN_NONE)
+ me->cells[me->ncells].alignment = me->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].cLine = 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++;
+
+ ret = me->ncells - 1;
+ trace_and_return:
+ CTRACE2(TRACE_TRST,
+ (tfp, " => prev_state=%s, state=%s, ret=%d\n",
+ cellstate_s(s->prev_state), cellstate_s(s->state), ret));
+ return (ret);
+
+ trace_and_fail:
+ ret = -1;
+ goto trace_and_return;
+}
+
+/* returns -1 on error, 0 otherwise */
+/* assumes cells have already been allocated (but may need more) */
+static int Stbl_reserveCellsInRow(STable_rowinfo *me, int icell,
+ int colspan)
+{
+ STable_cellinfo *cells;
+ int i;
+ int growby = 1 + icell + colspan - me->allocated;
+
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_reserveCellsInRow(icell=%d, colspan=%d) growby=%d\n",
+ icell, colspan, growby));
+ if (growby > 0) {
+ cells = typeRealloc(STable_cellinfo, me->cells,
+ (unsigned) (me->allocated + growby));
+
+ if (cells) {
+ for (i = 0; i < growby; i++) {
+ cells[me->allocated + i].alignment = HT_ALIGN_NONE;
+ }
+ me->allocated += growby;
+ me->cells = cells;
+ } else {
+ return -1;
+ }
+ }
+ for (i = icell; i < icell + colspan; i++) {
+ me->cells[i].cLine = -1;
+ me->cells[i].pos = -1;
+ me->cells[i].len = -1;
+ me->cells[i].colspan = 0;
+ me->cells[i].alignment = RESERVEDCELL;
+ }
+ me->cells[icell].colspan = colspan;
+ return 0;
+}
+
+/* Returns -1 on failure. */
+static int Stbl_finishCellInRow(STable_rowinfo *me, STable_states *s, int end_td,
+ int lineno,
+ int pos)
+{
+ STable_cellinfo *lastcell;
+ cellstate_t newstate = CS_invalid;
+ int multiline = NO, empty;
+ int ret;
+
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_finishCellInRow line=%d pos=%d end_td=%d ncells=%d pnd_len=%d\n",
+ lineno, pos, (int) end_td, me->ncells, s->pending_len));
+
+ if (me->ncells <= 0)
+ return -1;
+ lastcell = me->cells + (me->ncells - 1);
+ multiline = (lineno != lastcell->cLine || lineno != s->lineno);
+ empty = multiline ? (pos == 0) : (pos <= s->x_td);
+
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ " [lines: lastCell=%d state=%d multi=%d] empty=%d (prev)state=(%s) %s\n",
+ lastcell->cLine, s->lineno, multiline, empty,
+ cellstate_s(s->prev_state), cellstate_s(s->state)));
+
+ if (multiline) {
+ if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
+ switch (s->state) {
+ case CS_invalid:
+ newstate = empty ? CS_invalid : CS__cbc;
+ break;
+ case CS__0new:
+ newstate = empty ? CS__0eb : CS__0cb;
+ break;
+ case CS__0eb:
+ newstate = empty ? CS__0eb : CS__ebc;
+ s->state = newstate;
+ if (empty) {
+ ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+ } else {
+ ret = (lastcell->len <= 0 ? 0 : -1);
+ }
+ goto trace_and_return;
+ case CS__0cb:
+ if (!me->fixed_line) {
+ if (!empty) {
+ if (s->icell_core == -1)
+ me->Line = -1;
+ }
+ }
+ if (s->pending_len && empty) { /* First line non-empty */
+ if ((me->fixed_line && me->Line == lastcell->cLine) ||
+ 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__0ef:
+ 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 (empty) {
+ ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+ } else {
+ ret = (lastcell->len <= 0 ? 0 : -1);
+ }
+ goto trace_and_return;
+ 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 !! */
+ ret = -1;
+ if (empty) {
+ if (!me->fixed_line) {
+ me->fixed_line = YES; /* type=b def of fixed_line i */
+ me->Line = lastcell->cLine; /* should've happened in break */
+ } else {
+ if (me->Line != lastcell->cLine)
+ goto trace_and_return;
+ }
+ newstate = CS__cb;
+ } else {
+ if (!me->fixed_line) {
+ me->fixed_line = YES; /* type=b def of fixed_line ii */
+ me->Line = lastcell->cLine; /* should've happened in break */
+ }
+ s->state = CS__cbc;
+ goto trace_and_return;
+ }
+ break;
+ case CS__ef:
+ ret = 0;
+ goto trace_and_return;
+ case CS__cf:
+ ret = lastcell->len; /* ##523_change_state? */
+ goto trace_and_return;
+ 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 { /* multiline cell, processing </TD>: */
+ 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;
+ ret = -1;
+ goto trace_and_return;
+ }
+ /* ##541_set_len_0_Line_-1_sometimes: */
+ lastcell->len = 0;
+ lastcell->cLine = -1;
+ /* fall thru ##546 really fall thru??: */
+ newstate = empty ? CS_invalid : CS__cbc;
+ break;
+ case CS__0new:
+ newstate = empty ? CS__0ef : CS__0cf;
+ break;
+ case CS__0eb:
+ newstate = empty ? CS__0ef : CS__0cf; /* ebc?? */
+ s->state = newstate;
+ if (empty) {
+ ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+ } else {
+ ret = (lastcell->len <= 0 ? 0 : -1);
+ }
+ goto trace_and_return;
+ 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)
+ /* first cell before <BR></TD> => the core cell */
+ s->icell_core = me->ncells - 1;
+ /* lastcell->cLine = 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__0ef:
+ newstate = CS__0ef;
+ /* FALLTHRU */
+ case CS__0cf:
+ break;
+ case CS__new:
+ newstate = empty ? CS__ef : CS__cf;
+ break;
+ case CS__eb:
+ newstate = CS__ef;
+ s->state = newstate;
+ if (empty) {
+ ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+ } else {
+ ret = (lastcell->len <= 0 ? 0 : -1);
+ }
+ goto trace_and_return;
+ case CS__cb:
+ if (s->pending_len && empty) {
+ lastcell->len = s->pending_len;
+ s->pending_len = 0;
+ }
+ ret = -1;
+ if (empty) {
+ if (!me->fixed_line) {
+ me->fixed_line = YES; /* type=c def of fixed_line */
+ me->Line = lastcell->cLine; /* should've happened in break */
+ } else {
+ if (me->Line != lastcell->cLine)
+ goto trace_and_return;
+ }
+ newstate = CS__cf;
+ } else {
+ goto trace_and_return;
+ }
+ 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->cLine = -1;
+ }
+ }
+ s->pending_len = 0;
+ newstate = empty ? CS_invalid : CS__cbc;
+ break;
+ case CS__cbc: /* ##586 */
+ lastcell->len = 0; /* ##613 */
+ ret = -1;
+ if (me->fixed_line && me->Line == lastcell->cLine)
+ goto trace_and_return;
+ if (!me->fixed_line) {
+ if (empty) {
+ if (s->icell_core == -1)
+ me->Line = lineno;
+ }
+ }
+ s->pending_len = 0; /* ##629 v */
+ newstate = empty ? CS_invalid : CS__cbc;
+ break;
+ }
+ }
+ } else { /* (!multiline) */
+ if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
+ switch (s->state) {
+ case CS_invalid:
+ case CS__0new:
+ s->pending_len = empty ? 0 : pos - lastcell->pos;
+ newstate = empty ? CS__0eb : CS__0cb;
+ s->state = newstate;
+ ret = 0; /* or 0 for xlen to s->pending_len?? */
+ goto trace_and_return;
+ case CS__0eb: /* cannot happen */
+ newstate = CS__eb;
+ break;
+ case CS__0cb: /* cannot happen */
+ newstate = CS__cb;
+ break;
+ case CS__0ef:
+ case CS__0cf:
+ break;
+ case CS__new:
+ ret = -1;
+ if (!empty && s->prev_state == CS__cbc) /* ##609: */
+ goto trace_and_return;
+ if (!empty) {
+ if (!me->fixed_line) {
+ me->fixed_line = YES; /* type=d def of fixed_line */
+ me->Line = lineno;
+ } else {
+ if (me->Line != lineno)
+ goto trace_and_return;
+ }
+ }
+ newstate = empty ? CS__eb : CS__cb;
+ s->state = newstate;
+ if (!me->fixed_line) {
+ s->pending_len = empty ? 0 : pos - lastcell->pos;
+ ret = 0;
+ goto trace_and_return;
+ } else {
+ s->pending_len = 0;
+ lastcell->len = empty ? 0 : pos - lastcell->pos;
+ ret = lastcell->len;
+ goto trace_and_return;
+ }
+ 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:
+ ret = 0;
+ goto trace_and_return;
+ case CS__cf:
+ ret = lastcell->len;
+ goto trace_and_return;
+ case CS__cbc: /* ??? */
+ break;
+ default:
+ break;
+ }
+ } else { /* !multiline, processing </TD>: */
+ s->x_td = -1;
+ switch (s->state) {
+ case CS_invalid: /* ##691_no_lastcell_len_for_invalid: */
+ if (!(me->fixed_line && me->Line == lastcell->cLine))
+ lastcell->len = 0;
+ /* FALLTHRU */
+ case CS__0new:
+ newstate = empty ? CS__0ef : CS__0cf;
+ break; /* ##630 */
+ case CS__0eb:
+ newstate = CS__0ef;
+ break; /* ??? */
+ case CS__0cb:
+ newstate = empty ? CS__0cf : CS__cbc;
+ break; /* ??? */
+ case CS__0ef:
+ newstate = CS__0ef;
+ break; /* ??? */
+ case CS__0cf:
+ break; /* ??? */
+ case CS__new:
+ ret = -1;
+ if (!empty && s->prev_state == CS__cbc)
+ goto trace_and_return;
+ if (!empty) { /* ##642_set_fixed!: */
+ if (!me->fixed_line) {
+ me->fixed_line = YES; /* type=e def of fixed_line */
+ me->Line = lineno;
+ } else {
+ if (me->Line != lineno)
+ goto trace_and_return;
+ }
+ }
+ if (lastcell->len < 0)
+ lastcell->len = empty ? 0 : pos - lastcell->pos;
+ newstate = empty ? CS__ef : CS__cf;
+ s->state = newstate;
+ ret = ((me->fixed_line && lineno != me->Line)
+ ? -1 : lastcell->len);
+ goto trace_and_return;
+ case CS__eb:
+ newstate = empty ? CS__ef : CS__cf;
+ break; /* ??? */
+ case CS__cb:
+ newstate = CS__cf;
+ break; /* ??? */
+ case CS__ef: /* ignored error */
+ case CS__cf: /* ignored error */
+ default:
+ break;
+ }
+ lastcell->len = pos - lastcell->pos;
+ } /* if (!end_td) ... else */
+ } /* if (multiline) ... else */
+
+ s->state = newstate;
+ ret = lastcell->len;
+#ifdef EXP_NESTED_TABLES
+ if (nested_tables) {
+ if (ret == -1 && pos == 0)
+ ret = 0; /* XXXX Hack to allow trailing <P> in multiline cells. */
+ }
+#endif
+
+/* lastcell->len = pos - lastcell->pos; */
+ trace_and_return:
+ CTRACE2(TRACE_TRST,
+ (tfp, " => prev_state=%s, state=%s, return=%d\n",
+ cellstate_s(s->prev_state), cellstate_s(s->state), ret));
+ return ret;
+}
+
+/*
+ * Reserve cells, each of given colspan, in (rowspan-1) rows after the current
+ * row of rowspan>1. If rowspan==0, use special 'row' rowspans2eog to keep
+ * track of rowspans that are to remain in effect until the end of the row
+ * group (until next THEAD/TFOOT/TBODY) or table.
+ */
+static int Stbl_reserveCellsInTable(STable_info *me, int icell,
+ int colspan,
+ int rowspan)
+{
+ STable_rowinfo *rows, *row;
+ int growby;
+ int i;
+
+ if (colspan > TRST_MAXCOLSPAN) {
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:*** COLSPAN=%d is too large, ignored!\n",
+ colspan));
+ return -1;
+ }
+ if (rowspan > TRST_MAXROWSPAN) {
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:*** ROWSPAN=%d is too large, ignored!\n",
+ rowspan));
+ return -1;
+ }
+ if (me->nrows <= 0)
+ return -1; /* must already have at least one row */
+
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_reserveCellsInTable(icell=%d, colspan=%d, rowspan=%d)\n",
+ icell, colspan, rowspan));
+ if (rowspan == 0) {
+ if (!me->rowspans2eog.cells) {
+ me->rowspans2eog.cells = typecallocn(STable_cellinfo,
+ (unsigned) HTMAX(1, icell + colspan));
+
+ if (!me->rowspans2eog.cells)
+ return 0; /* fail silently */
+ else
+ me->rowspans2eog.allocated = icell + colspan;
+ }
+ Stbl_reserveCellsInRow(&me->rowspans2eog, icell, colspan);
+ }
+
+ growby = me->nrows + rowspan - 1 - me->allocated_rows;
+ if (growby > 0) {
+ rows = typeRealloc(STable_rowinfo, me->rows,
+ (unsigned) (me->allocated_rows + growby));
+
+ if (!rows)
+ return 0; /* ignore silently, no free memory, may be recoverable */
+ for (i = 0; i < growby; i++) {
+ row = rows + me->allocated_rows + i;
+ row->allocated = 0;
+ row->offset = 0;
+ row->content = 0;
+ if (!me->rowspans2eog.allocated) {
+ row->cells = NULL;
+ } else {
+ row->cells = typecallocn(STable_cellinfo,
+ (unsigned) me->rowspans2eog.allocated);
+
+ if (row->cells) {
+ row->allocated = me->rowspans2eog.allocated;
+ memcpy(row->cells, me->rowspans2eog.cells,
+ ((unsigned) row->allocated * sizeof(STable_cellinfo)));
+ }
+ }
+ row->ncells = 0;
+ row->fixed_line = NO;
+ row->alignment = HT_ALIGN_NONE;
+ }
+ me->allocated_rows += growby;
+ me->rows = rows;
+ }
+ for (i = me->nrows;
+ i < (rowspan == 0 ? me->allocated_rows : me->nrows + rowspan - 1);
+ i++) {
+ if (!me->rows[i].allocated) {
+ me->rows[i].cells = typecallocn(STable_cellinfo,
+ (unsigned) HTMAX(1,
+ icell
+ + colspan));
+
+ if (!me->rows[i].cells)
+ return 0; /* fail silently */
+ else
+ me->rows[i].allocated = icell + colspan;
+ }
+ Stbl_reserveCellsInRow(me->rows + i, icell, colspan);
+ }
+ return 0;
+}
+
+/* Remove reserved cells in trailing rows that were added for rowspan,
+ * to be used when a THEAD/TFOOT/TBODY ends. */
+static void Stbl_cancelRowSpans(STable_info *me)
+{
+ int i;
+
+ CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_cancelRowSpans()"));
+ for (i = me->nrows; i < me->allocated_rows; i++) {
+ if (!me->rows[i].ncells) { /* should always be the case */
+ FREE(me->rows[i].cells);
+ me->rows[i].allocated = 0;
+ }
+ }
+ free_rowinfo(&me->rowspans2eog);
+ me->rowspans2eog.allocated = 0;
+}
+
+/*
+ * Returns -1 on error, otherwise index of just-added table row.
+ */
+int Stbl_addRowToTable(STable_info *me, int alignment,
+ int lineno)
+{
+ STable_rowinfo *rows, *row;
+ STable_states *s = &me->s;
+
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_addRowToTable(alignment=%d, lineno=%d)\n",
+ alignment, lineno));
+ 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;
+ }
+ 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 = typecallocn(STable_rowinfo, (unsigned) growby);
+ } else {
+ rows = typeRealloc(STable_rowinfo, me->rows,
+ (unsigned) (me->allocated_rows + growby));
+
+ for (i = 0; rows && i < growby; i++) {
+ row = rows + me->allocated_rows + i;
+ if (!me->rowspans2eog.allocated) {
+ row->allocated = 0;
+ row->cells = NULL;
+ } else {
+ row->cells = typecallocn(STable_cellinfo,
+ (unsigned) me->rowspans2eog.allocated);
+
+ if (row->cells) {
+ row->allocated = me->rowspans2eog.allocated;
+ memcpy(row->cells, me->rowspans2eog.cells,
+ (unsigned) row->allocated * sizeof(STable_cellinfo));
+ } else {
+ FREE(rows);
+ break;
+ }
+ }
+ row->ncells = 0;
+ row->fixed_line = NO;
+ row->alignment = HT_ALIGN_NONE;
+ row->offset = 0;
+ row->content = 0;
+ }
+ }
+ 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;
+ if (alignment != HT_ALIGN_NONE)
+ me->rows[me->nrows].alignment = alignment;
+ else
+ me->rows[me->nrows].alignment =
+ (me->rowgroup_align == HT_ALIGN_NONE) ?
+ me->alignment : me->rowgroup_align;
+ me->nrows++;
+ if (me->pending_colgroup_next > me->ncolinfo) {
+ me->ncolinfo = me->pending_colgroup_next;
+ me->pending_colgroup_next = 0;
+ }
+ me->rows[me->nrows].Line = -1; /* not yet used */
+ me->rows[me->nrows].ended = ROW_not_ended; /* No </tr> yet */
+ return (me->nrows - 1);
+}
+
+/*
+ * Returns -1 on error, otherwise current number of rows.
+ */
+static int Stbl_finishRowInTable(STable_info *me)
+{
+ STable_rowinfo *lastrow;
+ STable_states *s = &me->s;
+
+ CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishRowInTable()\n"));
+ if (!me->rows || !me->nrows)
+ return -1; /* no row started! */
+ lastrow = me->rows + (me->nrows - 1);
+ lastrow->ended = ROW_ended_by_endtr;
+ 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 (s->icell_core >= 0 && !lastrow->fixed_line &&
+ lastrow->cells[s->icell_core].cLine >= 0)
+ lastrow->Line = lastrow->cells[s->icell_core].cLine;
+ s->icell_core = -1;
+ return (me->nrows);
+}
+
+static void update_sumcols0(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;
+ }
+ }
+ }
+ }
+}
+
+static int get_remaining_colspan(STable_rowinfo *me,
+ STable_cellinfo *colinfo,
+ int ncolinfo,
+ int colspan,
+ int ncols_sofar)
+{
+ int i;
+ int last_colspan = (me->ncells
+ ? me->cells[me->ncells - 1].colspan
+ : 1);
+
+ if (ncolinfo == 0 || me->ncells + last_colspan > ncolinfo) {
+ colspan = HTMIN(TRST_MAXCOLSPAN,
+ ncols_sofar - (me->ncells + last_colspan - 1));
+ colspan = HTMAX(colspan, 0);
+ } else {
+ for (i = me->ncells + last_colspan - 1; i < ncolinfo - 1; i++)
+ if (colinfo[i].cLine == EOCOLG)
+ break;
+ colspan = i - (me->ncells + last_colspan - 2);
+ }
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:get_remaining_colspan; colspan = %d\n",
+ colspan));
+ return colspan;
+}
+
+#ifdef EXP_NESTED_TABLES
+/* Returns -1 on failure, 1 if faking was performed, 0 if not needed. */
+static int Stbl_fakeFinishCellInTable(STable_info *me,
+ STable_rowinfo *lastrow,
+ int lineno,
+ int finishing) /* Processing finish or start */
+{
+ STable_states *s = &me->s;
+ int fake = 0;
+
+ switch (s->state) { /* We care only about trailing <BR> */
+ case CS_invalid:
+ case CS__0new:
+ case CS__0ef:
+ case CS__0cf:
+ case CS__new:
+ case CS__cbc:
+ case CS__ef:
+ case CS__cf:
+ default:
+ /* <BR></TD> may produce these (XXXX instead of CS__cbf?). But if
+ finishing==0, the caller already checked that we are on a
+ different line. */
+ if (finishing == 0)
+ fake = 1;
+ break; /* Either can't happen, or may be ignored */
+ case CS__eb:
+ case CS__0eb:
+ case CS__0cb:
+ case CS__cb:
+ fake = 1;
+ break;
+ }
+ if (fake) {
+ /* The previous action we did was putting a linebreak. Now we
+ want to put another one. Fake necessary
+ </TD></TR><TR><TD></TD><TD> (and possibly </TD>) instead. */
+ int ncells = lastrow->ncells;
+ int i;
+ int al = lastrow->alignment;
+ int cs = lastrow->cells[lastrow->ncells - 1].colspan;
+ int rs = 1; /* XXXX How to find rowspan? */
+ int ih = 0; /* XXXX How to find is_header? */
+ int end_td = (TRST_ENDCELL_ENDTD | TRST_FAKING_CELLS);
+ int need_reserved = 0;
+ int prev_reserved_last = -1;
+ STable_rowinfo *prev_row;
+ int prev_row_n2 = (int) (lastrow - me->rows);
+
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_fakeFinishCellInTable(lineno=%d, finishing=%d) START FAKING\n",
+ lineno, finishing));
+
+ /* Although here we use pos=0, this may commit the previous
+ cell which had <BR> as a last element. This may overflow
+ the screen width, so the additional checks performed in
+ Stbl_finishCellInTable (comparing to Stbl_finishCellInRow)
+ are needed. */
+ if (finishing) {
+ /* Fake </TD> at BOL */
+ if (Stbl_finishCellInTable(me, end_td, lineno, 0, 0) < 0) {
+ return -1;
+ }
+ }
+
+ /* Fake </TR> at BOL */
+ /* Stbl_finishCellInTable(lineno, 0, 0); */
+ /* Needed? */
+
+ /* Fake <TR> at BOL */
+ if (Stbl_addRowToTable(me, al, lineno) < 0) {
+ return -1;
+ }
+ lastrow = me->rows + (me->nrows - 1);
+ lastrow->content = IS_CONTINUATION_OF_CELL;
+ for (i = 0; i < lastrow->allocated; i++) {
+ if (lastrow->cells[i].alignment == RESERVEDCELL) {
+ need_reserved = 1;
+ break;
+ }
+ }
+
+ prev_row = me->rows + prev_row_n2;
+ for (i = ncells; i < prev_row->allocated; i++) {
+ if (prev_row->cells[i].alignment == RESERVEDCELL)
+ prev_reserved_last = i;
+ }
+ if (need_reserved || prev_reserved_last >= 0) {
+ /* Oups, we are going to stomp over a line which somebody
+ cares about already, or the previous line had reserved
+ cells which were not skipped over.
+
+ Remember that STable_rowinfo is about logical (TR)
+ table lines, not displayed lines. We need to duplicate
+ the reservation structure when we fake new logical lines. */
+ int prev_row_n = (int) (prev_row - me->rows);
+ STable_rowinfo *rows = typeRealloc(STable_rowinfo, me->rows,
+ (unsigned) (me->allocated_rows
+ + 1));
+ int need_cells = prev_reserved_last + 1;
+ int n;
+
+ if (!rows)
+ return -1; /* ignore silently, no free memory, may be recoverable */
+
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_fakeFinishCellInTable REALLOC ROWSPAN\n"));
+ me->rows = rows;
+ lastrow = me->rows + (me->nrows - 1);
+ prev_row = me->rows + prev_row_n;
+ me->allocated_rows++;
+
+ /* Insert a duplicate row after lastrow */
+ for (n = me->allocated_rows - me->nrows - 1; n >= 0; --n)
+ lastrow[n + 1] = lastrow[n];
+
+ /* Ignore cells, they belong to the next row now */
+ lastrow->allocated = 0;
+ lastrow->cells = 0;
+ if (need_cells) {
+ lastrow->cells = typecallocn(STable_cellinfo, (unsigned) need_cells);
+
+ /* ignore silently, no free memory, may be recoverable */
+ if (!lastrow->cells) {
+ return -1;
+ }
+ lastrow->allocated = need_cells;
+ memcpy(lastrow->cells, prev_row->cells,
+ (unsigned) lastrow->allocated * sizeof(STable_cellinfo));
+
+ i = -1;
+ while (++i < ncells) {
+ /* Stbl_addCellToTable grants RESERVEDCELL, but we do not
+ want this action for fake cells.
+ XXX Maybe always fake RESERVEDCELL instead of explicitly
+ creating/destroying cells? */
+ if (lastrow->cells[i].alignment == RESERVEDCELL)
+ lastrow->cells[i].alignment = HT_LEFT;
+ }
+ }
+ }
+
+ /* Fake <TD></TD>...<TD> (and maybe a </TD>) at BOL. */
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_fakeFinishCellInTable FAKE %d elts%s\n",
+ ncells, (finishing ? ", last unfinished" : "")));
+ i = 0;
+ while (++i <= ncells) {
+ /* XXXX A lot of args may be wrong... */
+ if (Stbl_addCellToTable(me, (i == ncells ? cs : 1), rs, al,
+ ih, lineno, 0, 0) < 0) {
+ return -1;
+ }
+ lastrow->content &= ~HAS_BEG_OF_CELL; /* BEG_OF_CELL was fake */
+ /* We cannot run out of width here, so it is safe to not
+ call Stbl_finishCellInTable(), but Stbl_finishCellInRow. */
+ if (!finishing || (i != ncells)) {
+ if (Stbl_finishCellInRow(lastrow, s, end_td, lineno, 0) < 0) {
+ return -1;
+ }
+ }
+ }
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_fakeFinishCellInTable(lineno=%d) FINISH FAKING\n",
+ lineno));
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+int Stbl_addCellToTable(STable_info *me, int colspan,
+ int rowspan,
+ int alignment,
+ int isheader,
+ int lineno,
+ int offset_not_used_yet GCC_UNUSED,
+ int pos)
+{
+ STable_states *s = &me->s;
+ STable_rowinfo *lastrow;
+ STable_cellinfo *sumcols, *sumcol;
+ int i, icell, ncells, sumpos;
+
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_addCellToTable(lineno=%d, pos=%d, isheader=%d, cs=%d, rs=%d, al=%d)\n",
+ lineno, pos, (int) isheader, colspan, rowspan, alignment));
+ if (!me->rows || !me->nrows)
+ return -1; /* no row started! */
+ /* ##850_fail_if_fail?? */
+ if (me->rows[me->nrows - 1].ended != ROW_not_ended) {
+ Stbl_addRowToTable(me, alignment, lineno);
+ }
+ Stbl_finishCellInTable(me, TRST_ENDCELL_ENDTD, lineno, 0, pos);
+ lastrow = me->rows + (me->nrows - 1);
+
+#ifdef EXP_NESTED_TABLES
+ if (nested_tables) {
+ /* If the last cell was finished by <BR></TD>, we need to fake an
+ appropriate amount of cells */
+ if (!NO_AGGRESSIVE_NEWROW && pos == 0 && lastrow->ncells > 0
+ && lastrow->cells[lastrow->ncells - 1].cLine != lineno) {
+ int rc;
+
+ rc = Stbl_fakeFinishCellInTable(me, lastrow, lineno, 0);
+
+ if (rc < 0)
+ return -1;
+ if (rc)
+ lastrow = me->rows + (me->nrows - 1);
+ }
+ }
+#endif
+ if (colspan == 0) {
+ colspan = get_remaining_colspan(lastrow, me->sumcols, me->ncolinfo,
+ colspan, me->ncols);
+ }
+ ncells = lastrow->ncells; /* remember what it was before adding cell. */
+ icell = Stbl_addCellToRow(lastrow, me->sumcols, me->ncolinfo, s,
+ colspan, alignment, isheader,
+ lineno, &pos);
+ if (icell < 0)
+ return icell;
+ if (me->nrows == 1 && me->startline < lastrow->Line)
+ me->startline = lastrow->Line;
+
+ if (rowspan != 1) {
+ Stbl_reserveCellsInTable(me, icell, colspan, rowspan);
+ /* me->rows may now have been realloc'd, make lastrow valid pointer */
+ lastrow = me->rows + (me->nrows - 1);
+ }
+ lastrow->content |= HAS_BEG_OF_CELL;
+
+ {
+ int growby = 0;
+
+ while (icell + colspan + 1 >= me->allocated_sumcols + growby)
+ growby += CELLS_GROWBY;
+ if (growby) {
+ if (me->allocated_sumcols == 0 && !me->sumcols) {
+ sumcols = typecallocn(STable_cellinfo, (unsigned) growby);
+ } else {
+ sumcols = typeRealloc(STable_cellinfo, me->sumcols,
+ (unsigned) (me->allocated_sumcols + growby));
+
+ 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;
+ sumcol->cLine = 0;
+ sumcol->alignment = HT_ALIGN_NONE;
+ }
+ }
+ if (sumcols) {
+ me->allocated_sumcols += growby;
+ me->sumcols = sumcols;
+ } else {
+ return -1;
+ }
+ }
+ }
+ 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 - me->sumcols[icell].pos,
+ icell, 0, me->allocated_sumcols);
+
+ 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.
+ */
+int Stbl_finishCellInTable(STable_info *me, int end_td,
+ int lineno,
+ int offset,
+ int pos)
+{
+ STable_states *s = &me->s;
+ STable_rowinfo *lastrow;
+ int len, xlen, icell;
+ int i;
+
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_finishCellInTable(lineno=%d, pos=%d, off=%d, end_td=%d)\n",
+ lineno, pos, offset, (int) end_td));
+ 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) { /* Stray </TD> or just-in-case, as on </TR> */
+ if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK)
+ lastrow->ended = ROW_ended_by_splitline;
+ return 0;
+ }
+#ifdef EXP_NESTED_TABLES
+ if (nested_tables) {
+ if (!NO_AGGRESSIVE_NEWROW && !(end_td & TRST_FAKING_CELLS)) {
+ int rc;
+
+ rc = Stbl_fakeFinishCellInTable(me, lastrow, lineno, 1);
+
+ if (rc) {
+ if (rc < 0)
+ return -1;
+ lastrow = me->rows + (me->nrows - 1);
+ icell = lastrow->ncells - 1;
+ }
+ }
+ }
+#endif
+ len = Stbl_finishCellInRow(lastrow, s, end_td, lineno, pos);
+ if (len == -1) {
+ return len;
+ }
+ xlen = (len > 0) ? len : s->pending_len; /* ##890 use xlen if fixed_line?: */
+ if (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);
+ } 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 ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
+ lastrow->ended = ROW_ended_by_splitline;
+ lastrow->content |= BELIEVE_OFFSET;
+ lastrow->offset = offset;
+ }
+#ifdef EXP_NESTED_TABLES /* maxlen may already include contribution of a cell in this column */
+ if (nested_tables) {
+ if (me->maxlen > MAX_STBL_POS) {
+ return -1;
+ }
+ } else
+#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;
+}
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+int Stbl_addColInfo(STable_info *me,
+ int colspan,
+ int alignment,
+ int isgroup)
+{
+ STable_cellinfo *sumcols, *sumcol;
+ int i, icolinfo;
+
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_addColInfo(cs=%d, al=%d, isgroup=%d)\n",
+ colspan, alignment, (int) isgroup));
+ if (isgroup) {
+ if (me->pending_colgroup_next > me->ncolinfo)
+ me->ncolinfo = me->pending_colgroup_next;
+ me->pending_colgroup_next = me->ncolinfo + colspan;
+ if (me->ncolinfo > 0)
+ me->sumcols[me->ncolinfo - 1].cLine = EOCOLG;
+ me->pending_colgroup_align = (short) alignment;
+ } else {
+ for (i = me->pending_colgroup_next - 1;
+ i >= me->ncolinfo + colspan; i--)
+ me->sumcols[i].alignment = HT_ALIGN_NONE;
+ me->pending_colgroup_next = me->ncolinfo + colspan;
+ }
+ icolinfo = me->ncolinfo;
+ if (!isgroup)
+ me->ncolinfo += colspan;
+
+ {
+ int growby = 0;
+
+ while (icolinfo + colspan + 1 >= me->allocated_sumcols + growby)
+ growby += CELLS_GROWBY;
+ if (growby) {
+ if (me->allocated_sumcols == 0) {
+ sumcols = typecallocn(STable_cellinfo, (unsigned) growby);
+ } else {
+ sumcols = typeRealloc(STable_cellinfo, me->sumcols,
+ (unsigned) (me->allocated_sumcols + growby));
+
+ 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;
+ sumcol->cLine = 0;
+ }
+ }
+ if (sumcols) {
+ me->allocated_sumcols += growby;
+ me->sumcols = sumcols;
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ if (alignment == HT_ALIGN_NONE)
+ alignment = me->pending_colgroup_align;
+ for (i = icolinfo; i < icolinfo + colspan; i++) {
+ me->sumcols[i].alignment = alignment;
+ }
+ return 0;
+}
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+int Stbl_finishColGroup(STable_info *me)
+{
+ CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishColGroup()\n"));
+ if (me->pending_colgroup_next >= me->ncolinfo) {
+ me->ncolinfo = me->pending_colgroup_next;
+ if (me->ncolinfo > 0)
+ me->sumcols[me->ncolinfo - 1].cLine = EOCOLG;
+ }
+ me->pending_colgroup_next = 0;
+ me->pending_colgroup_align = HT_ALIGN_NONE;
+ return 0;
+}
+
+int Stbl_addRowGroup(STable_info *me, int alignment)
+{
+ CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_addRowGroup()\n"));
+ Stbl_cancelRowSpans(me);
+ me->rowgroup_align = (short) alignment;
+ return 0; /* that's all! */
+}
+
+int Stbl_finishTABLE(STable_info *me)
+{
+ STable_states *s = &me->s;
+ int i;
+ int curpos = 0;
+
+ CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishTABLE()\n"));
+ 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;
+ }
+ Stbl_finishRowInTable(me);
+ /* take into account offsets on multi-line cells.
+ XXX We cannot do it honestly, since two cells on the same row may
+ participate in multi-line table entries, and we preserve only
+ one offset per row. This implementation may ignore
+ horizontal offsets for the last row of a multirow table entry. */
+ for (i = 0; i < me->nrows - 1; i++) {
+ int j = i + 1, leading = i, non_empty = 0;
+ STable_rowinfo *nextrow = me->rows + j;
+ int minoffset, have_offsets;
+ int foundcell = -1, max_width;
+
+ if ((nextrow->content & (IS_CONTINUATION_OF_CELL | HAS_BEG_OF_CELL | BELIEVE_OFFSET))
+ != (IS_CONTINUATION_OF_CELL | BELIEVE_OFFSET))
+ continue; /* Not a continuation line */
+ minoffset = nextrow[-1].offset; /* Line before first continuation */
+ CTRACE2(TRACE_TRST, (tfp,
+ "TRST:Stbl_finishTABLE, l=%d, offset=%d, ended=%u.\n",
+ i, nextrow[-1].offset, nextrow[-1].ended));
+
+ /* Find the common part of the requested offsets */
+ while (j < me->nrows
+ && ((nextrow->content &
+ (IS_CONTINUATION_OF_CELL
+ | HAS_BEG_OF_CELL
+ | BELIEVE_OFFSET))
+ == (IS_CONTINUATION_OF_CELL | BELIEVE_OFFSET))) {
+ if (minoffset > nextrow->offset)
+ minoffset = nextrow->offset;
+ CTRACE2(TRACE_TRST,
+ (tfp,
+ "TRST:Stbl_finishTABLE, l=%d, offset=%d, ended=%u.\n",
+ j, nextrow->offset, nextrow[-1].ended));
+ nextrow++;
+ j++;
+ }
+ i = j - 1; /* Continue after this line */
+ /* Cancel the common part of offsets */
+ j = leading; /* Restart */
+ nextrow = me->rows + j; /* Line before first continuation */
+ have_offsets = 0;
+ nextrow->content |= OFFSET_IS_VALID_LAST_CELL;
+ while (j <= i) { /* A continuation line */
+ nextrow->offset -= minoffset;
+ nextrow->content |= OFFSET_IS_VALID;
+ if (nextrow->offset)
+ have_offsets = 1;
+ nextrow++;
+ j++;
+ }
+ if (!have_offsets)
+ continue; /* No offsets to deal with */
+
+ /* Find the cell number */
+ foundcell = -1;
+ j = leading + 1; /* Restart */
+ nextrow = me->rows + j; /* First continuation line */
+ while (foundcell == -1 && j <= i) { /* A continuation line */
+ int curcell = -1;
+
+ while (foundcell == -1 && ++curcell < nextrow->ncells)
+ if (nextrow->cells[curcell].len)
+ foundcell = curcell, non_empty = j;
+ nextrow++;
+ j++;
+ }
+ if (foundcell == -1) /* Can it happen? */
+ continue;
+ /* Find the max width */
+ max_width = 0;
+ j = leading; /* Restart */
+ nextrow = me->rows + j; /* Include the pre-continuation line */
+ while (j <= i) { /* A continuation line */
+ if (nextrow->ncells > foundcell) {
+ int curwid = nextrow->cells[foundcell].len + nextrow->offset;
+
+ if (curwid > max_width)
+ max_width = curwid;
+ }
+ nextrow++;
+ j++;
+ }
+ /* Update the widths */
+ j = non_empty; /* Restart from the first nonempty */
+ nextrow = me->rows + j;
+ /* Register the increase of the width */
+ update_sumcols0(me->sumcols, me->rows + non_empty,
+ 0 /* width only */ , max_width,
+ foundcell, nextrow->cells[foundcell].colspan,
+ me->allocated_sumcols);
+ j = leading; /* Restart from pre-continuation */
+ nextrow = me->rows + j;
+ while (j <= i) { /* A continuation line */
+ if (nextrow->ncells > foundcell)
+ nextrow->cells[foundcell].len = max_width;
+ nextrow++;
+ j++;
+ }
+ } /* END of Offsets processing */
+
+ 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;
+ }
+ }
+ /* need to recheck curpos: though it is checked each time a cell
+ is added, sometimes the result is ignored, as in split_line(). */
+ return (curpos > MAX_STBL_POS ? -1 : me->ncols);
+}
+
+short Stbl_getAlignment(STable_info *me)
+{
+ return (short) (me ? me->alignment : HT_ALIGN_NONE);
+}
+
+static int get_fixup_positions(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) {
+ int offset;
+
+ next_i = i + HTMAX(1, me->cells[i].colspan);
+ if (me->cells[i].cLine != me->Line) {
+ if (me->cells[i].cLine > me->Line)
+ break;
+ i = next_i;
+ continue;
+ }
+ oldpos[ip] = me->cells[i].pos;
+ if ((me->content & OFFSET_IS_VALID)
+ && (i == me->ncells - 1
+ || !((me->content & OFFSET_IS_VALID_LAST_CELL))))
+ offset = me->offset;
+ else
+ offset = 0;
+ newpos[ip] = sumcols[i].pos + offset;
+ 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[ip] += newlen - me->cells[i].len;
+ } else {
+ newpos[ip] += (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.
+ */
+int Stbl_getFixupPositions(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;
+}
+
+int Stbl_getStartLine(STable_info *me)
+{
+ if (!me)
+ return -1;
+ else
+ return me->startline;
+}
+
+#ifdef EXP_NESTED_TABLES
+
+int Stbl_getStartLineDeep(STable_info *me)
+{
+ if (!me)
+ return -1;
+ while (me->enclosing)
+ me = me->enclosing;
+ return me->startline;
+}
+
+void Stbl_update_enclosing(STable_info *me, int max_width,
+ int last_lineno)
+{
+ int l;
+
+ if (!me || !me->enclosing || !max_width)
+ return;
+ CTRACE2(TRACE_TRST,
+ (tfp, "TRST:Stbl_update_enclosing, width=%d, lines=%d...%d.\n",
+ max_width, me->startline, last_lineno));
+ for (l = me->startline; l <= last_lineno; l++) {
+ /* Fake <BR> in appropriate positions */
+ if (Stbl_finishCellInTable(me->enclosing,
+ TRST_ENDCELL_LINEBREAK,
+ l,
+ 0,
+ max_width) < 0) {
+ /* It is not handy to let the caller delete me->enclosing,
+ and it does not buy us anything. Do it directly. */
+ STable_info *stbl = me->enclosing;
+
+ CTRACE2(TRACE_TRST, (tfp,
+ "TRST:Stbl_update_enclosing: width too large, aborting enclosing\n"));
+ me->enclosing = 0;
+ while (stbl) {
+ STable_info *enclosing = stbl->enclosing;
+
+ Stbl_free(stbl);
+ stbl = enclosing;
+ }
+ break;
+ }
+ }
+ return;
+}
+
+void Stbl_set_enclosing(STable_info *me, STable_info *enclosing, struct _TextAnchor *enclosing_last_anchor_before_stbl)
+{
+ if (!me)
+ return;
+ me->enclosing = enclosing;
+ me->enclosing_last_anchor_before_stbl = enclosing_last_anchor_before_stbl;
+}
+
+STable_info *Stbl_get_enclosing(STable_info *me)
+{
+ if (!me)
+ return 0;
+ return me->enclosing;
+}
+
+struct _TextAnchor *Stbl_get_last_anchor_before(STable_info *me)
+{
+ if (!me)
+ return 0;
+ return me->enclosing_last_anchor_before_stbl;
+}
+#endif