1 /* HTML tables parser */
2 
3 /* Note that this does *not* fit to the HTML parser infrastructure yet, it has
4  * some special custom calling conventions and is managed from
5  * document/html/tables.c. */
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10 
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include "elinks.h"
16 
17 #include "document/html/parser/parse.h"
18 #include "document/html/parser/table.h"
19 #include "document/html/parser.h"
20 #include "document/options.h"
21 #include "util/color.h"
22 #include "util/conv.h"
23 #include "util/error.h"
24 #include "util/memory.h"
25 
26 /* Unsafe macros */
27 #include "document/html/internal.h"
28 
29 
30 #define INIT_REAL_COLS		2
31 #define INIT_REAL_ROWS		2
32 
33 #define realloc_bad_html(bad_html, size) \
34 	mem_align_alloc(bad_html, size, (size) + 1, struct html_start_end, 0xFF)
35 
36 static void
add_table_bad_html_start(struct table * table,unsigned char * start)37 add_table_bad_html_start(struct table *table, unsigned char *start)
38 {
39 	if (table->caption.start && !table->caption.end)
40 		return;
41 
42 	/* Either no bad html or last one not needing @end pointer */
43 	if (table->bad_html_size
44 	    && !table->bad_html[table->bad_html_size - 1].end)
45 		return;
46 
47 	if (realloc_bad_html(&table->bad_html, table->bad_html_size))
48 		table->bad_html[table->bad_html_size++].start = start;
49 }
50 
51 static void
add_table_bad_html_end(struct table * table,unsigned char * end)52 add_table_bad_html_end(struct table *table, unsigned char *end)
53 {
54 	if (table->caption.start && !table->caption.end) {
55 		table->caption.end = end;
56 		return;
57 	}
58 
59 	if (table->bad_html_size
60 	    && !table->bad_html[table->bad_html_size - 1].end)
61 		table->bad_html[table->bad_html_size - 1].end = end;
62 }
63 
64 
65 static void
get_bordercolor(struct html_context * html_context,unsigned char * a,color_T * rgb)66 get_bordercolor(struct html_context *html_context, unsigned char *a, color_T *rgb)
67 {
68 	unsigned char *at;
69 
70 	if (!use_document_fg_colors(html_context->options))
71 		return;
72 
73 	at = get_attr_val(a, "bordercolor", html_context->options);
74 	/* Try some other MSIE-specific attributes if any. */
75 	if (!at)
76 		at = get_attr_val(a, "bordercolorlight", html_context->options);
77 	if (!at)
78 		at = get_attr_val(a, "bordercolordark", html_context->options);
79 	if (!at) return;
80 
81 	decode_color(at, strlen(at), rgb);
82 	mem_free(at);
83 }
84 
85 static void
get_align(struct html_context * html_context,unsigned char * attr,int * a)86 get_align(struct html_context *html_context, unsigned char *attr, int *a)
87 {
88 	unsigned char *al = get_attr_val(attr, "align", html_context->options);
89 
90 	if (!al) return;
91 
92 	if (!c_strcasecmp(al, "left")) *a = ALIGN_LEFT;
93 	else if (!c_strcasecmp(al, "right")) *a = ALIGN_RIGHT;
94 	else if (!c_strcasecmp(al, "center")) *a = ALIGN_CENTER;
95 	else if (!c_strcasecmp(al, "justify")) *a = ALIGN_JUSTIFY;
96 	else if (!c_strcasecmp(al, "char")) *a = ALIGN_RIGHT; /* NOT IMPLEMENTED */
97 	mem_free(al);
98 }
99 
100 static void
get_valign(struct html_context * html_context,unsigned char * attr,int * a)101 get_valign(struct html_context *html_context, unsigned char *attr, int *a)
102 {
103 	unsigned char *al = get_attr_val(attr, "valign", html_context->options);
104 
105 	if (!al) return;
106 
107 	if (!c_strcasecmp(al, "top")) *a = VALIGN_TOP;
108 	else if (!c_strcasecmp(al, "middle")) *a = VALIGN_MIDDLE;
109 	else if (!c_strcasecmp(al, "bottom")) *a = VALIGN_BOTTOM;
110 	else if (!c_strcasecmp(al, "baseline")) *a = VALIGN_BASELINE; /* NOT IMPLEMENTED */
111 	mem_free(al);
112 }
113 
114 static void
get_column_width(unsigned char * attr,int * width,int sh,struct html_context * html_context)115 get_column_width(unsigned char *attr, int *width, int sh,
116                  struct html_context *html_context)
117 {
118 	unsigned char *al = get_attr_val(attr, "width", html_context->options);
119 	int len;
120 
121 	if (!al) return;
122 
123 	len = strlen(al);
124 	if (len && al[len - 1] == '*') {
125 		unsigned char *en;
126 		int n;
127 
128 		al[len - 1] = '\0';
129 		errno = 0;
130 		n = strtoul(al, (char **) &en, 10);
131 		if (!errno && n >= 0 && !*en)
132 			*width = WIDTH_RELATIVE - n;
133 	} else {
134 		int w = get_width(attr, "width", sh, html_context);
135 
136 		if (w >= 0) *width = w;
137 	}
138 	mem_free(al);
139 }
140 
141 static void
set_table_frame(struct html_context * html_context,struct table * table,unsigned char * attr)142 set_table_frame(struct html_context *html_context, struct table *table,
143                 unsigned char *attr)
144 {
145 	unsigned char *al;
146 
147 	if (!table->border) {
148 		table->frame = TABLE_FRAME_VOID;
149 		return;
150 	}
151 
152 	table->frame = TABLE_FRAME_BOX;
153 
154 	al = get_attr_val(attr, "frame", html_context->options);
155 	if (!al) return;
156 
157 	if (!c_strcasecmp(al, "void")) table->frame = TABLE_FRAME_VOID;
158 	else if (!c_strcasecmp(al, "above")) table->frame = TABLE_FRAME_ABOVE;
159 	else if (!c_strcasecmp(al, "below")) table->frame = TABLE_FRAME_BELOW;
160 	else if (!c_strcasecmp(al, "hsides")) table->frame = TABLE_FRAME_HSIDES;
161 	else if (!c_strcasecmp(al, "vsides")) table->frame = TABLE_FRAME_VSIDES;
162 	else if (!c_strcasecmp(al, "lhs")) table->frame = TABLE_FRAME_LHS;
163 	else if (!c_strcasecmp(al, "rhs")) table->frame = TABLE_FRAME_RHS;
164 	/* Following tests are useless since TABLE_FRAME_BOX is the default.
165 	 * else if (!c_strcasecmp(al, "box")) table->frame = TABLE_FRAME_BOX;
166 	 * else if (!c_strcasecmp(al, "border")) table->frame = TABLE_FRAME_BOX;
167 	 */
168 	mem_free(al);
169 }
170 
171 static void
set_table_rules(struct html_context * html_context,struct table * table,unsigned char * attr)172 set_table_rules(struct html_context *html_context, struct table *table,
173                 unsigned char *attr)
174 {
175 	unsigned char *al;
176 
177 	table->rules = table->border ? TABLE_RULE_ALL : TABLE_RULE_NONE;
178 
179 	al = get_attr_val(attr, "rules", html_context->options);
180 	if (!al) return;
181 
182 	if (!c_strcasecmp(al, "none")) table->rules = TABLE_RULE_NONE;
183 	else if (!c_strcasecmp(al, "groups")) table->rules = TABLE_RULE_GROUPS;
184 	else if (!c_strcasecmp(al, "rows")) table->rules = TABLE_RULE_ROWS;
185 	else if (!c_strcasecmp(al, "cols")) table->rules = TABLE_RULE_COLS;
186 	else if (!c_strcasecmp(al, "all")) table->rules = TABLE_RULE_ALL;
187 	mem_free(al);
188 }
189 
190 static void
parse_table_attributes(struct table * table,unsigned char * attr,int real,struct html_context * html_context)191 parse_table_attributes(struct table *table, unsigned char *attr, int real,
192                        struct html_context *html_context)
193 {
194 	table->fragment_id = get_attr_val(attr, "id", html_context->options);
195 
196 	get_bordercolor(html_context, attr, &table->bordercolor);
197 
198 	table->width = get_width(attr, "width", real, html_context);
199 	if (table->width == -1) {
200 		table->width = get_html_max_width();
201 		table->full_width = 1;
202 	}
203 
204 	/* From http://www.w3.org/TR/html4/struct/tables.html#adef-border-TABLE
205 	 * The following settings should be observed by user agents for
206 	 * backwards compatibility.
207 	 * Setting border="0" implies frame="void" and, unless otherwise
208 	 * specified, rules="none".
209 	 * Other values of border imply frame="border" and, unless otherwise
210 	 * specified, rules="all".
211 	 * The value "border" in the start tag of the TABLE element should be
212 	 * interpreted as the value of the frame attribute. It implies
213 	 * rules="all" and some default (non-zero) value for the border
214 	 * attribute. */
215 	table->border = get_num(attr, "border", html_context->options);
216 	if (table->border == -1) {
217 		table->border =
218 		              has_attr(attr, "border", html_context->options)
219 			      || has_attr(attr, "rules", html_context->options)
220 			      || has_attr(attr, "frame", html_context->options);
221 	}
222 
223 	if (table->border) {
224 		int_upper_bound(&table->border, 2);
225 
226 		table->cellspacing = get_num(attr, "cellspacing", html_context->options);
227 		int_bounds(&table->cellspacing, 1, 2);
228 	}
229 
230 	set_table_frame(html_context, table, attr);
231 
232 	/* TODO: cellpadding may be expressed as a percentage, this is not
233 	 * handled yet. */
234 	table->cellpadding = get_num(attr, "cellpadding", html_context->options);
235 	if (table->cellpadding == -1) {
236 		table->vcellpadding = 0;
237 		table->cellpadding = !!table->border;
238 	} else {
239 		table->vcellpadding = (table->cellpadding >= HTML_CHAR_HEIGHT / 2 + 1);
240 		table->cellpadding = (table->cellpadding >= HTML_CHAR_WIDTH / 2 + 1);
241 	}
242 
243 	set_table_rules(html_context, table, attr);
244 
245 	table->align = par_format.align;
246 	get_align(html_context, attr, &table->align);
247 
248 	table->bgcolor = par_format.bgcolor;
249 	get_bgcolor(html_context, attr, &table->bgcolor);
250 }
251 
252 
253 static struct table *
new_table(void)254 new_table(void)
255 {
256 	struct table *table = mem_calloc(1, sizeof(*table));
257 
258 	if (!table) return NULL;
259 
260 	table->cells = mem_calloc(INIT_REAL_COLS * INIT_REAL_ROWS,
261 				  sizeof(*table->cells));
262 	if (!table->cells) {
263 		mem_free(table);
264 		return NULL;
265 	}
266 	table->real_cols = INIT_REAL_COLS;
267 	table->real_rows = INIT_REAL_ROWS;
268 
269 	table->columns = mem_calloc(INIT_REAL_COLS, sizeof(*table->columns));
270 	if (!table->columns) {
271 		mem_free(table->cells);
272 		mem_free(table);
273 		return NULL;
274 	}
275 	table->real_columns_count = INIT_REAL_COLS;
276 
277 	return table;
278 }
279 
280 void
free_table(struct table * table)281 free_table(struct table *table)
282 {
283 	int col, row;
284 
285 	mem_free_if(table->min_cols_widths);
286 	mem_free_if(table->max_cols_widths);
287 	mem_free_if(table->cols_widths);
288 	mem_free_if(table->rows_heights);
289 	mem_free_if(table->fragment_id);
290 	mem_free_if(table->cols_x);
291 	mem_free_if(table->bad_html);
292 
293 	for (col = 0; col < table->cols; col++)
294 		for (row = 0; row < table->rows; row++)
295 			mem_free_if(CELL(table, col, row)->fragment_id);
296 
297 	mem_free(table->cells);
298 	mem_free(table->columns);
299 	mem_free(table);
300 }
301 
302 static void
expand_cells(struct table * table,int dest_col,int dest_row)303 expand_cells(struct table *table, int dest_col, int dest_row)
304 {
305 	if (dest_col >= table->cols) {
306 		if (table->cols) {
307 			int last_col = table->cols - 1;
308 			int row;
309 
310 			for (row = 0; row < table->rows; row++) {
311 				int col;
312 				struct table_cell *cellp = CELL(table, last_col, row);
313 
314 				if (cellp->colspan != -1) continue;
315 
316 				for (col = table->cols; col <= dest_col; col++) {
317 					struct table_cell *cell = CELL(table, col, row);
318 
319 					cell->is_used = 1;
320 					cell->is_spanned = 1;
321 					cell->rowspan = cellp->rowspan;
322 					cell->colspan = -1;
323 					cell->col = cellp->col;
324 					cell->row = cellp->row;
325 				}
326 			}
327 		}
328 		table->cols = dest_col + 1;
329 	}
330 
331 	if (dest_row >= table->rows) {
332 		if (table->rows) {
333 			int last_row = table->rows - 1;
334 			int col;
335 
336 			for (col = 0; col < table->cols; col++) {
337 				int row;
338 				struct table_cell *cellp = CELL(table, col, last_row);
339 
340 				if (cellp->rowspan != -1) continue;
341 
342 				for (row = table->rows; row <= dest_row; row++) {
343 					struct table_cell *cell = CELL(table, col, row);
344 
345 					cell->is_used = 1;
346 					cell->is_spanned = 1;
347 					cell->rowspan = -1;
348 					cell->colspan = cellp->colspan;
349 					cell->col = cellp->col;
350 					cell->row = cellp->row;
351 				}
352 			}
353 		}
354 		table->rows = dest_row + 1;
355 	}
356 }
357 
358 static void
copy_table(struct table * table_src,struct table * table_dst)359 copy_table(struct table *table_src, struct table *table_dst)
360 {
361 	int row;
362 	int size = sizeof(*table_dst->cells) * table_src->cols;
363 
364 	if (!size) return;
365 
366 	for (row = 0; row < table_src->rows; row++) {
367 		memcpy(&table_dst->cells[row * table_dst->real_cols],
368 		       &table_src->cells[row * table_src->real_cols],
369 		       size);
370 	}
371 }
372 
373 
374 #define SMART_RAISE_LIMIT 256*1024
375 static inline int
smart_raise(int target,int base,int unit,int limit)376 smart_raise(int target, int base, int unit, int limit)
377 {
378 	while (target > base) {
379 		int orig_base = base;
380 
381 		/* Until we reach 256kb we go fast. Then we raise
382 		 * by 256kb amounts. */
383 		if (base < limit / unit) {
384 			base <<= 1;
385 		} else {
386 			base += limit / unit;
387 		}
388 		/* Overflow? */
389 		if (base <= orig_base) return 0;
390 	}
391 	return base;
392 }
393 
394 static struct table_cell *
new_cell(struct table * table,int dest_col,int dest_row)395 new_cell(struct table *table, int dest_col, int dest_row)
396 {
397 	if (dest_col < table->cols && dest_row < table->rows)
398 		return CELL(table, dest_col, dest_row);
399 
400 	while (1) {
401 		struct table new;
402 		int limit;
403 
404 		if (dest_col < table->real_cols && dest_row < table->real_rows) {
405 			expand_cells(table, dest_col, dest_row);
406 			return CELL(table, dest_col, dest_row);
407 		}
408 
409 		new.real_cols = smart_raise(dest_col + 1, table->real_cols,
410 					    sizeof(*new.cells),
411 					    /* assume square distribution of
412 					     * cols/rows */
413 					    SMART_RAISE_LIMIT / 2);
414 		if (!new.real_cols) return NULL;
415 
416 		limit = SMART_RAISE_LIMIT
417 		      - sizeof(*new.cells) * new.real_cols;
418 		limit = MAX(limit, SMART_RAISE_LIMIT/2);
419 
420 		new.real_rows = smart_raise(dest_row + 1, table->real_rows,
421 					    sizeof(*new.cells), limit);
422 		if (!new.real_rows) return NULL;
423 
424 		new.cells = mem_calloc(new.real_cols * new.real_rows,
425 				       sizeof(*new.cells));
426 		if (!new.cells) return NULL;
427 
428 		copy_table(table, &new);
429 
430 		mem_free(table->cells);
431 		table->cells	 = new.cells;
432 		table->real_cols = new.real_cols;
433 		table->real_rows = new.real_rows;
434 	}
435 }
436 
437 static void
new_columns(struct table * table,int span,int width,int align,int valign,int group)438 new_columns(struct table *table, int span, int width, int align,
439 	    int valign, int group)
440 {
441 	if (table->columns_count + span > table->real_columns_count) {
442 		int n = table->real_columns_count;
443 		struct table_column *new_columns;
444 
445 		n = smart_raise(table->columns_count + span, n,
446 				sizeof(*new_columns), SMART_RAISE_LIMIT);
447 		if (!n) return;
448 
449 		new_columns = mem_realloc(table->columns, n * sizeof(*new_columns));
450 		if (!new_columns) return;
451 
452 		table->real_columns_count = n;
453 		table->columns = new_columns;
454 	}
455 
456 	while (span--) {
457 		struct table_column *column = &table->columns[table->columns_count++];
458 
459 		column->align = align;
460 		column->valign = valign;
461 		column->width = width;
462 		column->group = group;
463 		group = 0;
464 	}
465 }
466 
467 static void
set_td_width(struct table * table,int col,int width,int force)468 set_td_width(struct table *table, int col, int width, int force)
469 {
470 	if (col >= table->cols_x_count) {
471 		int n = table->cols_x_count;
472 		int i;
473 		int *new_cols_x;
474 
475 		n = smart_raise(col + 1, n, sizeof(*new_cols_x), SMART_RAISE_LIMIT);
476 		if (!n && table->cols_x_count) return;
477 		if (!n) n = col + 1;
478 
479 		new_cols_x = mem_realloc(table->cols_x, n * sizeof(*new_cols_x));
480 		if (!new_cols_x) return;
481 
482 		for (i = table->cols_x_count; i < n; i++)
483 			new_cols_x[i] = WIDTH_AUTO;
484 		table->cols_x_count = n;
485 		table->cols_x = new_cols_x;
486 	}
487 
488 	if (force || table->cols_x[col] == WIDTH_AUTO) {
489 		table->cols_x[col] = width;
490 		return;
491 	}
492 
493 	if (width == WIDTH_AUTO) return;
494 
495 	if (width < 0 && table->cols_x[col] >= 0) {
496 		table->cols_x[col] = width;
497 		return;
498 	}
499 
500 	if (width >= 0 && table->cols_x[col] < 0) return;
501 	table->cols_x[col] = (table->cols_x[col] + width) >> 1;
502 }
503 
504 static unsigned char *
skip_table(unsigned char * html,unsigned char * eof)505 skip_table(unsigned char *html, unsigned char *eof)
506 {
507 	int level = 1;
508 
509 	while (1) {
510 		unsigned char *name;
511 		int namelen, closing_tag = 0;
512 
513 		while (html < eof
514 		       && (*html != '<'
515 			    || parse_element(html, eof, &name, &namelen, NULL,
516 					    &html)))
517 			html++;
518 
519 		if (html >= eof) return eof;
520 
521 		if (!namelen) continue;
522 
523 		if (*name == '/') {
524 			closing_tag = 1;
525 			name++; namelen--;
526 			if (!namelen) continue;
527 		}
528 
529 
530 		if (!c_strlcasecmp(name, namelen, "TABLE", 5)) {
531 			if (!closing_tag) {
532 				level++;
533 			} else {
534 				level--;
535 				if (!level) return html;
536 			}
537 		}
538 	}
539 }
540 
541 struct table *
parse_table(unsigned char * html,unsigned char * eof,unsigned char ** end,unsigned char * attr,int sh,struct html_context * html_context)542 parse_table(unsigned char *html, unsigned char *eof, unsigned char **end,
543 	    unsigned char *attr, int sh, struct html_context *html_context)
544 {
545 	struct table *table;
546 	struct table_cell *cell;
547 	unsigned char *t_attr, *en, *name;
548 	unsigned char *l_fragment_id = NULL;
549 	color_T last_bgcolor;
550 	int namelen;
551 	int in_cell = 0;
552 	int l_al = ALIGN_LEFT;
553 	int l_val = VALIGN_MIDDLE;
554 	int colspan, rowspan;
555 	int group = 0;
556 	int i, j, k;
557 	int c_al = ALIGN_TR, c_val = VALIGN_TR, c_width = WIDTH_AUTO, c_span = 0;
558 	int cols, rows;
559 	int col = 0, row = -1;
560 	int maxj;
561 	int closing_tag, is_header;
562 	unsigned char c;
563 
564 	*end = html;
565 
566 	table = new_table();
567 	if (!table) return NULL;
568 
569 	parse_table_attributes(table, attr, sh, html_context);
570 	last_bgcolor = table->bgcolor;
571 
572 se:
573 	en = html;
574 
575 see:
576 	html = en;
577 	if (!in_cell) {
578 		add_table_bad_html_start(table, html);
579 	}
580 
581 	while (html < eof && *html != '<') html++;
582 
583 	if (html >= eof) {
584 		if (in_cell) CELL(table, col, row)->end = html;
585 		add_table_bad_html_end(table, html);
586 		goto scan_done;
587 	}
588 
589 	if (html + 2 <= eof && (html[1] == '!' || html[1] == '?')) {
590 		html = skip_comment(html, eof);
591 		goto se;
592 	}
593 
594 	if (parse_element(html, eof, &name, &namelen, &t_attr, &en)) {
595 		html++;
596 		goto se;
597 	}
598 
599 	if (!namelen) goto see;
600 
601 	if (name[0] == '/') {
602 		namelen--;
603 		if (!namelen) goto see;
604 	       	name++;
605 		closing_tag = 1;
606 
607 	} else {
608 		closing_tag = 0;
609 	}
610 
611 	if (!c_strlcasecmp(name, namelen, "TABLE", 5)) {
612 		if (!closing_tag) {
613 			en = skip_table(en, eof);
614 			goto see;
615 
616 		} else {
617 			if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
618 			if (in_cell) CELL(table, col, row)->end = html;
619 
620 			add_table_bad_html_end(table, html);
621 
622 			goto scan_done;
623 		}
624 	}
625 
626 	if (!c_strlcasecmp(name, namelen, "CAPTION", 7)) {
627 		if (!closing_tag) {
628 			add_table_bad_html_end(table, html);
629 			if (!table->caption.start)
630 				table->caption.start = html;
631 
632 		} else {
633 			if (table->caption.start && !table->caption.end)
634 				table->caption.end = html;
635 		}
636 
637 		goto see;
638 	}
639 
640 	if (!c_strlcasecmp(name, namelen, "COLGROUP", 8)) {
641 		if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
642 
643 		add_table_bad_html_end(table, html);
644 
645 		c_al = ALIGN_TR;
646 		c_val = VALIGN_TR;
647 		c_width = WIDTH_AUTO;
648 
649 		if (!closing_tag) {
650 			get_align(html_context, t_attr, &c_al);
651 			get_valign(html_context, t_attr, &c_val);
652 			get_column_width(t_attr, &c_width, sh, html_context);
653 			c_span = get_num(t_attr, "span", html_context->options);
654 			if (c_span == -1)
655 				c_span = 1;
656 			else if (c_span > HTML_MAX_COLSPAN)
657 				c_span = HTML_MAX_COLSPAN;
658 
659 		} else {
660 			c_span = 0;
661 		}
662 
663 		goto see;
664 	}
665 
666 	if (!closing_tag && !c_strlcasecmp(name, namelen, "COL", 3)) {
667 		int sp, width, al, val;
668 
669 		add_table_bad_html_end(table, html);
670 
671 		sp = get_num(t_attr, "span", html_context->options);
672 		if (sp == -1) sp = 1;
673 		else if (sp > HTML_MAX_COLSPAN) sp = HTML_MAX_COLSPAN;
674 
675 		width = c_width;
676 		al = c_al;
677 		val = c_val;
678 		get_align(html_context, t_attr, &al);
679 		get_valign(html_context, t_attr, &val);
680 		get_column_width(t_attr, &width, sh, html_context);
681 		new_columns(table, sp, width, al, val, !!c_span);
682 		c_span = 0;
683 		goto see;
684 	}
685 
686 	/* All following tags have T as first letter. */
687 	if (c_toupper(name[0]) != 'T') goto see;
688 
689 	name++; namelen--;
690 	if (namelen == 0) goto see;
691 
692 	c = c_toupper(name[0]);
693 
694 	/* /TR /TD /TH */
695 	if (closing_tag && namelen == 1) {
696 		if (c == 'R' || c == 'D' || c == 'H') {
697 			if (c_span)
698 				new_columns(table, c_span, c_width, c_al, c_val, 1);
699 
700 			if (in_cell) {
701 				CELL(table, col, row)->end = html;
702 				in_cell = 0;
703 			}
704 
705 			add_table_bad_html_end(table, html);
706 			goto see;
707 		}
708 	}
709 
710 	/* Beyond that point, opening tags only. */
711 	if (closing_tag) goto see;
712 
713 	/* THEAD TBODY TFOOT */
714 	if (namelen == 4
715 	    && ((!c_strlcasecmp(name, namelen, "HEAD", 4)) ||
716 		(!c_strlcasecmp(name, namelen, "BODY", 4)) ||
717 		(!c_strlcasecmp(name, namelen, "FOOT", 4)))) {
718 		if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
719 
720 		add_table_bad_html_end(table, html);
721 
722 		group = 2;
723 		goto see;
724 	}
725 
726 	/* Beyond this point, only two letters tags. */
727 	if (namelen != 1) goto see;
728 
729 	/* TR */
730 	if (c == 'R') {
731 		if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
732 
733 		if (in_cell) {
734 			CELL(table, col, row)->end = html;
735 			in_cell = 0;
736 		}
737 
738 		add_table_bad_html_end(table, html);
739 
740 		if (group) group--;
741 		l_al = ALIGN_LEFT;
742 		l_val = VALIGN_MIDDLE;
743 		last_bgcolor = table->bgcolor;
744 		get_align(html_context, t_attr, &l_al);
745 		get_valign(html_context, t_attr, &l_val);
746 		get_bgcolor(html_context, t_attr, &last_bgcolor);
747 		mem_free_set(&l_fragment_id,
748 		             get_attr_val(t_attr, "id", html_context->options));
749 		row++;
750 		col = 0;
751 		goto see;
752 	}
753 
754 	/* TD TH */
755 	is_header = (c == 'H');
756 
757 	if (!is_header && c != 'D')
758 		goto see;
759 
760 	if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
761 
762 	add_table_bad_html_end(table, html);
763 
764 	if (in_cell) {
765 		CELL(table, col, row)->end = html;
766 		in_cell = 0;
767 	}
768 
769 	if (row == -1) {
770 		row = 0;
771 		col = 0;
772 	}
773 
774 	for (;;col++) {
775 		cell = new_cell(table, col, row);
776 		if (!cell) goto see;
777 
778 		if (!cell->is_used) break;
779 		if (cell->colspan == -1) goto see;
780 	}
781 
782 	in_cell = 1;
783 
784 	cell->col = col;
785 	cell->row = row;
786 	cell->is_used = 1;
787 	cell->start = en;
788 
789 	cell->align = l_al;
790 	cell->valign = l_val;
791 	cell->fragment_id = get_attr_val(t_attr, "id", html_context->options);
792 	if (!cell->fragment_id && l_fragment_id) {
793 		cell->fragment_id = l_fragment_id;
794 		l_fragment_id = NULL;
795 	}
796 
797 	cell->is_header = is_header;
798 	if (cell->is_header) cell->align = ALIGN_CENTER;
799 
800 	if (group == 1) cell->is_group = 1;
801 
802 	if (col < table->columns_count) {
803 		if (table->columns[col].align != ALIGN_TR)
804 			cell->align = table->columns[col].align;
805 		if (table->columns[col].valign != VALIGN_TR)
806 			cell->valign = table->columns[col].valign;
807 	}
808 
809 	cell->bgcolor = last_bgcolor;
810 
811 	get_align(html_context, t_attr, &cell->align);
812 	get_valign(html_context, t_attr, &cell->valign);
813 	get_bgcolor(html_context, t_attr, &cell->bgcolor);
814 
815 	colspan = get_num(t_attr, "colspan", html_context->options);
816 	if (colspan == -1) colspan = 1;
817 	else if (!colspan) colspan = -1;
818 	else if (colspan > HTML_MAX_COLSPAN) colspan = HTML_MAX_COLSPAN;
819 
820 	rowspan = get_num(t_attr, "rowspan", html_context->options);
821 	if (rowspan == -1) rowspan = 1;
822 	else if (!rowspan) rowspan = -1;
823 	else if (rowspan > HTML_MAX_ROWSPAN) rowspan = HTML_MAX_ROWSPAN;
824 
825 	cell->colspan = colspan;
826 	cell->rowspan = rowspan;
827 
828 	if (colspan == 1) {
829 		int width = WIDTH_AUTO;
830 
831 		get_column_width(t_attr, &width, sh, html_context);
832 		if (width != WIDTH_AUTO)
833 			set_td_width(table, col, width, 0);
834 	}
835 
836 	cols = table->cols;
837 	for (i = 1; colspan != -1 ? i < colspan : i < cols; i++) {
838 		struct table_cell *span_cell = new_cell(table, col + i, row);
839 
840 		if (!span_cell)
841 			goto abort;
842 
843 		if (span_cell->is_used) {
844 			colspan = i;
845 			for (k = 0; k < i; k++)
846 				CELL(table, col + k, row)->colspan = colspan;
847 			break;
848 		}
849 
850 		span_cell->is_used = span_cell->is_spanned = 1;
851 		span_cell->rowspan = rowspan;
852 		span_cell->colspan = colspan;
853 		span_cell->col = col;
854 		span_cell->row = row;
855 	}
856 
857 	rows = table->rows;
858 	maxj = rowspan != -1 ? rowspan : rows;
859 	/* Out of memory prevention, limit allocated memory to HTML_MAX_CELLS_MEMORY.
860 	 * Not perfect but better than nothing. */
861 	if (maxj * i > HTML_MAX_CELLS_MEMORY / sizeof(*cell))
862 		goto abort;
863 
864 	for (j = 1; j < maxj; j++) {
865 		for (k = 0; k < i; k++) {
866 			struct table_cell *span_cell = new_cell(table, col + k, row + j);
867 
868 			if (!span_cell)
869 				goto abort;
870 
871 			if (span_cell->is_used) {
872 				int l, m;
873 
874 				if (span_cell->col == col
875 				    && span_cell->row == row)
876 					continue;
877 
878 				for (l = 0; l < k; l++)
879 					memset(CELL(table, col + l, row + j), 0,
880 					       sizeof(*span_cell));
881 
882 				rowspan = j;
883 
884 				for (l = 0; l < i; l++)
885 					for (m = 0; m < j; m++)
886 						CELL(table, col + l, row + m)->rowspan = j;
887 				goto see;
888 			}
889 
890 			span_cell->is_used = span_cell->is_spanned = 1;
891 			span_cell->rowspan = rowspan;
892 			span_cell->colspan = colspan;
893 			span_cell->col = col;
894 			span_cell->row = row;
895 		}
896 	}
897 
898 	goto see;
899 
900 scan_done:
901 	*end = html;
902 
903 	mem_free_if(l_fragment_id);
904 
905 	for (col = 0; col < table->cols; col++) for (row = 0; row < table->rows; row++) {
906 		struct table_cell *cell = CELL(table, col, row);
907 
908 		if (!cell->is_spanned) {
909 			if (cell->colspan == -1) cell->colspan = table->cols - col;
910 			if (cell->rowspan == -1) cell->rowspan = table->rows - row;
911 		}
912 	}
913 
914 	if (table->rows) {
915 		table->rows_heights = mem_calloc(table->rows, sizeof(*table->rows_heights));
916 		if (!table->rows_heights)
917 			goto abort;
918 	} else {
919 		table->rows_heights = NULL;
920 	}
921 
922 	for (col = 0; col < table->columns_count; col++)
923 		if (table->columns[col].width != WIDTH_AUTO)
924 			set_td_width(table, col, table->columns[col].width, 1);
925 	set_td_width(table, table->cols, WIDTH_AUTO, 0);
926 
927 	return table;
928 
929 abort:
930 	*end = eof;
931 	free_table(table);
932 	return NULL;
933 }
934