links

lynx-like text mode web browser
git clone anongit@rnpnr.xyz:links.git
Log | Files | Refs | Feed | README | LICENSE

html_tbl.c (44495B)


      1 /* html_tbl.c
      2  * Tables in HTML
      3  * (c) 2002 Mikulas Patocka
      4  * This file is a part of the Links program, released under GPL.
      5  */
      6 
      7 #include <limits.h>
      8 
      9 #include "links.h"
     10 
     11 static void free_table_cache(void);
     12 
     13 #define RECT_BOUND_BITS 10 /* --- bound at 1024 pixels */
     14 
     15 #define AL_TR -1
     16 
     17 #define VAL_TR     -1
     18 #define VAL_TOP    0
     19 #define VAL_MIDDLE 1
     20 #define VAL_BOTTOM 2
     21 
     22 #define W_AUTO -1
     23 #define W_REL  -2
     24 
     25 #define F_VOID   0
     26 #define F_ABOVE  1
     27 #define F_BELOW  2
     28 #define F_HSIDES 3
     29 #define F_LHS    4
     30 #define F_RHS    8
     31 #define F_VSIDES 12
     32 #define F_BOX    15
     33 
     34 #define R_NONE   0
     35 #define R_ROWS   1
     36 #define R_COLS   2
     37 #define R_ALL    3
     38 #define R_GROUPS 4
     39 
     40 static void
     41 get_align(unsigned char *attr, int *a)
     42 {
     43 	unsigned char *al;
     44 	if ((al = get_attr_val(attr, cast_uchar "align"))) {
     45 		if (!(casestrcmp(al, cast_uchar "left")))
     46 			*a = AL_LEFT;
     47 		if (!(casestrcmp(al, cast_uchar "right")))
     48 			*a = AL_RIGHT;
     49 		if (!(casestrcmp(al, cast_uchar "center")))
     50 			*a = AL_CENTER;
     51 		if (!(casestrcmp(al, cast_uchar "justify")))
     52 			*a = AL_BLOCK;
     53 		if (!(casestrcmp(al, cast_uchar "char")))
     54 			*a = AL_RIGHT; /* NOT IMPLEMENTED */
     55 		free(al);
     56 	}
     57 }
     58 
     59 static void
     60 get_valign(unsigned char *attr, int *a)
     61 {
     62 	unsigned char *al;
     63 	if ((al = get_attr_val(attr, cast_uchar "valign"))) {
     64 		if (!(casestrcmp(al, cast_uchar "top")))
     65 			*a = VAL_TOP;
     66 		if (!(casestrcmp(al, cast_uchar "middle")))
     67 			*a = VAL_MIDDLE;
     68 		if (!(casestrcmp(al, cast_uchar "bottom")))
     69 			*a = VAL_BOTTOM;
     70 		if (!(casestrcmp(al, cast_uchar "baseline")))
     71 			*a = VAL_TOP; /* NOT IMPLEMENTED */
     72 		free(al);
     73 	}
     74 }
     75 
     76 static void
     77 get_c_width(unsigned char *attr, int *w, int sh)
     78 {
     79 	unsigned char *al;
     80 	if ((al = get_attr_val(attr, cast_uchar "width"))) {
     81 		if (*al && al[strlen(cast_const_char al) - 1] == '*') {
     82 			char *end;
     83 			unsigned long n;
     84 			al[strlen(cast_const_char al) - 1] = 0;
     85 			n = strtoul(cast_const_char al, &end, 10);
     86 			if (n < 10000 && !*end)
     87 				*w = W_REL - (int)n;
     88 		} else {
     89 			int p = get_width(attr, cast_uchar "width", sh);
     90 			if (p >= 0)
     91 				*w = p;
     92 		}
     93 		free(al);
     94 	}
     95 }
     96 
     97 #define INIT_X 8
     98 #define INIT_Y 8
     99 
    100 struct table_cell {
    101 	int used;
    102 	int spanned;
    103 	int mx, my;
    104 	unsigned char *start;
    105 	unsigned char *end;
    106 	int align;
    107 	int valign;
    108 	int b;
    109 	struct rgb bgcolor;
    110 	int group;
    111 	int colspan;
    112 	int rowspan;
    113 	int min_width;
    114 	int max_width;
    115 	int x_width;
    116 	int height;
    117 	int xpos, ypos, xw, yw;
    118 	int link_num;
    119 	unsigned char *tag;
    120 };
    121 
    122 struct table_column {
    123 	int group;
    124 	int align;
    125 	int valign;
    126 	int width;
    127 };
    128 
    129 struct table {
    130 	struct part *p;
    131 	int x, y;
    132 	int rx, ry;
    133 	int align;
    134 	int border, cellpd, vcellpd, cellsp;
    135 	int frame, rules, width, wf;
    136 	unsigned char *bordercolor;
    137 	int *min_c, *max_c;
    138 	int *w_c;
    139 	int rw;
    140 	int min_t, max_t;
    141 	struct table_cell *cells;
    142 	int c, rc;
    143 	struct table_column *cols;
    144 	int xc;
    145 	int *xcols;
    146 	int *r_heights;
    147 	int rh;
    148 	int link_num;
    149 	struct rgb bgcolor;
    150 };
    151 
    152 #define CELL(t, x, y) (&(t)->cells[(y) * (t)->rx + (x)])
    153 
    154 static unsigned char frame_table[81] = {
    155 	0x00, 0xb3, 0xba, 0xc4, 0xc0, 0xd3, 0xcd, 0xd4, 0xc8,
    156 	0xc4, 0xd9, 0xbd, 0xc4, 0xc1, 0xd0, 0xcd, 0xd4, 0xc8,
    157 	0xcd, 0xbe, 0xbc, 0xcd, 0xbe, 0xbc, 0xcd, 0xcf, 0xca,
    158 
    159 	0xb3, 0xb3, 0xba, 0xda, 0xc3, 0xd3, 0xd5, 0xc6, 0xc8,
    160 	0xbf, 0xb4, 0xbd, 0xc2, 0xc5, 0xd0, 0xd5, 0xc6, 0xc8,
    161 	0xb8, 0xb5, 0xbc, 0xb8, 0xb5, 0xbc, 0xd1, 0xd8, 0xca,
    162 
    163 	0xba, 0xba, 0xba, 0xd6, 0xd6, 0xc7, 0xc9, 0xc9, 0xcc,
    164 	0xb7, 0xb7, 0xb6, 0xd2, 0xd2, 0xd7, 0xc9, 0xc9, 0xcc,
    165 	0xbb, 0xbb, 0xb9, 0xbb, 0xbb, 0xb9, 0xcb, 0xcb, 0xce,
    166 };
    167 
    168 static unsigned char hline_table[3] = { 0x20, 0xc4, 0xcd };
    169 static unsigned char vline_table[3] = { 0x20, 0xb3, 0xba };
    170 
    171 static struct table *
    172 new_table(void)
    173 {
    174 	struct table *t;
    175 	t = mem_calloc(sizeof(struct table));
    176 	t->p = NULL;
    177 	t->x = t->y = 0;
    178 	t->rx = INIT_X;
    179 	t->ry = INIT_Y;
    180 	t->cells = mem_calloc(INIT_X * INIT_Y * sizeof(struct table_cell));
    181 	t->c = 0;
    182 	t->rc = INIT_X;
    183 	t->cols = mem_calloc(INIT_X * sizeof(struct table_column));
    184 	t->xcols = NULL;
    185 	t->xc = 0;
    186 	t->r_heights = NULL;
    187 	return t;
    188 }
    189 
    190 static void
    191 free_table(struct table *t)
    192 {
    193 	int i, j;
    194 	for (j = 0; j < t->y; j++)
    195 		for (i = 0; i < t->x; i++) {
    196 			struct table_cell *c = CELL(t, i, j);
    197 			free(c->tag);
    198 		}
    199 	free(t->bordercolor);
    200 	free(t->min_c);
    201 	free(t->max_c);
    202 	free(t->w_c);
    203 	free(t->r_heights);
    204 	free(t->cols);
    205 	free(t->xcols);
    206 	free(t->cells);
    207 	free(t);
    208 }
    209 
    210 static void
    211 expand_cells(struct table *t, int x, int y)
    212 {
    213 	int i, j;
    214 	if (x >= t->x) {
    215 		if (t->x) {
    216 			for (i = 0; i < t->y; i++)
    217 				if (CELL(t, t->x - 1, i)->colspan == -1) {
    218 					for (j = t->x; j <= x; j++) {
    219 						CELL(t, j, i)->used = 1;
    220 						CELL(t, j, i)->spanned = 1;
    221 						CELL(t, j, i)->rowspan =
    222 						    CELL(t, t->x - 1, i)
    223 							->rowspan;
    224 						CELL(t, j, i)->colspan = -1;
    225 						CELL(t, j, i)->mx =
    226 						    CELL(t, t->x - 1, i)->mx;
    227 						CELL(t, j, i)->my =
    228 						    CELL(t, t->x - 1, i)->my;
    229 					}
    230 				}
    231 		}
    232 		t->x = safe_add(x, 1);
    233 	}
    234 	if (y >= t->y) {
    235 		if (t->y) {
    236 			for (i = 0; i < t->x; i++)
    237 				if (CELL(t, i, t->y - 1)->rowspan == -1) {
    238 					for (j = t->y; j <= y; j++) {
    239 						CELL(t, i, j)->used = 1;
    240 						CELL(t, i, j)->spanned = 1;
    241 						CELL(t, i, j)->rowspan = -1;
    242 						CELL(t, i, j)->colspan =
    243 						    CELL(t, i, t->y - 1)
    244 							->colspan;
    245 						CELL(t, i, j)->mx =
    246 						    CELL(t, i, t->y - 1)->mx;
    247 						CELL(t, i, j)->my =
    248 						    CELL(t, i, t->y - 1)->my;
    249 					}
    250 				}
    251 		}
    252 		t->y = safe_add(y, 1);
    253 	}
    254 }
    255 
    256 static struct table_cell *
    257 new_cell(struct table *t, int x, int y)
    258 {
    259 	struct table nt;
    260 	int i, j;
    261 	if (x < t->x && y < t->y)
    262 		goto ret;
    263 rep:
    264 	if (x < t->rx && y < t->ry) {
    265 		expand_cells(t, x, y);
    266 		goto ret;
    267 	}
    268 	nt.rx = t->rx;
    269 	nt.ry = t->ry;
    270 	while (x >= nt.rx) {
    271 		if ((unsigned)nt.rx > INT_MAX / 2)
    272 			overalloc();
    273 		nt.rx *= 2;
    274 	}
    275 	while (y >= nt.ry) {
    276 		if ((unsigned)nt.ry > INT_MAX / 2)
    277 			overalloc();
    278 		nt.ry *= 2;
    279 	}
    280 	if ((unsigned)nt.rx * (unsigned)nt.ry / (unsigned)nt.rx
    281 	    != (unsigned)nt.ry)
    282 		overalloc();
    283 	if ((unsigned)nt.rx * (unsigned)nt.ry
    284 	    > INT_MAX / sizeof(struct table_cell))
    285 		overalloc();
    286 	nt.cells = mem_calloc(nt.rx * nt.ry * sizeof(struct table_cell));
    287 	for (i = 0; i < t->x; i++)
    288 		for (j = 0; j < t->y; j++)
    289 			memcpy(CELL(&nt, i, j), CELL(t, i, j),
    290 			       sizeof(struct table_cell));
    291 	free(t->cells);
    292 	t->cells = nt.cells;
    293 	t->rx = nt.rx;
    294 	t->ry = nt.ry;
    295 	goto rep;
    296 
    297 ret:
    298 	return CELL(t, x, y);
    299 }
    300 
    301 static void
    302 new_columns(struct table *t, int span, int width, int align, int valign,
    303             int group)
    304 {
    305 	if (safe_add(t->c, span) > t->rc) {
    306 		int n = t->rc;
    307 		struct table_column *nc;
    308 		while (t->c + span > n) {
    309 			if ((unsigned)n > INT_MAX / 2)
    310 				overalloc();
    311 			n *= 2;
    312 		}
    313 		if ((unsigned)n > INT_MAX / sizeof(struct table_column))
    314 			overalloc();
    315 		nc = xrealloc(t->cols, n * sizeof(struct table_column));
    316 		t->rc = n;
    317 		t->cols = nc;
    318 	}
    319 	while (span--) {
    320 		t->cols[t->c].align = align;
    321 		t->cols[t->c].valign = valign;
    322 		t->cols[t->c].width = width;
    323 		t->cols[t->c++].group = group;
    324 		group = 0;
    325 	}
    326 }
    327 
    328 static void
    329 set_td_width(struct table *t, int x, int width, int f)
    330 {
    331 	if (x >= t->xc) {
    332 		int n = t->xc ? t->xc : 1;
    333 		int i;
    334 		int *nc;
    335 		while (x >= n) {
    336 			if ((unsigned)n > INT_MAX / 2)
    337 				overalloc();
    338 			n *= 2;
    339 		}
    340 		if ((unsigned)n > INT_MAX / sizeof(int))
    341 			overalloc();
    342 		nc = xrealloc(t->xcols, n * sizeof(int));
    343 		for (i = t->xc; i < n; i++)
    344 			nc[i] = W_AUTO;
    345 		t->xc = n;
    346 		t->xcols = nc;
    347 	}
    348 	if (t->xcols[x] == W_AUTO || f) {
    349 set:
    350 		t->xcols[x] = width;
    351 		return;
    352 	}
    353 	if (width == W_AUTO)
    354 		return;
    355 	if (width < 0 && t->xcols[x] >= 0)
    356 		goto set;
    357 	if (width >= 0 && t->xcols[x] < 0)
    358 		return;
    359 	t->xcols[x] = safe_add(t->xcols[x], width) / 2;
    360 }
    361 
    362 unsigned char *
    363 skip_element(unsigned char *html, unsigned char *eof, unsigned char *what,
    364              int sub)
    365 {
    366 	int l = (int)strlen(cast_const_char what);
    367 	int level = 1;
    368 	unsigned char *name;
    369 	int namelen;
    370 r:
    371 	while (html < eof && (*html != '<'))
    372 rr:
    373 		html++;
    374 	if (eof - html >= 2 && (html[1] == '!' || html[1] == '?')) {
    375 		html = skip_comment(html, eof);
    376 		goto r;
    377 	}
    378 	if (html >= eof)
    379 		return eof;
    380 	if (parse_element(html, eof, &name, &namelen, NULL, &html))
    381 		goto rr;
    382 	if (namelen == l && !casecmp(name, what, l) && sub)
    383 		level = safe_add(level, 1);
    384 	if (namelen == l + 1 && name[0] == '/' && !casecmp(name + 1, what, l))
    385 		if (!--level)
    386 			return html;
    387 	goto r;
    388 }
    389 
    390 struct s_e {
    391 	unsigned char *s, *e;
    392 };
    393 
    394 static int
    395 default_line_align(void)
    396 {
    397 	return par_format.align == AL_NO || par_format.align == AL_NO_BREAKABLE
    398 	           ? par_format.align
    399 	           : AL_LEFT;
    400 }
    401 
    402 static struct table *
    403 parse_table(unsigned char *html, unsigned char *eof, unsigned char **end,
    404             struct rgb *bgcolor, int sh, struct s_e **bad_html, int *bhp)
    405 {
    406 	int qqq;
    407 	struct table *t;
    408 	struct table_cell *cell;
    409 	unsigned char *t_name, *t_attr, *en, *a;
    410 	int t_namelen;
    411 	int x = 0, y = -1;
    412 	int p = 0;
    413 	unsigned char *lbhp = NULL;
    414 	int l_al = default_line_align();
    415 	int l_val = VAL_MIDDLE;
    416 	int csp, rsp;
    417 	int group = 0;
    418 	int i, j, k;
    419 	struct rgb l_col;
    420 	int c_al = AL_TR, c_val = VAL_TR, c_width = W_AUTO, c_span = 0;
    421 	memcpy(&l_col, bgcolor, sizeof(struct rgb));
    422 	*end = html;
    423 	if (bad_html) {
    424 		*bad_html = NULL;
    425 		*bhp = 0;
    426 	}
    427 	t = new_table();
    428 	memcpy(&t->bgcolor, bgcolor, sizeof(struct rgb));
    429 se:
    430 	en = html;
    431 see:
    432 	html = en;
    433 	if (bad_html && !p && !lbhp) {
    434 		if (!(*bhp & (ALLOC_GR - 1))) {
    435 			if ((unsigned)*bhp
    436 			    > INT_MAX / sizeof(struct s_e) - ALLOC_GR)
    437 				overalloc();
    438 			*bad_html = xrealloc(
    439 			    *bad_html, (*bhp + ALLOC_GR) * sizeof(struct s_e));
    440 		}
    441 		lbhp = (*bad_html)[(*bhp)++].s = html;
    442 	}
    443 	while (html < eof && *html != '<')
    444 		html++;
    445 	if (html >= eof) {
    446 		if (p)
    447 			CELL(t, x, y)->end = html;
    448 		if (lbhp)
    449 			(*bad_html)[*bhp - 1].e = html;
    450 		goto scan_done;
    451 	}
    452 	if (eof - html >= 2 && (html[1] == '!' || html[1] == '?')) {
    453 		html = skip_comment(html, eof);
    454 		goto se;
    455 	}
    456 	if (parse_element(html, eof, &t_name, &t_namelen, &t_attr, &en)) {
    457 		html++;
    458 		goto se;
    459 	}
    460 	if (t_namelen == 5 && !casecmp(t_name, cast_uchar "TABLE", 5)) {
    461 		en = skip_element(en, eof, cast_uchar "TABLE", 1);
    462 		goto see;
    463 	}
    464 	if (t_namelen == 6 && !casecmp(t_name, cast_uchar "SCRIPT", 5)) {
    465 		if (should_skip_script(t_attr)) {
    466 			en = skip_element(en, eof, cast_uchar "SCRIPT", 0);
    467 			goto see;
    468 		}
    469 	}
    470 	if (t_namelen == 6 && !casecmp(t_name, cast_uchar "/TABLE", 6)) {
    471 		if (c_span)
    472 			new_columns(t, c_span, c_width, c_al, c_val, 1);
    473 		if (p)
    474 			CELL(t, x, y)->end = html;
    475 		if (lbhp)
    476 			(*bad_html)[*bhp - 1].e = html;
    477 		goto scan_done;
    478 	}
    479 	if (t_namelen == 8 && !casecmp(t_name, cast_uchar "COLGROUP", 8)) {
    480 		if (c_span)
    481 			new_columns(t, c_span, c_width, c_al, c_val, 1);
    482 		if (lbhp) {
    483 			(*bad_html)[*bhp - 1].e = html;
    484 			lbhp = NULL;
    485 		}
    486 		c_al = AL_TR;
    487 		c_val = VAL_TR;
    488 		c_width = W_AUTO;
    489 		get_align(t_attr, &c_al);
    490 		get_valign(t_attr, &c_val);
    491 		get_c_width(t_attr, &c_width, sh);
    492 		if ((c_span = get_num(t_attr, cast_uchar "span")) == -1)
    493 			c_span = 1;
    494 		goto see;
    495 	}
    496 	if (t_namelen == 9 && !casecmp(t_name, cast_uchar "/COLGROUP", 9)) {
    497 		if (c_span)
    498 			new_columns(t, c_span, c_width, c_al, c_val, 1);
    499 		if (lbhp) {
    500 			(*bad_html)[*bhp - 1].e = html;
    501 			lbhp = NULL;
    502 		}
    503 		c_span = 0;
    504 		c_al = AL_TR;
    505 		c_val = VAL_TR;
    506 		c_width = W_AUTO;
    507 		goto see;
    508 	}
    509 	if (t_namelen == 3 && !casecmp(t_name, cast_uchar "COL", 3)) {
    510 		int sp, wi, al, val;
    511 		if (lbhp) {
    512 			(*bad_html)[*bhp - 1].e = html;
    513 			lbhp = NULL;
    514 		}
    515 		if ((sp = get_num(t_attr, cast_uchar "span")) == -1)
    516 			sp = 1;
    517 		wi = c_width;
    518 		al = c_al;
    519 		val = c_val;
    520 		get_align(t_attr, &al);
    521 		get_valign(t_attr, &val);
    522 		get_c_width(t_attr, &wi, sh);
    523 		new_columns(t, sp, wi, al, val, !!c_span);
    524 		c_span = 0;
    525 		goto see;
    526 	}
    527 	if (t_namelen == 3
    528 	    && (!casecmp(t_name, cast_uchar "/TR", 3)
    529 	        || !casecmp(t_name, cast_uchar "/TD", 3)
    530 	        || !casecmp(t_name, cast_uchar "/TH", 3))) {
    531 		if (c_span)
    532 			new_columns(t, c_span, c_width, c_al, c_val, 1);
    533 		if (p) {
    534 			CELL(t, x, y)->end = html;
    535 			p = 0;
    536 		}
    537 		if (lbhp) {
    538 			(*bad_html)[*bhp - 1].e = html;
    539 			lbhp = NULL;
    540 		}
    541 	}
    542 	if (t_namelen == 2 && !casecmp(t_name, cast_uchar "TR", 2)) {
    543 		if (c_span)
    544 			new_columns(t, c_span, c_width, c_al, c_val, 1);
    545 		if (p) {
    546 			CELL(t, x, y)->end = html;
    547 			p = 0;
    548 		}
    549 		if (lbhp) {
    550 			(*bad_html)[*bhp - 1].e = html;
    551 			lbhp = NULL;
    552 		}
    553 		if (group)
    554 			group--;
    555 		l_al = default_line_align();
    556 		l_val = VAL_MIDDLE;
    557 		memcpy(&l_col, bgcolor, sizeof(struct rgb));
    558 		get_align(t_attr, &l_al);
    559 		get_valign(t_attr, &l_val);
    560 		get_bgcolor(t_attr, &l_col);
    561 		y++;
    562 		x = 0;
    563 		goto see;
    564 	}
    565 	if (t_namelen == 5
    566 	    && ((!casecmp(t_name, cast_uchar "THEAD", 5))
    567 	        || (!casecmp(t_name, cast_uchar "TBODY", 5))
    568 	        || (!casecmp(t_name, cast_uchar "TFOOT", 5)))) {
    569 		if (c_span)
    570 			new_columns(t, c_span, c_width, c_al, c_val, 1);
    571 		if (lbhp) {
    572 			(*bad_html)[*bhp - 1].e = html;
    573 			lbhp = NULL;
    574 		}
    575 		group = 2;
    576 	}
    577 	if (t_namelen != 2
    578 	    || (casecmp(t_name, cast_uchar "TD", 2)
    579 	        && casecmp(t_name, cast_uchar "TH", 2)))
    580 		goto see;
    581 	if (c_span)
    582 		new_columns(t, c_span, c_width, c_al, c_val, 1);
    583 	if (lbhp) {
    584 		(*bad_html)[*bhp - 1].e = html;
    585 		lbhp = NULL;
    586 	}
    587 	if (p) {
    588 		CELL(t, x, y)->end = html;
    589 		p = 0;
    590 	}
    591 	if (y == -1) {
    592 		y = 0;
    593 		x = 0;
    594 	}
    595 nc:
    596 	if (!(cell = new_cell(t, x, y)))
    597 		goto see;
    598 	if (cell->used) {
    599 		if (cell->colspan == -1)
    600 			goto see;
    601 		x++;
    602 		goto nc;
    603 	}
    604 	cell->mx = x;
    605 	cell->my = y;
    606 	cell->used = 1;
    607 	cell->start = en;
    608 	p = 1;
    609 	cell->align = l_al;
    610 	cell->valign = l_val;
    611 	cell->b = 0;
    612 #if 0
    613 	if (upcase(t_name[1]) == 'H') {
    614 		unsigned char *e = en;
    615 		while (e < eof && WHITECHAR(*e)) e++;
    616 		if (eof - e > 6 && !casecmp(e, cast_uchar "<TABLE", 6)) goto no_th; /* hack for www.root.cz */
    617 		cell->b = 1;
    618 		cell->align = AL_CENTER;
    619 		no_th:;
    620 	}
    621 #endif
    622 	if (group == 1)
    623 		cell->group = 1;
    624 	if (x < t->c) {
    625 		if (t->cols[x].align != AL_TR)
    626 			cell->align = t->cols[x].align;
    627 		if (t->cols[x].valign != VAL_TR)
    628 			cell->valign = t->cols[x].valign;
    629 	}
    630 	memcpy(&cell->bgcolor, &l_col, sizeof(struct rgb));
    631 	get_align(t_attr, &cell->align);
    632 	get_valign(t_attr, &cell->valign);
    633 	get_bgcolor(t_attr, &cell->bgcolor);
    634 	if ((a = get_attr_val(t_attr, cast_uchar "id")))
    635 		cell->tag = a;
    636 	if ((a = get_attr_val(t_attr, cast_uchar "class"))) {
    637 		if (strstr(cast_const_char a, "blob-code-inner")
    638 		    || /* github hack */
    639 		    !strcmp(cast_const_char a, "changelog")
    640 		    || !strcmp(cast_const_char a, "vc_file_line_text")
    641 		    || /* https://gcc.gnu.org/viewcvs/gcc/trunk/gcc/opts.c?view=markup
    642 		        */
    643 		    !strcmp(cast_const_char a, "FileContents-lineContents")
    644 		    || /* https://android.googlesource.com/ */
    645 		    0) {
    646 			cell->align = !par_format.implicit_pre_wrap
    647 			                  ? AL_NO
    648 			                  : AL_NO_BREAKABLE;
    649 		}
    650 		free(a);
    651 	}
    652 	if ((csp = get_num(t_attr, cast_uchar "colspan")) == -1)
    653 		csp = 1;
    654 	if (!csp)
    655 		csp = -1;
    656 	if ((rsp = get_num(t_attr, cast_uchar "rowspan")) == -1)
    657 		rsp = 1;
    658 	if (!rsp)
    659 		rsp = -1;
    660 	if (csp >= 0 && rsp >= 0 && csp * rsp > 100000) {
    661 		if (csp > 10)
    662 			csp = -1;
    663 		if (rsp > 10)
    664 			rsp = -1;
    665 	}
    666 	cell->colspan = csp;
    667 	cell->rowspan = rsp;
    668 	if (csp == 1) {
    669 		int w = W_AUTO;
    670 		get_c_width(t_attr, &w, sh);
    671 		if (w != W_AUTO)
    672 			set_td_width(t, x, w, 0);
    673 	}
    674 	qqq = t->x;
    675 	for (i = 1; csp != -1 ? i < csp : x + i < qqq; i++) {
    676 		struct table_cell *sc;
    677 		if (!(sc = new_cell(t, x + i, y)) || sc->used) {
    678 			csp = i;
    679 			for (k = 0; k < i; k++)
    680 				CELL(t, x + k, y)->colspan = csp;
    681 			break;
    682 		}
    683 		sc->used = sc->spanned = 1;
    684 		sc->rowspan = rsp;
    685 		sc->colspan = csp;
    686 		sc->mx = x;
    687 		sc->my = y;
    688 	}
    689 	qqq = t->y;
    690 	for (j = 1; rsp != -1 ? j < rsp : y + j < qqq; j++) {
    691 		for (k = 0; k < i; k++) {
    692 			struct table_cell *sc;
    693 			if (!(sc = new_cell(t, x + k, y + j)) || sc->used) {
    694 				int l, m;
    695 				if (sc->mx == x && sc->my == y)
    696 					continue;
    697 				for (l = 0; l < k; l++)
    698 					memset(CELL(t, x + l, y + j), 0,
    699 					       sizeof(struct table_cell));
    700 				for (l = 0; l < i; l++)
    701 					for (m = 0; m < j; m++)
    702 						CELL(t, x + l, y + m)->rowspan =
    703 						    j;
    704 				goto brk;
    705 			}
    706 			sc->used = sc->spanned = 1;
    707 			sc->rowspan = rsp;
    708 			sc->colspan = csp;
    709 			sc->mx = x;
    710 			sc->my = y;
    711 		}
    712 	}
    713 brk:
    714 	goto see;
    715 
    716 scan_done:
    717 	*end = html;
    718 
    719 	for (x = 0; x < t->x; x++)
    720 		for (y = 0; y < t->y; y++) {
    721 			struct table_cell *c = CELL(t, x, y);
    722 			if (!c->spanned) {
    723 				if (c->colspan == -1)
    724 					c->colspan = t->x - x;
    725 				if (c->rowspan == -1)
    726 					c->rowspan = t->y - y;
    727 			}
    728 		}
    729 
    730 	if ((unsigned)t->y > INT_MAX / sizeof(int))
    731 		overalloc();
    732 	t->r_heights = mem_calloc(t->y * sizeof(int));
    733 
    734 	for (x = 0; x < t->c; x++)
    735 		if (t->cols[x].width != W_AUTO)
    736 			set_td_width(t, x, t->cols[x].width, 1);
    737 	set_td_width(t, t->x, W_AUTO, 0);
    738 
    739 	return t;
    740 }
    741 
    742 static void
    743 get_cell_width(struct table *t, struct table_cell *c, int w, int a, int *min,
    744                int *max, int *n_links)
    745 {
    746 	struct part *p;
    747 
    748 	if (min)
    749 		*min = -1;
    750 	if (max)
    751 		*max = -1;
    752 	if (n_links)
    753 		*n_links = c->link_num;
    754 	if (!(p = format_html_part(
    755 		  c->start, c->end,
    756 		  c->align == AL_NO || c->align == AL_NO_BREAKABLE ? c->align
    757 								   : AL_LEFT,
    758 		  t->cellpd, w, NULL, !!a, !!a, NULL, c->link_num)))
    759 		return;
    760 	if (min)
    761 		*min = p->x;
    762 	if (max)
    763 		*max = p->xmax;
    764 	if (n_links)
    765 		*n_links = p->link_num;
    766 	free(p);
    767 }
    768 
    769 #define g_c_w(cc)                                                              \
    770 	do {                                                                   \
    771 		struct table_cell *c = cc;                                     \
    772 		if (!c->start)                                                 \
    773 			continue;                                              \
    774 		c->link_num = nl;                                              \
    775 		get_cell_width(t, c, 0, 0, &c->min_width, &c->max_width, &nl); \
    776 	} while (0)
    777 
    778 static void
    779 get_cell_widths(struct table *t)
    780 {
    781 	int nl = t->p->link_num;
    782 	int i, j;
    783 	if (!d_opt->table_order)
    784 		for (j = 0; j < t->y; j++)
    785 			for (i = 0; i < t->x; i++)
    786 				g_c_w(CELL(t, i, j));
    787 	else
    788 		for (i = 0; i < t->x; i++)
    789 			for (j = 0; j < t->y; j++)
    790 				g_c_w(CELL(t, i, j));
    791 	t->link_num = nl;
    792 }
    793 
    794 static void
    795 dst_width(int *p, int n, int w, int *lim)
    796 {
    797 	int i, s = 0, d, r;
    798 	for (i = 0; i < n; i++)
    799 		s = safe_add(s, p[i]);
    800 	if (s >= w)
    801 		return;
    802 	if (!n)
    803 		return;
    804 again:
    805 	d = (w - s) / n;
    806 	r = (w - s) % n;
    807 	w = 0;
    808 	for (i = 0; i < n; i++) {
    809 		p[i] = safe_add(p[i], d + (i < r));
    810 		if (lim && p[i] > lim[i]) {
    811 			w = safe_add(w, p[i] - lim[i]);
    812 			p[i] = lim[i];
    813 		}
    814 	}
    815 	if (w) {
    816 		lim = NULL;
    817 		s = 0;
    818 		goto again;
    819 	}
    820 }
    821 
    822 static int
    823 get_vline_width(struct table *t, int col)
    824 { /* return: -1 none, 0, space, 1 line, 2 double */
    825 	int w = 0;
    826 	if (!col)
    827 		return -1;
    828 	if (t->rules == R_COLS || t->rules == R_ALL)
    829 		w = t->cellsp;
    830 	else if (t->rules == R_GROUPS)
    831 		w = col < t->c && t->cols[col].group;
    832 	if (!w && t->cellpd)
    833 		w = -1;
    834 	return w;
    835 }
    836 
    837 static int
    838 get_hline_width(struct table *t, int row)
    839 {
    840 	int w = 0;
    841 	if (!row)
    842 		return -1;
    843 	if (t->rules == R_ROWS || t->rules == R_ALL) {
    844 x:
    845 		if (t->cellsp || t->vcellpd)
    846 			return t->cellsp;
    847 		return -1;
    848 	} else if (t->rules == R_GROUPS) {
    849 		int q;
    850 		for (q = 0; q < t->x; q++)
    851 			if (CELL(t, q, row)->group)
    852 				goto x;
    853 		return t->vcellpd ? 0 : -1;
    854 	}
    855 	if (!w && !t->vcellpd)
    856 		w = -1;
    857 	return w;
    858 }
    859 
    860 static int
    861 get_column_widths(struct table *t)
    862 {
    863 	int i, j, s, ns;
    864 	if ((unsigned)t->x > INT_MAX / sizeof(int))
    865 		overalloc();
    866 	if (!t->min_c)
    867 		t->min_c = xmalloc(t->x * sizeof(int));
    868 	if (!t->max_c)
    869 		t->max_c = xmalloc(t->x * sizeof(int));
    870 	if (!t->w_c)
    871 		t->w_c = xmalloc(t->x * sizeof(int));
    872 	memset(t->min_c, 0, t->x * sizeof(int));
    873 	memset(t->max_c, 0, t->x * sizeof(int));
    874 	s = 1;
    875 	do {
    876 		ns = INT_MAX;
    877 		for (i = 0; i < t->x; i++)
    878 			for (j = 0; j < t->y; j++) {
    879 				struct table_cell *c = CELL(t, i, j);
    880 				if (c->spanned || !c->used)
    881 					continue;
    882 				if (c->colspan + i > t->x) {
    883 					/*internal("colspan out of table");
    884 					return -1;*/
    885 					continue;
    886 				}
    887 				if (c->colspan == s) {
    888 					int k, p = 0;
    889 					/*int pp = t->max_c[i];*/
    890 					int m = 0;
    891 					for (k = 1; k < s; k++) {
    892 						if (get_vline_width(t, i + k)
    893 						    >= 0)
    894 							p = safe_add(p, 1);
    895 					}
    896 					dst_width(t->min_c + i, s,
    897 					          c->min_width - p,
    898 					          t->max_c + i);
    899 					dst_width(t->max_c + i, s,
    900 					          c->max_width - p - m, NULL);
    901 					for (k = 0; k < s; k++)
    902 						if (t->min_c[i + k]
    903 						    > t->max_c[i + k])
    904 							t->max_c[i + k] =
    905 							    t->min_c[i + k];
    906 				} else if (c->colspan > s && c->colspan < ns)
    907 					ns = c->colspan;
    908 			}
    909 	} while ((s = ns) != INT_MAX);
    910 	return 0;
    911 }
    912 
    913 static void
    914 get_table_width(struct table *t)
    915 {
    916 	int i, vl;
    917 	int min = 0, max = 0;
    918 	for (i = 0; i < t->x; i++) {
    919 		vl = get_vline_width(t, i) >= 0;
    920 		min = safe_add(min, vl);
    921 		max = safe_add(max, vl);
    922 		min = safe_add(min, t->min_c[i]);
    923 		if (t->xcols[i] > t->max_c[i])
    924 			max = safe_add(max, t->xcols[i]);
    925 		max = safe_add(max, t->max_c[i]);
    926 	}
    927 	vl = (!!(t->frame & F_LHS) + !!(t->frame & F_RHS)) * !!t->border;
    928 	min = safe_add(min, vl);
    929 	max = safe_add(max, vl);
    930 	t->min_t = min;
    931 	t->max_t = max;
    932 }
    933 
    934 static void
    935 distribute_widths(struct table *t, int width)
    936 {
    937 	int i;
    938 	int d = width - t->min_t;
    939 	int om = 0;
    940 	unsigned char *u;
    941 	int *w, *mx;
    942 	int mmax_c = 0;
    943 	t->rw = 0;
    944 	if (!t->x)
    945 		return;
    946 	if (d < 0) {
    947 		/*internal("too small width %d, required %d", width,
    948 		 * t->min_t);*/
    949 		return;
    950 	}
    951 	for (i = 0; i < t->x; i++)
    952 		if (t->max_c[i] > mmax_c)
    953 			mmax_c = t->max_c[i];
    954 	memcpy(t->w_c, t->min_c, t->x * sizeof(int));
    955 	t->rw = width;
    956 	if ((unsigned)t->x > INT_MAX / sizeof(int))
    957 		overalloc();
    958 	u = xmalloc(t->x);
    959 	w = xmalloc(t->x * sizeof(int));
    960 	mx = xmalloc(t->x * sizeof(int));
    961 	while (d) {
    962 		int mss, mii;
    963 		int p = 0;
    964 		int wq;
    965 		int dd;
    966 		memset(w, 0, t->x * sizeof(int));
    967 		memset(mx, 0, t->x * sizeof(int));
    968 		for (i = 0; i < t->x; i++) {
    969 			switch (om) {
    970 			case 0:
    971 				if (t->w_c[i] < t->xcols[i]) {
    972 					w[i] = 1;
    973 					mx[i] = (t->xcols[i] > t->max_c[i]
    974 					             ? t->max_c[i]
    975 					             : t->xcols[i])
    976 					        - t->w_c[i];
    977 					if (mx[i] <= 0)
    978 						w[i] = 0;
    979 				}
    980 				break;
    981 			case 1:
    982 				if (t->xcols[i] < -1 && t->xcols[i] != -2) {
    983 					w[i] = t->xcols[i] <= -2
    984 					           ? -2 - t->xcols[i]
    985 					           : 1;
    986 					mx[i] = t->max_c[i] - t->w_c[i];
    987 					if (mx[i] <= 0)
    988 						w[i] = 0;
    989 				}
    990 				break;
    991 			case 2:
    992 			case 3:
    993 				if (t->w_c[i] < t->max_c[i]
    994 				    && (om == 3 || t->xcols[i] == W_AUTO)) {
    995 					mx[i] = t->max_c[i] - t->w_c[i];
    996 					if (mmax_c)
    997 						w[i] =
    998 						    safe_add(5, t->max_c[i] * 10
    999 						                    / mmax_c);
   1000 					else
   1001 						w[i] = 1;
   1002 				}
   1003 				break;
   1004 			case 4:
   1005 				if (t->xcols[i] >= 0) {
   1006 					w[i] = 1;
   1007 					mx[i] = t->xcols[i] - t->w_c[i];
   1008 					if (mx[i] <= 0)
   1009 						w[i] = 0;
   1010 				}
   1011 				break;
   1012 			case 5:
   1013 				if (t->xcols[i] < 0) {
   1014 					w[i] = t->xcols[i] <= -2
   1015 					           ? -2 - t->xcols[i]
   1016 					           : 1;
   1017 					mx[i] = INT_MAX;
   1018 				}
   1019 				break;
   1020 			case 6:
   1021 				w[i] = 1;
   1022 				mx[i] = INT_MAX;
   1023 				break;
   1024 			default:
   1025 				/*internal("could not expand table");*/
   1026 				goto end2;
   1027 			}
   1028 			p = safe_add(p, w[i]);
   1029 		}
   1030 		if (!p) {
   1031 			om++;
   1032 			continue;
   1033 		}
   1034 		wq = 0;
   1035 		if (u)
   1036 			memset(u, 0, t->x);
   1037 		dd = d;
   1038 a:
   1039 		mss = 0;
   1040 		mii = -1;
   1041 		for (i = 0; i < t->x; i++)
   1042 			if (w[i]) {
   1043 				int ss;
   1044 				if (u && u[i])
   1045 					continue;
   1046 				if (!(ss = dd * w[i] / p))
   1047 					ss = 1;
   1048 				if (ss > mx[i])
   1049 					ss = mx[i];
   1050 				if (ss > mss) {
   1051 					mss = ss;
   1052 					mii = i;
   1053 				}
   1054 			}
   1055 		if (mii != -1) {
   1056 			int q = t->w_c[mii];
   1057 			if (u)
   1058 				u[mii] = 1;
   1059 			t->w_c[mii] = safe_add(t->w_c[mii], mss);
   1060 			d -= t->w_c[mii] - q;
   1061 			while (d < 0) {
   1062 				t->w_c[mii]--;
   1063 				d++;
   1064 			}
   1065 			if (t->w_c[mii] < q) {
   1066 				/*internal("shrinking cell");*/
   1067 				t->w_c[mii] = q;
   1068 			}
   1069 			wq = 1;
   1070 			if (d)
   1071 				goto a;
   1072 		} else if (!wq)
   1073 			om++;
   1074 	}
   1075 end2:
   1076 	free(mx);
   1077 	free(w);
   1078 	free(u);
   1079 }
   1080 
   1081 #ifdef HTML_TABLE_2ND_PASS
   1082 static void
   1083 check_table_widths(struct table *t)
   1084 {
   1085 	int *w;
   1086 	int i, j;
   1087 	int s, ns;
   1088 	int m, mi = 0; /* go away, warning! */
   1089 	if ((unsigned)t->x > INT_MAX / sizeof(int))
   1090 		overalloc();
   1091 	w = mem_calloc(t->x * sizeof(int));
   1092 	for (j = 0; j < t->y; j++)
   1093 		for (i = 0; i < t->x; i++) {
   1094 			struct table_cell *c = CELL(t, i, j);
   1095 			int k, p = 0;
   1096 			if (!c->start)
   1097 				continue;
   1098 			for (k = 1; k < c->colspan; k++)
   1099 				if (get_vline_width(t, i + k) >= 0)
   1100 					p = safe_add(p, 1);
   1101 			for (k = 0; k < c->colspan; k++)
   1102 				p = safe_add(p, t->w_c[i + k]);
   1103 			get_cell_width(t, c, p, 1, &c->x_width, NULL, NULL);
   1104 			if (c->x_width > p) {
   1105 				/*int min, max;
   1106 				get_cell_width(t, c, 0, 0, &min, &max, NULL);
   1107 				internal("cell is now wider (%d > %d) min = %d,
   1108 				max = %d, now_min = %d, now_max = %d",
   1109 				c->x_width, p, t->min_c[i], t->max_c[i], min,
   1110 				max);*/
   1111 				/* sbohem, internale. chytl jsi mi spoustu chyb
   1112 				 * v tabulkovaci, ale ted je proste cas jit ...
   1113 				 * ;-( */
   1114 				c->x_width = p;
   1115 			}
   1116 		}
   1117 	s = 1;
   1118 	do {
   1119 		ns = INT_MAX;
   1120 		for (i = 0; i < t->x; i++)
   1121 			for (j = 0; j < t->y; j++) {
   1122 				struct table_cell *c = CELL(t, i, j);
   1123 				if (!c->start)
   1124 					continue;
   1125 				if (c->colspan + i > t->x) {
   1126 					/*internal("colspan out of table");*/
   1127 					free(w);
   1128 					return;
   1129 				}
   1130 				if (c->colspan == s) {
   1131 					int k, p = 0;
   1132 					for (k = 1; k < s; k++)
   1133 						if (get_vline_width(t, i + k)
   1134 						    >= 0)
   1135 							p = safe_add(p, 1);
   1136 					dst_width(w + i, s, c->x_width - p,
   1137 					          t->max_c + i);
   1138 					/*for (k = i; k < i + s; k++) if (w[k] >
   1139 					t->w_c[k]) { int l; int c; ag: c = 0;
   1140 					        for (l = i; l < i + s; l++) if
   1141 					(w[l] < t->w_c[k]) w[l] = safe_add(w[l],
   1142 					1), w[k]--, c = 1; if (w[k] > t->w_c[k])
   1143 					{ if (!c) internal("can't shrink cell");
   1144 					                else goto ag;
   1145 					        }
   1146 					}*/
   1147 				} else if (c->colspan > s && c->colspan < ns)
   1148 					ns = c->colspan;
   1149 			}
   1150 	} while ((s = ns) != INT_MAX);
   1151 
   1152 	s = 0;
   1153 	ns = 0;
   1154 	for (i = 0; i < t->x; i++) {
   1155 		s = safe_add(s, t->w_c[i]);
   1156 		ns = safe_add(ns, w[i]);
   1157 		/*if (w[i] > t->w_c[i]) {
   1158 		        int k;
   1159 		        for (k = 0; k < t->x; k++) debug("%d, %d", t->w_c[k],
   1160 		w[k]); debug("column %d: new width(%d) is larger than
   1161 		previous(%d)", i, w[i], t->w_c[i]);
   1162 		}*/
   1163 	}
   1164 	if (ns > s) {
   1165 		/*internal("new width(%d) is larger than previous(%d)", ns,
   1166 		 * s);*/
   1167 		free(w);
   1168 		return;
   1169 	}
   1170 	m = -1;
   1171 	for (i = 0; i < t->x; i++) {
   1172 		/*if (table_level == 1) debug("%d: %d %d %d %d", i, t->max_c[i],
   1173 		 * t->min_c[i], t->w_c[i], w[i]);*/
   1174 		if (t->max_c[i] > m) {
   1175 			m = t->max_c[i];
   1176 			mi = i;
   1177 		}
   1178 	}
   1179 	/*if (table_level == 1) debug("%d %d", mi, s - ns);*/
   1180 	if (m != -1) {
   1181 		w[mi] = safe_add(w[mi], s - ns);
   1182 		if (w[mi] <= t->max_c[mi]) {
   1183 			free(t->w_c);
   1184 			t->w_c = w;
   1185 			return;
   1186 		}
   1187 	}
   1188 	free(w);
   1189 }
   1190 #endif
   1191 
   1192 static void
   1193 get_table_heights(struct table *t)
   1194 {
   1195 	int s, ns;
   1196 	int i, j;
   1197 	for (j = 0; j < t->y; j++) {
   1198 		for (i = 0; i < t->x; i++) {
   1199 			struct table_cell *cell = CELL(t, i, j);
   1200 			struct part *p;
   1201 			int xw = 0, sp;
   1202 			if (!cell->used || cell->spanned)
   1203 				continue;
   1204 			for (sp = 0; sp < cell->colspan; sp++) {
   1205 				xw = safe_add(xw, t->w_c[i + sp]);
   1206 				if (sp < cell->colspan - 1) {
   1207 					if (get_vline_width(t, i + sp + 1) >= 0)
   1208 						xw = safe_add(xw, 1);
   1209 				}
   1210 			}
   1211 			if (!(p = format_html_part(cell->start, cell->end,
   1212 			                           cell->align, t->cellpd, xw,
   1213 			                           NULL, 2, 2, NULL,
   1214 			                           cell->link_num)))
   1215 				return;
   1216 			cell->height = p->y;
   1217 			free(p);
   1218 		}
   1219 	}
   1220 	s = 1;
   1221 	do {
   1222 		ns = INT_MAX;
   1223 		for (j = 0; j < t->y; j++) {
   1224 			for (i = 0; i < t->x; i++) {
   1225 				struct table_cell *cell = CELL(t, i, j);
   1226 				if (!cell->used || cell->spanned)
   1227 					continue;
   1228 				if (cell->rowspan == s) {
   1229 					int k, p = 0;
   1230 					for (k = 1; k < s; k++) {
   1231 						if (get_hline_width(t, j + k)
   1232 						    >= 0)
   1233 							p = safe_add(p, 1);
   1234 					}
   1235 					dst_width(t->r_heights + j, s,
   1236 					          cell->height - p, NULL);
   1237 				} else if (cell->rowspan > s
   1238 				           && cell->rowspan < ns)
   1239 					ns = cell->rowspan;
   1240 			}
   1241 		}
   1242 	} while ((s = ns) != INT_MAX);
   1243 	t->rh = (!!(t->frame & F_ABOVE) + !!(t->frame & F_BELOW)) * !!t->border;
   1244 	for (j = 0; j < t->y; j++) {
   1245 		t->rh = safe_add(t->rh, t->r_heights[j]);
   1246 		if (j && get_hline_width(t, j) >= 0)
   1247 			t->rh = safe_add(t->rh, 1);
   1248 	}
   1249 }
   1250 
   1251 static void
   1252 display_complicated_table(struct table *t, int x, int y, int *yy)
   1253 {
   1254 	int i, j;
   1255 	struct f_data *f = t->p->data;
   1256 	int yp, xp = safe_add(x, (t->frame & F_LHS) && t->border);
   1257 	for (i = 0; i < t->x; i++) {
   1258 		yp = safe_add(y, (t->frame & F_ABOVE) && t->border);
   1259 		for (j = 0; j < t->y; j++) {
   1260 			struct table_cell *cell = CELL(t, i, j);
   1261 			if (cell->start) {
   1262 				int yt;
   1263 				struct part *p = NULL;
   1264 				int xw = 0, yw = 0, s;
   1265 				for (s = 0; s < cell->colspan; s++) {
   1266 					xw = safe_add(xw, t->w_c[i + s]);
   1267 					if (s < cell->colspan - 1) {
   1268 						if (get_vline_width(t,
   1269 						                    i + s + 1)
   1270 						    >= 0)
   1271 							xw = safe_add(xw, 1);
   1272 					}
   1273 				}
   1274 				for (s = 0; s < cell->rowspan; s++) {
   1275 					yw += t->r_heights[j + s];
   1276 					if (s < cell->rowspan - 1) {
   1277 						if (get_hline_width(t,
   1278 						                    j + s + 1)
   1279 						    >= 0)
   1280 							yw = safe_add(yw, 1);
   1281 					}
   1282 				}
   1283 				html_stack_dup();
   1284 				html_top.dontkill = 1;
   1285 				if (cell->b)
   1286 					format_.attr |= AT_BOLD;
   1287 				memcpy(&format_.bg, &cell->bgcolor,
   1288 				       sizeof(struct rgb));
   1289 				memcpy(&par_format.bgcolor, &cell->bgcolor,
   1290 				       sizeof(struct rgb));
   1291 				if (cell->tag) {
   1292 					if (f)
   1293 						html_tag(
   1294 						    f, cell->tag,
   1295 						    safe_add(t->p->xp, xp),
   1296 						    safe_add(t->p->yp, yp));
   1297 				}
   1298 				p = format_html_part(
   1299 				    cell->start, cell->end, cell->align,
   1300 				    t->cellpd, xw, f, safe_add(t->p->xp, xp),
   1301 				    safe_add(
   1302 					safe_add(t->p->yp, yp),
   1303 					cell->valign != VAL_MIDDLE
   1304 						&& cell->valign != VAL_BOTTOM
   1305 					    ? 0
   1306 					    : (yw - cell->height)
   1307 						  / (cell->valign == VAL_MIDDLE
   1308 				                         ? 2
   1309 				                         : 1)),
   1310 				    NULL, cell->link_num);
   1311 				cell->xpos = xp;
   1312 				cell->ypos = yp;
   1313 				cell->xw = xw;
   1314 				cell->yw = yw;
   1315 				for (yt = 0; yt < p->y; yt++) {
   1316 					xxpand_lines(t->p, safe_add(yp, yt));
   1317 					xxpand_line(t->p, yp + yt,
   1318 					            safe_add(xp, t->w_c[i]));
   1319 				}
   1320 				kill_html_stack_item(&html_top);
   1321 				free(p);
   1322 			}
   1323 			cell->xpos = xp;
   1324 			cell->ypos = yp;
   1325 			cell->xw = t->w_c[i];
   1326 			yp = safe_add(yp, t->r_heights[j]);
   1327 			if (j < t->y - 1)
   1328 				if (get_hline_width(t, j + 1) >= 0)
   1329 					yp = safe_add(yp, 1);
   1330 		}
   1331 		if (i < t->x - 1) {
   1332 			if (get_vline_width(t, i + 1) >= 0)
   1333 				xp = safe_add(xp, 1);
   1334 			xp = safe_add(xp, t->w_c[i]);
   1335 		}
   1336 	}
   1337 	yp = y;
   1338 	for (j = 0; j < t->y; j++) {
   1339 		yp = safe_add(yp, t->r_heights[j]);
   1340 		if (j < t->y - 1)
   1341 			if (get_hline_width(t, j + 1) >= 0)
   1342 				yp = safe_add(yp, 1);
   1343 	}
   1344 	*yy = safe_add(yp, (!!(t->frame & F_ABOVE) + !!(t->frame & F_BELOW))
   1345 	                       * !!t->border);
   1346 }
   1347 
   1348 static unsigned char AF;
   1349 
   1350 #define draw_frame_point(xx, yy, ii, jj)                                       \
   1351 	if (H_LINE_X((ii - 1), (jj)) >= 0 || H_LINE_X((ii), (jj)) >= 0         \
   1352 	    || V_LINE_X((ii), (jj - 1)) >= 0 || V_LINE_X((ii), (jj)) >= 0)     \
   1353 	xset_hchar(                                                            \
   1354 	    t->p, (xx), (yy),                                                  \
   1355 	    frame_table[V_LINE((ii), (jj)-1) + 3 * H_LINE((ii), (jj))          \
   1356 	                + 9 * H_LINE((ii)-1, (jj)) + 27 * V_LINE((ii), (jj))], \
   1357 	    AF)
   1358 
   1359 #define draw_frame_hline(xx, yy, ll, ii, jj)                                   \
   1360 	if (H_LINE_X((ii), (jj)) >= 0)                                         \
   1361 	xset_hchars(t->p, (xx), (yy), (ll), hline_table[H_LINE((ii), (jj))], AF)
   1362 
   1363 #define draw_frame_vline(xx, yy, ll, ii, jj)                                   \
   1364 	{                                                                      \
   1365 		int qq;                                                        \
   1366 		if (V_LINE_X((ii), (jj)) >= 0)                                 \
   1367 			for (qq = 0; qq < (ll); qq++)                          \
   1368 				xset_hchar(t->p, (xx), safe_add((yy), qq),     \
   1369 				           vline_table[V_LINE((ii), (jj))],    \
   1370 				           AF);                                \
   1371 	}
   1372 
   1373 #define H_LINE_X(xx, yy) fh[(xx) + 1 + (t->x + 2) * (yy)]
   1374 #define V_LINE_X(xx, yy) fv[(yy) + 1 + (t->y + 2) * (xx)]
   1375 #define H_LINE(xx, yy)   (H_LINE_X((xx), (yy)) < 0 ? 0 : H_LINE_X((xx), (yy)))
   1376 #define V_LINE(xx, yy)   (V_LINE_X((xx), (yy)) < 0 ? 0 : V_LINE_X((xx), (yy)))
   1377 
   1378 static void
   1379 get_table_frame(struct table *t, short *fv, short *fh)
   1380 {
   1381 	int i, j;
   1382 	memset(fh, -1, (t->x + 2) * (t->y + 1) * sizeof(short));
   1383 	memset(fv, -1, (t->x + 1) * (t->y + 2) * sizeof(short));
   1384 	for (j = 0; j < t->y; j++)
   1385 		for (i = 0; i < t->x; i++) {
   1386 			int x, y;
   1387 			int xsp, ysp;
   1388 			struct table_cell *cell = CELL(t, i, j);
   1389 			if (!cell->used || cell->spanned)
   1390 				continue;
   1391 			if ((xsp = cell->colspan) == 0)
   1392 				xsp = t->x - i;
   1393 			if ((ysp = cell->rowspan) == 0)
   1394 				ysp = t->y - j;
   1395 			if (t->rules != R_NONE && t->rules != R_COLS)
   1396 				for (x = 0; x < xsp; x++) {
   1397 					H_LINE_X(i + x, j) = t->cellsp;
   1398 					H_LINE_X(i + x, j + ysp) = t->cellsp;
   1399 				}
   1400 			if (t->rules != R_NONE && t->rules != R_ROWS)
   1401 				for (y = 0; y < ysp; y++) {
   1402 					V_LINE_X(i, j + y) = t->cellsp;
   1403 					V_LINE_X(i + xsp, j + y) = t->cellsp;
   1404 				}
   1405 		}
   1406 	if (t->rules == R_GROUPS) {
   1407 		for (i = 1; i < t->x; i++) {
   1408 			if (/*i < t->xc &&*/ t->xcols[i])
   1409 				continue;
   1410 			for (j = 0; j < t->y; j++)
   1411 				V_LINE_X(i, j) = 0;
   1412 		}
   1413 		for (j = 1; j < t->y; j++) {
   1414 			for (i = 0; i < t->x; i++)
   1415 				if (CELL(t, i, j)->group)
   1416 					goto c;
   1417 			for (i = 0; i < t->x; i++)
   1418 				H_LINE_X(i, j) = 0;
   1419 c:;
   1420 		}
   1421 	}
   1422 	for (i = 0; i < t->x; i++) {
   1423 		H_LINE_X(i, 0) = t->border * !!(t->frame & F_ABOVE);
   1424 		H_LINE_X(i, t->y) = t->border * !!(t->frame & F_BELOW);
   1425 	}
   1426 	for (j = 0; j < t->y; j++) {
   1427 		V_LINE_X(0, j) = t->border * !!(t->frame & F_LHS);
   1428 		V_LINE_X(t->x, j) = t->border * !!(t->frame & F_RHS);
   1429 	}
   1430 }
   1431 
   1432 static void
   1433 display_table_frames(struct table *t, int x, int y)
   1434 {
   1435 	short *fh, *fv;
   1436 	int i, j;
   1437 	int cx, cy;
   1438 	if ((unsigned)t->x > INT_MAX)
   1439 		overalloc();
   1440 	if ((unsigned)t->y > INT_MAX)
   1441 		overalloc();
   1442 	if (((unsigned)t->x + 2) * ((unsigned)t->y + 2) / ((unsigned)t->x + 2)
   1443 	    != ((unsigned)t->y + 2))
   1444 		overalloc();
   1445 	if (((unsigned)t->x + 2) * ((unsigned)t->y + 2) > INT_MAX)
   1446 		overalloc();
   1447 	fh = xmalloc((t->x + 2) * (t->y + 1) * sizeof(short));
   1448 	fv = xmalloc((t->x + 1) * (t->y + 2) * sizeof(short));
   1449 	get_table_frame(t, fv, fh);
   1450 
   1451 	cy = y;
   1452 	for (j = 0; j <= t->y; j++) {
   1453 		cx = x;
   1454 		if ((j > 0 && j < t->y && get_hline_width(t, j) >= 0)
   1455 		    || (j == 0 && t->border && (t->frame & F_ABOVE))
   1456 		    || (j == t->y && t->border && (t->frame & F_BELOW))) {
   1457 			for (i = 0; i < t->x; i++) {
   1458 				int w;
   1459 				if (i > 0)
   1460 					w = get_vline_width(t, i);
   1461 				else
   1462 					w = t->border && (t->frame & F_LHS)
   1463 					        ? t->border
   1464 					        : -1;
   1465 				if (w >= 0) {
   1466 					draw_frame_point(cx, cy, i, j);
   1467 					if (j < t->y)
   1468 						draw_frame_vline(
   1469 						    cx, safe_add(cy, 1),
   1470 						    t->r_heights[j], i, j);
   1471 					cx = safe_add(cx, 1);
   1472 				}
   1473 				w = t->w_c[i];
   1474 				draw_frame_hline(cx, cy, w, i, j);
   1475 				cx = safe_add(cx, w);
   1476 			}
   1477 			if (t->border && (t->frame & F_RHS)) {
   1478 				draw_frame_point(cx, cy, i, j);
   1479 				if (j < t->y)
   1480 					draw_frame_vline(cx, safe_add(cy, 1),
   1481 					                 t->r_heights[j], i, j);
   1482 			}
   1483 			cy = safe_add(cy, 1);
   1484 		} else if (j < t->y) {
   1485 			for (i = 0; i <= t->x; i++) {
   1486 				if ((i > 0 && i < t->x
   1487 				     && get_vline_width(t, i) >= 0)
   1488 				    || (i == 0 && t->border
   1489 				        && (t->frame & F_LHS))
   1490 				    || (i == t->x && t->border
   1491 				        && (t->frame & F_RHS))) {
   1492 					draw_frame_vline(cx, cy,
   1493 					                 t->r_heights[j], i, j);
   1494 					cx = safe_add(cx, 1);
   1495 				}
   1496 				if (i < t->x)
   1497 					cx = safe_add(cx, t->w_c[i]);
   1498 			}
   1499 		}
   1500 		if (j < t->y)
   1501 			cy = safe_add(cy, t->r_heights[j]);
   1502 		/*for (cyy = cy1; cyy < cy; cyy++) xxpand_line(t->p, cyy, cx -
   1503 		 * 1);*/
   1504 	}
   1505 	free(fh);
   1506 	free(fv);
   1507 }
   1508 
   1509 void
   1510 format_table(unsigned char *attr, unsigned char *html, unsigned char *eof,
   1511              unsigned char **end, void *f)
   1512 {
   1513 	struct part *p = f;
   1514 	int bg, fg;
   1515 	int border, cellsp, vcellpd, cellpd, align;
   1516 	int frame, rules, width, wf;
   1517 	struct rgb bgcolor;
   1518 	struct table *t;
   1519 	unsigned char *al;
   1520 	int cye;
   1521 	int x;
   1522 	int i;
   1523 	struct s_e *bad_html = NULL;
   1524 	int bad_html_n;
   1525 	struct node *n, *nn;
   1526 	int cpd_pass, cpd_width, cpd_last;
   1527 	unsigned char AF_SAVE = AF;
   1528 	table_level++;
   1529 	memcpy(&bgcolor, &par_format.bgcolor, sizeof(struct rgb));
   1530 	get_bgcolor(attr, &bgcolor);
   1531 	bg = find_nearest_color(&bgcolor, 8);
   1532 	fg = find_nearest_color(&d_opt->default_fg, 16);
   1533 	AF = ATTR_FRAME | get_attribute(fg, bg);
   1534 	html_stack_dup();
   1535 	html_top.dontkill = 1;
   1536 	par_format.align = AL_LEFT;
   1537 	if ((border = get_num(attr, cast_uchar "border")) == -1)
   1538 		border = has_attr(attr, cast_uchar "border")
   1539 		         || has_attr(attr, cast_uchar "rules")
   1540 		         || has_attr(attr, cast_uchar "frame");
   1541 	if ((cellsp = get_num(attr, cast_uchar "cellspacing")) == -1)
   1542 		cellsp = 1;
   1543 	if ((cellpd = get_num(attr, cast_uchar "cellpadding")) == -1) {
   1544 		vcellpd = 0;
   1545 		cellpd = !!border;
   1546 	} else {
   1547 		vcellpd = cellpd >= HTML_CHAR_HEIGHT / 2 + 1;
   1548 		cellpd = cellpd >= HTML_CHAR_WIDTH / 2 + 1;
   1549 	}
   1550 	if (!border)
   1551 		cellsp = 0;
   1552 	else if (!cellsp)
   1553 		cellsp = 1;
   1554 	if (border > 2)
   1555 		border = 2;
   1556 	if (cellsp > 2)
   1557 		cellsp = 2;
   1558 	align = par_format.align;
   1559 	if (align == AL_NO || align == AL_NO_BREAKABLE || align == AL_BLOCK)
   1560 		align = AL_LEFT;
   1561 	if ((al = get_attr_val(attr, cast_uchar "summary"))) {
   1562 		if (!strcmp(cast_const_char al, "diff")) {
   1563 			free(al);
   1564 			if ((al = get_attr_val(attr, cast_uchar "class"))) {
   1565 				if (!strcmp(cast_const_char al, "diff")) {
   1566 					format_.attr |= AT_FIXED;
   1567 					par_format.align = AL_NO;
   1568 				}
   1569 				free(al);
   1570 			}
   1571 		} else
   1572 			free(al);
   1573 	}
   1574 	if ((al = get_attr_val(attr, cast_uchar "align"))) {
   1575 		if (!casestrcmp(al, cast_uchar "left"))
   1576 			align = AL_LEFT;
   1577 		if (!casestrcmp(al, cast_uchar "center"))
   1578 			align = AL_CENTER;
   1579 		if (!casestrcmp(al, cast_uchar "right"))
   1580 			align = AL_RIGHT;
   1581 		free(al);
   1582 	}
   1583 	frame = F_BOX;
   1584 	if ((al = get_attr_val(attr, cast_uchar "frame"))) {
   1585 		if (!casestrcmp(al, cast_uchar "void"))
   1586 			frame = F_VOID;
   1587 		if (!casestrcmp(al, cast_uchar "above"))
   1588 			frame = F_ABOVE;
   1589 		if (!casestrcmp(al, cast_uchar "below"))
   1590 			frame = F_BELOW;
   1591 		if (!casestrcmp(al, cast_uchar "hsides"))
   1592 			frame = F_HSIDES;
   1593 		if (!casestrcmp(al, cast_uchar "vsides"))
   1594 			frame = F_VSIDES;
   1595 		if (!casestrcmp(al, cast_uchar "lhs"))
   1596 			frame = F_LHS;
   1597 		if (!casestrcmp(al, cast_uchar "rhs"))
   1598 			frame = F_RHS;
   1599 		if (!casestrcmp(al, cast_uchar "box"))
   1600 			frame = F_BOX;
   1601 		if (!casestrcmp(al, cast_uchar "border"))
   1602 			frame = F_BOX;
   1603 		free(al);
   1604 	}
   1605 	rules = border ? R_ALL : R_NONE;
   1606 	if ((al = get_attr_val(attr, cast_uchar "rules"))) {
   1607 		if (!casestrcmp(al, cast_uchar "none"))
   1608 			rules = R_NONE;
   1609 		if (!casestrcmp(al, cast_uchar "groups"))
   1610 			rules = R_GROUPS;
   1611 		if (!casestrcmp(al, cast_uchar "rows"))
   1612 			rules = R_ROWS;
   1613 		if (!casestrcmp(al, cast_uchar "cols"))
   1614 			rules = R_COLS;
   1615 		if (!casestrcmp(al, cast_uchar "all"))
   1616 			rules = R_ALL;
   1617 		free(al);
   1618 	}
   1619 	if (!border)
   1620 		frame = F_VOID;
   1621 	wf = 0;
   1622 	if ((width = get_width(attr, cast_uchar "width", (p->data || p->xp)))
   1623 	    == -1) {
   1624 		width =
   1625 		    par_format.width
   1626 		    - safe_add(par_format.leftmargin, par_format.rightmargin);
   1627 		if (width < 0)
   1628 			width = 0;
   1629 		wf = 1;
   1630 	}
   1631 	t = parse_table(html, eof, end, &bgcolor, (p->data || p->xp), &bad_html,
   1632 	                &bad_html_n);
   1633 	for (i = 0; i < bad_html_n; i++) {
   1634 		while (bad_html[i].s < bad_html[i].e
   1635 		       && WHITECHAR(*bad_html[i].s))
   1636 			bad_html[i].s++;
   1637 		while (bad_html[i].s < bad_html[i].e
   1638 		       && WHITECHAR(bad_html[i].e[-1]))
   1639 			bad_html[i].e--;
   1640 		if (bad_html[i].s < bad_html[i].e)
   1641 			parse_html(bad_html[i].s, bad_html[i].e, put_chars_f,
   1642 			           line_break_f, special_f, (void *)p, NULL);
   1643 	}
   1644 	free(bad_html);
   1645 	t->p = p;
   1646 	t->bordercolor = get_attr_val(attr, cast_uchar "bordercolor");
   1647 	t->align = align;
   1648 	t->border = border;
   1649 	t->cellpd = cellpd;
   1650 	t->vcellpd = vcellpd;
   1651 	t->cellsp = cellsp;
   1652 	t->frame = frame;
   1653 	t->rules = rules;
   1654 	t->width = width;
   1655 	t->wf = wf;
   1656 	cpd_pass = 0;
   1657 	cpd_last = t->cellpd;
   1658 	cpd_width = 0; /* not needed, but let the warning go away */
   1659 again:
   1660 	get_cell_widths(t);
   1661 	if (get_column_widths(t))
   1662 		goto ret2;
   1663 	get_table_width(t);
   1664 	if ((!p->data && !p->xp)) {
   1665 		if (!wf && t->max_t > width)
   1666 			t->max_t = width;
   1667 		if (t->max_t < t->min_t)
   1668 			t->max_t = t->min_t;
   1669 		if (safe_add(t->max_t, safe_add(par_format.leftmargin,
   1670 		                                par_format.rightmargin))
   1671 		    > p->xmax)
   1672 			p->xmax = t->max_t + par_format.leftmargin
   1673 			          + par_format.rightmargin;
   1674 		if (safe_add(t->min_t, safe_add(par_format.leftmargin,
   1675 		                                par_format.rightmargin))
   1676 		    > p->x)
   1677 			p->x = t->min_t + par_format.leftmargin
   1678 			       + par_format.rightmargin;
   1679 		goto ret2;
   1680 	}
   1681 	if (!cpd_pass && t->min_t > width && t->cellpd) {
   1682 		t->cellpd = 0;
   1683 		cpd_pass = 1;
   1684 		cpd_width = t->min_t;
   1685 		goto again;
   1686 	}
   1687 	if (cpd_pass == 1 && t->min_t > cpd_width) {
   1688 		t->cellpd = cpd_last;
   1689 		cpd_pass = 2;
   1690 		goto again;
   1691 	}
   1692 	if (t->min_t >= width)
   1693 		distribute_widths(t, t->min_t);
   1694 	else if (t->max_t < width && wf)
   1695 		distribute_widths(t, t->max_t);
   1696 	else
   1697 		distribute_widths(t, width);
   1698 	if (!p->data && p->xp == 1) {
   1699 		int ww = safe_add(t->rw, safe_add(par_format.leftmargin,
   1700 		                                  par_format.rightmargin));
   1701 		if (ww > par_format.width)
   1702 			ww = par_format.width;
   1703 		if (ww < t->rw)
   1704 			ww = t->rw;
   1705 		if (ww > p->x)
   1706 			p->x = ww;
   1707 		p->cy = safe_add(p->cy, t->rh);
   1708 		goto ret2;
   1709 	}
   1710 #ifdef HTML_TABLE_2ND_PASS
   1711 	check_table_widths(t);
   1712 #endif
   1713 	get_table_heights(t);
   1714 
   1715 	x = par_format.leftmargin;
   1716 	if (align == AL_CENTER)
   1717 		x = (safe_add(par_format.width, par_format.leftmargin)
   1718 		     - par_format.rightmargin - t->rw)
   1719 		    / 2;
   1720 	if (align == AL_RIGHT)
   1721 		x = par_format.width - par_format.rightmargin - t->rw;
   1722 	if (safe_add(x, t->rw) > par_format.width)
   1723 		x = par_format.width - t->rw;
   1724 	if (x < 0)
   1725 		x = 0;
   1726 	/*display_table(t, x, p->cy, &cye);*/
   1727 	if (!p->data) {
   1728 		if (safe_add(t->rw, safe_add(par_format.leftmargin,
   1729 		                             par_format.rightmargin))
   1730 		    > p->x)
   1731 			p->x = t->rw + par_format.leftmargin
   1732 			       + par_format.rightmargin;
   1733 		p->cy = safe_add(p->cy, t->rh);
   1734 		goto ret2;
   1735 	}
   1736 
   1737 	n = list_struct(p->data->nodes.next, struct node);
   1738 	n->yw = p->yp - n->y + p->cy;
   1739 	display_complicated_table(t, x, p->cy, &cye);
   1740 	display_table_frames(t, x, p->cy);
   1741 	nn = xmalloc(sizeof(struct node));
   1742 	nn->x = n->x;
   1743 	nn->y = safe_add(p->yp, cye);
   1744 	nn->xw = n->xw;
   1745 	add_to_list(p->data->nodes, nn);
   1746 	p->cy = cye;
   1747 
   1748 ret2:
   1749 	p->link_num = t->link_num;
   1750 	if (p->cy > p->y)
   1751 		p->y = p->cy;
   1752 	if (t)
   1753 		free_table(t);
   1754 	kill_html_stack_item(&html_top);
   1755 	table_level--;
   1756 	if (!table_level) {
   1757 		free_table_cache();
   1758 	}
   1759 	AF = AF_SAVE;
   1760 }
   1761 
   1762 struct table_cache_entry {
   1763 	list_entry_1st;
   1764 	struct table_cache_entry *hash_next;
   1765 	unsigned char *start;
   1766 	unsigned char *end;
   1767 	int align;
   1768 	int m;
   1769 	int width;
   1770 	int xs;
   1771 	int link_num;
   1772 	union {
   1773 		struct part p;
   1774 	} u;
   1775 };
   1776 
   1777 static struct list_head table_cache = { &table_cache, &table_cache };
   1778 
   1779 #define TC_HASH_SIZE 8192
   1780 
   1781 static struct table_cache_entry *table_cache_hash[TC_HASH_SIZE] = { NULL };
   1782 
   1783 static inline int
   1784 make_hash(unsigned char *start, int xs)
   1785 {
   1786 	return ((int)(unsigned long)start + xs) & (TC_HASH_SIZE - 1);
   1787 }
   1788 
   1789 void *
   1790 find_table_cache_entry(unsigned char *start, unsigned char *end, int align,
   1791                        int m, int width, int xs, int link_num)
   1792 {
   1793 	int hash = make_hash(start, xs);
   1794 	struct table_cache_entry *tce;
   1795 	for (tce = table_cache_hash[hash]; tce; tce = tce->hash_next) {
   1796 		if (tce->start == start && tce->end == end
   1797 		    && tce->align == align && tce->m == m && tce->width == width
   1798 		    && tce->xs == xs && tce->link_num == link_num) {
   1799 			struct part *p = xmalloc(sizeof(struct part));
   1800 			memcpy(p, &tce->u.p, sizeof(struct part));
   1801 			return p;
   1802 		}
   1803 	}
   1804 	return NULL;
   1805 }
   1806 
   1807 void
   1808 add_table_cache_entry(unsigned char *start, unsigned char *end, int align,
   1809                       int m, int width, int xs, int link_num, void *p)
   1810 {
   1811 	int hash;
   1812 	struct table_cache_entry *tce =
   1813 	    xmalloc(sizeof(struct table_cache_entry));
   1814 	tce->start = start;
   1815 	tce->end = end;
   1816 	tce->align = align;
   1817 	tce->m = m;
   1818 	tce->width = width;
   1819 	tce->xs = xs;
   1820 	tce->link_num = link_num;
   1821 	memcpy(&tce->u.p, p, sizeof(struct part));
   1822 	hash = make_hash(start, xs);
   1823 	tce->hash_next = table_cache_hash[hash];
   1824 	table_cache_hash[hash] = tce;
   1825 	add_to_list(table_cache, tce);
   1826 }
   1827 
   1828 static void
   1829 free_table_cache(void)
   1830 {
   1831 	struct table_cache_entry *tce = NULL;
   1832 	struct list_head *ltce;
   1833 	foreach (struct table_cache_entry, tce, ltce, table_cache) {
   1834 		int hash = make_hash(tce->start, tce->xs);
   1835 		table_cache_hash[hash] = NULL;
   1836 	}
   1837 	free_list(struct table_cache_entry, table_cache);
   1838 }