Bug 784: Add html_context->doc_cp and parse attributes with it.
[elinks.git] / src / document / html / parser / table.c
blobdeddd12fe10e84987bd61e266e1961baef9e921d
1 /* HTML tables parser */
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. */
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <string.h>
15 #include "elinks.h"
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"
26 /* Unsafe macros */
27 #include "document/html/internal.h"
30 #define INIT_REAL_COLS 2
31 #define INIT_REAL_ROWS 2
33 #define realloc_bad_html(bad_html, size) \
34 mem_align_alloc(bad_html, size, (size) + 1, 0xFF)
36 static void
37 add_table_bad_html_start(struct table *table, unsigned char *start)
39 if (table->caption.start && !table->caption.end)
40 return;
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;
47 if (realloc_bad_html(&table->bad_html, table->bad_html_size))
48 table->bad_html[table->bad_html_size++].start = start;
51 static void
52 add_table_bad_html_end(struct table *table, unsigned char *end)
54 if (table->caption.start && !table->caption.end) {
55 table->caption.end = end;
56 return;
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;
65 static void
66 get_bordercolor(struct html_context *html_context, unsigned char *a, color_T *rgb)
68 unsigned char *at;
70 if (!use_document_fg_colors(html_context->options))
71 return;
73 at = get_attr_val(a, "bordercolor", html_context->doc_cp);
74 /* Try some other MSIE-specific attributes if any. */
75 if (!at)
76 at = get_attr_val(a, "bordercolorlight", html_context->doc_cp);
77 if (!at)
78 at = get_attr_val(a, "bordercolordark", html_context->doc_cp);
79 if (!at) return;
81 decode_color(at, strlen(at), rgb);
82 mem_free(at);
85 static void
86 get_align(struct html_context *html_context, unsigned char *attr, int *a)
88 unsigned char *al = get_attr_val(attr, "align", html_context->doc_cp);
90 if (!al) return;
92 if (!strcasecmp(al, "left")) *a = ALIGN_LEFT;
93 else if (!strcasecmp(al, "right")) *a = ALIGN_RIGHT;
94 else if (!strcasecmp(al, "center")) *a = ALIGN_CENTER;
95 else if (!strcasecmp(al, "justify")) *a = ALIGN_JUSTIFY;
96 else if (!strcasecmp(al, "char")) *a = ALIGN_RIGHT; /* NOT IMPLEMENTED */
97 mem_free(al);
100 static void
101 get_valign(struct html_context *html_context, unsigned char *attr, int *a)
103 unsigned char *al = get_attr_val(attr, "valign", html_context->doc_cp);
105 if (!al) return;
107 if (!strcasecmp(al, "top")) *a = VALIGN_TOP;
108 else if (!strcasecmp(al, "middle")) *a = VALIGN_MIDDLE;
109 else if (!strcasecmp(al, "bottom")) *a = VALIGN_BOTTOM;
110 else if (!strcasecmp(al, "baseline")) *a = VALIGN_BASELINE; /* NOT IMPLEMENTED */
111 mem_free(al);
114 static void
115 get_column_width(unsigned char *attr, int *width, int sh,
116 struct html_context *html_context)
118 unsigned char *al = get_attr_val(attr, "width", html_context->doc_cp);
119 int len;
121 if (!al) return;
123 len = strlen(al);
124 if (len && al[len - 1] == '*') {
125 unsigned char *en;
126 int n;
128 al[len - 1] = '\0';
129 errno = 0;
130 n = strtoul(al, (char **) &en, 10);
131 if (!errno && n >= 0 && (!*en || *en == '.'))
132 *width = WIDTH_RELATIVE - n;
133 } else {
134 int w = get_width(attr, "width", sh, html_context);
136 if (w >= 0) *width = w;
138 mem_free(al);
141 static void
142 set_table_frame(struct html_context *html_context, struct table *table,
143 unsigned char *attr)
145 unsigned char *al;
147 if (!table->border) {
148 table->frame = TABLE_FRAME_VOID;
149 return;
152 table->frame = TABLE_FRAME_BOX;
154 al = get_attr_val(attr, "frame", html_context->doc_cp);
155 if (!al) return;
157 if (!strcasecmp(al, "void")) table->frame = TABLE_FRAME_VOID;
158 else if (!strcasecmp(al, "above")) table->frame = TABLE_FRAME_ABOVE;
159 else if (!strcasecmp(al, "below")) table->frame = TABLE_FRAME_BELOW;
160 else if (!strcasecmp(al, "hsides")) table->frame = TABLE_FRAME_HSIDES;
161 else if (!strcasecmp(al, "vsides")) table->frame = TABLE_FRAME_VSIDES;
162 else if (!strcasecmp(al, "lhs")) table->frame = TABLE_FRAME_LHS;
163 else if (!strcasecmp(al, "rhs")) table->frame = TABLE_FRAME_RHS;
164 /* Following tests are useless since TABLE_FRAME_BOX is the default.
165 * else if (!strcasecmp(al, "box")) table->frame = TABLE_FRAME_BOX;
166 * else if (!strcasecmp(al, "border")) table->frame = TABLE_FRAME_BOX;
168 mem_free(al);
171 static void
172 set_table_rules(struct html_context *html_context, struct table *table,
173 unsigned char *attr)
175 unsigned char *al;
177 table->rules = table->border ? TABLE_RULE_ALL : TABLE_RULE_NONE;
179 al = get_attr_val(attr, "rules", html_context->doc_cp);
180 if (!al) return;
182 if (!strcasecmp(al, "none")) table->rules = TABLE_RULE_NONE;
183 else if (!strcasecmp(al, "groups")) table->rules = TABLE_RULE_GROUPS;
184 else if (!strcasecmp(al, "rows")) table->rules = TABLE_RULE_ROWS;
185 else if (!strcasecmp(al, "cols")) table->rules = TABLE_RULE_COLS;
186 else if (!strcasecmp(al, "all")) table->rules = TABLE_RULE_ALL;
187 mem_free(al);
190 static void
191 parse_table_attributes(struct table *table, unsigned char *attr, int real,
192 struct html_context *html_context)
194 table->fragment_id = get_attr_val(attr, "id", html_context->doc_cp);
196 get_bordercolor(html_context, attr, &table->bordercolor);
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;
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->doc_cp);
216 if (table->border == -1) {
217 table->border =
218 has_attr(attr, "border", html_context->doc_cp)
219 || has_attr(attr, "rules", html_context->doc_cp)
220 || has_attr(attr, "frame", html_context->doc_cp);
223 if (table->border) {
224 int_upper_bound(&table->border, 2);
226 table->cellspacing = get_num(attr, "cellspacing", html_context->doc_cp);
227 int_bounds(&table->cellspacing, 1, 2);
230 set_table_frame(html_context, table, attr);
232 /* TODO: cellpadding may be expressed as a percentage, this is not
233 * handled yet. */
234 table->cellpadding = get_num(attr, "cellpadding", html_context->doc_cp);
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);
243 set_table_rules(html_context, table, attr);
245 table->align = par_format.align;
246 get_align(html_context, attr, &table->align);
248 table->bgcolor = par_format.bgcolor;
249 get_bgcolor(html_context, attr, &table->bgcolor);
253 static struct table *
254 new_table(void)
256 struct table *table = mem_calloc(1, sizeof(*table));
258 if (!table) return NULL;
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;
266 table->real_cols = INIT_REAL_COLS;
267 table->real_rows = INIT_REAL_ROWS;
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;
275 table->real_columns_count = INIT_REAL_COLS;
277 return table;
280 void
281 free_table(struct table *table)
283 int col, row;
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);
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);
297 mem_free(table->cells);
298 mem_free(table->columns);
299 mem_free(table);
302 static void
303 expand_cells(struct table *table, int dest_col, int dest_row)
305 if (dest_col >= table->cols) {
306 if (table->cols) {
307 int last_col = table->cols - 1;
308 int row;
310 for (row = 0; row < table->rows; row++) {
311 int col;
312 struct table_cell *cellp = CELL(table, last_col, row);
314 if (cellp->colspan != -1) continue;
316 for (col = table->cols; col <= dest_col; col++) {
317 struct table_cell *cell = CELL(table, col, row);
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;
328 table->cols = dest_col + 1;
331 if (dest_row >= table->rows) {
332 if (table->rows) {
333 int last_row = table->rows - 1;
334 int col;
336 for (col = 0; col < table->cols; col++) {
337 int row;
338 struct table_cell *cellp = CELL(table, col, last_row);
340 if (cellp->rowspan != -1) continue;
342 for (row = table->rows; row <= dest_row; row++) {
343 struct table_cell *cell = CELL(table, col, row);
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;
354 table->rows = dest_row + 1;
358 static void
359 copy_table(struct table *table_src, struct table *table_dst)
361 int row;
362 int size = sizeof(*table_dst->cells) * table_src->cols;
364 if (!size) return;
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);
374 #define SMART_RAISE_LIMIT 256*1024
375 static inline int
376 smart_raise(int target, int base, int unit, int limit)
378 while (target > base) {
379 int orig_base = base;
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;
388 /* Overflow? */
389 if (base <= orig_base) return 0;
391 return base;
394 static struct table_cell *
395 new_cell(struct table *table, int dest_col, int dest_row)
397 if (dest_col < table->cols && dest_row < table->rows)
398 return CELL(table, dest_col, dest_row);
400 while (1) {
401 struct table new;
402 int limit;
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);
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;
416 limit = SMART_RAISE_LIMIT
417 - sizeof(*new.cells) * new.real_cols;
418 limit = MAX(limit, SMART_RAISE_LIMIT/2);
420 new.real_rows = smart_raise(dest_row + 1, table->real_rows,
421 sizeof(*new.cells), limit);
422 if (!new.real_rows) return NULL;
424 new.cells = mem_calloc(new.real_cols * new.real_rows,
425 sizeof(*new.cells));
426 if (!new.cells) return NULL;
428 copy_table(table, &new);
430 mem_free(table->cells);
431 table->cells = new.cells;
432 table->real_cols = new.real_cols;
433 table->real_rows = new.real_rows;
437 static void
438 new_columns(struct table *table, int span, int width, int align,
439 int valign, int group)
441 if (table->columns_count + span > table->real_columns_count) {
442 int n = table->real_columns_count;
443 struct table_column *new_columns;
445 n = smart_raise(table->columns_count + span, n,
446 sizeof(*new_columns), SMART_RAISE_LIMIT);
447 if (!n) return;
449 new_columns = mem_realloc(table->columns, n * sizeof(*new_columns));
450 if (!new_columns) return;
452 table->real_columns_count = n;
453 table->columns = new_columns;
456 while (span--) {
457 struct table_column *column = &table->columns[table->columns_count++];
459 column->align = align;
460 column->valign = valign;
461 column->width = width;
462 column->group = group;
463 group = 0;
467 static void
468 set_td_width(struct table *table, int col, int width, int force)
470 if (col >= table->cols_x_count) {
471 int n = table->cols_x_count;
472 int i;
473 int *new_cols_x;
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;
479 new_cols_x = mem_realloc(table->cols_x, n * sizeof(*new_cols_x));
480 if (!new_cols_x) return;
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;
488 if (force || table->cols_x[col] == WIDTH_AUTO) {
489 table->cols_x[col] = width;
490 return;
493 if (width == WIDTH_AUTO) return;
495 if (width < 0 && table->cols_x[col] >= 0) {
496 table->cols_x[col] = width;
497 return;
500 if (width >= 0 && table->cols_x[col] < 0) return;
501 table->cols_x[col] = (table->cols_x[col] + width) >> 1;
504 static unsigned char *
505 skip_table(unsigned char *html, unsigned char *eof)
507 int level = 1;
509 while (1) {
510 unsigned char *name;
511 int namelen, closing_tag = 0;
513 while (html < eof
514 && (*html != '<'
515 || parse_element(html, eof, &name, &namelen, NULL,
516 &html)))
517 html++;
519 if (html >= eof) return eof;
521 if (!namelen) continue;
523 if (*name == '/') {
524 closing_tag = 1;
525 name++; namelen--;
526 if (!namelen) continue;
530 if (!strlcasecmp(name, namelen, "TABLE", 5)) {
531 if (!closing_tag) {
532 level++;
533 } else {
534 level--;
535 if (!level) return html;
541 struct table *
542 parse_table(unsigned char *html, unsigned char *eof, unsigned char **end,
543 unsigned char *attr, int sh, struct html_context *html_context)
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;
564 *end = html;
566 table = new_table();
567 if (!table) return NULL;
569 parse_table_attributes(table, attr, sh, html_context);
570 last_bgcolor = table->bgcolor;
573 en = html;
575 see:
576 html = en;
577 if (!in_cell) {
578 add_table_bad_html_start(table, html);
581 while (html < eof && *html != '<') html++;
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;
589 if (html + 2 <= eof && (html[1] == '!' || html[1] == '?')) {
590 html = skip_comment(html, eof);
591 goto se;
594 if (parse_element(html, eof, &name, &namelen, &t_attr, &en)) {
595 html++;
596 goto se;
599 if (!namelen) goto see;
601 if (name[0] == '/') {
602 namelen--;
603 if (!namelen) goto see;
604 name++;
605 closing_tag = 1;
607 } else {
608 closing_tag = 0;
611 if (!strlcasecmp(name, namelen, "TABLE", 5)) {
612 if (!closing_tag) {
613 en = skip_table(en, eof);
614 goto see;
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;
620 add_table_bad_html_end(table, html);
622 goto scan_done;
626 if (!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;
632 } else {
633 if (table->caption.start && !table->caption.end)
634 table->caption.end = html;
637 goto see;
640 if (!strlcasecmp(name, namelen, "COLGROUP", 8)) {
641 if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
643 add_table_bad_html_end(table, html);
645 c_al = ALIGN_TR;
646 c_val = VALIGN_TR;
647 c_width = WIDTH_AUTO;
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->doc_cp);
654 if (c_span == -1)
655 c_span = 1;
656 else if (c_span > HTML_MAX_COLSPAN)
657 c_span = HTML_MAX_COLSPAN;
659 } else {
660 c_span = 0;
663 goto see;
666 if (!closing_tag && !strlcasecmp(name, namelen, "COL", 3)) {
667 int sp, width, al, val;
669 add_table_bad_html_end(table, html);
671 sp = get_num(t_attr, "span", html_context->doc_cp);
672 if (sp == -1) sp = 1;
673 else if (sp > HTML_MAX_COLSPAN) sp = HTML_MAX_COLSPAN;
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;
686 /* All following tags have T as first letter. */
687 if (toupper(name[0]) != 'T') goto see;
689 name++; namelen--;
690 if (namelen == 0) goto see;
692 c = toupper(name[0]);
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);
700 if (in_cell) {
701 CELL(table, col, row)->end = html;
702 in_cell = 0;
705 add_table_bad_html_end(table, html);
706 goto see;
710 /* Beyond that point, opening tags only. */
711 if (closing_tag) goto see;
713 /* THEAD TBODY TFOOT */
714 if (namelen == 4
715 && ((!strlcasecmp(name, namelen, "HEAD", 4)) ||
716 (!strlcasecmp(name, namelen, "BODY", 4)) ||
717 (!strlcasecmp(name, namelen, "FOOT", 4)))) {
718 if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
720 add_table_bad_html_end(table, html);
722 group = 2;
723 goto see;
726 /* Beyond this point, only two letters tags. */
727 if (namelen != 1) goto see;
729 /* TR */
730 if (c == 'R') {
731 if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
733 if (in_cell) {
734 CELL(table, col, row)->end = html;
735 in_cell = 0;
738 add_table_bad_html_end(table, html);
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->doc_cp));
749 row++;
750 col = 0;
751 goto see;
754 /* TD TH */
755 is_header = (c == 'H');
757 if (!is_header && c != 'D')
758 goto see;
760 if (c_span) new_columns(table, c_span, c_width, c_al, c_val, 1);
762 add_table_bad_html_end(table, html);
764 if (in_cell) {
765 CELL(table, col, row)->end = html;
766 in_cell = 0;
769 if (row == -1) {
770 row = 0;
771 col = 0;
774 for (;;col++) {
775 cell = new_cell(table, col, row);
776 if (!cell) goto see;
778 if (!cell->is_used) break;
779 if (cell->colspan == -1) goto see;
782 in_cell = 1;
784 cell->col = col;
785 cell->row = row;
786 cell->is_used = 1;
787 cell->start = en;
789 cell->align = l_al;
790 cell->valign = l_val;
791 cell->fragment_id = get_attr_val(t_attr, "id", html_context->doc_cp);
792 if (!cell->fragment_id && l_fragment_id) {
793 cell->fragment_id = l_fragment_id;
794 l_fragment_id = NULL;
797 cell->is_header = is_header;
798 if (cell->is_header) cell->align = ALIGN_CENTER;
800 if (group == 1) cell->is_group = 1;
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;
809 cell->bgcolor = last_bgcolor;
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);
815 colspan = get_num(t_attr, "colspan", html_context->doc_cp);
816 if (colspan == -1) colspan = 1;
817 else if (!colspan) colspan = -1;
818 else if (colspan > HTML_MAX_COLSPAN) colspan = HTML_MAX_COLSPAN;
820 rowspan = get_num(t_attr, "rowspan", html_context->doc_cp);
821 if (rowspan == -1) rowspan = 1;
822 else if (!rowspan) rowspan = -1;
823 else if (rowspan > HTML_MAX_ROWSPAN) rowspan = HTML_MAX_ROWSPAN;
825 cell->colspan = colspan;
826 cell->rowspan = rowspan;
828 if (colspan == 1) {
829 int width = WIDTH_AUTO;
831 get_column_width(t_attr, &width, sh, html_context);
832 if (width != WIDTH_AUTO)
833 set_td_width(table, col, width, 0);
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);
840 if (!span_cell)
841 goto abort;
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;
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;
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;
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);
868 if (!span_cell)
869 goto abort;
871 if (span_cell->is_used) {
872 int l, m;
874 if (span_cell->col == col
875 && span_cell->row == row)
876 continue;
878 for (l = 0; l < k; l++)
879 memset(CELL(table, col + l, row + j), 0,
880 sizeof(*span_cell));
882 rowspan = j;
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;
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;
898 goto see;
900 scan_done:
901 *end = html;
903 mem_free_if(l_fragment_id);
905 for (col = 0; col < table->cols; col++) for (row = 0; row < table->rows; row++) {
906 struct table_cell *cell = CELL(table, col, row);
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;
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;
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);
927 return table;
929 abort:
930 *end = eof;
931 free_table(table);
932 return NULL;