1 /*
2  * $LynxId: TRSTable.c,v 1.31 2013/05/01 01:00:38 tom Exp $
3  *		Simple table object
4  *		===================
5  * Authors
6  *	KW	Klaus Weide <kweide@enteract.com>
7  * History:
8  *   2 Jul 1999	KW	Created.
9  */
10 
11 #include <HTUtils.h>
12 #include <HTStyle.h>		/* for HT_LEFT, HT_CENTER, HT_RIGHT */
13 #include <LYCurses.h>
14 #include <TRSTable.h>
15 #include <LYGlobalDefs.h>
16 
17 #include <LYLeaks.h>
18 
19 #ifdef SAVE_TIME_NOT_SPACE
20 #define CELLS_GROWBY 16
21 #define ROWS_GROWBY 16
22 #else
23 #define CELLS_GROWBY 2
24 #define ROWS_GROWBY 2
25 #endif
26 
27 #ifdef USE_CURSES_PADS
28 #  define MAX_STBL_POS (LYwideLines ? MAX_COLS - 1 : LYcolLimit)
29 #else
30 #  define MAX_STBL_POS (LYcolLimit)
31 #endif
32 
33 /* must be different from HT_ALIGN_NONE and HT_LEFT, HT_CENTER etc.: */
34 #define RESERVEDCELL (-2)	/* cell's alignment field is overloaded, this
35 				   value means cell was reserved by ROWSPAN */
36 #define EOCOLG (-2)		/* sumcols' Line field isn't used for line info, this
37 				   special value means end of COLGROUP */
38 #ifndef NO_AGGRESSIVE_NEWROW
39 #  define NO_AGGRESSIVE_NEWROW	0
40 #endif
41 
42 typedef enum {
43     CS_invalid = -1,		/* cell "before the first",
44 				   or empty lines after [ce]bc,
45 				   or TRST aborted */
46     CS__new = 0,
47     CS__0new,			/* new, at BOL */
48     CS__0eb,			/* starts at BOL, empty, break */
49     CS__eb,			/* empty, break */
50     CS__0cb,			/* starts at BOL, content, break */
51     CS__cb,			/* content, break */
52     CS__0ef,			/* starts at BOL, empty, finished */
53     CS__ef,			/* empty, finished */
54     CS__0cf,			/* starts at BOL, content, finished */
55     CS__cf,			/* content, finished */
56     CS__ebc,			/* empty, break, more content (maybe @BOL) */
57     CS__cbc			/* content, break, more content (maybe @BOL) */
58 } cellstate_t;
59 
60 typedef struct _STable_states {
61     cellstate_t prev_state;	/* Contents type of the previous cell */
62     cellstate_t state;		/* Contents type of the worked-on cell */
63     int lineno;			/* Start line of the current cell */
64     int icell_core;		/* -1 or the 1st cell with <BR></TD> on row */
65     int x_td;			/* x start pos of the current cell or -1 */
66     int pending_len;		/* For multiline cells, the length of
67 				   the part on the first line (if
68 				   state is CS__0?[ec]b) (??), or 0 */
69 } STable_states;
70 
71 typedef struct _STable_cellinfo {
72     int cLine;			/* lineno in doc (zero-based): -1 for
73 				   contentless cells (and cells we do
74 				   not want to measure and count?),
75 				   line-of-the-start otherwise.  */
76     int pos;			/* column where cell starts */
77     int len;			/* number of character positions */
78     int colspan;		/* number of columns to span */
79     int alignment;		/* one of HT_LEFT, HT_CENTER, HT_RIGHT,
80 				   or RESERVEDCELL */
81 } STable_cellinfo;
82 
83 enum ended_state {
84     ROW_not_ended,
85     ROW_ended_by_endtr,
86     ROW_ended_by_splitline
87 };
88 
89 #define HAS_END_OF_CELL			1
90 #define HAS_BEG_OF_CELL			2
91 #define IS_CONTINUATION_OF_CELL		4
92 #define OFFSET_IS_VALID			8
93 #define OFFSET_IS_VALID_LAST_CELL	0x10
94 #define BELIEVE_OFFSET			0x20
95 
96 typedef struct _STable_rowinfo {
97     /* Each row may be displayed on many display lines, but we fix up
98        positions of cells on this display line only: */
99     int Line;			/* lineno in doc (zero-based) */
100     int ncells;			/* number of table cells */
101 
102     /* What is the meaning of this?!  It is set if:
103        [search for      def of fixed_line       below]
104 
105        a1) a non-last cell is not at BOL,
106        a2) a non-last cell has something on the first line,
107        b) a >=3-lines-cell not at BOL, the first row non-empty, the 2nd empty;
108        c) a multiline cell not at BOL, the first row non-empty, the rest empty;
109        d) a multiline cell not at BOL, the first row non-empty;
110        e) a singleline non-empty cell not at BOL;
111 
112        Summary: have seen a cell which is one of:
113        (Notation: B: at BOL; L: last; E: the first row is non-empty)
114 
115        bcde:    !B && !E
116        a1:      !L && !B
117        a2:      !L && !E
118 
119        Or: has at least two of !B, !L, !E, or: has at most one of B,L,E.
120 
121        REMARK: If this variable is not set, but icell_core is, Line is
122        reset to the line of icell_core.
123      */
124     BOOL fixed_line;		/* if we have a 'core' line of cells */
125     enum ended_state ended;	/* if we saw </tr> etc */
126     int content;		/* Whether contains end-of-cell etc */
127     int offset;			/* >=0 after line break in a multiline cell */
128     int allocated;		/* number of table cells allocated */
129     STable_cellinfo *cells;
130     int alignment;		/* global align attribute for this row */
131 } STable_rowinfo;
132 
133 struct _STable_info {
134 #ifdef EXP_NESTED_TABLES
135     struct _STable_info *enclosing;	/* The table which contain us */
136     struct _TextAnchor *enclosing_last_anchor_before_stbl;
137 #endif
138     int startline;		/* lineno where table starts (zero-based) */
139     int nrows;			/* number of rows */
140     int ncols;			/* number of rows */
141     int maxlen;			/* sum of max. cell lengths of any row */
142     int maxpos;			/* max. of max. cell pos of any row */
143     int allocated_rows;		/* number of rows allocated */
144     int allocated_sumcols;	/* number of sumcols allocated */
145     int ncolinfo;		/* number of COL info collected */
146     STable_cellinfo *sumcols;	/* for summary (max len/pos) col info */
147     STable_rowinfo *rows;
148     STable_rowinfo rowspans2eog;
149     short alignment;		/* global align attribute for this table */
150     short rowgroup_align;	/* align default for current group of rows */
151     short pending_colgroup_align;
152     int pending_colgroup_next;
153     STable_states s;
154 };
155 
156 /*
157  *  Functions and structures in this source file keep track of positions.
158  *  They don't know about the character data in those lines, or about
159  *  the HText and HTLine structures.  GridText.c doesn't know about our
160  *  structures.  It should stay that way.
161  *
162  *  The basic idea: we let the code in HTML.c/GridText.c produce and format
163  *  output "as usual", i.e. as without Simple Table support.  We keep track
164  *  of the positions in the generated output where cells and rows start (or
165  *  end).  If all goes well, that preliminary output (stored in HText/HTLine
166  *  structures) can be fixed up when the TABLE end tag is processed, by just
167  *  inserting spaces in the right places (and possibly changing alignment).
168  *  If all goes not well, we already have a safe fallback.
169  *
170  *  Note that positions passed to and from these functions should be
171  *  in terms of screen positions, not just byte counts in a HTLine.data
172  *  (cf. line->data vs. HText_TrueLineSize).
173  *
174  *  Memory is allocated dynamically, so we can have tables of arbitrary
175  *  length.  On allocation error we just return and error indication
176  *  instead of outofmem(), so caller can give up table tracking and maybe
177  *  recover memory.
178  *
179  *  Implemented:
180  *  - ALIGN={left,right,center,justify} applied to individual table cells
181  *    ("justify" is treated as "left")
182  *  - Inheritance of horizontal alignment according to HTML 4.0
183  *  - COLSPAN >1 (may work incorrectly for some tables?)
184  *  - ROWSPAN >1 (reserving cells in following rows)
185  *  - Line breaks at start of first cell or at end of last cell are treated
186  *    as if they were not part of the cell and row.  This allows us to
187  *    cooperate with one way in which tables have been made friendly to
188  *    browsers without any table support.
189  *  Missing, but can be added:
190  *  - Support for COLGROUP/COL
191  *  - Tables wider than display.  The limitation is not here but in GridText.c
192  *    etc.  If horizontal scrolling were implemented there, the mechanisms
193  *    here coudl deal with wide tables (just change MAX_STBL_POS code).
194  *  Missing, unlikely to add:
195  *  - Support for non-LTR directionality.  A general problem, support is
196  *    lacking throughout the lynx code.
197  *  - Support for most other table-related attributes.  Most of them are
198  *    for decorative purposes.
199  *  Impossible or very unlikely (because it doesn't fit the model):
200  *  - Any cell contents of more than one line, line breaks within cells.
201  *    Anything that requires handling cell contents as paragraphs (block
202  *    elements), like reflowing.  Vertical alignment.
203  */
204 static int Stbl_finishCellInRow(STable_rowinfo *me, STable_states *s, int end_td,
205 				int lineno,
206 				int pos);
207 static int Stbl_finishRowInTable(STable_info *me);
208 
cellstate_s(cellstate_t state)209 static const char *cellstate_s(cellstate_t state)
210 {
211     const char *result = "?";
212     /* *INDENT-OFF* */
213     switch (state) {
214     case CS_invalid:	result = "CS_invalid";	break;
215     case CS__new:	result = "CS__new";	break;
216     case CS__0new:	result = "CS__0new";	break;
217     case CS__0eb:	result = "CS__0eb";	break;
218     case CS__eb:	result = "CS__eb";	break;
219     case CS__0cb:	result = "CS__0cb";	break;
220     case CS__cb:	result = "CS__cb";	break;
221     case CS__0ef:	result = "CS__0ef";	break;
222     case CS__ef:	result = "CS__ef";	break;
223     case CS__0cf:	result = "CS__0cf";	break;
224     case CS__cf:	result = "CS__cf";	break;
225     case CS__ebc:	result = "CS__ebc";	break;
226     case CS__cbc:	result = "CS__cbc";	break;
227     }
228     /* *INDENT-ON* */
229 
230     return result;
231 }
232 
Stbl_startTABLE(int alignment)233 struct _STable_info *Stbl_startTABLE(int alignment)
234 {
235     STable_info *me = typecalloc(STable_info);
236 
237     CTRACE2(TRACE_TRST,
238 	    (tfp, "TRST:Stbl_startTABLE(align=%d)\n", (int) alignment));
239     if (me) {
240 	me->alignment = (short) alignment;
241 	me->rowgroup_align = HT_ALIGN_NONE;
242 	me->pending_colgroup_align = HT_ALIGN_NONE;
243 	me->s.x_td = -1;
244 	me->s.icell_core = -1;
245 #ifdef EXP_NESTED_TABLES
246 	if (nested_tables)
247 	    me->enclosing = 0;
248 #endif
249     }
250     return me;
251 }
252 
free_rowinfo(STable_rowinfo * me)253 static void free_rowinfo(STable_rowinfo *me)
254 {
255     if (me && me->allocated) {
256 	FREE(me->cells);
257     }
258 }
259 
Stbl_free(STable_info * me)260 void Stbl_free(STable_info *me)
261 {
262     CTRACE2(TRACE_TRST,
263 	    (tfp, "TRST:Stbl_free()\n"));
264     if (me && me->allocated_rows && me->rows) {
265 	int i;
266 
267 	for (i = 0; i < me->allocated_rows; i++)
268 	    free_rowinfo(me->rows + i);
269 	FREE(me->rows);
270     }
271     free_rowinfo(&me->rowspans2eog);
272     if (me)
273 	FREE(me->sumcols);
274     FREE(me);
275 }
276 
277 /*
278  * Returns -1 on error, otherwise index of just-added table cell.
279  */
Stbl_addCellToRow(STable_rowinfo * me,STable_cellinfo * colinfo,int ncolinfo,STable_states * s,int colspan,int alignment,int isheader,int lineno,int * ppos)280 static int Stbl_addCellToRow(STable_rowinfo *me, STable_cellinfo *colinfo, int ncolinfo,
281 			     STable_states *s,
282 			     int colspan,
283 			     int alignment,
284 			     int isheader,
285 			     int lineno,
286 			     int *ppos)
287 {
288     STable_cellinfo *cells;
289     int i;
290     int last_colspan = me->ncells ?
291     me->cells[me->ncells - 1].colspan : 1;
292     cellstate_t newstate;
293     int ret;
294 
295     CTRACE2(TRACE_TRST,
296 	    (tfp, "TRST:Stbl_addCellToRow, line=%d, pos=%d, colspan=%d\n",
297 	     lineno, *ppos, colspan));
298     CTRACE2(TRACE_TRST,
299 	    (tfp,
300 	     " ncells=%d, stateLine=%d, pending_len=%d, pstate=%s, state=%s\n",
301 	     me->ncells, s->lineno, s->pending_len,
302 	     cellstate_s(s->prev_state), cellstate_s(s->state)));
303     if (me->ncells == 0)
304 	s->prev_state = CS_invalid;
305     else if (s->prev_state == CS_invalid ||
306 	     (s->state != CS__0new &&
307 	      s->state != CS__ef && s->state != CS__0ef))
308 	s->prev_state = s->state;
309 
310     if (me->ncells == 0 || *ppos == 0)
311 	newstate = CS__0new;
312     else
313 	newstate = CS__new;
314 
315     if (me->ncells > 0 && s->pending_len > 0) {
316 	if (s->prev_state != CS__cbc)
317 	    me->cells[me->ncells - 1].len = s->pending_len;
318 	s->pending_len = 0;
319     }
320     s->x_td = *ppos;
321 
322     if (lineno != s->lineno) {
323 	if (!me->fixed_line) {
324 	    if (me->ncells == 0 || *ppos == 0) {
325 		switch (s->prev_state) {
326 		case CS_invalid:
327 		case CS__0new:
328 		case CS__0eb:
329 		case CS__0cb:
330 		case CS__0ef:
331 		case CS__0cf:
332 		    if (me->ncells > 0)
333 			for (i = me->ncells + last_colspan - 2;
334 			     i >= me->ncells - 1; i--) {
335 			    me->cells[i].pos = *ppos;
336 			    me->cells[i].cLine = lineno;
337 			}
338 		    me->Line = lineno;
339 		    break;
340 		case CS__new:
341 		case CS__eb:
342 		case CS__ef:
343 		case CS__cf:
344 		default:
345 		    break;
346 		case CS__cb:
347 		    *ppos = me->cells[me->ncells - 1].pos +
348 			me->cells[me->ncells - 1].len;
349 		}
350 	    } else {		/* last cell multiline, ncells != 0, pos != 0 */
351 		switch (s->prev_state) {
352 		case CS__0new:
353 		case CS__0eb:
354 		case CS__0ef:
355 		    /* Do not fail, but do not set fixed_line either */
356 		    break;
357 		case CS__cb:
358 		    goto trace_and_fail;
359 		case CS__cf:
360 		    goto trace_and_fail;
361 		case CS__0cb:
362 		case CS__0cf:
363 		    if (*ppos > me->cells[0].pos)
364 			me->Line = lineno;
365 		    me->fixed_line = YES;	/* type=a def of fixed_line i */
366 		    break;
367 		case CS__new:
368 		case CS__eb:
369 		case CS__ef:
370 		default:
371 		    me->fixed_line = YES;	/* type=e def of fixed_line ii */
372 		    break;
373 		case CS__cbc:
374 		    goto trace_and_fail;
375 		}
376 	    }
377 	}
378 	if (me->fixed_line && lineno != me->Line) {
379 	    switch (s->prev_state) {
380 	    case CS__cb:
381 	    case CS__cf:
382 		if (*ppos > 0)
383 		    goto trace_and_fail;
384 		else
385 		    *ppos = me->cells[me->ncells - 1].pos /* == 0 */  +
386 			me->cells[me->ncells - 1].len;
387 		break;
388 	    case CS__0cf:
389 	    case CS__0cb:
390 		if (*ppos == 0 || *ppos <= me->cells[0].pos)
391 		    *ppos = me->cells[me->ncells - 1].pos /* == 0 */  +
392 			me->cells[me->ncells - 1].len;
393 		break;
394 	    case CS__0new:
395 	    case CS__0ef:
396 	    case CS__0eb:
397 		break;
398 	    case CS__new:
399 	    case CS__eb:
400 	    case CS__ef:
401 	    default:
402 		*ppos = me->cells[me->ncells - 1].pos;
403 		break;
404 	    case CS__cbc:
405 		break;
406 	    case CS_invalid:
407 		break;
408 	    }
409 	}
410 	s->lineno = lineno;
411     } else {			/* lineno == s->lineno: */
412 	switch (s->prev_state) {
413 	case CS_invalid:
414 	case CS__0new:
415 	case CS__0eb:		/* cannot happen */
416 	case CS__0cb:		/* cannot happen */
417 	case CS__0ef:
418 	case CS__0cf:		/* ##302?? set icell_core? or only in finish? */
419 	    break;
420 	case CS__eb:		/* cannot happen */
421 	case CS__cb:		/* cannot happen */
422 	case CS__ef:
423 	    break;
424 	case CS__ebc:		/* should have done smth in finish */
425 	case CS__cbc:		/* should have done smth in finish */
426 	    break;
427 	case CS__new:
428 	case CS__cf:
429 	    if (me->fixed_line && me->Line != lineno) {
430 		goto trace_and_fail;
431 	    } else {
432 		me->fixed_line = YES;
433 		me->Line = lineno;
434 	    }
435 	}
436     }
437 
438     s->state = newstate;
439 
440     if (me->ncells > 0 && me->cells[me->ncells - 1].colspan > 1) {
441 	me->ncells += me->cells[me->ncells - 1].colspan - 1;
442     }
443     while (me->ncells < me->allocated &&
444 	   me->cells[me->ncells].alignment == RESERVEDCELL) {
445 	me->ncells++;
446     }
447     {
448 	int growby = 0;
449 
450 	while (me->ncells + colspan + 1 > me->allocated + growby)
451 	    growby += CELLS_GROWBY;
452 	if (growby) {
453 	    if (me->allocated == 0 && !me->cells) {
454 		cells = typecallocn(STable_cellinfo, (unsigned) growby);
455 	    } else {
456 		cells = typeRealloc(STable_cellinfo, me->cells,
457 				      (unsigned) (me->allocated + growby));
458 
459 		for (i = 0; cells && i < growby; i++) {
460 		    cells[me->allocated + i].alignment = HT_ALIGN_NONE;
461 		}
462 	    }
463 	    if (cells) {
464 		me->allocated += growby;
465 		me->cells = cells;
466 	    } else {
467 		goto trace_and_fail;
468 	    }
469 	}
470     }
471 
472     me->cells[me->ncells].cLine = lineno;
473     me->cells[me->ncells].pos = *ppos;
474     me->cells[me->ncells].len = -1;
475     me->cells[me->ncells].colspan = colspan;
476 
477     if (alignment != HT_ALIGN_NONE)
478 	me->cells[me->ncells].alignment = alignment;
479     else {
480 	if (ncolinfo >= me->ncells + 1)
481 	    me->cells[me->ncells].alignment = colinfo[me->ncells].alignment;
482 	else
483 	    me->cells[me->ncells].alignment = me->alignment;
484 	if (me->cells[me->ncells].alignment == HT_ALIGN_NONE)
485 	    me->cells[me->ncells].alignment = me->alignment;
486 	if (me->cells[me->ncells].alignment == HT_ALIGN_NONE)
487 	    me->cells[me->ncells].alignment = isheader ? HT_CENTER : HT_LEFT;
488     }
489     for (i = me->ncells + 1; i < me->ncells + colspan; i++) {
490 	me->cells[i].cLine = lineno;
491 	me->cells[i].pos = *ppos;
492 	me->cells[i].len = -1;
493 	me->cells[i].colspan = 0;
494 	me->cells[i].alignment = HT_LEFT;
495     }
496     me->cells[me->ncells + colspan].pos = -1;	/* not yet used */
497     me->ncells++;
498 
499     ret = me->ncells - 1;
500   trace_and_return:
501     CTRACE2(TRACE_TRST,
502 	    (tfp, " => prev_state=%s, state=%s, ret=%d\n",
503 	     cellstate_s(s->prev_state), cellstate_s(s->state), ret));
504     return (ret);
505 
506   trace_and_fail:
507     ret = -1;
508     goto trace_and_return;
509 }
510 
511 /* returns -1 on error, 0 otherwise */
512 /* assumes cells have already been allocated (but may need more) */
Stbl_reserveCellsInRow(STable_rowinfo * me,int icell,int colspan)513 static int Stbl_reserveCellsInRow(STable_rowinfo *me, int icell,
514 				  int colspan)
515 {
516     STable_cellinfo *cells;
517     int i;
518     int growby = 1 + icell + colspan - me->allocated;
519 
520     CTRACE2(TRACE_TRST,
521 	    (tfp, "TRST:Stbl_reserveCellsInRow(icell=%d, colspan=%d\n",
522 	     icell, colspan));
523     if (growby > 0) {
524 	cells = typeRealloc(STable_cellinfo, me->cells,
525 			      (unsigned) (me->allocated + growby));
526 
527 	if (cells) {
528 	    for (i = 0; i < growby; i++) {
529 		cells[me->allocated + i].alignment = HT_ALIGN_NONE;
530 	    }
531 	    me->allocated += growby;
532 	    me->cells = cells;
533 	} else {
534 	    return -1;
535 	}
536     }
537     for (i = icell; i < icell + colspan; i++) {
538 	me->cells[i].cLine = -1;
539 	me->cells[i].pos = -1;
540 	me->cells[i].len = -1;
541 	me->cells[i].colspan = 0;
542 	me->cells[i].alignment = RESERVEDCELL;
543     }
544     me->cells[icell].colspan = colspan;
545     return 0;
546 }
547 
548 /* Returns -1 on failure. */
Stbl_finishCellInRow(STable_rowinfo * me,STable_states * s,int end_td,int lineno,int pos)549 static int Stbl_finishCellInRow(STable_rowinfo *me, STable_states *s, int end_td,
550 				int lineno,
551 				int pos)
552 {
553     STable_cellinfo *lastcell;
554     cellstate_t newstate = CS_invalid;
555     int multiline = NO, empty;
556     int ret;
557 
558     CTRACE2(TRACE_TRST,
559 	    (tfp,
560 	     "TRST:Stbl_finishCellInRow line=%d pos=%d end_td=%d ncells=%d pnd_len=%d\n",
561 	     lineno, pos, (int) end_td, me->ncells, s->pending_len));
562 
563     if (me->ncells <= 0)
564 	return -1;
565     lastcell = me->cells + (me->ncells - 1);
566     multiline = (lineno != lastcell->cLine || lineno != s->lineno);
567     empty = multiline ? (pos == 0) : (pos <= s->x_td);
568 
569     CTRACE2(TRACE_TRST,
570 	    (tfp,
571 	     " [lines: lastCell=%d state=%d multi=%d] empty=%d (prev)state=(%s) %s\n",
572 	     lastcell->cLine, s->lineno, multiline, empty,
573 	     cellstate_s(s->prev_state), cellstate_s(s->state)));
574 
575     if (multiline) {
576 	if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
577 	    switch (s->state) {
578 	    case CS_invalid:
579 		newstate = empty ? CS_invalid : CS__cbc;
580 		break;
581 	    case CS__0new:
582 		newstate = empty ? CS__0eb : CS__0cb;
583 		break;
584 	    case CS__0eb:
585 		newstate = empty ? CS__0eb : CS__ebc;
586 		s->state = newstate;
587 		if (me->fixed_line) {
588 		    if (empty)
589 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
590 		    else
591 			ret = (lastcell->len <= 0 ? 0 : -1);
592 		} else {
593 		    if (empty)
594 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
595 		    else
596 			ret = (lastcell->len <= 0 ? 0 : 0);
597 		}
598 		goto trace_and_return;
599 	    case CS__0cb:
600 		if (!me->fixed_line) {
601 		    if (!empty) {
602 			if (s->icell_core == -1)
603 			    me->Line = -1;
604 		    }
605 		}
606 		if (s->pending_len && empty) {	/* First line non-empty */
607 		    if ((me->fixed_line && me->Line == lastcell->cLine) ||
608 			s->icell_core == me->ncells - 1)
609 			lastcell->len = s->pending_len;
610 		    s->pending_len = 0;
611 		}		/* @@@ for empty do smth. about ->Line / ->icell_core !! */
612 		newstate = empty ? CS__0cb : CS__cbc;	/* ##474_needs_len!=-1? */
613 		break;
614 	    case CS__0ef:
615 	    case CS__0cf:
616 		break;
617 	    case CS__new:
618 		newstate = empty ? CS__eb : CS__cb;
619 		break;
620 	    case CS__eb:	/* ##484_set_pending_ret_0_if_empty? */
621 		newstate = empty ? CS__eb : CS__ebc;
622 		s->state = newstate;
623 		if (me->fixed_line) {
624 		    if (empty)
625 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
626 		    else
627 			ret = (lastcell->len <= 0 ? 0 : -1);
628 		} else {
629 		    if (empty)
630 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
631 		    else
632 			ret = (lastcell->len <= 0 ? 0 : -1);
633 		}
634 		goto trace_and_return;
635 	    case CS__cb:
636 		if (s->pending_len && empty) {	/* ##496: */
637 		    lastcell->len = s->pending_len;
638 		    s->pending_len = 0;
639 		}		/* @@@ for empty do smth. about ->Line / ->icell_core !! */
640 		ret = -1;
641 		if (empty) {
642 		    if (!me->fixed_line) {
643 			me->fixed_line = YES;	/* type=b def of fixed_line i */
644 			me->Line = lastcell->cLine;	/* should've happened in break */
645 		    } else {
646 			if (me->Line != lastcell->cLine)
647 			    goto trace_and_return;
648 		    }
649 		    newstate = CS__cb;
650 		} else {
651 		    if (!me->fixed_line) {
652 			me->fixed_line = YES;	/* type=b def of fixed_line ii */
653 			me->Line = lastcell->cLine;	/* should've happened in break */
654 		    }
655 		    s->state = CS__cbc;
656 		    goto trace_and_return;
657 		}
658 		break;
659 	    case CS__ef:
660 		ret = 0;
661 		goto trace_and_return;
662 	    case CS__cf:
663 		ret = lastcell->len;	/* ##523_change_state? */
664 		goto trace_and_return;
665 	    case CS__cbc:
666 		if (!me->fixed_line) {
667 		    if (empty) {
668 			if (s->icell_core == -1)	/* ##528??: */
669 			    me->Line = lineno;
670 			/* lastcell->Line = lineno; */
671 		    } else {	/* !empty */
672 			if (s->icell_core == -1)
673 			    me->Line = -1;
674 		    }
675 		}
676 		s->pending_len = 0;
677 		newstate = empty ? CS_invalid : CS__cbc;
678 		break;
679 	    default:
680 		break;
681 	    }
682 	} else {		/* multiline cell, processing </TD>: */
683 	    s->x_td = -1;
684 	    switch (s->state) {
685 	    case CS_invalid:
686 		/* ##540_return_-1_for_invalid_if_len!: */
687 		if (!empty && lastcell->len > 0) {
688 		    newstate = CS__0cf;
689 		    s->state = newstate;
690 		    ret = -1;
691 		    goto trace_and_return;
692 		}
693 		/* ##541_set_len_0_Line_-1_sometimes: */
694 		lastcell->len = 0;
695 		lastcell->cLine = -1;
696 		/* fall thru ##546 really fall thru??: */
697 		newstate = empty ? CS_invalid : CS__cbc;
698 		break;
699 	    case CS__0new:
700 		newstate = empty ? CS__0ef : CS__0cf;
701 		break;
702 	    case CS__0eb:
703 		newstate = empty ? CS__0ef : CS__0cf;	/* ebc?? */
704 		s->state = newstate;
705 		if (me->fixed_line) {
706 		    if (empty)
707 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
708 		    else
709 			ret = (lastcell->len <= 0 ? 0 : -1);
710 		} else {
711 		    if (empty)
712 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
713 		    else
714 			ret = (lastcell->len <= 0 ? 0 : 0);
715 		}
716 		goto trace_and_return;
717 	    case CS__0cb:
718 		if (s->pending_len) {
719 		    if (empty)
720 			lastcell->len = s->pending_len;
721 		    else
722 			lastcell->len = 0;
723 		    s->pending_len = 0;
724 		}
725 		if (!me->fixed_line) {
726 		    if (empty) {
727 			if (s->icell_core == -1)
728 			    /* first cell before <BR></TD> => the core cell */
729 			    s->icell_core = me->ncells - 1;
730 			/* lastcell->cLine = lineno; */
731 		    } else {	/* !empty */
732 			if (s->icell_core == -1)
733 			    me->Line = -1;
734 		    }
735 		}
736 		if (s->pending_len && empty) {
737 		    lastcell->len = s->pending_len;
738 		    s->pending_len = 0;
739 		}		/* @@@ for empty do smth. about ->Line / ->icell_core !! */
740 		newstate = empty ? CS__0cf : CS__cbc;
741 		break;
742 	    case CS__0ef:
743 		newstate = CS__0ef;
744 		/* FALLTHRU */
745 	    case CS__0cf:
746 		break;
747 	    case CS__new:
748 		newstate = empty ? CS__ef : CS__cf;
749 		break;
750 	    case CS__eb:
751 		newstate = empty ? CS__ef : CS__ef;	/* ##579??? !!!!! */
752 		s->state = newstate;
753 		if (me->fixed_line) {
754 		    if (empty)
755 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
756 		    else
757 			ret = (lastcell->len <= 0 ? 0 : -1);
758 		} else {
759 		    if (empty)
760 			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
761 		    else
762 			ret = (lastcell->len <= 0 ? 0 : -1);
763 		}
764 		goto trace_and_return;
765 	    case CS__cb:
766 		if (s->pending_len && empty) {
767 		    lastcell->len = s->pending_len;
768 		    s->pending_len = 0;
769 		}
770 		ret = -1;
771 		if (empty) {
772 		    if (!me->fixed_line) {
773 			me->fixed_line = YES;	/* type=c def of fixed_line */
774 			me->Line = lastcell->cLine;	/* should've happened in break */
775 		    } else {
776 			if (me->Line != lastcell->cLine)
777 			    goto trace_and_return;
778 		    }
779 		    newstate = CS__cf;
780 		} else {
781 		    goto trace_and_return;
782 		}
783 		break;
784 	    case CS__ef:	/* ignored error */
785 	    case CS__cf:	/* ignored error */
786 		break;
787 	    case CS__ebc:	/* ##540_handle_ebc: */
788 		lastcell->len = 0;
789 		if (!me->fixed_line) {
790 		    if (!empty) {
791 			if (s->icell_core == -1)
792 			    lastcell->cLine = -1;
793 		    }
794 		}
795 		s->pending_len = 0;
796 		newstate = empty ? CS_invalid : CS__cbc;
797 		break;
798 	    case CS__cbc:	/* ##586 */
799 		lastcell->len = 0;	/* ##613 */
800 		ret = -1;
801 		if (me->fixed_line && me->Line == lastcell->cLine)
802 		    goto trace_and_return;
803 		if (!me->fixed_line) {
804 		    if (empty) {
805 			if (s->icell_core == -1)
806 			    me->Line = lineno;
807 		    }
808 		}
809 		s->pending_len = 0;	/* ##629 v */
810 		newstate = empty ? CS_invalid : CS__cbc;
811 		break;
812 	    }
813 	}
814     } else {			/* (!multiline) */
815 	if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
816 	    switch (s->state) {
817 	    case CS_invalid:
818 	    case CS__0new:
819 		s->pending_len = empty ? 0 : pos - lastcell->pos;
820 		newstate = empty ? CS__0eb : CS__0cb;
821 		s->state = newstate;
822 		ret = 0;	/* or 0 for xlen to s->pending_len?? */
823 		goto trace_and_return;
824 	    case CS__0eb:	/* cannot happen */
825 		newstate = CS__eb;
826 		break;
827 	    case CS__0cb:	/* cannot happen */
828 		newstate = CS__cb;
829 		break;
830 	    case CS__0ef:
831 	    case CS__0cf:
832 		break;
833 	    case CS__new:
834 		ret = -1;
835 		if (!empty && s->prev_state == CS__cbc)		/* ##609: */
836 		    goto trace_and_return;
837 		if (!empty) {
838 		    if (!me->fixed_line) {
839 			me->fixed_line = YES;	/* type=d def of fixed_line */
840 			me->Line = lineno;
841 		    } else {
842 			if (me->Line != lineno)
843 			    goto trace_and_return;
844 		    }
845 		}
846 		newstate = empty ? CS__eb : CS__cb;
847 		s->state = newstate;
848 		if (!me->fixed_line) {
849 		    s->pending_len = empty ? 0 : pos - lastcell->pos;
850 		    ret = 0;
851 		    goto trace_and_return;
852 		} else {
853 		    s->pending_len = 0;
854 		    lastcell->len = empty ? 0 : pos - lastcell->pos;
855 		    ret = lastcell->len;
856 		    goto trace_and_return;
857 		}
858 	    case CS__eb:	/* cannot happen */
859 		newstate = empty ? CS__eb : CS__ebc;
860 		break;
861 	    case CS__cb:	/* cannot happen */
862 		newstate = empty ? CS__cb : CS__cbc;
863 		break;
864 	    case CS__ef:
865 		ret = 0;
866 		goto trace_and_return;
867 	    case CS__cf:
868 		ret = lastcell->len;
869 		goto trace_and_return;
870 	    case CS__cbc:	/* ??? */
871 		break;
872 	    default:
873 		break;
874 	    }
875 	} else {		/* !multiline, processing </TD>: */
876 	    s->x_td = -1;
877 	    switch (s->state) {
878 	    case CS_invalid:	/* ##691_no_lastcell_len_for_invalid: */
879 		if (!(me->fixed_line && me->Line == lastcell->cLine))
880 		    lastcell->len = 0;
881 		/* FALLTHRU */
882 	    case CS__0new:
883 		newstate = empty ? CS__0ef : CS__0cf;
884 		break;		/* ##630 */
885 	    case CS__0eb:
886 		newstate = empty ? CS__0ef : CS__0ef;
887 		break;		/* ??? */
888 	    case CS__0cb:
889 		newstate = empty ? CS__0cf : CS__cbc;
890 		break;		/* ??? */
891 	    case CS__0ef:
892 		newstate = CS__0ef;
893 		break;		/* ??? */
894 	    case CS__0cf:
895 		break;		/* ??? */
896 	    case CS__new:
897 		ret = -1;
898 		if (!empty && s->prev_state == CS__cbc)
899 		    goto trace_and_return;
900 		if (!empty) {	/* ##642_set_fixed!: */
901 		    if (!me->fixed_line) {
902 			me->fixed_line = YES;	/* type=e def of fixed_line */
903 			me->Line = lineno;
904 		    } else {
905 			if (me->Line != lineno)
906 			    goto trace_and_return;
907 		    }
908 		}
909 		if (lastcell->len < 0)
910 		    lastcell->len = empty ? 0 : pos - lastcell->pos;
911 		newstate = empty ? CS__ef : CS__cf;
912 		s->state = newstate;
913 		ret = ((me->fixed_line && lineno != me->Line)
914 		       ? -1 : lastcell->len);
915 		goto trace_and_return;
916 	    case CS__eb:
917 		newstate = empty ? CS__ef : CS__cf;
918 		break;		/* ??? */
919 	    case CS__cb:
920 		newstate = empty ? CS__cf : CS__cf;
921 		break;		/* ??? */
922 	    case CS__ef:	/* ignored error */
923 	    case CS__cf:	/* ignored error */
924 	    default:
925 		break;
926 	    }
927 	    lastcell->len = pos - lastcell->pos;
928 	}			/* if (!end_td) ... else */
929     }				/* if (multiline) ... else */
930 
931     s->state = newstate;
932     ret = lastcell->len;
933 #ifdef EXP_NESTED_TABLES
934     if (nested_tables) {
935 	if (ret == -1 && pos == 0)
936 	    ret = 0;		/* XXXX Hack to allow trailing <P> in multiline cells. */
937     }
938 #endif
939 
940 /*    lastcell->len = pos - lastcell->pos; */
941   trace_and_return:
942     CTRACE2(TRACE_TRST,
943 	    (tfp, " => prev_state=%s, state=%s, return=%d\n",
944 	     cellstate_s(s->prev_state), cellstate_s(s->state), ret));
945     return ret;
946 }
947 
948 /*
949  * Reserve cells, each of given colspan, in (rowspan-1) rows after the current
950  * row of rowspan>1.  If rowspan==0, use special 'row' rowspans2eog to keep
951  * track of rowspans that are to remain in effect until the end of the row
952  * group (until next THEAD/TFOOT/TBODY) or table.
953  */
Stbl_reserveCellsInTable(STable_info * me,int icell,int colspan,int rowspan)954 static int Stbl_reserveCellsInTable(STable_info *me, int icell,
955 				    int colspan,
956 				    int rowspan)
957 {
958     STable_rowinfo *rows, *row;
959     int growby;
960     int i;
961 
962     if (colspan > TRST_MAXCOLSPAN) {
963 	CTRACE2(TRACE_TRST,
964 		(tfp,
965 		 "TRST:*** COLSPAN=%d is too large, ignored!\n",
966 		 colspan));
967 	return -1;
968     }
969     if (rowspan > TRST_MAXROWSPAN) {
970 	CTRACE2(TRACE_TRST,
971 		(tfp,
972 		 "TRST:*** ROWSPAN=%d is too large, ignored!\n",
973 		 rowspan));
974 	return -1;
975     }
976     if (me->nrows <= 0)
977 	return -1;		/* must already have at least one row */
978 
979     CTRACE2(TRACE_TRST,
980 	    (tfp,
981 	     "TRST:Stbl_reserveCellsInTable(icell=%d, colspan=%d, rowspan=%d)\n",
982 	     icell, colspan, rowspan));
983     if (rowspan == 0) {
984 	if (!me->rowspans2eog.cells) {
985 	    me->rowspans2eog.cells = typecallocn(STable_cellinfo,
986 						   (unsigned) HTMAX(1, icell + colspan));
987 
988 	    if (!me->rowspans2eog.cells)
989 		return 0;	/* fail silently */
990 	    else
991 		me->rowspans2eog.allocated = icell + colspan;
992 	}
993 	Stbl_reserveCellsInRow(&me->rowspans2eog, icell, colspan);
994     }
995 
996     growby = me->nrows + rowspan - 1 - me->allocated_rows;
997     if (growby > 0) {
998 	rows = typeRealloc(STable_rowinfo, me->rows,
999 			     (unsigned) (me->allocated_rows + growby));
1000 
1001 	if (!rows)
1002 	    return 0;		/* ignore silently, no free memory, may be recoverable */
1003 	for (i = 0; i < growby; i++) {
1004 	    row = rows + me->allocated_rows + i;
1005 	    row->allocated = 0;
1006 	    row->offset = 0;
1007 	    row->content = 0;
1008 	    if (!me->rowspans2eog.allocated) {
1009 		row->cells = NULL;
1010 	    } else {
1011 		row->cells = typecallocn(STable_cellinfo,
1012 					   (unsigned) me->rowspans2eog.allocated);
1013 
1014 		if (row->cells) {
1015 		    row->allocated = me->rowspans2eog.allocated;
1016 		    memcpy(row->cells, me->rowspans2eog.cells,
1017 			   ((unsigned) row->allocated * sizeof(STable_cellinfo)));
1018 		}
1019 	    }
1020 	    row->ncells = 0;
1021 	    row->fixed_line = NO;
1022 	    row->alignment = HT_ALIGN_NONE;
1023 	}
1024 	me->allocated_rows += growby;
1025 	me->rows = rows;
1026     }
1027     for (i = me->nrows;
1028 	 i < (rowspan == 0 ? me->allocated_rows : me->nrows + rowspan - 1);
1029 	 i++) {
1030 	if (!me->rows[i].allocated) {
1031 	    me->rows[i].cells = typecallocn(STable_cellinfo,
1032 					      (unsigned) HTMAX(1,
1033 							       icell
1034 							       + colspan));
1035 
1036 	    if (!me->rows[i].cells)
1037 		return 0;	/* fail silently */
1038 	    else
1039 		me->rows[i].allocated = icell + colspan;
1040 	}
1041 	Stbl_reserveCellsInRow(me->rows + i, icell, colspan);
1042     }
1043     return 0;
1044 }
1045 
1046 /* Remove reserved cells in trailing rows that were added for rowspan,
1047  * to be used when a THEAD/TFOOT/TBODY ends. */
Stbl_cancelRowSpans(STable_info * me)1048 static void Stbl_cancelRowSpans(STable_info *me)
1049 {
1050     int i;
1051 
1052     CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_cancelRowSpans()"));
1053     for (i = me->nrows; i < me->allocated_rows; i++) {
1054 	if (!me->rows[i].ncells) {	/* should always be the case */
1055 	    FREE(me->rows[i].cells);
1056 	    me->rows[i].allocated = 0;
1057 	}
1058     }
1059     free_rowinfo(&me->rowspans2eog);
1060     me->rowspans2eog.allocated = 0;
1061 }
1062 
1063 /*
1064  * Returns -1 on error, otherwise index of just-added table row.
1065  */
Stbl_addRowToTable(STable_info * me,int alignment,int lineno)1066 int Stbl_addRowToTable(STable_info *me, int alignment,
1067 		       int lineno)
1068 {
1069     STable_rowinfo *rows, *row;
1070     STable_states *s = &me->s;
1071 
1072     CTRACE2(TRACE_TRST,
1073 	    (tfp, "TRST:Stbl_addRowToTable(alignment=%d, lineno=%d)\n",
1074 	     alignment, lineno));
1075     if (me->nrows > 0 && me->rows[me->nrows - 1].ncells > 0) {
1076 	if (s->pending_len > 0)
1077 	    me->rows[me->nrows - 1].cells[
1078 					     me->rows[me->nrows - 1].ncells - 1
1079 		].len =
1080 		s->pending_len;
1081 	s->pending_len = 0;
1082     }
1083     Stbl_finishRowInTable(me);
1084     if (me->nrows > 0 && me->rows[me->nrows - 1].Line == lineno)
1085 	me->rows[me->nrows - 1].Line = -1;
1086     s->pending_len = 0;
1087     s->x_td = -1;
1088 
1089     {
1090 	int i;
1091 	int growby = 0;
1092 
1093 	while (me->nrows + 2 > me->allocated_rows + growby)
1094 	    growby += ROWS_GROWBY;
1095 	if (growby) {
1096 	    if (me->allocated_rows == 0 && !me->rows) {
1097 		rows = typecallocn(STable_rowinfo, (unsigned) growby);
1098 	    } else {
1099 		rows = typeRealloc(STable_rowinfo, me->rows,
1100 				     (unsigned) (me->allocated_rows + growby));
1101 
1102 		for (i = 0; rows && i < growby; i++) {
1103 		    row = rows + me->allocated_rows + i;
1104 		    if (!me->rowspans2eog.allocated) {
1105 			row->allocated = 0;
1106 			row->cells = NULL;
1107 		    } else {
1108 			row->cells = typecallocn(STable_cellinfo,
1109 						   (unsigned) me->rowspans2eog.allocated);
1110 
1111 			if (row->cells) {
1112 			    row->allocated = me->rowspans2eog.allocated;
1113 			    memcpy(row->cells, me->rowspans2eog.cells,
1114 				   (unsigned) row->allocated * sizeof(STable_cellinfo));
1115 			} else {
1116 			    FREE(rows);
1117 			    break;
1118 			}
1119 		    }
1120 		    row->ncells = 0;
1121 		    row->fixed_line = NO;
1122 		    row->alignment = HT_ALIGN_NONE;
1123 		    row->offset = 0;
1124 		    row->content = 0;
1125 		}
1126 	    }
1127 	    if (rows) {
1128 		me->allocated_rows += growby;
1129 		me->rows = rows;
1130 	    } else {
1131 		return -1;
1132 	    }
1133 	}
1134     }
1135 
1136     me->rows[me->nrows].Line = lineno;
1137     if (me->nrows == 0)
1138 	me->startline = lineno;
1139     if (alignment != HT_ALIGN_NONE)
1140 	me->rows[me->nrows].alignment = alignment;
1141     else
1142 	me->rows[me->nrows].alignment =
1143 	    (me->rowgroup_align == HT_ALIGN_NONE) ?
1144 	    me->alignment : me->rowgroup_align;
1145     me->nrows++;
1146     if (me->pending_colgroup_next > me->ncolinfo) {
1147 	me->ncolinfo = me->pending_colgroup_next;
1148 	me->pending_colgroup_next = 0;
1149     }
1150     me->rows[me->nrows].Line = -1;	/* not yet used */
1151     me->rows[me->nrows].ended = ROW_not_ended;	/* No </tr> yet */
1152     return (me->nrows - 1);
1153 }
1154 
1155 /*
1156  * Returns -1 on error, otherwise current number of rows.
1157  */
Stbl_finishRowInTable(STable_info * me)1158 static int Stbl_finishRowInTable(STable_info *me)
1159 {
1160     STable_rowinfo *lastrow;
1161     STable_states *s = &me->s;
1162 
1163     CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishRowInTable()\n"));
1164     if (!me->rows || !me->nrows)
1165 	return -1;		/* no row started! */
1166     lastrow = me->rows + (me->nrows - 1);
1167     lastrow->ended = ROW_ended_by_endtr;
1168     if (lastrow->ncells > 0) {
1169 	if (s->pending_len > 0)
1170 	    lastrow->cells[lastrow->ncells - 1].len = s->pending_len;
1171 	s->pending_len = 0;
1172     }
1173     s->prev_state = s->state = CS_invalid;
1174     s->lineno = -1;
1175 
1176     if (s->icell_core >= 0 && !lastrow->fixed_line &&
1177 	lastrow->cells[s->icell_core].cLine >= 0)
1178 	lastrow->Line = lastrow->cells[s->icell_core].cLine;
1179     s->icell_core = -1;
1180     return (me->nrows);
1181 }
1182 
update_sumcols0(STable_cellinfo * sumcols,STable_rowinfo * lastrow,int pos,int len,int icell,int ispan,int allocated_sumcols)1183 static void update_sumcols0(STable_cellinfo *sumcols,
1184 			    STable_rowinfo *lastrow,
1185 			    int pos,
1186 			    int len,
1187 			    int icell,
1188 			    int ispan,
1189 			    int allocated_sumcols)
1190 {
1191     int i;
1192 
1193     if (len > 0) {
1194 	int sumpos = pos;
1195 	int prevsumpos = sumcols[icell + ispan].pos;
1196 	int advance;
1197 
1198 	if (ispan > 0) {
1199 	    if (lastrow->cells[icell].pos + len > sumpos)
1200 		sumpos = lastrow->cells[icell].pos + len;
1201 	    if (sumcols[icell + ispan - 1].pos +
1202 		sumcols[icell + ispan - 1].len >
1203 		sumpos)
1204 		sumpos = sumcols[icell + ispan - 1].pos +
1205 		    sumcols[icell + ispan - 1].len;
1206 	}
1207 	advance = sumpos - prevsumpos;
1208 	if (advance > 0) {
1209 	    for (i = icell + ispan; i < allocated_sumcols; i++) {
1210 		if (ispan > 0 && sumcols[i].colspan < -1) {
1211 		    if (i + sumcols[i].colspan < icell + ispan) {
1212 			advance = sumpos - sumcols[i].pos;
1213 			if (i > 0)
1214 			    advance = HTMAX(advance,
1215 					    sumcols[i - 1].pos +
1216 					    sumcols[i - 1].len
1217 					    - (sumcols[i].pos));
1218 			if (advance <= 0)
1219 			    break;
1220 		    }
1221 		}
1222 		if (sumcols[i].pos >= 0)
1223 		    sumcols[i].pos += advance;
1224 		else {
1225 		    sumcols[i].pos = sumpos;
1226 		    break;
1227 		}
1228 	    }
1229 	}
1230     }
1231 }
1232 
get_remaining_colspan(STable_rowinfo * me,STable_cellinfo * colinfo,int ncolinfo,int colspan,int ncols_sofar)1233 static int get_remaining_colspan(STable_rowinfo *me,
1234 				 STable_cellinfo *colinfo,
1235 				 int ncolinfo,
1236 				 int colspan,
1237 				 int ncols_sofar)
1238 {
1239     int i;
1240     int last_colspan = me->ncells ?
1241     me->cells[me->ncells - 1].colspan : 1;
1242 
1243     if (ncolinfo == 0 || me->ncells + last_colspan > ncolinfo) {
1244 	colspan = HTMIN(TRST_MAXCOLSPAN,
1245 			ncols_sofar - (me->ncells + last_colspan - 1));
1246     } else {
1247 	for (i = me->ncells + last_colspan - 1; i < ncolinfo - 1; i++)
1248 	    if (colinfo[i].cLine == EOCOLG)
1249 		break;
1250 	colspan = i - (me->ncells + last_colspan - 2);
1251     }
1252     CTRACE2(TRACE_TRST,
1253 	    (tfp, "TRST:get_remaining_colspan; colspan = %d\n",
1254 	     colspan));
1255     return colspan;
1256 }
1257 
1258 #ifdef EXP_NESTED_TABLES
1259 /* Returns -1 on failure, 1 if faking was performed, 0 if not needed. */
Stbl_fakeFinishCellInTable(STable_info * me,STable_rowinfo * lastrow,int lineno,int finishing)1260 static int Stbl_fakeFinishCellInTable(STable_info *me,
1261 				      STable_rowinfo *lastrow,
1262 				      int lineno,
1263 				      int finishing)	/* Processing finish or start */
1264 {
1265     STable_states *s = &me->s;
1266     int fake = 0;
1267 
1268     switch (s->state) {		/* We care only about trailing <BR> */
1269     case CS_invalid:
1270     case CS__0new:
1271     case CS__0ef:
1272     case CS__0cf:
1273     case CS__new:
1274     case CS__cbc:
1275     case CS__ef:
1276     case CS__cf:
1277     default:
1278 	/* <BR></TD> may produce these (XXXX instead of CS__cbf?).  But if
1279 	   finishing==0, the caller already checked that we are on a
1280 	   different line.  */
1281 	if (finishing == 0)
1282 	    fake = 1;
1283 	break;			/* Either can't happen, or may be ignored */
1284     case CS__eb:
1285     case CS__0eb:
1286     case CS__0cb:
1287     case CS__cb:
1288 	fake = 1;
1289 	break;
1290     }
1291     if (fake) {
1292 	/* The previous action we did was putting a linebreak.  Now we
1293 	   want to put another one.  Fake necessary
1294 	   </TD></TR><TR><TD></TD><TD> (and possibly </TD>) instead. */
1295 	int ncells = lastrow->ncells;
1296 	int i;
1297 	int al = lastrow->alignment;
1298 	int cs = lastrow->cells[lastrow->ncells - 1].colspan;
1299 	int rs = 1;		/* XXXX How to find rowspan? */
1300 	int ih = 0;		/* XXXX How to find is_header? */
1301 	int end_td = (TRST_ENDCELL_ENDTD | TRST_FAKING_CELLS);
1302 	int need_reserved = 0;
1303 	int prev_reserved_last = -1;
1304 	STable_rowinfo *prev_row;
1305 	int prev_row_n2 = (int) (lastrow - me->rows);
1306 
1307 	CTRACE2(TRACE_TRST,
1308 		(tfp,
1309 		 "TRST:Stbl_fakeFinishCellInTable(lineno=%d, finishing=%d) START FAKING\n",
1310 		 lineno, finishing));
1311 
1312 	/* Although here we use pos=0, this may commit the previous
1313 	   cell which had <BR> as a last element.  This may overflow
1314 	   the screen width, so the additional checks performed in
1315 	   Stbl_finishCellInTable (comparing to Stbl_finishCellInRow)
1316 	   are needed. */
1317 	if (finishing) {
1318 	    /* Fake </TD> at BOL */
1319 	    if (Stbl_finishCellInTable(me, end_td, lineno, 0, 0) < 0) {
1320 		return -1;
1321 	    }
1322 	}
1323 
1324 	/* Fake </TR> at BOL */
1325 	/* Stbl_finishCellInTable(lineno, 0, 0); */
1326 	/* Needed? */
1327 
1328 	/* Fake <TR> at BOL */
1329 	if (Stbl_addRowToTable(me, al, lineno) < 0) {
1330 	    return -1;
1331 	}
1332 	lastrow = me->rows + (me->nrows - 1);
1333 	lastrow->content = IS_CONTINUATION_OF_CELL;
1334 	for (i = 0; i < lastrow->allocated; i++) {
1335 	    if (lastrow->cells[i].alignment == RESERVEDCELL) {
1336 		need_reserved = 1;
1337 		break;
1338 	    }
1339 	}
1340 
1341 	prev_row = me->rows + prev_row_n2;
1342 	for (i = ncells; i < prev_row->allocated; i++) {
1343 	    if (prev_row->cells[i].alignment == RESERVEDCELL)
1344 		prev_reserved_last = i;
1345 	}
1346 	if (need_reserved || prev_reserved_last >= 0) {
1347 	    /* Oups, we are going to stomp over a line which somebody
1348 	       cares about already, or the previous line had reserved
1349 	       cells which were not skipped over.
1350 
1351 	       Remember that STable_rowinfo is about logical (TR)
1352 	       table lines, not displayed lines.  We need to duplicate
1353 	       the reservation structure when we fake new logical lines.  */
1354 	    int prev_row_n = (int) (prev_row - me->rows);
1355 	    STable_rowinfo *rows = typeRealloc(STable_rowinfo, me->rows,
1356 					       (unsigned) (me->allocated_rows
1357 							   + 1));
1358 	    int need_cells = prev_reserved_last + 1;
1359 	    int n;
1360 
1361 	    if (!rows)
1362 		return -1;	/* ignore silently, no free memory, may be recoverable */
1363 
1364 	    CTRACE2(TRACE_TRST,
1365 		    (tfp, "TRST:Stbl_fakeFinishCellInTable REALLOC ROWSPAN\n"));
1366 	    me->rows = rows;
1367 	    lastrow = me->rows + (me->nrows - 1);
1368 	    prev_row = me->rows + prev_row_n;
1369 	    me->allocated_rows++;
1370 
1371 	    /* Insert a duplicate row after lastrow */
1372 	    for (n = me->allocated_rows - me->nrows - 1; n >= 0; --n)
1373 		lastrow[n + 1] = lastrow[n];
1374 
1375 	    /* Ignore cells, they belong to the next row now */
1376 	    lastrow->allocated = 0;
1377 	    lastrow->cells = 0;
1378 	    if (need_cells) {
1379 		lastrow->cells = typecallocn(STable_cellinfo, (unsigned) need_cells);
1380 
1381 		/* ignore silently, no free memory, may be recoverable */
1382 		if (!lastrow->cells) {
1383 		    return -1;
1384 		}
1385 		lastrow->allocated = need_cells;
1386 		memcpy(lastrow->cells, prev_row->cells,
1387 		       (unsigned) lastrow->allocated * sizeof(STable_cellinfo));
1388 
1389 		i = -1;
1390 		while (++i < ncells) {
1391 		    /* Stbl_addCellToTable grants RESERVEDCELL, but we do not
1392 		       want this action for fake cells.
1393 		       XXX Maybe always fake RESERVEDCELL instead of explicitly
1394 		       creating/destroying cells?  */
1395 		    if (lastrow->cells[i].alignment == RESERVEDCELL)
1396 			lastrow->cells[i].alignment = HT_LEFT;
1397 		}
1398 	    }
1399 	}
1400 
1401 	/* Fake <TD></TD>...<TD> (and maybe a </TD>) at BOL. */
1402 	CTRACE2(TRACE_TRST,
1403 		(tfp, "TRST:Stbl_fakeFinishCellInTable FAKE %d elts%s\n",
1404 		 ncells, (finishing ? ", last unfinished" : "")));
1405 	i = 0;
1406 	while (++i <= ncells) {
1407 	    /* XXXX A lot of args may be wrong... */
1408 	    if (Stbl_addCellToTable(me, (i == ncells ? cs : 1), rs, al,
1409 				    ih, lineno, 0, 0) < 0) {
1410 		return -1;
1411 	    }
1412 	    lastrow->content &= ~HAS_BEG_OF_CELL;	/* BEG_OF_CELL was fake */
1413 	    /* We cannot run out of width here, so it is safe to not
1414 	       call Stbl_finishCellInTable(), but Stbl_finishCellInRow. */
1415 	    if (!finishing || (i != ncells)) {
1416 		if (Stbl_finishCellInRow(lastrow, s, end_td, lineno, 0) < 0) {
1417 		    return -1;
1418 		}
1419 	    }
1420 	}
1421 	CTRACE2(TRACE_TRST,
1422 		(tfp,
1423 		 "TRST:Stbl_fakeFinishCellInTable(lineno=%d) FINISH FAKING\n",
1424 		 lineno));
1425 	return 1;
1426     }
1427     return 0;
1428 }
1429 #endif
1430 
1431 /*
1432  * Returns -1 on error, otherwise 0.
1433  */
Stbl_addCellToTable(STable_info * me,int colspan,int rowspan,int alignment,int isheader,int lineno,int offset_not_used_yet GCC_UNUSED,int pos)1434 int Stbl_addCellToTable(STable_info *me, int colspan,
1435 			int rowspan,
1436 			int alignment,
1437 			int isheader,
1438 			int lineno,
1439 			int offset_not_used_yet GCC_UNUSED,
1440 			int pos)
1441 {
1442     STable_states *s = &me->s;
1443     STable_rowinfo *lastrow;
1444     STable_cellinfo *sumcols, *sumcol;
1445     int i, icell, ncells, sumpos;
1446 
1447     CTRACE2(TRACE_TRST,
1448 	    (tfp,
1449 	     "TRST:Stbl_addCellToTable(lineno=%d, pos=%d, isheader=%d, cs=%d, rs=%d, al=%d)\n",
1450 	     lineno, pos, (int) isheader, colspan, rowspan, alignment));
1451     if (!me->rows || !me->nrows)
1452 	return -1;		/* no row started! */
1453     /* ##850_fail_if_fail?? */
1454     if (me->rows[me->nrows - 1].ended != ROW_not_ended)
1455 	Stbl_addRowToTable(me, alignment, lineno);
1456     Stbl_finishCellInTable(me, TRST_ENDCELL_ENDTD, lineno, 0, pos);
1457     lastrow = me->rows + (me->nrows - 1);
1458 
1459 #ifdef EXP_NESTED_TABLES
1460     if (nested_tables) {
1461 	/* If the last cell was finished by <BR></TD>, we need to fake an
1462 	   appropriate amount of cells */
1463 	if (!NO_AGGRESSIVE_NEWROW && pos == 0 && lastrow->ncells > 0
1464 	    && lastrow->cells[lastrow->ncells - 1].cLine != lineno) {
1465 	    int rc = Stbl_fakeFinishCellInTable(me, lastrow, lineno, 0);
1466 
1467 	    if (rc < 0)
1468 		return -1;
1469 	    if (rc)
1470 		lastrow = me->rows + (me->nrows - 1);
1471 	}
1472     }
1473 #endif
1474     if (colspan == 0) {
1475 	colspan = get_remaining_colspan(lastrow, me->sumcols, me->ncolinfo,
1476 					colspan, me->ncols);
1477     }
1478     ncells = lastrow->ncells;	/* remember what it was before adding cell. */
1479     icell = Stbl_addCellToRow(lastrow, me->sumcols, me->ncolinfo, s,
1480 			      colspan, alignment, isheader,
1481 			      lineno, &pos);
1482     if (icell < 0)
1483 	return icell;
1484     if (me->nrows == 1 && me->startline < lastrow->Line)
1485 	me->startline = lastrow->Line;
1486 
1487     if (rowspan != 1) {
1488 	Stbl_reserveCellsInTable(me, icell, colspan, rowspan);
1489 	/* me->rows may now have been realloc'd, make lastrow valid pointer */
1490 	lastrow = me->rows + (me->nrows - 1);
1491     }
1492     lastrow->content |= HAS_BEG_OF_CELL;
1493 
1494     {
1495 	int growby = 0;
1496 
1497 	while (icell + colspan + 1 > me->allocated_sumcols + growby)
1498 	    growby += CELLS_GROWBY;
1499 	if (growby) {
1500 	    if (me->allocated_sumcols == 0 && !me->sumcols) {
1501 		sumcols = typecallocn(STable_cellinfo, (unsigned) growby);
1502 	    } else {
1503 		sumcols = typeRealloc(STable_cellinfo, me->sumcols,
1504 				        (unsigned) (me->allocated_sumcols + growby));
1505 
1506 		for (i = 0; sumcols && i < growby; i++) {
1507 		    sumcol = sumcols + me->allocated_sumcols + i;
1508 		    sumcol->pos = sumcols[me->allocated_sumcols - 1].pos;
1509 		    sumcol->len = 0;
1510 		    sumcol->colspan = 0;
1511 		    sumcol->cLine = 0;
1512 		    sumcol->alignment = HT_ALIGN_NONE;
1513 		}
1514 	    }
1515 	    if (sumcols) {
1516 		me->allocated_sumcols += growby;
1517 		me->sumcols = sumcols;
1518 	    } else {
1519 		return -1;
1520 	    }
1521 	}
1522     }
1523     if (icell + 1 > me->ncols) {
1524 	me->ncols = icell + 1;
1525     }
1526     if (colspan > 1 && colspan + me->sumcols[icell + colspan].colspan > 0)
1527 	me->sumcols[icell + colspan].colspan = -colspan;
1528     sumpos = pos;
1529     if (ncells > 0)
1530 	sumpos += me->sumcols[ncells - 1].pos - lastrow->cells[ncells - 1].pos;
1531     update_sumcols0(me->sumcols, lastrow, sumpos,
1532 		    sumpos - ((ncells > 0)
1533 			      ? me->sumcols[icell].pos
1534 			      : me->sumcols[icell].pos),
1535 		    icell, 0, me->allocated_sumcols);
1536 
1537     me->maxpos = me->sumcols[me->allocated_sumcols - 1].pos;
1538     if (me->maxpos > /* @@@ max. line length we can accept */ MAX_STBL_POS)
1539 	return -1;
1540     return 0;
1541 }
1542 
1543 /*
1544  * Returns -1 on error, otherwise 0.
1545  */
Stbl_finishCellInTable(STable_info * me,int end_td,int lineno,int offset,int pos)1546 int Stbl_finishCellInTable(STable_info *me, int end_td,
1547 			   int lineno,
1548 			   int offset,
1549 			   int pos)
1550 {
1551     STable_states *s = &me->s;
1552     STable_rowinfo *lastrow;
1553     int len, xlen, icell;
1554     int i;
1555 
1556     CTRACE2(TRACE_TRST,
1557 	    (tfp,
1558 	     "TRST:Stbl_finishCellInTable(lineno=%d, pos=%d, off=%d, end_td=%d)\n",
1559 	     lineno, pos, offset, (int) end_td));
1560     if (me->nrows == 0)
1561 	return -1;
1562     lastrow = me->rows + (me->nrows - 1);
1563     icell = lastrow->ncells - 1;
1564     if (icell < 0)
1565 	return icell;
1566     if (s->x_td == -1) {	/* Stray </TD> or just-in-case, as on </TR> */
1567 	if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK)
1568 	    lastrow->ended = ROW_ended_by_splitline;
1569 	return 0;
1570     }
1571 #ifdef EXP_NESTED_TABLES
1572     if (nested_tables) {
1573 	if (!NO_AGGRESSIVE_NEWROW && !(end_td & TRST_FAKING_CELLS)) {
1574 	    int rc = Stbl_fakeFinishCellInTable(me, lastrow, lineno, 1);
1575 
1576 	    if (rc) {
1577 		if (rc < 0)
1578 		    return -1;
1579 		lastrow = me->rows + (me->nrows - 1);
1580 		icell = lastrow->ncells - 1;
1581 	    }
1582 	}
1583     }
1584 #endif
1585     len = Stbl_finishCellInRow(lastrow, s, end_td, lineno, pos);
1586     if (len == -1)
1587 	return len;
1588     xlen = (len > 0) ? len : s->pending_len;	/* ##890 use xlen if fixed_line?: */
1589     if (lastrow->Line == lineno)
1590 	len = xlen;
1591     if (lastrow->cells[icell].colspan > 1) {
1592 	/*
1593 	 * @@@ This is all a too-complicated mess; do we need
1594 	 * sumcols len at all, or is pos enough??
1595 	 * Answer: sumcols len is at least used for center/right
1596 	 * alignment, and should probably continue to be used there;
1597 	 * all other uses are probably not necessary.
1598 	 */
1599 	int spanlen = 0, spanlend = 0;
1600 
1601 	for (i = icell; i < icell + lastrow->cells[icell].colspan; i++) {
1602 	    if (me->sumcols[i].len > 0) {
1603 		spanlen += me->sumcols[i].len;
1604 		if (i > icell)
1605 		    spanlen++;
1606 	    }
1607 	    spanlend = HTMAX(spanlend,
1608 			     me->sumcols[i + 1].pos - me->sumcols[icell].pos);
1609 	}
1610 	if (spanlend)
1611 	    spanlend--;
1612 	if (spanlend > spanlen)
1613 	    spanlen = spanlend;
1614 	/* @@@ could overcount? */
1615 	if (len > spanlen)
1616 	    me->maxlen += (len - spanlen);
1617     } else if (len > me->sumcols[icell].len) {
1618 	if (me->sumcols[icell + 1].colspan >= -1)
1619 	    me->maxlen += (len - me->sumcols[icell].len);
1620 	me->sumcols[icell].len = len;
1621     }
1622 
1623     if (len > 0) {
1624 	update_sumcols0(me->sumcols, lastrow, pos, len,
1625 			icell, lastrow->cells[icell].colspan,
1626 			me->allocated_sumcols);
1627 	me->maxpos = me->sumcols[me->allocated_sumcols - 1].pos;
1628     }
1629 
1630     if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
1631 	lastrow->ended = ROW_ended_by_splitline;
1632 	lastrow->content |= BELIEVE_OFFSET;
1633 	lastrow->offset = offset;
1634     }
1635 #ifdef EXP_NESTED_TABLES	/* maxlen may already include contribution of a cell in this column */
1636     if (nested_tables) {
1637 	if (me->maxlen > MAX_STBL_POS)
1638 	    return -1;
1639     } else
1640 #endif
1641     {
1642 	if (me->maxlen + (xlen - len) > MAX_STBL_POS)
1643 	    return -1;
1644     }
1645     if (me->maxpos > /* @@@ max. line length we can accept */ MAX_STBL_POS)
1646 	return -1;
1647 
1648     if (lineno != lastrow->Line) {
1649 	/* @@@ Do something here?  Or is it taken care of in
1650 	   Stbl_finishCellInRow ? */
1651     }
1652 
1653     return 0;
1654 }
1655 
1656 /*
1657  * Returns -1 on error, otherwise 0.
1658  */
Stbl_addColInfo(STable_info * me,int colspan,int alignment,int isgroup)1659 int Stbl_addColInfo(STable_info *me,
1660 		    int colspan,
1661 		    int alignment,
1662 		    int isgroup)
1663 {
1664     STable_cellinfo *sumcols, *sumcol;
1665     int i, icolinfo;
1666 
1667     CTRACE2(TRACE_TRST,
1668 	    (tfp, "TRST:Stbl_addColInfo(cs=%d, al=%d, isgroup=%d)\n",
1669 	     colspan, alignment, (int) isgroup));
1670     if (isgroup) {
1671 	if (me->pending_colgroup_next > me->ncolinfo)
1672 	    me->ncolinfo = me->pending_colgroup_next;
1673 	me->pending_colgroup_next = me->ncolinfo + colspan;
1674 	if (me->ncolinfo > 0)
1675 	    me->sumcols[me->ncolinfo - 1].cLine = EOCOLG;
1676 	me->pending_colgroup_align = (short) alignment;
1677     } else {
1678 	for (i = me->pending_colgroup_next - 1;
1679 	     i >= me->ncolinfo + colspan; i--)
1680 	    me->sumcols[i].alignment = HT_ALIGN_NONE;
1681 	me->pending_colgroup_next = me->ncolinfo + colspan;
1682     }
1683     icolinfo = me->ncolinfo;
1684     if (!isgroup)
1685 	me->ncolinfo += colspan;
1686 
1687     {
1688 	int growby = 0;
1689 
1690 	while (icolinfo + colspan + 1 > me->allocated_sumcols + growby)
1691 	    growby += CELLS_GROWBY;
1692 	if (growby) {
1693 	    if (me->allocated_sumcols == 0) {
1694 		sumcols = typecallocn(STable_cellinfo, (unsigned) growby);
1695 	    } else {
1696 		sumcols = typeRealloc(STable_cellinfo, me->sumcols,
1697 				        (unsigned) (me->allocated_sumcols + growby));
1698 
1699 		for (i = 0; sumcols && i < growby; i++) {
1700 		    sumcol = sumcols + me->allocated_sumcols + i;
1701 		    sumcol->pos = sumcols[me->allocated_sumcols - 1].pos;
1702 		    sumcol->len = 0;
1703 		    sumcol->colspan = 0;
1704 		    sumcol->cLine = 0;
1705 		}
1706 	    }
1707 	    if (sumcols) {
1708 		me->allocated_sumcols += growby;
1709 		me->sumcols = sumcols;
1710 	    } else {
1711 		return -1;
1712 	    }
1713 	}
1714     }
1715 
1716     if (alignment == HT_ALIGN_NONE)
1717 	alignment = me->pending_colgroup_align;
1718     for (i = icolinfo; i < icolinfo + colspan; i++) {
1719 	me->sumcols[i].alignment = alignment;
1720     }
1721     return 0;
1722 }
1723 
1724 /*
1725  * Returns -1 on error, otherwise 0.
1726  */
Stbl_finishColGroup(STable_info * me)1727 int Stbl_finishColGroup(STable_info *me)
1728 {
1729     CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishColGroup()\n"));
1730     if (me->pending_colgroup_next >= me->ncolinfo) {
1731 	me->ncolinfo = me->pending_colgroup_next;
1732 	if (me->ncolinfo > 0)
1733 	    me->sumcols[me->ncolinfo - 1].cLine = EOCOLG;
1734     }
1735     me->pending_colgroup_next = 0;
1736     me->pending_colgroup_align = HT_ALIGN_NONE;
1737     return 0;
1738 }
1739 
Stbl_addRowGroup(STable_info * me,int alignment)1740 int Stbl_addRowGroup(STable_info *me, int alignment)
1741 {
1742     CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_addRowGroup()\n"));
1743     Stbl_cancelRowSpans(me);
1744     me->rowgroup_align = (short) alignment;
1745     return 0;			/* that's all! */
1746 }
1747 
Stbl_finishTABLE(STable_info * me)1748 int Stbl_finishTABLE(STable_info *me)
1749 {
1750     STable_states *s = &me->s;
1751     int i;
1752     int curpos = 0;
1753 
1754     CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishTABLE()\n"));
1755     if (!me || me->nrows <= 0 || me->ncols <= 0) {
1756 	return -1;
1757     }
1758     if (me->nrows > 0 && me->rows[me->nrows - 1].ncells > 0) {
1759 	if (s->pending_len > 0)
1760 	    me->rows[me->nrows - 1].cells[
1761 					     me->rows[me->nrows - 1].ncells - 1
1762 		].len = s->pending_len;
1763 	s->pending_len = 0;
1764     }
1765     Stbl_finishRowInTable(me);
1766     /* take into account offsets on multi-line cells.
1767        XXX We cannot do it honestly, since two cells on the same row may
1768        participate in multi-line table entries, and we preserve only
1769        one offset per row.  This implementation may ignore
1770        horizontal offsets for the last row of a multirow table entry.  */
1771     for (i = 0; i < me->nrows - 1; i++) {
1772 	int j = i + 1, leading = i, non_empty = 0;
1773 	STable_rowinfo *nextrow = me->rows + j;
1774 	int minoffset, have_offsets;
1775 	int foundcell = -1, max_width;
1776 
1777 	if ((nextrow->content & (IS_CONTINUATION_OF_CELL | HAS_BEG_OF_CELL | BELIEVE_OFFSET))
1778 	    != (IS_CONTINUATION_OF_CELL | BELIEVE_OFFSET))
1779 	    continue;		/* Not a continuation line */
1780 	minoffset = nextrow[-1].offset;		/* Line before first continuation */
1781 	CTRACE2(TRACE_TRST, (tfp,
1782 			     "TRST:Stbl_finishTABLE, l=%d, offset=%d, ended=%u.\n",
1783 			     i, nextrow[-1].offset, nextrow[-1].ended));
1784 
1785 	/* Find the common part of the requested offsets */
1786 	while (j < me->nrows
1787 	       && ((nextrow->content &
1788 		    (IS_CONTINUATION_OF_CELL
1789 		     | HAS_BEG_OF_CELL
1790 		     | BELIEVE_OFFSET))
1791 		   == (IS_CONTINUATION_OF_CELL | BELIEVE_OFFSET))) {
1792 	    if (minoffset > nextrow->offset)
1793 		minoffset = nextrow->offset;
1794 	    CTRACE2(TRACE_TRST,
1795 		    (tfp,
1796 		     "TRST:Stbl_finishTABLE, l=%d, offset=%d, ended=%u.\n",
1797 		     j, nextrow->offset, nextrow[-1].ended));
1798 	    nextrow++;
1799 	    j++;
1800 	}
1801 	i = j - 1;		/* Continue after this line */
1802 	/* Cancel the common part of offsets */
1803 	j = leading;		/* Restart */
1804 	nextrow = me->rows + j;	/* Line before first continuation */
1805 	have_offsets = 0;
1806 	nextrow->content |= OFFSET_IS_VALID_LAST_CELL;
1807 	while (j <= i) {	/* A continuation line */
1808 	    nextrow->offset -= minoffset;
1809 	    nextrow->content |= OFFSET_IS_VALID;
1810 	    if (nextrow->offset)
1811 		have_offsets = 1;
1812 	    nextrow++;
1813 	    j++;
1814 	}
1815 	if (!have_offsets)
1816 	    continue;		/* No offsets to deal with */
1817 
1818 	/* Find the cell number */
1819 	foundcell = -1;
1820 	j = leading + 1;	/* Restart */
1821 	nextrow = me->rows + j;	/* First continuation line */
1822 	while (foundcell == -1 && j <= i) {	/* A continuation line */
1823 	    int curcell = -1;
1824 
1825 	    while (foundcell == -1 && ++curcell < nextrow->ncells)
1826 		if (nextrow->cells[curcell].len)
1827 		    foundcell = curcell, non_empty = j;
1828 	    nextrow++;
1829 	    j++;
1830 	}
1831 	if (foundcell == -1)	/* Can it happen? */
1832 	    continue;
1833 	/* Find the max width */
1834 	max_width = 0;
1835 	j = leading;		/* Restart */
1836 	nextrow = me->rows + j;	/* Include the pre-continuation line */
1837 	while (j <= i) {	/* A continuation line */
1838 	    if (nextrow->ncells > foundcell) {
1839 		int curwid = nextrow->cells[foundcell].len + nextrow->offset;
1840 
1841 		if (curwid > max_width)
1842 		    max_width = curwid;
1843 	    }
1844 	    nextrow++;
1845 	    j++;
1846 	}
1847 	/* Update the widths */
1848 	j = non_empty;		/* Restart from the first nonempty */
1849 	nextrow = me->rows + j;
1850 	/* Register the increase of the width */
1851 	update_sumcols0(me->sumcols, me->rows + non_empty,
1852 			0 /* width only */ , max_width,
1853 			foundcell, nextrow->cells[foundcell].colspan,
1854 			me->allocated_sumcols);
1855 	j = leading;		/* Restart from pre-continuation */
1856 	nextrow = me->rows + j;
1857 	while (j <= i) {	/* A continuation line */
1858 	    if (nextrow->ncells > foundcell)
1859 		nextrow->cells[foundcell].len = max_width;
1860 	    nextrow++;
1861 	    j++;
1862 	}
1863     }				/* END of Offsets processing */
1864 
1865     for (i = 0; i < me->ncols; i++) {
1866 	if (me->sumcols[i].pos < curpos) {
1867 	    me->sumcols[i].pos = curpos;
1868 	} else {
1869 	    curpos = me->sumcols[i].pos;
1870 	}
1871 	if (me->sumcols[i].len > 0) {
1872 	    curpos += me->sumcols[i].len;
1873 	}
1874     }
1875     /* need to recheck curpos: though it is checked each time a cell
1876        is added, sometimes the result is ignored, as in split_line(). */
1877     return (curpos > MAX_STBL_POS ? -1 : me->ncols);
1878 }
1879 
Stbl_getAlignment(STable_info * me)1880 short Stbl_getAlignment(STable_info *me)
1881 {
1882     return (short) (me ? me->alignment : HT_ALIGN_NONE);
1883 }
1884 
get_fixup_positions(STable_rowinfo * me,int * oldpos,int * newpos,STable_cellinfo * sumcols)1885 static int get_fixup_positions(STable_rowinfo *me, int *oldpos,
1886 			       int *newpos,
1887 			       STable_cellinfo *sumcols)
1888 {
1889     int i = 0, ip = 0;
1890     int next_i, newlen;
1891     int ninserts;
1892 
1893     if (!me)
1894 	return -1;
1895     while (i < me->ncells) {
1896 	int offset;
1897 
1898 	next_i = i + HTMAX(1, me->cells[i].colspan);
1899 	if (me->cells[i].cLine != me->Line) {
1900 	    if (me->cells[i].cLine > me->Line)
1901 		break;
1902 	    i = next_i;
1903 	    continue;
1904 	}
1905 	oldpos[ip] = me->cells[i].pos;
1906 	if ((me->content & OFFSET_IS_VALID)
1907 	    && (i == me->ncells - 1
1908 		|| !((me->content & OFFSET_IS_VALID_LAST_CELL))))
1909 	    offset = me->offset;
1910 	else
1911 	    offset = 0;
1912 	newpos[ip] = sumcols[i].pos + offset;
1913 	if ((me->cells[i].alignment == HT_CENTER ||
1914 	     me->cells[i].alignment == HT_RIGHT) &&
1915 	    me->cells[i].len > 0) {
1916 	    newlen = sumcols[next_i].pos - newpos[ip] - 1;
1917 	    newlen = HTMAX(newlen, sumcols[i].len);
1918 	    if (me->cells[i].len < newlen) {
1919 		if (me->cells[i].alignment == HT_RIGHT) {
1920 		    newpos[ip] += newlen - me->cells[i].len;
1921 		} else {
1922 		    newpos[ip] += (newlen - me->cells[i].len) / 2;
1923 		}
1924 	    }
1925 	}
1926 	ip++;
1927 	i = next_i;
1928     }
1929     ninserts = ip;
1930     return ninserts;
1931 }
1932 
1933 /*
1934  * Returns -1 if we have no row for this lineno, or for other error,
1935  *	    0 or greater (number of oldpos/newpos pairs) if we have
1936  *	      a table row.
1937  */
Stbl_getFixupPositions(STable_info * me,int lineno,int * oldpos,int * newpos)1938 int Stbl_getFixupPositions(STable_info *me, int lineno,
1939 			   int *oldpos,
1940 			   int *newpos)
1941 {
1942     STable_rowinfo *row;
1943     int j;
1944     int ninserts = -1;
1945 
1946     if (!me || !me->nrows)
1947 	return -1;
1948     for (j = 0; j < me->nrows; j++) {
1949 	row = me->rows + j;
1950 	if (row->Line == lineno) {
1951 	    ninserts = get_fixup_positions(row, oldpos, newpos,
1952 					   me->sumcols);
1953 	    break;
1954 	}
1955     }
1956     return ninserts;
1957 }
1958 
Stbl_getStartLine(STable_info * me)1959 int Stbl_getStartLine(STable_info *me)
1960 {
1961     if (!me)
1962 	return -1;
1963     else
1964 	return me->startline;
1965 }
1966 
1967 #ifdef EXP_NESTED_TABLES
1968 
Stbl_getStartLineDeep(STable_info * me)1969 int Stbl_getStartLineDeep(STable_info *me)
1970 {
1971     if (!me)
1972 	return -1;
1973     while (me->enclosing)
1974 	me = me->enclosing;
1975     return me->startline;
1976 }
1977 
Stbl_update_enclosing(STable_info * me,int max_width,int last_lineno)1978 void Stbl_update_enclosing(STable_info *me, int max_width,
1979 			   int last_lineno)
1980 {
1981     int l;
1982 
1983     if (!me || !me->enclosing || !max_width)
1984 	return;
1985     CTRACE2(TRACE_TRST,
1986 	    (tfp, "TRST:Stbl_update_enclosing, width=%d, lines=%d...%d.\n",
1987 	     max_width, me->startline, last_lineno));
1988     for (l = me->startline; l <= last_lineno; l++) {
1989 	/* Fake <BR> in appropriate positions */
1990 	if (Stbl_finishCellInTable(me->enclosing,
1991 				   TRST_ENDCELL_LINEBREAK,
1992 				   l,
1993 				   0,
1994 				   max_width) < 0) {
1995 	    /* It is not handy to let the caller delete me->enclosing,
1996 	       and it does not buy us anything.  Do it directly. */
1997 	    STable_info *stbl = me->enclosing;
1998 
1999 	    CTRACE2(TRACE_TRST, (tfp,
2000 				 "TRST:Stbl_update_enclosing: width too large, aborting enclosing\n"));
2001 	    me->enclosing = 0;
2002 	    while (stbl) {
2003 		STable_info *enclosing = stbl->enclosing;
2004 
2005 		Stbl_free(stbl);
2006 		stbl = enclosing;
2007 	    }
2008 	    break;
2009 	}
2010     }
2011     return;
2012 }
2013 
Stbl_set_enclosing(STable_info * me,STable_info * enclosing,struct _TextAnchor * enclosing_last_anchor_before_stbl)2014 void Stbl_set_enclosing(STable_info *me, STable_info *enclosing, struct _TextAnchor *enclosing_last_anchor_before_stbl)
2015 {
2016     if (!me)
2017 	return;
2018     me->enclosing = enclosing;
2019     me->enclosing_last_anchor_before_stbl = enclosing_last_anchor_before_stbl;
2020 }
2021 
Stbl_get_enclosing(STable_info * me)2022 STable_info *Stbl_get_enclosing(STable_info *me)
2023 {
2024     if (!me)
2025 	return 0;
2026     return me->enclosing;
2027 }
2028 
Stbl_get_last_anchor_before(STable_info * me)2029 struct _TextAnchor *Stbl_get_last_anchor_before(STable_info *me)
2030 {
2031     if (!me)
2032 	return 0;
2033     return me->enclosing_last_anchor_before_stbl;
2034 }
2035 #endif
2036