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