w3m

Unnamed repository; edit this file to name it for gitweb.
git clone https://logand.com/git/w3m.git/
Log | Files | Refs | README

table.c (85799B)


      1 /* $Id$ */
      2 /* 
      3  * HTML table
      4  */
      5 #include <sys/types.h>
      6 #include <stdio.h>
      7 #include <string.h>
      8 #include <math.h>
      9 #include "fm.h"
     10 #include "html.h"
     11 #include "parsetagx.h"
     12 #include "Str.h"
     13 #include "myctype.h"
     14 
     15 int symbol_width = 0;
     16 int symbol_width0 = 0;
     17 
     18 #define RULE_WIDTH symbol_width
     19 #define RULE(mode,n) (((mode) == BORDER_THICK) ? ((n) + 16) : (n))
     20 #define TK_VERTICALBAR(mode) RULE(mode,5)
     21 
     22 #define BORDERWIDTH     2
     23 #define BORDERHEIGHT    1
     24 #define NOBORDERWIDTH   1
     25 #define NOBORDERHEIGHT  0
     26 
     27 #define HTT_X   1
     28 #define HTT_Y   2
     29 #define HTT_ALIGN  0x30
     30 #define HTT_LEFT   0x00
     31 #define HTT_CENTER 0x10
     32 #define HTT_RIGHT  0x20
     33 #define HTT_TRSET  0x40
     34 #define HTT_VALIGN 0x700
     35 #define HTT_TOP    0x100
     36 #define HTT_MIDDLE 0x200
     37 #define HTT_BOTTOM 0x400
     38 #define HTT_VTRSET 0x800
     39 #ifdef NOWRAP
     40 #define HTT_NOWRAP  4
     41 #endif				/* NOWRAP */
     42 #define TAG_IS(s,tag,len) (strncasecmp(s,tag,len)==0&&(s[len] == '>' || IS_SPACE((int)s[len])))
     43 
     44 #ifndef max
     45 #define max(a,b)        ((a) > (b) ? (a) : (b))
     46 #endif				/* not max */
     47 #ifndef min
     48 #define min(a,b)        ((a) > (b) ? (b) : (a))
     49 #endif				/* not min */
     50 #ifndef abs
     51 #define abs(a)          ((a) >= 0. ? (a) : -(a))
     52 #endif				/* not abs */
     53 
     54 #define set_prevchar(x,y,n) Strcopy_charp_n((x),(y),(n))
     55 #define set_space_to_prevchar(x) Strcopy_charp_n((x)," ",1)
     56 
     57 #ifdef MATRIX
     58 #ifndef MESCHACH
     59 #include "matrix.c"
     60 #endif				/* not MESCHACH */
     61 #endif				/* MATRIX */
     62 
     63 #ifdef MATRIX
     64 int correct_table_matrix(struct table *, int, int, int, double);
     65 void set_table_matrix(struct table *, int);
     66 #endif				/* MATRIX */
     67 
     68 #ifdef MATRIX
     69 static double
     70 weight(int x)
     71 {
     72 
     73     if (x < COLS)
     74 	return (double)x;
     75     else
     76 	return COLS * (log((double)x / COLS) + 1.);
     77 }
     78 
     79 static double
     80 weight2(int a)
     81 {
     82     return (double)a / COLS * 4 + 1.;
     83 }
     84 
     85 #define sigma_td(a)       (0.5*weight2(a))	/* <td width=...> */
     86 #define sigma_td_nw(a)    (32*weight2(a))	/* <td ...> */
     87 #define sigma_table(a)    (0.25*weight2(a))	/* <table width=...> */
     88 #define sigma_table_nw(a) (2*weight2(a))	/* <table...> */
     89 #else				/* not MATRIX */
     90 #define LOG_MIN 1.0
     91 static double
     92 weight3(int x)
     93 {
     94     if (x < 0.1)
     95 	return 0.1;
     96     if (x < LOG_MIN)
     97 	return (double)x;
     98     else
     99 	return LOG_MIN * (log((double)x / LOG_MIN) + 1.);
    100 }
    101 #endif				/* not MATRIX */
    102 
    103 static int
    104 bsearch_2short(short e1, short *ent1, short e2, short *ent2, int base,
    105 	       short *indexarray, int nent)
    106 {
    107     int n = nent;
    108     int k = 0;
    109 
    110     int e = e1 * base + e2;
    111     while (n > 0) {
    112 	int nn = n / 2;
    113 	int idx = indexarray[k + nn];
    114 	int ne = ent1[idx] * base + ent2[idx];
    115 	if (ne == e) {
    116 	    k += nn;
    117 	    break;
    118 	}
    119 	else if (ne < e) {
    120 	    n -= nn + 1;
    121 	    k += nn + 1;
    122 	}
    123 	else {
    124 	    n = nn;
    125 	}
    126     }
    127     return k;
    128 }
    129 
    130 static int
    131 bsearch_double(double e, double *ent, short *indexarray, int nent)
    132 {
    133     int n = nent;
    134     int k = 0;
    135 
    136     while (n > 0) {
    137 	int nn = n / 2;
    138 	int idx = indexarray[k + nn];
    139 	double ne = ent[idx];
    140 	if (ne == e) {
    141 	    k += nn;
    142 	    break;
    143 	}
    144 	else if (ne > e) {
    145 	    n -= nn + 1;
    146 	    k += nn + 1;
    147 	}
    148 	else {
    149 	    n = nn;
    150 	}
    151     }
    152     return k;
    153 }
    154 
    155 static int
    156 ceil_at_intervals(int x, int step)
    157 {
    158     int mo = x % step;
    159     if (mo > 0)
    160 	x += step - mo;
    161     else if (mo < 0)
    162 	x -= mo;
    163     return x;
    164 }
    165 
    166 static int
    167 floor_at_intervals(int x, int step)
    168 {
    169     int mo = x % step;
    170     if (mo > 0)
    171 	x -= mo;
    172     else if (mo < 0)
    173 	x += step - mo;
    174     return x;
    175 }
    176 
    177 #define round(x) ((int)floor((x)+0.5))
    178 
    179 #ifndef MATRIX
    180 static void
    181 dv2sv(double *dv, short *iv, int size)
    182 {
    183     int i, k, iw;
    184     short *indexarray;
    185     double *edv;
    186     double w = 0., x;
    187 
    188     indexarray = NewAtom_N(short, size);
    189     edv = NewAtom_N(double, size);
    190     for (i = 0; i < size; i++) {
    191 	iv[i] = ceil(dv[i]);
    192 	edv[i] = (double)iv[i] - dv[i];
    193     }
    194 
    195     w = 0.;
    196     for (k = 0; k < size; k++) {
    197 	x = edv[k];
    198 	w += x;
    199 	i = bsearch_double(x, edv, indexarray, k);
    200 	if (k > i) {
    201 	    int ii;
    202 	    for (ii = k; ii > i; ii--)
    203 		indexarray[ii] = indexarray[ii - 1];
    204 	}
    205 	indexarray[i] = k;
    206     }
    207     iw = min((int)(w + 0.5), size);
    208     if (iw == 0)
    209 	return;
    210     x = edv[(int)indexarray[iw - 1]];
    211     for (i = 0; i < size; i++) {
    212 	k = indexarray[i];
    213 	if (i >= iw && abs(edv[k] - x) > 1e-6)
    214 	    break;
    215 	iv[k]--;
    216     }
    217 }
    218 #endif
    219 
    220 static int
    221 table_colspan(struct table *t, int row, int col)
    222 {
    223     int i;
    224     for (i = col + 1; i <= t->maxcol && (t->tabattr[row][i] & HTT_X); i++) ;
    225     return i - col;
    226 }
    227 
    228 static int
    229 table_rowspan(struct table *t, int row, int col)
    230 {
    231     int i;
    232     if (!t->tabattr[row])
    233 	return 0;
    234     for (i = row + 1; i <= t->maxrow && t->tabattr[i] &&
    235 	 (t->tabattr[i][col] & HTT_Y); i++) ;
    236     return i - row;
    237 }
    238 
    239 static int
    240 minimum_cellspacing(int border_mode)
    241 {
    242     switch (border_mode) {
    243     case BORDER_THIN:
    244     case BORDER_THICK:
    245     case BORDER_NOWIN:
    246 	return RULE_WIDTH;
    247     case BORDER_NONE:
    248 	return 1;
    249     default:
    250 	/* not reached */
    251 	return 0;
    252     }
    253 }
    254 
    255 static int
    256 table_border_width(struct table *t)
    257 {
    258     switch (t->border_mode) {
    259     case BORDER_THIN:
    260     case BORDER_THICK:
    261 	return t->maxcol * t->cellspacing + 2 * (RULE_WIDTH + t->cellpadding);
    262     case BORDER_NOWIN:
    263     case BORDER_NONE:
    264 	return t->maxcol * t->cellspacing;
    265     default:
    266 	/* not reached */
    267 	return 0;
    268     }
    269 }
    270 
    271 struct table *
    272 newTable()
    273 {
    274     struct table *t;
    275     int i, j;
    276 
    277     t = New(struct table);
    278     t->max_rowsize = MAXROW;
    279     t->tabdata = New_N(GeneralList **, MAXROW);
    280     t->tabattr = New_N(table_attr *, MAXROW);
    281     t->tabheight = NewAtom_N(short, MAXROW);
    282 #ifdef ID_EXT
    283     t->tabidvalue = New_N(Str *, MAXROW);
    284     t->tridvalue = New_N(Str, MAXROW);
    285 #endif				/* ID_EXT */
    286 
    287     for (i = 0; i < MAXROW; i++) {
    288 	t->tabdata[i] = NULL;
    289 	t->tabattr[i] = 0;
    290 	t->tabheight[i] = 0;
    291 #ifdef ID_EXT
    292 	t->tabidvalue[i] = NULL;
    293 	t->tridvalue[i] = NULL;
    294 #endif				/* ID_EXT */
    295     }
    296     for (j = 0; j < MAXCOL; j++) {
    297 	t->tabwidth[j] = 0;
    298 	t->minimum_width[j] = 0;
    299 	t->fixed_width[j] = 0;
    300     }
    301     t->cell.maxcell = -1;
    302     t->cell.icell = -1;
    303     t->ntable = 0;
    304     t->tables_size = 0;
    305     t->tables = NULL;
    306 #ifdef MATRIX
    307     t->matrix = NULL;
    308     t->vector = NULL;
    309 #endif				/* MATRIX */
    310 #if 0
    311     t->tabcontentssize = 0;
    312     t->indent = 0;
    313     t->linfo.prev_ctype = PC_ASCII;
    314     t->linfo.prev_spaces = -1;
    315 #endif
    316     t->linfo.prevchar = Strnew_size(8);
    317     set_prevchar(t->linfo.prevchar, "", 0);
    318     t->trattr = 0;
    319 
    320     t->caption = Strnew();
    321     t->suspended_data = NULL;
    322 #ifdef ID_EXT
    323     t->id = NULL;
    324 #endif
    325     return t;
    326 }
    327 
    328 static void
    329 check_row(struct table *t, int row)
    330 {
    331     int i, r;
    332     GeneralList ***tabdata;
    333     table_attr **tabattr;
    334     short *tabheight;
    335 #ifdef ID_EXT
    336     Str **tabidvalue;
    337     Str *tridvalue;
    338 #endif				/* ID_EXT */
    339 
    340     if (row >= t->max_rowsize) {
    341 	r = max(t->max_rowsize * 2, row + 1);
    342 	tabdata = New_N(GeneralList **, r);
    343 	tabattr = New_N(table_attr *, r);
    344 	tabheight = NewAtom_N(short, r);
    345 #ifdef ID_EXT
    346 	tabidvalue = New_N(Str *, r);
    347 	tridvalue = New_N(Str, r);
    348 #endif				/* ID_EXT */
    349 	for (i = 0; i < t->max_rowsize; i++) {
    350 	    tabdata[i] = t->tabdata[i];
    351 	    tabattr[i] = t->tabattr[i];
    352 	    tabheight[i] = t->tabheight[i];
    353 #ifdef ID_EXT
    354 	    tabidvalue[i] = t->tabidvalue[i];
    355 	    tridvalue[i] = t->tridvalue[i];
    356 #endif				/* ID_EXT */
    357 	}
    358 	for (; i < r; i++) {
    359 	    tabdata[i] = NULL;
    360 	    tabattr[i] = NULL;
    361 	    tabheight[i] = 0;
    362 #ifdef ID_EXT
    363 	    tabidvalue[i] = NULL;
    364 	    tridvalue[i] = NULL;
    365 #endif				/* ID_EXT */
    366 	}
    367 	t->tabdata = tabdata;
    368 	t->tabattr = tabattr;
    369 	t->tabheight = tabheight;
    370 #ifdef ID_EXT
    371 	t->tabidvalue = tabidvalue;
    372 	t->tridvalue = tridvalue;
    373 #endif				/* ID_EXT */
    374 	t->max_rowsize = r;
    375     }
    376 
    377     if (t->tabdata[row] == NULL) {
    378 	t->tabdata[row] = New_N(GeneralList *, MAXCOL);
    379 	t->tabattr[row] = NewAtom_N(table_attr, MAXCOL);
    380 #ifdef ID_EXT
    381 	t->tabidvalue[row] = New_N(Str, MAXCOL);
    382 #endif				/* ID_EXT */
    383 	for (i = 0; i < MAXCOL; i++) {
    384 	    t->tabdata[row][i] = NULL;
    385 	    t->tabattr[row][i] = 0;
    386 #ifdef ID_EXT
    387 	    t->tabidvalue[row][i] = NULL;
    388 #endif				/* ID_EXT */
    389 	}
    390     }
    391 }
    392 
    393 void
    394 pushdata(struct table *t, int row, int col, char *data)
    395 {
    396     check_row(t, row);
    397     if (t->tabdata[row][col] == NULL)
    398 	t->tabdata[row][col] = newGeneralList();
    399 
    400     pushText(t->tabdata[row][col], data ? data : "");
    401 }
    402 
    403 void
    404 suspend_or_pushdata(struct table *tbl, char *line)
    405 {
    406     if (tbl->flag & TBL_IN_COL)
    407 	pushdata(tbl, tbl->row, tbl->col, line);
    408     else {
    409 	if (!tbl->suspended_data)
    410 	    tbl->suspended_data = newTextList();
    411 	pushText(tbl->suspended_data, line ? line : "");
    412     }
    413 }
    414 
    415 #ifdef USE_M17N
    416 #define PUSH_TAG(str,n) Strcat_charp_n(tagbuf, str, n)
    417 #else
    418 #define PUSH_TAG(str,n) Strcat_char(tagbuf, *str)
    419 #endif
    420 
    421 int visible_length_offset = 0;
    422 int
    423 visible_length(char *str)
    424 {
    425     int len = 0, n, max_len = 0;
    426     int status = R_ST_NORMAL;
    427     int prev_status = status;
    428     Str tagbuf = Strnew();
    429     char *t, *r2;
    430     int amp_len = 0;
    431 
    432     t = str;
    433     while (*str) {
    434 	prev_status = status;
    435 	if (next_status(*str, &status)) {
    436 #ifdef USE_M17N
    437 	    len += get_mcwidth(str);
    438 	    n = get_mclen(str);
    439 	}
    440 	else {
    441 	    n = 1;
    442 	}
    443 #else
    444 	    len++;
    445 	}
    446 #endif
    447 	if (status == R_ST_TAG0) {
    448 	    Strclear(tagbuf);
    449 	    PUSH_TAG(str, n);
    450 	}
    451 	else if (status == R_ST_TAG || status == R_ST_DQUOTE
    452 		 || status == R_ST_QUOTE || status == R_ST_EQL
    453 		 || status == R_ST_VALUE) {
    454 	    PUSH_TAG(str, n);
    455 	}
    456 	else if (status == R_ST_AMP) {
    457 	    if (prev_status == R_ST_NORMAL) {
    458 		Strclear(tagbuf);
    459 		len--;
    460 		amp_len = 0;
    461 	    }
    462 	    else {
    463 		PUSH_TAG(str, n);
    464 		amp_len++;
    465 	    }
    466 	}
    467 	else if (status == R_ST_NORMAL && prev_status == R_ST_AMP) {
    468 	    PUSH_TAG(str, n);
    469 	    r2 = tagbuf->ptr;
    470 	    t = getescapecmd(&r2);
    471 	    if (!*r2 && (*t == '\r' || *t == '\n')) {
    472 		if (len > max_len)
    473 		    max_len = len;
    474 		len = 0;
    475 	    }
    476 	    else
    477 		len += get_strwidth(t) + get_strwidth(r2);
    478 	}
    479 	else if (status == R_ST_NORMAL && ST_IS_REAL_TAG(prev_status)) {
    480 	    ;
    481 	}
    482 	else if (*str == '\t') {
    483 	    len--;
    484 	    do {
    485 		len++;
    486 	    } while ((visible_length_offset + len) % Tabstop != 0);
    487 	}
    488 	else if (*str == '\r' || *str == '\n') {
    489 	    len--;
    490 	    if (len > max_len)
    491 		max_len = len;
    492 	    len = 0;
    493 	}
    494 #ifdef USE_M17N
    495 	str += n;
    496 #else
    497 	str++;
    498 #endif
    499     }
    500     if (status == R_ST_AMP) {
    501 	r2 = tagbuf->ptr;
    502 	t = getescapecmd(&r2);
    503 	if (*t != '\r' && *t != '\n')
    504 	    len += get_strwidth(t) + get_strwidth(r2);
    505     }
    506     return len > max_len ? len : max_len;
    507 }
    508 
    509 int
    510 visible_length_plain(char *str)
    511 {
    512     int len = 0, max_len = 0;
    513 
    514     while (*str) {
    515 	if (*str == '\t') {
    516 	    do {
    517 		len++;
    518 	    } while ((visible_length_offset + len) % Tabstop != 0);
    519 	    str++;
    520 	}
    521 	else if (*str == '\r' || *str == '\n') {
    522 	    if (len > max_len)
    523 		max_len = len;
    524 	    len = 0;
    525 	    str++;
    526 	}
    527 	else {
    528 #ifdef USE_M17N
    529 	    len += get_mcwidth(str);
    530 	    str += get_mclen(str);
    531 #else
    532 	    len++;
    533 	    str++;
    534 #endif
    535 	}
    536     }
    537     return len > max_len ? len : max_len;
    538 }
    539 
    540 static int
    541 maximum_visible_length(char *str, int offset)
    542 {
    543     visible_length_offset = offset;
    544     return visible_length(str);
    545 }
    546 
    547 static int
    548 maximum_visible_length_plain(char *str, int offset)
    549 {
    550     visible_length_offset = offset;
    551     return visible_length_plain(str);
    552 }
    553 
    554 void
    555 align(TextLine *lbuf, int width, int mode)
    556 {
    557     int i, l, l1, l2;
    558     Str buf, line = lbuf->line;
    559 
    560     if (line->length == 0) {
    561 	for (i = 0; i < width; i++)
    562 	    Strcat_char(line, ' ');
    563 	lbuf->pos = width;
    564 	return;
    565     }
    566     buf = Strnew();
    567     l = width - lbuf->pos;
    568     switch (mode) {
    569     case ALIGN_CENTER:
    570 	l1 = l / 2;
    571 	l2 = l - l1;
    572 	for (i = 0; i < l1; i++)
    573 	    Strcat_char(buf, ' ');
    574 	Strcat(buf, line);
    575 	for (i = 0; i < l2; i++)
    576 	    Strcat_char(buf, ' ');
    577 	break;
    578     case ALIGN_LEFT:
    579 	Strcat(buf, line);
    580 	for (i = 0; i < l; i++)
    581 	    Strcat_char(buf, ' ');
    582 	break;
    583     case ALIGN_RIGHT:
    584 	for (i = 0; i < l; i++)
    585 	    Strcat_char(buf, ' ');
    586 	Strcat(buf, line);
    587 	break;
    588     default:
    589 	return;
    590     }
    591     lbuf->line = buf;
    592     if (lbuf->pos < width)
    593 	lbuf->pos = width;
    594 }
    595 
    596 void
    597 print_item(struct table *t, int row, int col, int width, Str buf)
    598 {
    599     int alignment;
    600     TextLine *lbuf;
    601 
    602     if (t->tabdata[row])
    603 	lbuf = popTextLine(t->tabdata[row][col]);
    604     else
    605 	lbuf = NULL;
    606 
    607     if (lbuf != NULL) {
    608 	check_row(t, row);
    609 	alignment = ALIGN_CENTER;
    610 	if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_LEFT)
    611 	    alignment = ALIGN_LEFT;
    612 	else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_RIGHT)
    613 	    alignment = ALIGN_RIGHT;
    614 	else if ((t->tabattr[row][col] & HTT_ALIGN) == HTT_CENTER)
    615 	    alignment = ALIGN_CENTER;
    616 	align(lbuf, width, alignment);
    617 	Strcat(buf, lbuf->line);
    618     }
    619     else {
    620 	lbuf = newTextLine(NULL, 0);
    621 	align(lbuf, width, ALIGN_CENTER);
    622 	Strcat(buf, lbuf->line);
    623     }
    624 }
    625 
    626 
    627 #define T_TOP           0
    628 #define T_MIDDLE        1
    629 #define T_BOTTOM        2
    630 
    631 void
    632 print_sep(struct table *t, int row, int type, int maxcol, Str buf)
    633 {
    634     int forbid;
    635     int rule_mode;
    636     int i, k, l, m;
    637 
    638     if (row >= 0)
    639 	check_row(t, row);
    640     check_row(t, row + 1);
    641     if ((type == T_TOP || type == T_BOTTOM) && t->border_mode == BORDER_THICK) {
    642 	rule_mode = BORDER_THICK;
    643     }
    644     else {
    645 	rule_mode = BORDER_THIN;
    646     }
    647     forbid = 1;
    648     if (type == T_TOP)
    649 	forbid |= 2;
    650     else if (type == T_BOTTOM)
    651 	forbid |= 8;
    652     else if (t->tabattr[row + 1][0] & HTT_Y) {
    653 	forbid |= 4;
    654     }
    655     if (t->border_mode != BORDER_NOWIN) {
    656 	push_symbol(buf, RULE(t->border_mode, forbid), symbol_width, 1);
    657     }
    658     for (i = 0; i <= maxcol; i++) {
    659 	forbid = 10;
    660 	if (type != T_BOTTOM && (t->tabattr[row + 1][i] & HTT_Y)) {
    661 	    if (t->tabattr[row + 1][i] & HTT_X) {
    662 		goto do_last_sep;
    663 	    }
    664 	    else {
    665 		for (k = row;
    666 		     k >= 0 && t->tabattr[k] && (t->tabattr[k][i] & HTT_Y);
    667 		     k--) ;
    668 		m = t->tabwidth[i] + 2 * t->cellpadding;
    669 		for (l = i + 1; l <= t->maxcol && (t->tabattr[row][l] & HTT_X);
    670 		     l++)
    671 		    m += t->tabwidth[l] + t->cellspacing;
    672 		print_item(t, k, i, m, buf);
    673 	    }
    674 	}
    675 	else {
    676 	    int w = t->tabwidth[i] + 2 * t->cellpadding;
    677 	    if (RULE_WIDTH == 2)
    678 		w = (w + 1) / RULE_WIDTH;
    679 	    push_symbol(buf, RULE(rule_mode, forbid), symbol_width, w);
    680 	}
    681       do_last_sep:
    682 	if (i < maxcol) {
    683 	    forbid = 0;
    684 	    if (type == T_TOP)
    685 		forbid |= 2;
    686 	    else if (t->tabattr[row][i + 1] & HTT_X) {
    687 		forbid |= 2;
    688 	    }
    689 	    if (type == T_BOTTOM)
    690 		forbid |= 8;
    691 	    else {
    692 		if (t->tabattr[row + 1][i + 1] & HTT_X) {
    693 		    forbid |= 8;
    694 		}
    695 		if (t->tabattr[row + 1][i + 1] & HTT_Y) {
    696 		    forbid |= 4;
    697 		}
    698 		if (t->tabattr[row + 1][i] & HTT_Y) {
    699 		    forbid |= 1;
    700 		}
    701 	    }
    702 	    if (forbid != 15)	/* forbid==15 means 'no rule at all' */
    703 		push_symbol(buf, RULE(rule_mode, forbid), symbol_width, 1);
    704 	}
    705     }
    706     forbid = 4;
    707     if (type == T_TOP)
    708 	forbid |= 2;
    709     if (type == T_BOTTOM)
    710 	forbid |= 8;
    711     if (t->tabattr[row + 1][maxcol] & HTT_Y) {
    712 	forbid |= 1;
    713     }
    714     if (t->border_mode != BORDER_NOWIN)
    715 	push_symbol(buf, RULE(t->border_mode, forbid), symbol_width, 1);
    716 }
    717 
    718 static int
    719 get_spec_cell_width(struct table *tbl, int row, int col)
    720 {
    721     int i, w;
    722 
    723     w = tbl->tabwidth[col];
    724     for (i = col + 1; i <= tbl->maxcol; i++) {
    725 	check_row(tbl, row);
    726 	if (tbl->tabattr[row][i] & HTT_X)
    727 	    w += tbl->tabwidth[i] + tbl->cellspacing;
    728 	else
    729 	    break;
    730     }
    731     return w;
    732 }
    733 
    734 void
    735 do_refill(struct table *tbl, int row, int col, int maxlimit)
    736 {
    737     TextList *orgdata;
    738     TextListItem *l;
    739     struct readbuffer obuf;
    740     struct html_feed_environ h_env;
    741     struct environment envs[MAX_ENV_LEVEL];
    742     int colspan, icell;
    743 
    744     if (tbl->tabdata[row] == NULL || tbl->tabdata[row][col] == NULL)
    745 	return;
    746     orgdata = (TextList *)tbl->tabdata[row][col];
    747     tbl->tabdata[row][col] = newGeneralList();
    748 
    749     init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL,
    750 	      (TextLineList *)tbl->tabdata[row][col],
    751 	      get_spec_cell_width(tbl, row, col), 0);
    752     obuf.flag |= RB_INTABLE;
    753     if (h_env.limit > maxlimit)
    754 	h_env.limit = maxlimit;
    755     if (tbl->border_mode != BORDER_NONE && tbl->vcellpadding > 0)
    756 	do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
    757     for (l = orgdata->first; l != NULL; l = l->next) {
    758 	if (TAG_IS(l->ptr, "<table_alt", 10)) {
    759 	    int id = -1;
    760 	    char *p = l->ptr;
    761 	    struct parsed_tag *tag;
    762 	    if ((tag = parse_tag(&p, TRUE)) != NULL)
    763 		parsedtag_get_value(tag, ATTR_TID, &id);
    764 	    if (id >= 0 && id < tbl->ntable) {
    765 		int alignment;
    766 		TextLineListItem *ti;
    767 		struct table *t = tbl->tables[id].ptr;
    768 		int limit = tbl->tables[id].indent + t->total_width;
    769 		tbl->tables[id].ptr = NULL;
    770 		save_fonteffect(&h_env, h_env.obuf);
    771 		flushline(&h_env, &obuf, 0, 2, h_env.limit);
    772 		if (t->vspace > 0 && !(obuf.flag & RB_IGNORE_P))
    773 		    do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
    774 		if (RB_GET_ALIGN(h_env.obuf) == RB_CENTER)
    775 		    alignment = ALIGN_CENTER;
    776 		else if (RB_GET_ALIGN(h_env.obuf) == RB_RIGHT)
    777 		    alignment = ALIGN_RIGHT;
    778 		else
    779 		    alignment = ALIGN_LEFT;
    780 
    781 		if (alignment != ALIGN_LEFT) {
    782 		    for (ti = tbl->tables[id].buf->first;
    783 			 ti != NULL; ti = ti->next)
    784 			align(ti->ptr, h_env.limit, alignment);
    785 		}
    786 		appendTextLineList(h_env.buf, tbl->tables[id].buf);
    787 		if (h_env.maxlimit < limit)
    788 		    h_env.maxlimit = limit;
    789 		restore_fonteffect(&h_env, h_env.obuf);
    790 		obuf.flag &= ~RB_IGNORE_P;
    791 		h_env.blank_lines = 0;
    792 		if (t->vspace > 0) {
    793 		    do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
    794 		    obuf.flag |= RB_IGNORE_P;
    795 		}
    796 	    }
    797 	}
    798 	else
    799 	    HTMLlineproc1(l->ptr, &h_env);
    800     }
    801     if (obuf.status != R_ST_NORMAL) {
    802 	obuf.status = R_ST_EOL;
    803 	HTMLlineproc1("\n", &h_env);
    804     }
    805     completeHTMLstream(&h_env, &obuf);
    806     flushline(&h_env, &obuf, 0, 2, h_env.limit);
    807     if (tbl->border_mode == BORDER_NONE) {
    808 	int rowspan = table_rowspan(tbl, row, col);
    809 	if (row + rowspan <= tbl->maxrow) {
    810 	    if (tbl->vcellpadding > 0 && !(obuf.flag & RB_IGNORE_P))
    811 		do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
    812 	}
    813 	else {
    814 	    if (tbl->vspace > 0)
    815 		purgeline(&h_env);
    816 	}
    817     }
    818     else {
    819 	if (tbl->vcellpadding > 0) {
    820 	    if (!(obuf.flag & RB_IGNORE_P))
    821 		do_blankline(&h_env, &obuf, 0, 0, h_env.limit);
    822 	}
    823 	else
    824 	    purgeline(&h_env);
    825     }
    826     if ((colspan = table_colspan(tbl, row, col)) > 1) {
    827 	struct table_cell *cell = &tbl->cell;
    828 	int k;
    829 	k = bsearch_2short(colspan, cell->colspan, col, cell->col, MAXCOL,
    830 			   cell->index, cell->maxcell + 1);
    831 	icell = cell->index[k];
    832 	if (cell->minimum_width[icell] < h_env.maxlimit)
    833 	    cell->minimum_width[icell] = h_env.maxlimit;
    834     }
    835     else {
    836 	if (tbl->minimum_width[col] < h_env.maxlimit)
    837 	    tbl->minimum_width[col] = h_env.maxlimit;
    838     }
    839 }
    840 
    841 static int
    842 table_rule_width(struct table *t)
    843 {
    844     if (t->border_mode == BORDER_NONE)
    845 	return 1;
    846     return RULE_WIDTH;
    847 }
    848 
    849 static void
    850 check_cell_width(short *tabwidth, short *cellwidth,
    851 		 short *col, short *colspan, short maxcell,
    852 		 short *indexarray, int space, int dir)
    853 {
    854     int i, j, k, bcol, ecol;
    855     int swidth, width;
    856 
    857     for (k = 0; k <= maxcell; k++) {
    858 	j = indexarray[k];
    859 	if (cellwidth[j] <= 0)
    860 	    continue;
    861 	bcol = col[j];
    862 	ecol = bcol + colspan[j];
    863 	swidth = 0;
    864 	for (i = bcol; i < ecol; i++)
    865 	    swidth += tabwidth[i];
    866 
    867 	width = cellwidth[j] - (colspan[j] - 1) * space;
    868 	if (width > swidth) {
    869 	    int w = (width - swidth) / colspan[j];
    870 	    int r = (width - swidth) % colspan[j];
    871 	    for (i = bcol; i < ecol; i++)
    872 		tabwidth[i] += w;
    873 	    /* dir {0: horizontal, 1: vertical} */
    874 	    if (dir == 1 && r > 0)
    875 		r = colspan[j];
    876 	    for (i = 1; i <= r; i++)
    877 		tabwidth[ecol - i]++;
    878 	}
    879     }
    880 }
    881 
    882 void
    883 check_minimum_width(struct table *t, short *tabwidth)
    884 {
    885     int i;
    886     struct table_cell *cell = &t->cell;
    887 
    888     for (i = 0; i <= t->maxcol; i++) {
    889 	if (tabwidth[i] < t->minimum_width[i])
    890 	    tabwidth[i] = t->minimum_width[i];
    891     }
    892 
    893     check_cell_width(tabwidth, cell->minimum_width, cell->col, cell->colspan,
    894 		     cell->maxcell, cell->index, t->cellspacing, 0);
    895 }
    896 
    897 void
    898 check_maximum_width(struct table *t)
    899 {
    900     struct table_cell *cell = &t->cell;
    901 #ifdef MATRIX
    902     int i, j, bcol, ecol;
    903     int swidth, width;
    904 
    905     cell->necell = 0;
    906     for (j = 0; j <= cell->maxcell; j++) {
    907 	bcol = cell->col[j];
    908 	ecol = bcol + cell->colspan[j];
    909 	swidth = 0;
    910 	for (i = bcol; i < ecol; i++)
    911 	    swidth += t->tabwidth[i];
    912 
    913 	width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
    914 	if (width > swidth) {
    915 	    cell->eindex[cell->necell] = j;
    916 	    cell->necell++;
    917 	}
    918     }
    919 #else				/* not MATRIX */
    920     check_cell_width(t->tabwidth, cell->width, cell->col, cell->colspan,
    921 		     cell->maxcell, cell->index, t->cellspacing, 0);
    922     check_minimum_width(t, t->tabwidth);
    923 #endif				/* not MATRIX */
    924 }
    925 
    926 
    927 #ifdef MATRIX
    928 static void
    929 set_integered_width(struct table *t, double *dwidth, short *iwidth)
    930 {
    931     int i, j, k, n, bcol, ecol, step;
    932     short *indexarray;
    933     char *fixed;
    934     double *mod;
    935     double sum = 0., x = 0.;
    936     struct table_cell *cell = &t->cell;
    937     int rulewidth = table_rule_width(t);
    938 
    939     indexarray = NewAtom_N(short, t->maxcol + 1);
    940     mod = NewAtom_N(double, t->maxcol + 1);
    941     for (i = 0; i <= t->maxcol; i++) {
    942 	iwidth[i] = ceil_at_intervals(ceil(dwidth[i]), rulewidth);
    943 	mod[i] = (double)iwidth[i] - dwidth[i];
    944     }
    945 
    946     sum = 0.;
    947     for (k = 0; k <= t->maxcol; k++) {
    948 	x = mod[k];
    949 	sum += x;
    950 	i = bsearch_double(x, mod, indexarray, k);
    951 	if (k > i) {
    952 	    int ii;
    953 	    for (ii = k; ii > i; ii--)
    954 		indexarray[ii] = indexarray[ii - 1];
    955 	}
    956 	indexarray[i] = k;
    957     }
    958 
    959     fixed = NewAtom_N(char, t->maxcol + 1);
    960     bzero(fixed, t->maxcol + 1);
    961     for (step = 0; step < 2; step++) {
    962 	for (i = 0; i <= t->maxcol; i += n) {
    963 	    int nn;
    964 	    char *idx;
    965 	    double nsum;
    966 	    if (sum < 0.5)
    967 		return;
    968 	    for (n = 0; i + n <= t->maxcol; n++) {
    969 		int ii = indexarray[i + n];
    970 		if (n == 0)
    971 		    x = mod[ii];
    972 		else if (fabs(mod[ii] - x) > 1e-6)
    973 		    break;
    974 	    }
    975 	    for (k = 0; k < n; k++) {
    976 		int ii = indexarray[i + k];
    977 		if (fixed[ii] < 2 &&
    978 		    iwidth[ii] - rulewidth < t->minimum_width[ii])
    979 		    fixed[ii] = 2;
    980 		if (fixed[ii] < 1 &&
    981 		    iwidth[ii] - rulewidth < t->tabwidth[ii] &&
    982 		    (double)rulewidth - mod[ii] > 0.5)
    983 		    fixed[ii] = 1;
    984 	    }
    985 	    idx = NewAtom_N(char, n);
    986 	    for (k = 0; k < cell->maxcell; k++) {
    987 		int kk, w, width, m;
    988 		j = cell->index[k];
    989 		bcol = cell->col[j];
    990 		ecol = bcol + cell->colspan[j];
    991 		m = 0;
    992 		for (kk = 0; kk < n; kk++) {
    993 		    int ii = indexarray[i + kk];
    994 		    if (ii >= bcol && ii < ecol) {
    995 			idx[m] = ii;
    996 			m++;
    997 		    }
    998 		}
    999 		if (m == 0)
   1000 		    continue;
   1001 		width = (cell->colspan[j] - 1) * t->cellspacing;
   1002 		for (kk = bcol; kk < ecol; kk++)
   1003 		    width += iwidth[kk];
   1004 		w = 0;
   1005 		for (kk = 0; kk < m; kk++) {
   1006 		    if (fixed[(int)idx[kk]] < 2)
   1007 			w += rulewidth;
   1008 		}
   1009 		if (width - w < cell->minimum_width[j]) {
   1010 		    for (kk = 0; kk < m; kk++) {
   1011 			if (fixed[(int)idx[kk]] < 2)
   1012 			    fixed[(int)idx[kk]] = 2;
   1013 		    }
   1014 		}
   1015 		w = 0;
   1016 		for (kk = 0; kk < m; kk++) {
   1017 		    if (fixed[(int)idx[kk]] < 1 &&
   1018 			(double)rulewidth - mod[(int)idx[kk]] > 0.5)
   1019 			w += rulewidth;
   1020 		}
   1021 		if (width - w < cell->width[j]) {
   1022 		    for (kk = 0; kk < m; kk++) {
   1023 			if (fixed[(int)idx[kk]] < 1 &&
   1024 			    (double)rulewidth - mod[(int)idx[kk]] > 0.5)
   1025 			    fixed[(int)idx[kk]] = 1;
   1026 		    }
   1027 		}
   1028 	    }
   1029 	    nn = 0;
   1030 	    for (k = 0; k < n; k++) {
   1031 		int ii = indexarray[i + k];
   1032 		if (fixed[ii] <= step)
   1033 		    nn++;
   1034 	    }
   1035 	    nsum = sum - (double)(nn * rulewidth);
   1036 	    if (nsum < 0. && fabs(sum) <= fabs(nsum))
   1037 		return;
   1038 	    for (k = 0; k < n; k++) {
   1039 		int ii = indexarray[i + k];
   1040 		if (fixed[ii] <= step) {
   1041 		    iwidth[ii] -= rulewidth;
   1042 		    fixed[ii] = 3;
   1043 		}
   1044 	    }
   1045 	    sum = nsum;
   1046 	}
   1047     }
   1048 }
   1049 
   1050 static double
   1051 correlation_coefficient(double sxx, double syy, double sxy)
   1052 {
   1053     double coe, tmp;
   1054     tmp = sxx * syy;
   1055     if (tmp < Tiny)
   1056 	tmp = Tiny;
   1057     coe = sxy / sqrt(tmp);
   1058     if (coe > 1.)
   1059 	return 1.;
   1060     if (coe < -1.)
   1061 	return -1.;
   1062     return coe;
   1063 }
   1064 
   1065 static double
   1066 correlation_coefficient2(double sxx, double syy, double sxy)
   1067 {
   1068     double coe, tmp;
   1069     tmp = (syy + sxx - 2 * sxy) * sxx;
   1070     if (tmp < Tiny)
   1071 	tmp = Tiny;
   1072     coe = (sxx - sxy) / sqrt(tmp);
   1073     if (coe > 1.)
   1074 	return 1.;
   1075     if (coe < -1.)
   1076 	return -1.;
   1077     return coe;
   1078 }
   1079 
   1080 static double
   1081 recalc_width(double old, double swidth, int cwidth,
   1082 	     double sxx, double syy, double sxy, int is_inclusive)
   1083 {
   1084     double delta = swidth - (double)cwidth;
   1085     double rat = sxy / sxx,
   1086 	coe = correlation_coefficient(sxx, syy, sxy), w, ww;
   1087     if (old < 0.)
   1088 	old = 0.;
   1089     if (fabs(coe) < 1e-5)
   1090 	return old;
   1091     w = rat * old;
   1092     ww = delta;
   1093     if (w > 0.) {
   1094 	double wmin = 5e-3 * sqrt(syy * (1. - coe * coe));
   1095 	if (swidth < 0.2 && cwidth > 0 && is_inclusive) {
   1096 	    double coe1 = correlation_coefficient2(sxx, syy, sxy);
   1097 	    if (coe > 0.9 || coe1 > 0.9)
   1098 		return 0.;
   1099 	}
   1100 	if (wmin > 0.05)
   1101 	    wmin = 0.05;
   1102 	if (ww < 0.)
   1103 	    ww = 0.;
   1104 	ww += wmin;
   1105     }
   1106     else {
   1107 	double wmin = 5e-3 * sqrt(syy) * fabs(coe);
   1108 	if (rat > -0.001)
   1109 	    return old;
   1110 	if (wmin > 0.01)
   1111 	    wmin = 0.01;
   1112 	if (ww > 0.)
   1113 	    ww = 0.;
   1114 	ww -= wmin;
   1115     }
   1116     if (w > ww)
   1117 	return ww / rat;
   1118     return old;
   1119 }
   1120 
   1121 static int
   1122 check_compressible_cell(struct table *t, MAT * minv,
   1123 			double *newwidth, double *swidth, short *cwidth,
   1124 			double totalwidth, double *Sxx,
   1125 			int icol, int icell, double sxx, int corr)
   1126 {
   1127     struct table_cell *cell = &t->cell;
   1128     int i, j, k, m, bcol, ecol, span;
   1129     double delta, owidth;
   1130     double dmax, dmin, sxy;
   1131     int rulewidth = table_rule_width(t);
   1132 
   1133     if (sxx < 10.)
   1134 	return corr;
   1135 
   1136     if (icol >= 0) {
   1137 	owidth = newwidth[icol];
   1138 	delta = newwidth[icol] - (double)t->tabwidth[icol];
   1139 	bcol = icol;
   1140 	ecol = bcol + 1;
   1141     }
   1142     else if (icell >= 0) {
   1143 	owidth = swidth[icell];
   1144 	delta = swidth[icell] - (double)cwidth[icell];
   1145 	bcol = cell->col[icell];
   1146 	ecol = bcol + cell->colspan[icell];
   1147     }
   1148     else {
   1149 	owidth = totalwidth;
   1150 	delta = totalwidth;
   1151 	bcol = 0;
   1152 	ecol = t->maxcol + 1;
   1153     }
   1154 
   1155     dmin = delta;
   1156     dmax = -1.;
   1157     for (k = 0; k <= cell->maxcell; k++) {
   1158 	int bcol1, ecol1;
   1159 	int is_inclusive = 0;
   1160 	if (dmin <= 0.)
   1161 	    goto _end;
   1162 	j = cell->index[k];
   1163 	if (j == icell)
   1164 	    continue;
   1165 	bcol1 = cell->col[j];
   1166 	ecol1 = bcol1 + cell->colspan[j];
   1167 	sxy = 0.;
   1168 	for (m = bcol1; m < ecol1; m++) {
   1169 	    for (i = bcol; i < ecol; i++)
   1170 		sxy += m_entry(minv, i, m);
   1171 	}
   1172 	if (bcol1 >= bcol && ecol1 <= ecol) {
   1173 	    is_inclusive = 1;
   1174 	}
   1175 	if (sxy > 0.)
   1176 	    dmin = recalc_width(dmin, swidth[j], cwidth[j],
   1177 				sxx, Sxx[j], sxy, is_inclusive);
   1178 	else
   1179 	    dmax = recalc_width(dmax, swidth[j], cwidth[j],
   1180 				sxx, Sxx[j], sxy, is_inclusive);
   1181     }
   1182     for (m = 0; m <= t->maxcol; m++) {
   1183 	int is_inclusive = 0;
   1184 	if (dmin <= 0.)
   1185 	    goto _end;
   1186 	if (m == icol)
   1187 	    continue;
   1188 	sxy = 0.;
   1189 	for (i = bcol; i < ecol; i++)
   1190 	    sxy += m_entry(minv, i, m);
   1191 	if (m >= bcol && m < ecol) {
   1192 	    is_inclusive = 1;
   1193 	}
   1194 	if (sxy > 0.)
   1195 	    dmin = recalc_width(dmin, newwidth[m], t->tabwidth[m],
   1196 				sxx, m_entry(minv, m, m), sxy, is_inclusive);
   1197 	else
   1198 	    dmax = recalc_width(dmax, newwidth[m], t->tabwidth[m],
   1199 				sxx, m_entry(minv, m, m), sxy, is_inclusive);
   1200     }
   1201   _end:
   1202     if (dmax > 0. && dmin > dmax)
   1203 	dmin = dmax;
   1204     span = ecol - bcol;
   1205     if ((span == t->maxcol + 1 && dmin >= 0.) ||
   1206 	(span != t->maxcol + 1 && dmin > rulewidth * 0.5)) {
   1207 	int nwidth = ceil_at_intervals(round(owidth - dmin), rulewidth);
   1208 	correct_table_matrix(t, bcol, ecol - bcol, nwidth, 1.);
   1209 	corr++;
   1210     }
   1211     return corr;
   1212 }
   1213 
   1214 #define MAX_ITERATION 10
   1215 int
   1216 check_table_width(struct table *t, double *newwidth, MAT * minv, int itr)
   1217 {
   1218     int i, j, k, m, bcol, ecol;
   1219     int corr = 0;
   1220     struct table_cell *cell = &t->cell;
   1221 #ifdef __GNUC__
   1222     short orgwidth[t->maxcol + 1], corwidth[t->maxcol + 1];
   1223     short cwidth[cell->maxcell + 1];
   1224     double swidth[cell->maxcell + 1];
   1225 #else				/* __GNUC__ */
   1226     short orgwidth[MAXCOL], corwidth[MAXCOL];
   1227     short cwidth[MAXCELL];
   1228     double swidth[MAXCELL];
   1229 #endif				/* __GNUC__ */
   1230     double twidth, sxy, *Sxx, stotal;
   1231 
   1232     twidth = 0.;
   1233     stotal = 0.;
   1234     for (i = 0; i <= t->maxcol; i++) {
   1235 	twidth += newwidth[i];
   1236 	stotal += m_entry(minv, i, i);
   1237 	for (m = 0; m < i; m++) {
   1238 	    stotal += 2 * m_entry(minv, i, m);
   1239 	}
   1240     }
   1241 
   1242     Sxx = NewAtom_N(double, cell->maxcell + 1);
   1243     for (k = 0; k <= cell->maxcell; k++) {
   1244 	j = cell->index[k];
   1245 	bcol = cell->col[j];
   1246 	ecol = bcol + cell->colspan[j];
   1247 	swidth[j] = 0.;
   1248 	for (i = bcol; i < ecol; i++)
   1249 	    swidth[j] += newwidth[i];
   1250 	cwidth[j] = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
   1251 	Sxx[j] = 0.;
   1252 	for (i = bcol; i < ecol; i++) {
   1253 	    Sxx[j] += m_entry(minv, i, i);
   1254 	    for (m = bcol; m <= ecol; m++) {
   1255 		if (m < i)
   1256 		    Sxx[j] += 2 * m_entry(minv, i, m);
   1257 	    }
   1258 	}
   1259     }
   1260 
   1261     /* compress table */
   1262     corr = check_compressible_cell(t, minv, newwidth, swidth,
   1263 				   cwidth, twidth, Sxx, -1, -1, stotal, corr);
   1264     if (itr < MAX_ITERATION && corr > 0)
   1265 	return corr;
   1266 
   1267     /* compress multicolumn cell */
   1268     for (k = cell->maxcell; k >= 0; k--) {
   1269 	j = cell->index[k];
   1270 	corr = check_compressible_cell(t, minv, newwidth, swidth,
   1271 				       cwidth, twidth, Sxx,
   1272 				       -1, j, Sxx[j], corr);
   1273 	if (itr < MAX_ITERATION && corr > 0)
   1274 	    return corr;
   1275     }
   1276 
   1277     /* compress single column cell */
   1278     for (i = 0; i <= t->maxcol; i++) {
   1279 	corr = check_compressible_cell(t, minv, newwidth, swidth,
   1280 				       cwidth, twidth, Sxx,
   1281 				       i, -1, m_entry(minv, i, i), corr);
   1282 	if (itr < MAX_ITERATION && corr > 0)
   1283 	    return corr;
   1284     }
   1285 
   1286 
   1287     for (i = 0; i <= t->maxcol; i++)
   1288 	corwidth[i] = orgwidth[i] = round(newwidth[i]);
   1289 
   1290     check_minimum_width(t, corwidth);
   1291 
   1292     for (i = 0; i <= t->maxcol; i++) {
   1293 	double sx = sqrt(m_entry(minv, i, i));
   1294 	if (sx < 0.1)
   1295 	    continue;
   1296 	if (orgwidth[i] < t->minimum_width[i] &&
   1297 	    corwidth[i] == t->minimum_width[i]) {
   1298 	    double w = (sx > 0.5) ? 0.5 : sx * 0.2;
   1299 	    sxy = 0.;
   1300 	    for (m = 0; m <= t->maxcol; m++) {
   1301 		if (m == i)
   1302 		    continue;
   1303 		sxy += m_entry(minv, i, m);
   1304 	    }
   1305 	    if (sxy <= 0.) {
   1306 		correct_table_matrix(t, i, 1, t->minimum_width[i], w);
   1307 		corr++;
   1308 	    }
   1309 	}
   1310     }
   1311 
   1312     for (k = 0; k <= cell->maxcell; k++) {
   1313 	int nwidth = 0, mwidth;
   1314 	double sx;
   1315 
   1316 	j = cell->index[k];
   1317 	sx = sqrt(Sxx[j]);
   1318 	if (sx < 0.1)
   1319 	    continue;
   1320 	bcol = cell->col[j];
   1321 	ecol = bcol + cell->colspan[j];
   1322 	for (i = bcol; i < ecol; i++)
   1323 	    nwidth += corwidth[i];
   1324 	mwidth =
   1325 	    cell->minimum_width[j] - (cell->colspan[j] - 1) * t->cellspacing;
   1326 	if (mwidth > swidth[j] && mwidth == nwidth) {
   1327 	    double w = (sx > 0.5) ? 0.5 : sx * 0.2;
   1328 
   1329 	    sxy = 0.;
   1330 	    for (i = bcol; i < ecol; i++) {
   1331 		for (m = 0; m <= t->maxcol; m++) {
   1332 		    if (m >= bcol && m < ecol)
   1333 			continue;
   1334 		    sxy += m_entry(minv, i, m);
   1335 		}
   1336 	    }
   1337 	    if (sxy <= 0.) {
   1338 		correct_table_matrix(t, bcol, cell->colspan[j], mwidth, w);
   1339 		corr++;
   1340 	    }
   1341 	}
   1342     }
   1343 
   1344     if (itr >= MAX_ITERATION)
   1345 	return 0;
   1346     else
   1347 	return corr;
   1348 }
   1349 
   1350 #else				/* not MATRIX */
   1351 void
   1352 set_table_width(struct table *t, short *newwidth, int maxwidth)
   1353 {
   1354     int i, j, k, bcol, ecol;
   1355     struct table_cell *cell = &t->cell;
   1356     char *fixed;
   1357     int swidth, fwidth, width, nvar;
   1358     double s;
   1359     double *dwidth;
   1360     int try_again;
   1361 
   1362     fixed = NewAtom_N(char, t->maxcol + 1);
   1363     bzero(fixed, t->maxcol + 1);
   1364     dwidth = NewAtom_N(double, t->maxcol + 1);
   1365 
   1366     for (i = 0; i <= t->maxcol; i++) {
   1367 	dwidth[i] = 0.0;
   1368 	if (t->fixed_width[i] < 0) {
   1369 	    t->fixed_width[i] = -t->fixed_width[i] * maxwidth / 100;
   1370 	}
   1371 	if (t->fixed_width[i] > 0) {
   1372 	    newwidth[i] = t->fixed_width[i];
   1373 	    fixed[i] = 1;
   1374 	}
   1375 	else
   1376 	    newwidth[i] = 0;
   1377 	if (newwidth[i] < t->minimum_width[i])
   1378 	    newwidth[i] = t->minimum_width[i];
   1379     }
   1380 
   1381     for (k = 0; k <= cell->maxcell; k++) {
   1382 	j = cell->indexarray[k];
   1383 	bcol = cell->col[j];
   1384 	ecol = bcol + cell->colspan[j];
   1385 
   1386 	if (cell->fixed_width[j] < 0)
   1387 	    cell->fixed_width[j] = -cell->fixed_width[j] * maxwidth / 100;
   1388 
   1389 	swidth = 0;
   1390 	fwidth = 0;
   1391 	nvar = 0;
   1392 	for (i = bcol; i < ecol; i++) {
   1393 	    if (fixed[i]) {
   1394 		fwidth += newwidth[i];
   1395 	    }
   1396 	    else {
   1397 		swidth += newwidth[i];
   1398 		nvar++;
   1399 	    }
   1400 	}
   1401 	width = max(cell->fixed_width[j], cell->minimum_width[j])
   1402 	    - (cell->colspan[j] - 1) * t->cellspacing;
   1403 	if (nvar > 0 && width > fwidth + swidth) {
   1404 	    s = 0.;
   1405 	    for (i = bcol; i < ecol; i++) {
   1406 		if (!fixed[i])
   1407 		    s += weight3(t->tabwidth[i]);
   1408 	    }
   1409 	    for (i = bcol; i < ecol; i++) {
   1410 		if (!fixed[i])
   1411 		    dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s;
   1412 		else
   1413 		    dwidth[i] = (double)newwidth[i];
   1414 	    }
   1415 	    dv2sv(dwidth, newwidth, cell->colspan[j]);
   1416 	    if (cell->fixed_width[j] > 0) {
   1417 		for (i = bcol; i < ecol; i++)
   1418 		    fixed[i] = 1;
   1419 	    }
   1420 	}
   1421     }
   1422 
   1423     do {
   1424 	nvar = 0;
   1425 	swidth = 0;
   1426 	fwidth = 0;
   1427 	for (i = 0; i <= t->maxcol; i++) {
   1428 	    if (fixed[i]) {
   1429 		fwidth += newwidth[i];
   1430 	    }
   1431 	    else {
   1432 		swidth += newwidth[i];
   1433 		nvar++;
   1434 	    }
   1435 	}
   1436 	width = maxwidth - t->maxcol * t->cellspacing;
   1437 	if (nvar == 0 || width <= fwidth + swidth)
   1438 	    break;
   1439 
   1440 	s = 0.;
   1441 	for (i = 0; i <= t->maxcol; i++) {
   1442 	    if (!fixed[i])
   1443 		s += weight3(t->tabwidth[i]);
   1444 	}
   1445 	for (i = 0; i <= t->maxcol; i++) {
   1446 	    if (!fixed[i])
   1447 		dwidth[i] = (width - fwidth) * weight3(t->tabwidth[i]) / s;
   1448 	    else
   1449 		dwidth[i] = (double)newwidth[i];
   1450 	}
   1451 	dv2sv(dwidth, newwidth, t->maxcol + 1);
   1452 
   1453 	try_again = 0;
   1454 	for (i = 0; i <= t->maxcol; i++) {
   1455 	    if (!fixed[i]) {
   1456 		if (newwidth[i] > t->tabwidth[i]) {
   1457 		    newwidth[i] = t->tabwidth[i];
   1458 		    fixed[i] = 1;
   1459 		    try_again = 1;
   1460 		}
   1461 		else if (newwidth[i] < t->minimum_width[i]) {
   1462 		    newwidth[i] = t->minimum_width[i];
   1463 		    fixed[i] = 1;
   1464 		    try_again = 1;
   1465 		}
   1466 	    }
   1467 	}
   1468     } while (try_again);
   1469 }
   1470 #endif				/* not MATRIX */
   1471 
   1472 void
   1473 check_table_height(struct table *t)
   1474 {
   1475     int i, j, k;
   1476     struct {
   1477 	short *row;
   1478 	short *rowspan;
   1479 	short *indexarray;
   1480 	short maxcell;
   1481 	short size;
   1482 	short *height;
   1483     } cell;
   1484     int space = 0;
   1485 
   1486     cell.size = 0;
   1487     cell.maxcell = -1;
   1488 
   1489     for (j = 0; j <= t->maxrow; j++) {
   1490 	if (!t->tabattr[j])
   1491 	    continue;
   1492 	for (i = 0; i <= t->maxcol; i++) {
   1493 	    int t_dep, rowspan;
   1494 	    if (t->tabattr[j][i] & (HTT_X | HTT_Y))
   1495 		continue;
   1496 
   1497 	    if (t->tabdata[j][i] == NULL)
   1498 		t_dep = 0;
   1499 	    else
   1500 		t_dep = t->tabdata[j][i]->nitem;
   1501 
   1502 	    rowspan = table_rowspan(t, j, i);
   1503 	    if (rowspan > 1) {
   1504 		int c = cell.maxcell + 1;
   1505 		k = bsearch_2short(rowspan, cell.rowspan,
   1506 				   j, cell.row, t->maxrow + 1, cell.indexarray,
   1507 				   c);
   1508 		if (k <= cell.maxcell) {
   1509 		    int idx = cell.indexarray[k];
   1510 		    if (cell.row[idx] == j && cell.rowspan[idx] == rowspan)
   1511 			c = idx;
   1512 		}
   1513 		if (c >= MAXROWCELL)
   1514 		    continue;
   1515 		if (c >= cell.size) {
   1516 		    if (cell.size == 0) {
   1517 			cell.size = max(MAXCELL, c + 1);
   1518 			cell.row = NewAtom_N(short, cell.size);
   1519 			cell.rowspan = NewAtom_N(short, cell.size);
   1520 			cell.indexarray = NewAtom_N(short, cell.size);
   1521 			cell.height = NewAtom_N(short, cell.size);
   1522 		    }
   1523 		    else {
   1524 			cell.size = max(cell.size + MAXCELL, c + 1);
   1525 			cell.row = New_Reuse(short, cell.row, cell.size);
   1526 			cell.rowspan = New_Reuse(short, cell.rowspan,
   1527 						 cell.size);
   1528 			cell.indexarray = New_Reuse(short, cell.indexarray,
   1529 						    cell.size);
   1530 			cell.height = New_Reuse(short, cell.height, cell.size);
   1531 		    }
   1532 		}
   1533 		if (c > cell.maxcell) {
   1534 		    cell.maxcell++;
   1535 		    cell.row[cell.maxcell] = j;
   1536 		    cell.rowspan[cell.maxcell] = rowspan;
   1537 		    cell.height[cell.maxcell] = 0;
   1538 		    if (cell.maxcell > k) {
   1539 			int ii;
   1540 			for (ii = cell.maxcell; ii > k; ii--)
   1541 			    cell.indexarray[ii] = cell.indexarray[ii - 1];
   1542 		    }
   1543 		    cell.indexarray[k] = cell.maxcell;
   1544 		}
   1545 
   1546 		if (cell.height[c] < t_dep)
   1547 		    cell.height[c] = t_dep;
   1548 		continue;
   1549 	    }
   1550 	    if (t->tabheight[j] < t_dep)
   1551 		t->tabheight[j] = t_dep;
   1552 	}
   1553     }
   1554 
   1555     switch (t->border_mode) {
   1556     case BORDER_THIN:
   1557     case BORDER_THICK:
   1558     case BORDER_NOWIN:
   1559 	space = 1;
   1560 	break;
   1561     case BORDER_NONE:
   1562 	space = 0;
   1563     }
   1564     check_cell_width(t->tabheight, cell.height, cell.row, cell.rowspan,
   1565 		     cell.maxcell, cell.indexarray, space, 1);
   1566 }
   1567 
   1568 #define CHECK_MINIMUM	1
   1569 #define CHECK_FIXED	2
   1570 
   1571 int
   1572 get_table_width(struct table *t, short *orgwidth, short *cellwidth, int flag)
   1573 {
   1574 #ifdef __GNUC__
   1575     short newwidth[t->maxcol + 1];
   1576 #else				/* not __GNUC__ */
   1577     short newwidth[MAXCOL];
   1578 #endif				/* not __GNUC__ */
   1579     int i;
   1580     int swidth;
   1581     struct table_cell *cell = &t->cell;
   1582     int rulewidth = table_rule_width(t);
   1583 
   1584     for (i = 0; i <= t->maxcol; i++)
   1585 	newwidth[i] = max(orgwidth[i], 0);
   1586 
   1587     if (flag & CHECK_FIXED) {
   1588 #ifdef __GNUC__
   1589 	short ccellwidth[cell->maxcell + 1];
   1590 #else				/* not __GNUC__ */
   1591 	short ccellwidth[MAXCELL];
   1592 #endif				/* not __GNUC__ */
   1593 	for (i = 0; i <= t->maxcol; i++) {
   1594 	    if (newwidth[i] < t->fixed_width[i])
   1595 		newwidth[i] = t->fixed_width[i];
   1596 	}
   1597 	for (i = 0; i <= cell->maxcell; i++) {
   1598 	    ccellwidth[i] = cellwidth[i];
   1599 	    if (ccellwidth[i] < cell->fixed_width[i])
   1600 		ccellwidth[i] = cell->fixed_width[i];
   1601 	}
   1602 	check_cell_width(newwidth, ccellwidth, cell->col, cell->colspan,
   1603 			 cell->maxcell, cell->index, t->cellspacing, 0);
   1604     }
   1605     else {
   1606 	check_cell_width(newwidth, cellwidth, cell->col, cell->colspan,
   1607 			 cell->maxcell, cell->index, t->cellspacing, 0);
   1608     }
   1609     if (flag & CHECK_MINIMUM)
   1610 	check_minimum_width(t, newwidth);
   1611 
   1612     swidth = 0;
   1613     for (i = 0; i <= t->maxcol; i++) {
   1614 	swidth += ceil_at_intervals(newwidth[i], rulewidth);
   1615     }
   1616     swidth += table_border_width(t);
   1617     return swidth;
   1618 }
   1619 
   1620 #define minimum_table_width(t)\
   1621 (get_table_width(t,t->minimum_width,t->cell.minimum_width,0))
   1622 #define maximum_table_width(t)\
   1623   (get_table_width(t,t->tabwidth,t->cell.width,CHECK_FIXED))
   1624 #define fixed_table_width(t)\
   1625   (get_table_width(t,t->fixed_width,t->cell.fixed_width,CHECK_MINIMUM))
   1626 
   1627 void
   1628 renderCoTable(struct table *tbl, int maxlimit)
   1629 {
   1630     struct readbuffer obuf;
   1631     struct html_feed_environ h_env;
   1632     struct environment envs[MAX_ENV_LEVEL];
   1633     struct table *t;
   1634     int i, col, row;
   1635     int indent, maxwidth;
   1636 
   1637     for (i = 0; i < tbl->ntable; i++) {
   1638 	t = tbl->tables[i].ptr;
   1639 	col = tbl->tables[i].col;
   1640 	row = tbl->tables[i].row;
   1641 	indent = tbl->tables[i].indent;
   1642 
   1643 	init_henv(&h_env, &obuf, envs, MAX_ENV_LEVEL, tbl->tables[i].buf,
   1644 		  get_spec_cell_width(tbl, row, col), indent);
   1645 	check_row(tbl, row);
   1646 	if (h_env.limit > maxlimit)
   1647 	    h_env.limit = maxlimit;
   1648 	if (t->total_width == 0)
   1649 	    maxwidth = h_env.limit - indent;
   1650 	else if (t->total_width > 0)
   1651 	    maxwidth = t->total_width;
   1652 	else
   1653 	    maxwidth = t->total_width = -t->total_width * h_env.limit / 100;
   1654 	renderTable(t, maxwidth, &h_env);
   1655     }
   1656 }
   1657 
   1658 static void
   1659 make_caption(struct table *t, struct html_feed_environ *h_env)
   1660 {
   1661     struct html_feed_environ henv;
   1662     struct readbuffer obuf;
   1663     struct environment envs[MAX_ENV_LEVEL];
   1664     int limit;
   1665 
   1666     if (t->caption->length <= 0)
   1667 	return;
   1668 
   1669     if (t->total_width > 0)
   1670 	limit = t->total_width;
   1671     else
   1672 	limit = h_env->limit;
   1673     init_henv(&henv, &obuf, envs, MAX_ENV_LEVEL, newTextLineList(),
   1674 	      limit, h_env->envs[h_env->envc].indent);
   1675     HTMLlineproc1("<center>", &henv);
   1676     HTMLlineproc0(t->caption->ptr, &henv, FALSE);
   1677     HTMLlineproc1("</center>", &henv);
   1678 
   1679     if (t->total_width < henv.maxlimit)
   1680 	t->total_width = henv.maxlimit;
   1681     limit = h_env->limit;
   1682     h_env->limit = t->total_width;
   1683     HTMLlineproc1("<center>", h_env);
   1684     HTMLlineproc0(t->caption->ptr, h_env, FALSE);
   1685     HTMLlineproc1("</center>", h_env);
   1686     h_env->limit = limit;
   1687 }
   1688 
   1689 void
   1690 renderTable(struct table *t, int max_width, struct html_feed_environ *h_env)
   1691 {
   1692     int i, j, w, r, h;
   1693     Str renderbuf;
   1694     short new_tabwidth[MAXCOL];
   1695 #ifdef MATRIX
   1696     int itr;
   1697     VEC *newwidth;
   1698     MAT *mat, *minv;
   1699     PERM *pivot;
   1700 #endif				/* MATRIX */
   1701     int width;
   1702     int rulewidth;
   1703     Str vrulea = NULL, vruleb = NULL, vrulec = NULL;
   1704 #ifdef ID_EXT
   1705     Str idtag;
   1706 #endif				/* ID_EXT */
   1707 
   1708     t->total_height = 0;
   1709     if (t->maxcol < 0) {
   1710 	make_caption(t, h_env);
   1711 	return;
   1712     }
   1713 
   1714     if (t->sloppy_width > max_width)
   1715 	max_width = t->sloppy_width;
   1716 
   1717     rulewidth = table_rule_width(t);
   1718 
   1719     max_width -= table_border_width(t);
   1720 
   1721     if (rulewidth > 1)
   1722 	max_width = floor_at_intervals(max_width, rulewidth);
   1723 
   1724     if (max_width < rulewidth)
   1725 	max_width = rulewidth;
   1726 
   1727     check_maximum_width(t);
   1728 
   1729 #ifdef MATRIX
   1730     if (t->maxcol == 0) {
   1731 	if (t->tabwidth[0] > max_width)
   1732 	    t->tabwidth[0] = max_width;
   1733 	if (t->total_width > 0)
   1734 	    t->tabwidth[0] = max_width;
   1735 	else if (t->fixed_width[0] > 0)
   1736 	    t->tabwidth[0] = t->fixed_width[0];
   1737 	if (t->tabwidth[0] < t->minimum_width[0])
   1738 	    t->tabwidth[0] = t->minimum_width[0];
   1739     }
   1740     else {
   1741 	set_table_matrix(t, max_width);
   1742 
   1743 	itr = 0;
   1744 	mat = m_get(t->maxcol + 1, t->maxcol + 1);
   1745 	pivot = px_get(t->maxcol + 1);
   1746 	newwidth = v_get(t->maxcol + 1);
   1747 	minv = m_get(t->maxcol + 1, t->maxcol + 1);
   1748 	do {
   1749 	    m_copy(t->matrix, mat);
   1750 	    LUfactor(mat, pivot);
   1751 	    LUsolve(mat, pivot, t->vector, newwidth);
   1752 	    LUinverse(mat, pivot, minv);
   1753 #ifdef TABLE_DEBUG
   1754 	    set_integered_width(t, newwidth->ve, new_tabwidth);
   1755 	    fprintf(stderr, "itr=%d\n", itr);
   1756 	    fprintf(stderr, "max_width=%d\n", max_width);
   1757 	    fprintf(stderr, "minimum : ");
   1758 	    for (i = 0; i <= t->maxcol; i++)
   1759 		fprintf(stderr, "%2d ", t->minimum_width[i]);
   1760 	    fprintf(stderr, "\nfixed : ");
   1761 	    for (i = 0; i <= t->maxcol; i++)
   1762 		fprintf(stderr, "%2d ", t->fixed_width[i]);
   1763 	    fprintf(stderr, "\ndecided : ");
   1764 	    for (i = 0; i <= t->maxcol; i++)
   1765 		fprintf(stderr, "%2d ", new_tabwidth[i]);
   1766 	    fprintf(stderr, "\n");
   1767 #endif				/* TABLE_DEBUG */
   1768 	    itr++;
   1769 
   1770 	} while (check_table_width(t, newwidth->ve, minv, itr));
   1771 	set_integered_width(t, newwidth->ve, new_tabwidth);
   1772 	check_minimum_width(t, new_tabwidth);
   1773 	v_free(newwidth);
   1774 	px_free(pivot);
   1775 	m_free(mat);
   1776 	m_free(minv);
   1777 	m_free(t->matrix);
   1778 	v_free(t->vector);
   1779 	for (i = 0; i <= t->maxcol; i++) {
   1780 	    t->tabwidth[i] = new_tabwidth[i];
   1781 	}
   1782     }
   1783 #else				/* not MATRIX */
   1784     set_table_width(t, new_tabwidth, max_width);
   1785     for (i = 0; i <= t->maxcol; i++) {
   1786 	t->tabwidth[i] = new_tabwidth[i];
   1787     }
   1788 #endif				/* not MATRIX */
   1789 
   1790     check_minimum_width(t, t->tabwidth);
   1791     for (i = 0; i <= t->maxcol; i++)
   1792 	t->tabwidth[i] = ceil_at_intervals(t->tabwidth[i], rulewidth);
   1793 
   1794     renderCoTable(t, h_env->limit);
   1795 
   1796     for (i = 0; i <= t->maxcol; i++) {
   1797 	for (j = 0; j <= t->maxrow; j++) {
   1798 	    check_row(t, j);
   1799 	    if (t->tabattr[j][i] & HTT_Y)
   1800 		continue;
   1801 	    do_refill(t, j, i, h_env->limit);
   1802 	}
   1803     }
   1804 
   1805     check_minimum_width(t, t->tabwidth);
   1806     t->total_width = 0;
   1807     for (i = 0; i <= t->maxcol; i++) {
   1808 	t->tabwidth[i] = ceil_at_intervals(t->tabwidth[i], rulewidth);
   1809 	t->total_width += t->tabwidth[i];
   1810     }
   1811 
   1812     t->total_width += table_border_width(t);
   1813 
   1814     check_table_height(t);
   1815 
   1816     for (i = 0; i <= t->maxcol; i++) {
   1817 	for (j = 0; j <= t->maxrow; j++) {
   1818 	    TextLineList *l;
   1819 	    int k;
   1820 	    if ((t->tabattr[j][i] & HTT_Y) ||
   1821 		(t->tabattr[j][i] & HTT_TOP) || (t->tabdata[j][i] == NULL))
   1822 		continue;
   1823 	    h = t->tabheight[j];
   1824 	    for (k = j + 1; k <= t->maxrow; k++) {
   1825 		if (!(t->tabattr[k][i] & HTT_Y))
   1826 		    break;
   1827 		h += t->tabheight[k];
   1828 		switch (t->border_mode) {
   1829 		case BORDER_THIN:
   1830 		case BORDER_THICK:
   1831 		case BORDER_NOWIN:
   1832 		    h += 1;
   1833 		    break;
   1834 		}
   1835 	    }
   1836 	    h -= t->tabdata[j][i]->nitem;
   1837 	    if (t->tabattr[j][i] & HTT_MIDDLE)
   1838 		h /= 2;
   1839 	    if (h <= 0)
   1840 		continue;
   1841 	    l = newTextLineList();
   1842 	    for (k = 0; k < h; k++)
   1843 		pushTextLine(l, newTextLine(NULL, 0));
   1844 	    t->tabdata[j][i] = appendGeneralList((GeneralList *)l,
   1845 						 t->tabdata[j][i]);
   1846 	}
   1847     }
   1848 
   1849     /* table output */
   1850     width = t->total_width;
   1851 
   1852     make_caption(t, h_env);
   1853 
   1854     HTMLlineproc1("<pre for_table>", h_env);
   1855 #ifdef ID_EXT
   1856     if (t->id != NULL) {
   1857 	idtag = Sprintf("<_id id=\"%s\">", html_quote((t->id)->ptr));
   1858 	HTMLlineproc1(idtag->ptr, h_env);
   1859     }
   1860 #endif				/* ID_EXT */
   1861     switch (t->border_mode) {
   1862     case BORDER_THIN:
   1863     case BORDER_THICK:
   1864 	renderbuf = Strnew();
   1865 	print_sep(t, -1, T_TOP, t->maxcol, renderbuf);
   1866 	push_render_image(renderbuf, width, t->total_width, h_env);
   1867 	t->total_height += 1;
   1868 	break;
   1869     }
   1870     vruleb = Strnew();
   1871     switch (t->border_mode) {
   1872     case BORDER_THIN:
   1873     case BORDER_THICK:
   1874 	vrulea = Strnew();
   1875 	vrulec = Strnew();
   1876 	push_symbol(vrulea, TK_VERTICALBAR(t->border_mode), symbol_width, 1);
   1877 	for (i = 0; i < t->cellpadding; i++) {
   1878 	    Strcat_char(vrulea, ' ');
   1879 	    Strcat_char(vruleb, ' ');
   1880 	    Strcat_char(vrulec, ' ');
   1881 	}
   1882 	push_symbol(vrulec, TK_VERTICALBAR(t->border_mode), symbol_width, 1);
   1883     case BORDER_NOWIN:
   1884 	push_symbol(vruleb, TK_VERTICALBAR(BORDER_THIN), symbol_width, 1);
   1885 	for (i = 0; i < t->cellpadding; i++)
   1886 	    Strcat_char(vruleb, ' ');
   1887 	break;
   1888     case BORDER_NONE:
   1889 	for (i = 0; i < t->cellspacing; i++)
   1890 	    Strcat_char(vruleb, ' ');
   1891     }
   1892 
   1893     for (r = 0; r <= t->maxrow; r++) {
   1894 	for (h = 0; h < t->tabheight[r]; h++) {
   1895 	    renderbuf = Strnew();
   1896 	    if (t->border_mode == BORDER_THIN
   1897 		|| t->border_mode == BORDER_THICK)
   1898 		Strcat(renderbuf, vrulea);
   1899 #ifdef ID_EXT
   1900 	    if (t->tridvalue[r] != NULL && h == 0) {
   1901 		idtag = Sprintf("<_id id=\"%s\">",
   1902 				html_quote((t->tridvalue[r])->ptr));
   1903 		Strcat(renderbuf, idtag);
   1904 	    }
   1905 #endif				/* ID_EXT */
   1906 	    for (i = 0; i <= t->maxcol; i++) {
   1907 		check_row(t, r);
   1908 #ifdef ID_EXT
   1909 		if (t->tabidvalue[r][i] != NULL && h == 0) {
   1910 		    idtag = Sprintf("<_id id=\"%s\">",
   1911 				    html_quote((t->tabidvalue[r][i])->ptr));
   1912 		    Strcat(renderbuf, idtag);
   1913 		}
   1914 #endif				/* ID_EXT */
   1915 		if (!(t->tabattr[r][i] & HTT_X)) {
   1916 		    w = t->tabwidth[i];
   1917 		    for (j = i + 1;
   1918 			 j <= t->maxcol && (t->tabattr[r][j] & HTT_X); j++)
   1919 			w += t->tabwidth[j] + t->cellspacing;
   1920 		    if (t->tabattr[r][i] & HTT_Y) {
   1921 			for (j = r - 1; j >= 0 && t->tabattr[j]
   1922 			     && (t->tabattr[j][i] & HTT_Y); j--) ;
   1923 			print_item(t, j, i, w, renderbuf);
   1924 		    }
   1925 		    else
   1926 			print_item(t, r, i, w, renderbuf);
   1927 		}
   1928 		if (i < t->maxcol && !(t->tabattr[r][i + 1] & HTT_X))
   1929 		    Strcat(renderbuf, vruleb);
   1930 	    }
   1931 	    switch (t->border_mode) {
   1932 	    case BORDER_THIN:
   1933 	    case BORDER_THICK:
   1934 		Strcat(renderbuf, vrulec);
   1935 		t->total_height += 1;
   1936 		break;
   1937 	    }
   1938 	    push_render_image(renderbuf, width, t->total_width, h_env);
   1939 	}
   1940 	if (r < t->maxrow && t->border_mode != BORDER_NONE) {
   1941 	    renderbuf = Strnew();
   1942 	    print_sep(t, r, T_MIDDLE, t->maxcol, renderbuf);
   1943 	    push_render_image(renderbuf, width, t->total_width, h_env);
   1944 	}
   1945 	t->total_height += t->tabheight[r];
   1946     }
   1947     switch (t->border_mode) {
   1948     case BORDER_THIN:
   1949     case BORDER_THICK:
   1950 	renderbuf = Strnew();
   1951 	print_sep(t, t->maxrow, T_BOTTOM, t->maxcol, renderbuf);
   1952 	push_render_image(renderbuf, width, t->total_width, h_env);
   1953 	t->total_height += 1;
   1954 	break;
   1955     }
   1956     if (t->total_height == 0) {
   1957        renderbuf = Strnew_charp(" ");
   1958 	t->total_height++;
   1959 	t->total_width = 1;
   1960 	push_render_image(renderbuf, 1, t->total_width, h_env);
   1961     }
   1962     HTMLlineproc1("</pre>", h_env);
   1963 }
   1964 
   1965 #ifdef TABLE_NO_COMPACT
   1966 #define THR_PADDING 2
   1967 #else
   1968 #define THR_PADDING 4
   1969 #endif
   1970 
   1971 struct table *
   1972 begin_table(int border, int spacing, int padding, int vspace)
   1973 {
   1974     struct table *t;
   1975     int mincell = minimum_cellspacing(border);
   1976     int rcellspacing;
   1977     int mincell_pixels = round(mincell * pixel_per_char);
   1978     int ppc = round(pixel_per_char);
   1979 
   1980     t = newTable();
   1981     t->row = t->col = -1;
   1982     t->maxcol = -1;
   1983     t->maxrow = -1;
   1984     t->border_mode = border;
   1985     t->flag = 0;
   1986     if (border == BORDER_NOWIN)
   1987 	t->flag |= TBL_EXPAND_OK;
   1988 
   1989     rcellspacing = spacing + 2 * padding;
   1990     switch (border) {
   1991     case BORDER_THIN:
   1992     case BORDER_THICK:
   1993     case BORDER_NOWIN:
   1994 	t->cellpadding = padding - (mincell_pixels - 4) / 2;
   1995 	break;
   1996     case BORDER_NONE:
   1997 	t->cellpadding = rcellspacing - mincell_pixels;
   1998     }
   1999     if (t->cellpadding >= ppc)
   2000 	t->cellpadding /= ppc;
   2001     else if (t->cellpadding > 0)
   2002 	t->cellpadding = 1;
   2003     else
   2004 	t->cellpadding = 0;
   2005 
   2006     switch (border) {
   2007     case BORDER_THIN:
   2008     case BORDER_THICK:
   2009     case BORDER_NOWIN:
   2010 	t->cellspacing = 2 * t->cellpadding + mincell;
   2011 	break;
   2012     case BORDER_NONE:
   2013 	t->cellspacing = t->cellpadding + mincell;
   2014     }
   2015 
   2016     if (border == BORDER_NONE) {
   2017 	if (rcellspacing / 2 + vspace <= 1)
   2018 	    t->vspace = 0;
   2019 	else
   2020 	    t->vspace = 1;
   2021     }
   2022     else {
   2023 	if (vspace < ppc)
   2024 	    t->vspace = 0;
   2025 	else
   2026 	    t->vspace = 1;
   2027     }
   2028 
   2029     if (border == BORDER_NONE) {
   2030 	if (rcellspacing <= THR_PADDING)
   2031 	    t->vcellpadding = 0;
   2032 	else
   2033 	    t->vcellpadding = 1;
   2034     }
   2035     else {
   2036 	if (padding < 2 * ppc - 2)
   2037 	    t->vcellpadding = 0;
   2038 	else
   2039 	    t->vcellpadding = 1;
   2040     }
   2041 
   2042     return t;
   2043 }
   2044 
   2045 void
   2046 end_table(struct table *tbl)
   2047 {
   2048     struct table_cell *cell = &tbl->cell;
   2049     int i, rulewidth = table_rule_width(tbl);
   2050     if (rulewidth > 1) {
   2051 	if (tbl->total_width > 0)
   2052 	    tbl->total_width = ceil_at_intervals(tbl->total_width, rulewidth);
   2053 	for (i = 0; i <= tbl->maxcol; i++) {
   2054 	    tbl->minimum_width[i] =
   2055 		ceil_at_intervals(tbl->minimum_width[i], rulewidth);
   2056 	    tbl->tabwidth[i] = ceil_at_intervals(tbl->tabwidth[i], rulewidth);
   2057 	    if (tbl->fixed_width[i] > 0)
   2058 		tbl->fixed_width[i] =
   2059 		    ceil_at_intervals(tbl->fixed_width[i], rulewidth);
   2060 	}
   2061 	for (i = 0; i <= cell->maxcell; i++) {
   2062 	    cell->minimum_width[i] =
   2063 		ceil_at_intervals(cell->minimum_width[i], rulewidth);
   2064 	    cell->width[i] = ceil_at_intervals(cell->width[i], rulewidth);
   2065 	    if (cell->fixed_width[i] > 0)
   2066 		cell->fixed_width[i] =
   2067 		    ceil_at_intervals(cell->fixed_width[i], rulewidth);
   2068 	}
   2069     }
   2070     tbl->sloppy_width = fixed_table_width(tbl);
   2071     if (tbl->total_width > tbl->sloppy_width)
   2072 	tbl->sloppy_width = tbl->total_width;
   2073 }
   2074 
   2075 static void
   2076 check_minimum0(struct table *t, int min)
   2077 {
   2078     int i, w, ww;
   2079     struct table_cell *cell;
   2080 
   2081     if (t->col < 0)
   2082 	return;
   2083     if (t->tabwidth[t->col] < 0)
   2084 	return;
   2085     check_row(t, t->row);
   2086     w = table_colspan(t, t->row, t->col);
   2087     min += t->indent;
   2088     if (w == 1)
   2089 	ww = min;
   2090     else {
   2091 	cell = &t->cell;
   2092 	ww = 0;
   2093 	if (cell->icell >= 0 && cell->minimum_width[cell->icell] < min)
   2094 	    cell->minimum_width[cell->icell] = min;
   2095     }
   2096     for (i = t->col;
   2097 	 i <= t->maxcol && (i == t->col || (t->tabattr[t->row][i] & HTT_X));
   2098 	 i++) {
   2099 	if (t->minimum_width[i] < ww)
   2100 	    t->minimum_width[i] = ww;
   2101     }
   2102 }
   2103 
   2104 static int
   2105 setwidth0(struct table *t, struct table_mode *mode)
   2106 {
   2107     int w;
   2108     int width = t->tabcontentssize;
   2109     struct table_cell *cell = &t->cell;
   2110 
   2111     if (t->col < 0)
   2112 	return -1;
   2113     if (t->tabwidth[t->col] < 0)
   2114 	return -1;
   2115     check_row(t, t->row);
   2116     if (t->linfo.prev_spaces > 0)
   2117 	width -= t->linfo.prev_spaces;
   2118     w = table_colspan(t, t->row, t->col);
   2119     if (w == 1) {
   2120 	if (t->tabwidth[t->col] < width)
   2121 	    t->tabwidth[t->col] = width;
   2122     }
   2123     else if (cell->icell >= 0) {
   2124 	if (cell->width[cell->icell] < width)
   2125 	    cell->width[cell->icell] = width;
   2126     }
   2127     return width;
   2128 }
   2129 
   2130 static void
   2131 setwidth(struct table *t, struct table_mode *mode)
   2132 {
   2133     int width = setwidth0(t, mode);
   2134     if (width < 0)
   2135 	return;
   2136 #ifdef NOWRAP
   2137     if (t->tabattr[t->row][t->col] & HTT_NOWRAP)
   2138 	check_minimum0(t, width);
   2139 #endif				/* NOWRAP */
   2140     if (mode->pre_mode & (TBLM_NOBR | TBLM_PRE | TBLM_PRE_INT) &&
   2141 	mode->nobr_offset >= 0)
   2142 	check_minimum0(t, width - mode->nobr_offset);
   2143 }
   2144 
   2145 static void
   2146 addcontentssize(struct table *t, int width)
   2147 {
   2148 
   2149     if (t->col < 0)
   2150 	return;
   2151     if (t->tabwidth[t->col] < 0)
   2152 	return;
   2153     check_row(t, t->row);
   2154     t->tabcontentssize += width;
   2155 }
   2156 
   2157 static void table_close_anchor0(struct table *tbl, struct table_mode *mode);
   2158 
   2159 static void
   2160 clearcontentssize(struct table *t, struct table_mode *mode)
   2161 {
   2162     table_close_anchor0(t, mode);
   2163     mode->nobr_offset = 0;
   2164     t->linfo.prev_spaces = -1;
   2165     set_space_to_prevchar(t->linfo.prevchar);
   2166     t->linfo.prev_ctype = PC_ASCII;
   2167     t->linfo.length = 0;
   2168     t->tabcontentssize = 0;
   2169 }
   2170 
   2171 static void
   2172 begin_cell(struct table *t, struct table_mode *mode)
   2173 {
   2174     clearcontentssize(t, mode);
   2175     mode->indent_level = 0;
   2176     mode->nobr_level = 0;
   2177     mode->pre_mode = 0;
   2178     t->indent = 0;
   2179     t->flag |= TBL_IN_COL;
   2180 
   2181     if (t->suspended_data) {
   2182 	check_row(t, t->row);
   2183 	if (t->tabdata[t->row][t->col] == NULL)
   2184 	    t->tabdata[t->row][t->col] = newGeneralList();
   2185 	appendGeneralList(t->tabdata[t->row][t->col],
   2186 			  (GeneralList *)t->suspended_data);
   2187 	t->suspended_data = NULL;
   2188     }
   2189 }
   2190 
   2191 void
   2192 check_rowcol(struct table *tbl, struct table_mode *mode)
   2193 {
   2194     int row = tbl->row, col = tbl->col;
   2195 
   2196     if (!(tbl->flag & TBL_IN_ROW)) {
   2197 	tbl->flag |= TBL_IN_ROW;
   2198 	tbl->row++;
   2199 	if (tbl->row > tbl->maxrow)
   2200 	    tbl->maxrow = tbl->row;
   2201 	tbl->col = -1;
   2202     }
   2203     if (tbl->row == -1)
   2204 	tbl->row = 0;
   2205     if (tbl->col == -1)
   2206 	tbl->col = 0;
   2207 
   2208     for (;; tbl->row++) {
   2209 	check_row(tbl, tbl->row);
   2210 	for (; tbl->col < MAXCOL &&
   2211 	     tbl->tabattr[tbl->row][tbl->col] & (HTT_X | HTT_Y); tbl->col++) ;
   2212 	if (tbl->col < MAXCOL)
   2213 	    break;
   2214 	tbl->col = 0;
   2215     }
   2216     if (tbl->row > tbl->maxrow)
   2217 	tbl->maxrow = tbl->row;
   2218     if (tbl->col > tbl->maxcol)
   2219 	tbl->maxcol = tbl->col;
   2220 
   2221     if (tbl->row != row || tbl->col != col)
   2222 	begin_cell(tbl, mode);
   2223     tbl->flag |= TBL_IN_COL;
   2224 }
   2225 
   2226 int
   2227 skip_space(struct table *t, char *line, struct table_linfo *linfo,
   2228 	   int checkminimum)
   2229 {
   2230     int skip = 0, s = linfo->prev_spaces;
   2231     Lineprop ctype, prev_ctype = linfo->prev_ctype;
   2232     Str prevchar = linfo->prevchar;
   2233     int w = linfo->length;
   2234     int min = 1;
   2235 
   2236     if (*line == '<' && line[strlen(line) - 1] == '>') {
   2237 	if (checkminimum)
   2238 	    check_minimum0(t, visible_length(line));
   2239 	return 0;
   2240     }
   2241 
   2242     while (*line) {
   2243 	char *save = line, *c = line;
   2244 	int ec, len, wlen, plen;
   2245 	ctype = get_mctype(line);
   2246 	len = get_mcwidth(line);
   2247 	wlen = plen = get_mclen(line);
   2248 
   2249 	if (min < w)
   2250 	    min = w;
   2251 	if (ctype == PC_ASCII && IS_SPACE(*c)) {
   2252 	    w = 0;
   2253 	    s++;
   2254 	}
   2255 	else {
   2256 	    if (*c == '&') {
   2257 		ec = getescapechar(&line);
   2258 		if (ec >= 0) {
   2259 		    c = conv_entity(ec);
   2260 		    ctype = get_mctype(c);
   2261 		    len = get_strwidth(c);
   2262 		    wlen = line - save;
   2263 		    plen = get_mclen(c);
   2264 		}
   2265 	    }
   2266 	    if (prevchar->length && is_boundary((unsigned char *)prevchar->ptr,
   2267 						(unsigned char *)c)) {
   2268 		w = len;
   2269 	    }
   2270 	    else {
   2271 		w += len;
   2272 	    }
   2273 	    if (s > 0) {
   2274 #ifdef USE_M17N
   2275 		if (ctype == PC_KANJI1 && prev_ctype == PC_KANJI1)
   2276 		    skip += s;
   2277 		else
   2278 #endif
   2279 		    skip += s - 1;
   2280 	    }
   2281 	    s = 0;
   2282 	    prev_ctype = ctype;
   2283 	}
   2284 	set_prevchar(prevchar, c, plen);
   2285 	line = save + wlen;
   2286     }
   2287     if (s > 1) {
   2288 	skip += s - 1;
   2289 	linfo->prev_spaces = 1;
   2290     }
   2291     else {
   2292 	linfo->prev_spaces = s;
   2293     }
   2294     linfo->prev_ctype = prev_ctype;
   2295     linfo->prevchar = prevchar;
   2296 
   2297     if (checkminimum) {
   2298 	if (min < w)
   2299 	    min = w;
   2300 	linfo->length = w;
   2301 	check_minimum0(t, min);
   2302     }
   2303     return skip;
   2304 }
   2305 
   2306 static void
   2307 feed_table_inline_tag(struct table *tbl,
   2308 		      char *line, struct table_mode *mode, int width)
   2309 {
   2310     check_rowcol(tbl, mode);
   2311     pushdata(tbl, tbl->row, tbl->col, line);
   2312     if (width >= 0) {
   2313 	check_minimum0(tbl, width);
   2314 	addcontentssize(tbl, width);
   2315 	setwidth(tbl, mode);
   2316     }
   2317 }
   2318 
   2319 static void
   2320 feed_table_block_tag(struct table *tbl,
   2321 		     char *line, struct table_mode *mode, int indent, int cmd)
   2322 {
   2323     int offset;
   2324     if (mode->indent_level <= 0 && indent == -1)
   2325 	return;
   2326     setwidth(tbl, mode);
   2327     feed_table_inline_tag(tbl, line, mode, -1);
   2328     clearcontentssize(tbl, mode);
   2329     if (indent == 1) {
   2330 	mode->indent_level++;
   2331 	if (mode->indent_level <= MAX_INDENT_LEVEL)
   2332 	    tbl->indent += INDENT_INCR;
   2333     }
   2334     else if (indent == -1) {
   2335 	mode->indent_level--;
   2336 	if (mode->indent_level < MAX_INDENT_LEVEL)
   2337 	    tbl->indent -= INDENT_INCR;
   2338     }
   2339     offset = tbl->indent;
   2340     if (cmd == HTML_DT) {
   2341 	if (mode->indent_level > 0 && mode->indent_level <= MAX_INDENT_LEVEL)
   2342 	    offset -= INDENT_INCR;
   2343     }
   2344     if (tbl->indent > 0) {
   2345 	check_minimum0(tbl, 0);
   2346 	addcontentssize(tbl, offset);
   2347     }
   2348 }
   2349 
   2350 static void
   2351 table_close_select(struct table *tbl, struct table_mode *mode, int width)
   2352 {
   2353     Str tmp = process_n_select();
   2354     mode->pre_mode &= ~TBLM_INSELECT;
   2355     mode->end_tag = 0;
   2356     feed_table1(tbl, tmp, mode, width);
   2357 }
   2358 
   2359 static void
   2360 table_close_textarea(struct table *tbl, struct table_mode *mode, int width)
   2361 {
   2362     Str tmp = process_n_textarea();
   2363     mode->pre_mode &= ~TBLM_INTXTA;
   2364     mode->end_tag = 0;
   2365     feed_table1(tbl, tmp, mode, width);
   2366 }
   2367 
   2368 static void
   2369 table_close_anchor0(struct table *tbl, struct table_mode *mode)
   2370 {
   2371     if (!(mode->pre_mode & TBLM_ANCHOR))
   2372 	return;
   2373     mode->pre_mode &= ~TBLM_ANCHOR;
   2374     if (tbl->tabcontentssize == mode->anchor_offset) {
   2375 	check_minimum0(tbl, 1);
   2376 	addcontentssize(tbl, 1);
   2377 	setwidth(tbl, mode);
   2378     }
   2379     else if (tbl->linfo.prev_spaces > 0 &&
   2380 	     tbl->tabcontentssize - 1 == mode->anchor_offset) {
   2381 	if (tbl->linfo.prev_spaces > 0)
   2382 	    tbl->linfo.prev_spaces = -1;
   2383     }
   2384 }
   2385 
   2386 #define TAG_ACTION_NONE 0
   2387 #define TAG_ACTION_FEED 1
   2388 #define TAG_ACTION_TABLE 2
   2389 #define TAG_ACTION_N_TABLE 3
   2390 #define TAG_ACTION_PLAIN 4
   2391 
   2392 #define CASE_TABLE_TAG \
   2393 	case HTML_TABLE:\
   2394 	case HTML_N_TABLE:\
   2395 	case HTML_TR:\
   2396 	case HTML_N_TR:\
   2397 	case HTML_TD:\
   2398 	case HTML_N_TD:\
   2399 	case HTML_TH:\
   2400 	case HTML_N_TH:\
   2401 	case HTML_THEAD:\
   2402 	case HTML_N_THEAD:\
   2403 	case HTML_TBODY:\
   2404 	case HTML_N_TBODY:\
   2405 	case HTML_TFOOT:\
   2406 	case HTML_N_TFOOT:\
   2407 	case HTML_COLGROUP:\
   2408 	case HTML_N_COLGROUP:\
   2409 	case HTML_COL
   2410 
   2411 #define ATTR_ROWSPAN_MAX 32766
   2412 
   2413 static int
   2414 feed_table_tag(struct table *tbl, char *line, struct table_mode *mode,
   2415 	       int width, struct parsed_tag *tag)
   2416 {
   2417     int cmd;
   2418 #ifdef ID_EXT
   2419     char *p;
   2420 #endif
   2421     struct table_cell *cell = &tbl->cell;
   2422     int colspan, rowspan;
   2423     int col, prev_col;
   2424     int i, j, k, v, v0, w, id;
   2425     Str tok, tmp, anchor;
   2426     table_attr align, valign;
   2427 
   2428     cmd = tag->tagid;
   2429 
   2430     if (mode->pre_mode & TBLM_PLAIN) {
   2431 	if (mode->end_tag == cmd) {
   2432 	    mode->pre_mode &= ~TBLM_PLAIN;
   2433 	    mode->end_tag = 0;
   2434 	    feed_table_block_tag(tbl, line, mode, 0, cmd);
   2435 	    return TAG_ACTION_NONE;
   2436 	}
   2437 	return TAG_ACTION_PLAIN;
   2438     }
   2439     if (mode->pre_mode & TBLM_INTXTA) {
   2440 	switch (cmd) {
   2441 	CASE_TABLE_TAG:
   2442 	case HTML_N_TEXTAREA:
   2443 	    table_close_textarea(tbl, mode, width);
   2444 	    if (cmd == HTML_N_TEXTAREA)
   2445 		return TAG_ACTION_NONE;
   2446 	    break;
   2447 	default:
   2448 	    return TAG_ACTION_FEED;
   2449 	}
   2450     }
   2451     if (mode->pre_mode & TBLM_SCRIPT) {
   2452 	if (mode->end_tag == cmd) {
   2453 	    mode->pre_mode &= ~TBLM_SCRIPT;
   2454 	    mode->end_tag = 0;
   2455 	    return TAG_ACTION_NONE;
   2456 	}
   2457 	return TAG_ACTION_PLAIN;
   2458     }
   2459     if (mode->pre_mode & TBLM_STYLE) {
   2460 	if (mode->end_tag == cmd) {
   2461 	    mode->pre_mode &= ~TBLM_STYLE;
   2462 	    mode->end_tag = 0;
   2463 	    return TAG_ACTION_NONE;
   2464 	}
   2465 	return TAG_ACTION_PLAIN;
   2466     }
   2467     /* failsafe: a tag other than <option></option>and </select> in *
   2468      * <select> environment is regarded as the end of <select>. */
   2469     if (mode->pre_mode & TBLM_INSELECT) {
   2470 	switch (cmd) {
   2471 	  CASE_TABLE_TAG:
   2472 	case HTML_N_FORM:
   2473 	case HTML_N_SELECT:	/* mode->end_tag */
   2474 	    table_close_select(tbl, mode, width);
   2475 	    if (cmd == HTML_N_SELECT)
   2476 		return TAG_ACTION_NONE;
   2477 	    break;
   2478 	default:
   2479 	    return TAG_ACTION_FEED;
   2480 	}
   2481     }
   2482     if (mode->caption) {
   2483 	switch (cmd) {
   2484 	  CASE_TABLE_TAG:
   2485 	case HTML_N_CAPTION:
   2486 	    mode->caption = 0;
   2487 	    if (cmd == HTML_N_CAPTION)
   2488 		return TAG_ACTION_NONE;
   2489 	    break;
   2490 	default:
   2491 	    return TAG_ACTION_FEED;
   2492 	}
   2493     }
   2494 
   2495     if (mode->pre_mode & TBLM_PRE) {
   2496 	switch (cmd) {
   2497 	case HTML_NOBR:
   2498 	case HTML_N_NOBR:
   2499 	case HTML_PRE_INT:
   2500 	case HTML_N_PRE_INT:
   2501 	    return TAG_ACTION_NONE;
   2502 	}
   2503     }
   2504 
   2505     switch (cmd) {
   2506     case HTML_TABLE:
   2507 	check_rowcol(tbl, mode);
   2508 	return TAG_ACTION_TABLE;
   2509     case HTML_N_TABLE:
   2510 	if (tbl->suspended_data)
   2511 	    check_rowcol(tbl, mode);
   2512 	return TAG_ACTION_N_TABLE;
   2513     case HTML_TR:
   2514 	if (tbl->col >= 0 && tbl->tabcontentssize > 0)
   2515 	    setwidth(tbl, mode);
   2516 	tbl->col = -1;
   2517 	tbl->row++;
   2518 	tbl->flag |= TBL_IN_ROW;
   2519 	tbl->flag &= ~TBL_IN_COL;
   2520 	align = 0;
   2521 	valign = 0;
   2522 	if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) {
   2523 	    switch (i) {
   2524 	    case ALIGN_LEFT:
   2525 		align = (HTT_LEFT | HTT_TRSET);
   2526 		break;
   2527 	    case ALIGN_RIGHT:
   2528 		align = (HTT_RIGHT | HTT_TRSET);
   2529 		break;
   2530 	    case ALIGN_CENTER:
   2531 		align = (HTT_CENTER | HTT_TRSET);
   2532 		break;
   2533 	    }
   2534 	}
   2535 	if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) {
   2536 	    switch (i) {
   2537 	    case VALIGN_TOP:
   2538 		valign = (HTT_TOP | HTT_VTRSET);
   2539 		break;
   2540 	    case VALIGN_MIDDLE:
   2541 		valign = (HTT_MIDDLE | HTT_VTRSET);
   2542 		break;
   2543 	    case VALIGN_BOTTOM:
   2544 		valign = (HTT_BOTTOM | HTT_VTRSET);
   2545 		break;
   2546 	    }
   2547 	}
   2548 #ifdef ID_EXT
   2549 	if (parsedtag_get_value(tag, ATTR_ID, &p))
   2550 	    tbl->tridvalue[tbl->row] = Strnew_charp(p);
   2551 #endif				/* ID_EXT */
   2552 	tbl->trattr = align | valign;
   2553 	break;
   2554     case HTML_TH:
   2555     case HTML_TD:
   2556 	prev_col = tbl->col;
   2557 	if (tbl->col >= 0 && tbl->tabcontentssize > 0)
   2558 	    setwidth(tbl, mode);
   2559 	if (tbl->row == -1) {
   2560 	    /* for broken HTML... */
   2561 	    tbl->row = -1;
   2562 	    tbl->col = -1;
   2563 	    tbl->maxrow = tbl->row;
   2564 	}
   2565 	if (tbl->col == -1) {
   2566 	    if (!(tbl->flag & TBL_IN_ROW)) {
   2567 		tbl->row++;
   2568 		tbl->flag |= TBL_IN_ROW;
   2569 	    }
   2570 	    if (tbl->row > tbl->maxrow)
   2571 		tbl->maxrow = tbl->row;
   2572 	}
   2573 	tbl->col++;
   2574 	check_row(tbl, tbl->row);
   2575 	while (tbl->tabattr[tbl->row][tbl->col]) {
   2576 	    tbl->col++;
   2577 	}
   2578 	if (tbl->col > MAXCOL - 1) {
   2579 	    tbl->col = prev_col;
   2580 	    return TAG_ACTION_NONE;
   2581 	}
   2582 	if (tbl->col > tbl->maxcol) {
   2583 	    tbl->maxcol = tbl->col;
   2584 	}
   2585 	colspan = rowspan = 1;
   2586 	if (tbl->trattr & HTT_TRSET)
   2587 	    align = (tbl->trattr & HTT_ALIGN);
   2588 	else if (cmd == HTML_TH)
   2589 	    align = HTT_CENTER;
   2590 	else
   2591 	    align = HTT_LEFT;
   2592 	if (tbl->trattr & HTT_VTRSET)
   2593 	    valign = (tbl->trattr & HTT_VALIGN);
   2594 	else
   2595 	    valign = HTT_MIDDLE;
   2596 	if (parsedtag_get_value(tag, ATTR_ROWSPAN, &rowspan)) {
   2597 	    if(rowspan > ATTR_ROWSPAN_MAX) {
   2598 		rowspan = ATTR_ROWSPAN_MAX;
   2599 	    }
   2600 	    if ((tbl->row + rowspan) >= tbl->max_rowsize)
   2601 		check_row(tbl, tbl->row + rowspan);
   2602 	}
   2603 	if (parsedtag_get_value(tag, ATTR_COLSPAN, &colspan)) {
   2604 	    if ((tbl->col + colspan) >= MAXCOL) {
   2605 		/* Can't expand column */
   2606 		colspan = MAXCOL - tbl->col;
   2607 	    }
   2608 	}
   2609 	if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) {
   2610 	    switch (i) {
   2611 	    case ALIGN_LEFT:
   2612 		align = HTT_LEFT;
   2613 		break;
   2614 	    case ALIGN_RIGHT:
   2615 		align = HTT_RIGHT;
   2616 		break;
   2617 	    case ALIGN_CENTER:
   2618 		align = HTT_CENTER;
   2619 		break;
   2620 	    }
   2621 	}
   2622 	if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) {
   2623 	    switch (i) {
   2624 	    case VALIGN_TOP:
   2625 		valign = HTT_TOP;
   2626 		break;
   2627 	    case VALIGN_MIDDLE:
   2628 		valign = HTT_MIDDLE;
   2629 		break;
   2630 	    case VALIGN_BOTTOM:
   2631 		valign = HTT_BOTTOM;
   2632 		break;
   2633 	    }
   2634 	}
   2635 #ifdef NOWRAP
   2636 	if (parsedtag_exists(tag, ATTR_NOWRAP))
   2637 	    tbl->tabattr[tbl->row][tbl->col] |= HTT_NOWRAP;
   2638 #endif				/* NOWRAP */
   2639 	v = 0;
   2640 	if (parsedtag_get_value(tag, ATTR_WIDTH, &v)) {
   2641 #ifdef TABLE_EXPAND
   2642 	    if (v > 0) {
   2643 		if (tbl->real_width > 0)
   2644 		    v = -(v * 100) / (tbl->real_width * pixel_per_char);
   2645 		else
   2646 		    v = (int)(v / pixel_per_char);
   2647 	    }
   2648 #else
   2649 	    v = RELATIVE_WIDTH(v);
   2650 #endif				/* not TABLE_EXPAND */
   2651 	}
   2652 #ifdef ID_EXT
   2653 	if (parsedtag_get_value(tag, ATTR_ID, &p))
   2654 	    tbl->tabidvalue[tbl->row][tbl->col] = Strnew_charp(p);
   2655 #endif				/* ID_EXT */
   2656 #ifdef NOWRAP
   2657 	if (v != 0) {
   2658 	    /* NOWRAP and WIDTH= conflicts each other */
   2659 	    tbl->tabattr[tbl->row][tbl->col] &= ~HTT_NOWRAP;
   2660 	}
   2661 #endif				/* NOWRAP */
   2662 	tbl->tabattr[tbl->row][tbl->col] &= ~(HTT_ALIGN | HTT_VALIGN);
   2663 	tbl->tabattr[tbl->row][tbl->col] |= (align | valign);
   2664 	if (colspan > 1) {
   2665 	    col = tbl->col;
   2666 
   2667 	    cell->icell = cell->maxcell + 1;
   2668 	    k = bsearch_2short(colspan, cell->colspan, col, cell->col, MAXCOL,
   2669 			       cell->index, cell->icell);
   2670 	    if (k <= cell->maxcell) {
   2671 		i = cell->index[k];
   2672 		if (cell->col[i] == col && cell->colspan[i] == colspan)
   2673 		    cell->icell = i;
   2674 	    }
   2675 	    if (cell->icell > cell->maxcell && cell->icell < MAXCELL) {
   2676 		cell->maxcell++;
   2677 		cell->col[cell->maxcell] = col;
   2678 		cell->colspan[cell->maxcell] = colspan;
   2679 		cell->width[cell->maxcell] = 0;
   2680 		cell->minimum_width[cell->maxcell] = 0;
   2681 		cell->fixed_width[cell->maxcell] = 0;
   2682 		if (cell->maxcell > k) {
   2683 		    int ii;
   2684 		    for (ii = cell->maxcell; ii > k; ii--)
   2685 			cell->index[ii] = cell->index[ii - 1];
   2686 		}
   2687 		cell->index[k] = cell->maxcell;
   2688 	    }
   2689 	    if (cell->icell > cell->maxcell)
   2690 		cell->icell = -1;
   2691 	}
   2692 	if (v != 0) {
   2693 	    if (colspan == 1) {
   2694 		v0 = tbl->fixed_width[tbl->col];
   2695 		if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0)) {
   2696 #ifdef FEED_TABLE_DEBUG
   2697 		    fprintf(stderr, "width(%d) = %d\n", tbl->col, v);
   2698 #endif				/* TABLE_DEBUG */
   2699 		    tbl->fixed_width[tbl->col] = v;
   2700 		}
   2701 	    }
   2702 	    else if (cell->icell >= 0) {
   2703 		v0 = cell->fixed_width[cell->icell];
   2704 		if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0))
   2705 		    cell->fixed_width[cell->icell] = v;
   2706 	    }
   2707 	}
   2708 	for (i = 0; i < rowspan; i++) {
   2709 	    check_row(tbl, tbl->row + i);
   2710 	    for (j = 0; j < colspan; j++) {
   2711 #if 0
   2712 		tbl->tabattr[tbl->row + i][tbl->col + j] &= ~(HTT_X | HTT_Y);
   2713 #endif
   2714 		if (!(tbl->tabattr[tbl->row + i][tbl->col + j] &
   2715 		      (HTT_X | HTT_Y))) {
   2716 		    tbl->tabattr[tbl->row + i][tbl->col + j] |=
   2717 			((i > 0) ? HTT_Y : 0) | ((j > 0) ? HTT_X : 0);
   2718 		}
   2719 		if (tbl->col + j > tbl->maxcol) {
   2720 		    tbl->maxcol = tbl->col + j;
   2721 		}
   2722 	    }
   2723 	    if (tbl->row + i > tbl->maxrow) {
   2724 		tbl->maxrow = tbl->row + i;
   2725 	    }
   2726 	}
   2727 	begin_cell(tbl, mode);
   2728 	break;
   2729     case HTML_N_TR:
   2730 	setwidth(tbl, mode);
   2731 	tbl->col = -1;
   2732 	tbl->flag &= ~(TBL_IN_ROW | TBL_IN_COL);
   2733 	return TAG_ACTION_NONE;
   2734     case HTML_N_TH:
   2735     case HTML_N_TD:
   2736 	setwidth(tbl, mode);
   2737 	tbl->flag &= ~TBL_IN_COL;
   2738 #ifdef FEED_TABLE_DEBUG
   2739 	{
   2740 	    TextListItem *it;
   2741 	    int i = tbl->col, j = tbl->row;
   2742 	    fprintf(stderr, "(a) row,col: %d, %d\n", j, i);
   2743 	    if (tbl->tabdata[j] && tbl->tabdata[j][i]) {
   2744 		for (it = ((TextList *)tbl->tabdata[j][i])->first;
   2745 		     it; it = it->next)
   2746 		    fprintf(stderr, "  [%s] \n", it->ptr);
   2747 	    }
   2748 	}
   2749 #endif
   2750 	return TAG_ACTION_NONE;
   2751     case HTML_P:
   2752     case HTML_BR:
   2753     case HTML_CENTER:
   2754     case HTML_N_CENTER:
   2755     case HTML_DIV:
   2756     case HTML_N_DIV:
   2757 	if (!(tbl->flag & TBL_IN_ROW))
   2758 	    break;
   2759     case HTML_DT:
   2760     case HTML_DD:
   2761     case HTML_H:
   2762     case HTML_N_H:
   2763     case HTML_LI:
   2764     case HTML_PRE:
   2765     case HTML_N_PRE:
   2766     case HTML_HR:
   2767     case HTML_LISTING:
   2768     case HTML_XMP:
   2769     case HTML_PLAINTEXT:
   2770     case HTML_PRE_PLAIN:
   2771     case HTML_N_PRE_PLAIN:
   2772 	feed_table_block_tag(tbl, line, mode, 0, cmd);
   2773 	switch (cmd) {
   2774 	case HTML_PRE:
   2775 	case HTML_PRE_PLAIN:
   2776 	    mode->pre_mode |= TBLM_PRE;
   2777 	    break;
   2778 	case HTML_N_PRE:
   2779 	case HTML_N_PRE_PLAIN:
   2780 	    mode->pre_mode &= ~TBLM_PRE;
   2781 	    break;
   2782 	case HTML_LISTING:
   2783 	    mode->pre_mode |= TBLM_PLAIN;
   2784 	    mode->end_tag = HTML_N_LISTING;
   2785 	    break;
   2786 	case HTML_XMP:
   2787 	    mode->pre_mode |= TBLM_PLAIN;
   2788 	    mode->end_tag = HTML_N_XMP;
   2789 	    break;
   2790 	case HTML_PLAINTEXT:
   2791 	    mode->pre_mode |= TBLM_PLAIN;
   2792 	    mode->end_tag = MAX_HTMLTAG;
   2793 	    break;
   2794 	}
   2795 	break;
   2796     case HTML_DL:
   2797     case HTML_BLQ:
   2798     case HTML_OL:
   2799     case HTML_UL:
   2800 	feed_table_block_tag(tbl, line, mode, 1, cmd);
   2801 	break;
   2802     case HTML_N_DL:
   2803     case HTML_N_BLQ:
   2804     case HTML_N_OL:
   2805     case HTML_N_UL:
   2806 	feed_table_block_tag(tbl, line, mode, -1, cmd);
   2807 	break;
   2808     case HTML_NOBR:
   2809     case HTML_WBR:
   2810 	if (!(tbl->flag & TBL_IN_ROW))
   2811 	    break;
   2812     case HTML_PRE_INT:
   2813 	feed_table_inline_tag(tbl, line, mode, -1);
   2814 	switch (cmd) {
   2815 	case HTML_NOBR:
   2816 	    mode->nobr_level++;
   2817 	    if (mode->pre_mode & TBLM_NOBR)
   2818 		return TAG_ACTION_NONE;
   2819 	    mode->pre_mode |= TBLM_NOBR;
   2820 	    break;
   2821 	case HTML_PRE_INT:
   2822 	    if (mode->pre_mode & TBLM_PRE_INT)
   2823 		return TAG_ACTION_NONE;
   2824 	    mode->pre_mode |= TBLM_PRE_INT;
   2825 	    tbl->linfo.prev_spaces = 0;
   2826 	    break;
   2827 	}
   2828 	mode->nobr_offset = -1;
   2829 	if (tbl->linfo.length > 0) {
   2830 	    check_minimum0(tbl, tbl->linfo.length);
   2831 	    tbl->linfo.length = 0;
   2832 	}
   2833 	break;
   2834     case HTML_N_NOBR:
   2835 	if (!(tbl->flag & TBL_IN_ROW))
   2836 	    break;
   2837 	feed_table_inline_tag(tbl, line, mode, -1);
   2838 	if (mode->nobr_level > 0)
   2839 	    mode->nobr_level--;
   2840 	if (mode->nobr_level == 0)
   2841 	    mode->pre_mode &= ~TBLM_NOBR;
   2842 	break;
   2843     case HTML_N_PRE_INT:
   2844 	feed_table_inline_tag(tbl, line, mode, -1);
   2845 	mode->pre_mode &= ~TBLM_PRE_INT;
   2846 	break;
   2847     case HTML_IMG:
   2848 	check_rowcol(tbl, mode);
   2849 	w = tbl->fixed_width[tbl->col];
   2850 	if (w < 0) {
   2851 	    if (tbl->total_width > 0)
   2852 		w = -tbl->total_width * w / 100;
   2853 	    else if (width > 0)
   2854 		w = -width * w / 100;
   2855 	    else
   2856 		w = 0;
   2857 	}
   2858 	else if (w == 0) {
   2859 	    if (tbl->total_width > 0)
   2860 		w = tbl->total_width;
   2861 	    else if (width > 0)
   2862 		w = width;
   2863 	}
   2864 	tok = process_img(tag, w);
   2865 	feed_table1(tbl, tok, mode, width);
   2866 	break;
   2867     case HTML_FORM:
   2868 	feed_table_block_tag(tbl, "", mode, 0, cmd);
   2869 	tmp = process_form(tag);
   2870 	if (tmp)
   2871 	    feed_table1(tbl, tmp, mode, width);
   2872 	break;
   2873     case HTML_N_FORM:
   2874 	feed_table_block_tag(tbl, "", mode, 0, cmd);
   2875 	process_n_form();
   2876 	break;
   2877     case HTML_INPUT:
   2878 	tmp = process_input(tag);
   2879 	feed_table1(tbl, tmp, mode, width);
   2880 	break;
   2881     case HTML_SELECT:
   2882 	tmp = process_select(tag);
   2883 	if (tmp)
   2884 	    feed_table1(tbl, tmp, mode, width);
   2885 	mode->pre_mode |= TBLM_INSELECT;
   2886 	mode->end_tag = HTML_N_SELECT;
   2887 	break;
   2888     case HTML_N_SELECT:
   2889     case HTML_OPTION:
   2890 	/* nothing */
   2891 	break;
   2892     case HTML_TEXTAREA:
   2893 	w = 0;
   2894 	check_rowcol(tbl, mode);
   2895 	if (tbl->col + 1 <= tbl->maxcol &&
   2896 	    tbl->tabattr[tbl->row][tbl->col + 1] & HTT_X) {
   2897 	    if (cell->icell >= 0 && cell->fixed_width[cell->icell] > 0)
   2898 		w = cell->fixed_width[cell->icell];
   2899 	}
   2900 	else {
   2901 	    if (tbl->fixed_width[tbl->col] > 0)
   2902 		w = tbl->fixed_width[tbl->col];
   2903 	}
   2904 	tmp = process_textarea(tag, w);
   2905 	if (tmp)
   2906 	    feed_table1(tbl, tmp, mode, width);
   2907 	mode->pre_mode |= TBLM_INTXTA;
   2908 	mode->end_tag = HTML_N_TEXTAREA;
   2909 	break;
   2910     case HTML_A:
   2911 	table_close_anchor0(tbl, mode);
   2912 	anchor = NULL;
   2913 	i = 0;
   2914 	parsedtag_get_value(tag, ATTR_HREF, &anchor);
   2915 	parsedtag_get_value(tag, ATTR_HSEQ, &i);
   2916 	if (anchor) {
   2917 	    check_rowcol(tbl, mode);
   2918 	    if (i == 0) {
   2919 		Str tmp = process_anchor(tag, line);
   2920     		if (displayLinkNumber)
   2921 		{
   2922 			Str t = getLinkNumberStr(-1);
   2923 			feed_table_inline_tag(tbl, NULL, mode, t->length);
   2924 			Strcat(tmp, t);
   2925 		}
   2926 		pushdata(tbl, tbl->row, tbl->col, tmp->ptr);
   2927 	    }
   2928 	    else
   2929 		pushdata(tbl, tbl->row, tbl->col, line);
   2930 	    if (i >= 0) {
   2931 		mode->pre_mode |= TBLM_ANCHOR;
   2932 		mode->anchor_offset = tbl->tabcontentssize;
   2933 	    }
   2934 	}
   2935 	else
   2936 	    suspend_or_pushdata(tbl, line);
   2937 	break;
   2938     case HTML_DEL:
   2939 	switch (displayInsDel) {
   2940 	case DISPLAY_INS_DEL_SIMPLE:
   2941 	    mode->pre_mode |= TBLM_DEL;
   2942 	    break;
   2943 	case DISPLAY_INS_DEL_NORMAL:
   2944 	    feed_table_inline_tag(tbl, line, mode, 5);	/* [DEL: */
   2945 	    break;
   2946 	case DISPLAY_INS_DEL_FONTIFY:
   2947 	    feed_table_inline_tag(tbl, line, mode, -1);
   2948 	    break;
   2949 	}
   2950 	break;
   2951     case HTML_N_DEL:
   2952 	switch (displayInsDel) {
   2953 	case DISPLAY_INS_DEL_SIMPLE:
   2954 	    mode->pre_mode &= ~TBLM_DEL;
   2955 	    break;
   2956 	case DISPLAY_INS_DEL_NORMAL:
   2957 	    feed_table_inline_tag(tbl, line, mode, 5);	/* :DEL] */
   2958 	    break;
   2959 	case DISPLAY_INS_DEL_FONTIFY:
   2960 	    feed_table_inline_tag(tbl, line, mode, -1);
   2961 	    break;
   2962 	}
   2963 	break;
   2964     case HTML_S:
   2965 	switch (displayInsDel) {
   2966 	case DISPLAY_INS_DEL_SIMPLE:
   2967 	    mode->pre_mode |= TBLM_S;
   2968 	    break;
   2969 	case DISPLAY_INS_DEL_NORMAL:
   2970 	    feed_table_inline_tag(tbl, line, mode, 3);	/* [S: */
   2971 	    break;
   2972 	case DISPLAY_INS_DEL_FONTIFY:
   2973 	    feed_table_inline_tag(tbl, line, mode, -1);
   2974 	    break;
   2975 	}
   2976 	break;
   2977     case HTML_N_S:
   2978 	switch (displayInsDel) {
   2979 	case DISPLAY_INS_DEL_SIMPLE:
   2980 	    mode->pre_mode &= ~TBLM_S;
   2981 	    break;
   2982 	case DISPLAY_INS_DEL_NORMAL:
   2983 	    feed_table_inline_tag(tbl, line, mode, 3);	/* :S] */
   2984 	    break;
   2985 	case DISPLAY_INS_DEL_FONTIFY:
   2986 	    feed_table_inline_tag(tbl, line, mode, -1);
   2987 	    break;
   2988 	}
   2989 	break;
   2990     case HTML_INS:
   2991     case HTML_N_INS:
   2992 	switch (displayInsDel) {
   2993 	case DISPLAY_INS_DEL_SIMPLE:
   2994 	    break;
   2995 	case DISPLAY_INS_DEL_NORMAL:
   2996 	    feed_table_inline_tag(tbl, line, mode, 5);	/* [INS:, :INS] */
   2997 	    break;
   2998 	case DISPLAY_INS_DEL_FONTIFY:
   2999 	    feed_table_inline_tag(tbl, line, mode, -1);
   3000 	    break;
   3001 	}
   3002 	break;
   3003     case HTML_SUP:
   3004     case HTML_SUB:
   3005     case HTML_N_SUB:
   3006 	if (!(mode->pre_mode & (TBLM_DEL | TBLM_S)))
   3007 	    feed_table_inline_tag(tbl, line, mode, 1);	/* ^, [, ] */
   3008 	break;
   3009     case HTML_N_SUP:
   3010 	break;
   3011     case HTML_TABLE_ALT:
   3012 	id = -1;
   3013 	w = 0;
   3014 	parsedtag_get_value(tag, ATTR_TID, &id);
   3015 	if (id >= 0 && id < tbl->ntable) {
   3016 	    struct table *tbl1 = tbl->tables[id].ptr;
   3017 	    feed_table_block_tag(tbl, line, mode, 0, cmd);
   3018 	    addcontentssize(tbl, maximum_table_width(tbl1));
   3019 	    check_minimum0(tbl, tbl1->sloppy_width);
   3020 #ifdef TABLE_EXPAND
   3021 	    w = tbl1->total_width;
   3022 	    v = 0;
   3023 	    colspan = table_colspan(tbl, tbl->row, tbl->col);
   3024 	    if (colspan > 1) {
   3025 		if (cell->icell >= 0)
   3026 		    v = cell->fixed_width[cell->icell];
   3027 	    }
   3028 	    else
   3029 		v = tbl->fixed_width[tbl->col];
   3030 	    if (v < 0 && tbl->real_width > 0 && tbl1->real_width > 0)
   3031 		w = -(tbl1->real_width * 100) / tbl->real_width;
   3032 	    else
   3033 		w = tbl1->real_width;
   3034 	    if (w > 0)
   3035 		check_minimum0(tbl, w);
   3036 	    else if (w < 0 && v < w) {
   3037 		if (colspan > 1) {
   3038 		    if (cell->icell >= 0)
   3039 			cell->fixed_width[cell->icell] = w;
   3040 		}
   3041 		else
   3042 		    tbl->fixed_width[tbl->col] = w;
   3043 	    }
   3044 #endif
   3045 	    setwidth0(tbl, mode);
   3046 	    clearcontentssize(tbl, mode);
   3047 	}
   3048 	break;
   3049     case HTML_CAPTION:
   3050 	mode->caption = 1;
   3051 	break;
   3052     case HTML_N_CAPTION:
   3053     case HTML_THEAD:
   3054     case HTML_N_THEAD:
   3055     case HTML_TBODY:
   3056     case HTML_N_TBODY:
   3057     case HTML_TFOOT:
   3058     case HTML_N_TFOOT:
   3059     case HTML_COLGROUP:
   3060     case HTML_N_COLGROUP:
   3061     case HTML_COL:
   3062 	break;
   3063     case HTML_SCRIPT:
   3064 	mode->pre_mode |= TBLM_SCRIPT;
   3065 	mode->end_tag = HTML_N_SCRIPT;
   3066 	break;
   3067     case HTML_STYLE:
   3068 	mode->pre_mode |= TBLM_STYLE;
   3069 	mode->end_tag = HTML_N_STYLE;
   3070 	break;
   3071     case HTML_N_A:
   3072 	table_close_anchor0(tbl, mode);
   3073     case HTML_FONT:
   3074     case HTML_N_FONT:
   3075     case HTML_NOP:
   3076 	suspend_or_pushdata(tbl, line);
   3077 	break;
   3078     case HTML_INTERNAL:
   3079     case HTML_N_INTERNAL:
   3080     case HTML_FORM_INT:
   3081     case HTML_N_FORM_INT:
   3082     case HTML_INPUT_ALT:
   3083     case HTML_N_INPUT_ALT:
   3084     case HTML_SELECT_INT:
   3085     case HTML_N_SELECT_INT:
   3086     case HTML_OPTION_INT:
   3087     case HTML_TEXTAREA_INT:
   3088     case HTML_N_TEXTAREA_INT:
   3089     case HTML_IMG_ALT:
   3090     case HTML_SYMBOL:
   3091     case HTML_N_SYMBOL:
   3092     default:
   3093 	/* unknown tag: put into table */
   3094 	return TAG_ACTION_FEED;
   3095     }
   3096     return TAG_ACTION_NONE;
   3097 }
   3098 
   3099 
   3100 int
   3101 feed_table(struct table *tbl, char *line, struct table_mode *mode,
   3102 	   int width, int internal)
   3103 {
   3104     int i;
   3105     char *p;
   3106     Str tmp;
   3107     struct table_linfo *linfo = &tbl->linfo;
   3108 
   3109     if (*line == '<' && line[1] && REALLY_THE_BEGINNING_OF_A_TAG(line)) {
   3110 	struct parsed_tag *tag;
   3111 	p = line;
   3112 	tag = parse_tag(&p, internal);
   3113 	if (tag) {
   3114 	    switch (feed_table_tag(tbl, line, mode, width, tag)) {
   3115 	    case TAG_ACTION_NONE:
   3116 		return -1;
   3117 	    case TAG_ACTION_N_TABLE:
   3118 		return 0;
   3119 	    case TAG_ACTION_TABLE:
   3120 		return 1;
   3121 	    case TAG_ACTION_PLAIN:
   3122 		break;
   3123 	    case TAG_ACTION_FEED:
   3124 	    default:
   3125 		if (parsedtag_need_reconstruct(tag))
   3126 		    line = parsedtag2str(tag)->ptr;
   3127 	    }
   3128 	}
   3129 	else {
   3130 	    if (!(mode->pre_mode & (TBLM_PLAIN | TBLM_INTXTA | TBLM_INSELECT |
   3131 				    TBLM_SCRIPT | TBLM_STYLE)))
   3132 		return -1;
   3133 	}
   3134     }
   3135     else {
   3136 	if (mode->pre_mode & (TBLM_DEL | TBLM_S))
   3137 	    return -1;
   3138     }
   3139     if (mode->caption) {
   3140 	Strcat_charp(tbl->caption, line);
   3141 	return -1;
   3142     }
   3143     if (mode->pre_mode & TBLM_SCRIPT)
   3144 	return -1;
   3145     if (mode->pre_mode & TBLM_STYLE)
   3146 	return -1;
   3147     if (mode->pre_mode & TBLM_INTXTA) {
   3148 	feed_textarea(line);
   3149 	return -1;
   3150     }
   3151     if (mode->pre_mode & TBLM_INSELECT) {
   3152 	feed_select(line);
   3153 	return -1;
   3154     }
   3155     if (!(mode->pre_mode & TBLM_PLAIN) &&
   3156 	!(*line == '<' && line[strlen(line) - 1] == '>') &&
   3157 	strchr(line, '&') != NULL) {
   3158 	tmp = Strnew();
   3159 	for (p = line; *p;) {
   3160 	    char *q, *r;
   3161 	    if (*p == '&') {
   3162 		if (!strncasecmp(p, "&amp;", 5) ||
   3163 		    !strncasecmp(p, "&gt;", 4) || !strncasecmp(p, "&lt;", 4)) {
   3164 		    /* do not convert */
   3165 		    Strcat_char(tmp, *p);
   3166 		    p++;
   3167 		}
   3168 		else {
   3169 		    int ec;
   3170 		    q = p;
   3171 		    switch (ec = getescapechar(&p)) {
   3172 		    case '<':
   3173 			Strcat_charp(tmp, "&lt;");
   3174 			break;
   3175 		    case '>':
   3176 			Strcat_charp(tmp, "&gt;");
   3177 			break;
   3178 		    case '&':
   3179 			Strcat_charp(tmp, "&amp;");
   3180 			break;
   3181 		    case '\r':
   3182 			Strcat_char(tmp, '\n');
   3183 			break;
   3184 		    default:
   3185 			r = conv_entity(ec);
   3186 			if (r != NULL && strlen(r) == 1 &&
   3187 			    ec == (unsigned char)*r) {
   3188 			    Strcat_char(tmp, *r);
   3189 			    break;
   3190 			}
   3191 		    case -1:
   3192 			Strcat_char(tmp, *q);
   3193 			p = q + 1;
   3194 			break;
   3195 		    }
   3196 		}
   3197 	    }
   3198 	    else {
   3199 		Strcat_char(tmp, *p);
   3200 		p++;
   3201 	    }
   3202 	}
   3203 	line = tmp->ptr;
   3204     }
   3205     if (!(mode->pre_mode & (TBLM_SPECIAL & ~TBLM_NOBR))) {
   3206 	if (!(tbl->flag & TBL_IN_COL) || linfo->prev_spaces != 0)
   3207 	    while (IS_SPACE(*line))
   3208 		line++;
   3209 	if (*line == '\0')
   3210 	    return -1;
   3211 	check_rowcol(tbl, mode);
   3212 	if (mode->pre_mode & TBLM_NOBR && mode->nobr_offset < 0)
   3213 	    mode->nobr_offset = tbl->tabcontentssize;
   3214 
   3215 	/* count of number of spaces skipped in normal mode */
   3216 	i = skip_space(tbl, line, linfo, !(mode->pre_mode & TBLM_NOBR));
   3217 	addcontentssize(tbl, visible_length(line) - i);
   3218 	setwidth(tbl, mode);
   3219 	pushdata(tbl, tbl->row, tbl->col, line);
   3220     }
   3221     else if (mode->pre_mode & TBLM_PRE_INT) {
   3222 	check_rowcol(tbl, mode);
   3223 	if (mode->nobr_offset < 0)
   3224 	    mode->nobr_offset = tbl->tabcontentssize;
   3225 	addcontentssize(tbl, maximum_visible_length(line, tbl->tabcontentssize));
   3226 	setwidth(tbl, mode);
   3227 	pushdata(tbl, tbl->row, tbl->col, line);
   3228     }
   3229     else {
   3230 	/* <pre> mode or something like it */
   3231 	check_rowcol(tbl, mode);
   3232 	while (*line) {
   3233 	    int nl = FALSE;
   3234 	    if ((p = strchr(line, '\r')) || (p = strchr(line, '\n'))) {
   3235 		if (*p == '\r' && p[1] == '\n')
   3236 		    p++;
   3237 		if (p[1]) {
   3238 		    p++;
   3239 		    tmp = Strnew_charp_n(line, p - line);
   3240 		    line = p;
   3241 		    p = tmp->ptr;
   3242 		}
   3243 		else {
   3244 		    p = line;
   3245 		    line = "";
   3246 		}
   3247 		nl = TRUE;
   3248 	    }
   3249 	    else {
   3250 		p = line;
   3251 		line = "";
   3252 	    }
   3253 	    if (mode->pre_mode & TBLM_PLAIN)
   3254 		i = maximum_visible_length_plain(p, tbl->tabcontentssize);
   3255 	    else
   3256 		i = maximum_visible_length(p, tbl->tabcontentssize);
   3257 	    addcontentssize(tbl, i);
   3258 	    setwidth(tbl, mode);
   3259 	    if (nl)
   3260 		clearcontentssize(tbl, mode);
   3261 	    pushdata(tbl, tbl->row, tbl->col, p);
   3262 	}
   3263     }
   3264     return -1;
   3265 }
   3266 
   3267 void
   3268 feed_table1(struct table *tbl, Str tok, struct table_mode *mode, int width)
   3269 {
   3270     Str tokbuf;
   3271     int status;
   3272     char *line;
   3273     if (!tok)
   3274 	return;
   3275     tokbuf = Strnew();
   3276     status = R_ST_NORMAL;
   3277     line = tok->ptr;
   3278     while (read_token
   3279 	   (tokbuf, &line, &status, mode->pre_mode & TBLM_PREMODE, 0))
   3280 	feed_table(tbl, tokbuf->ptr, mode, width, TRUE);
   3281 }
   3282 
   3283 void
   3284 pushTable(struct table *tbl, struct table *tbl1)
   3285 {
   3286     int col;
   3287     int row;
   3288 
   3289     col = tbl->col;
   3290     row = tbl->row;
   3291 
   3292     if (tbl->ntable >= tbl->tables_size) {
   3293 	struct table_in *tmp;
   3294 	tbl->tables_size += MAX_TABLE_N;
   3295 	tmp = New_N(struct table_in, tbl->tables_size);
   3296 	if (tbl->tables)
   3297 	    bcopy(tbl->tables, tmp, tbl->ntable * sizeof(struct table_in));
   3298 	tbl->tables = tmp;
   3299     }
   3300 
   3301     tbl->tables[tbl->ntable].ptr = tbl1;
   3302     tbl->tables[tbl->ntable].col = col;
   3303     tbl->tables[tbl->ntable].row = row;
   3304     tbl->tables[tbl->ntable].indent = tbl->indent;
   3305     tbl->tables[tbl->ntable].buf = newTextLineList();
   3306     check_row(tbl, row);
   3307     if (col + 1 <= tbl->maxcol && tbl->tabattr[row][col + 1] & HTT_X)
   3308 	tbl->tables[tbl->ntable].cell = tbl->cell.icell;
   3309     else
   3310 	tbl->tables[tbl->ntable].cell = -1;
   3311     tbl->ntable++;
   3312 }
   3313 
   3314 #ifdef MATRIX
   3315 int
   3316 correct_table_matrix(struct table *t, int col, int cspan, int a, double b)
   3317 {
   3318     int i, j;
   3319     int ecol = col + cspan;
   3320     double w = 1. / (b * b);
   3321 
   3322     for (i = col; i < ecol; i++) {
   3323 	v_add_val(t->vector, i, w * a);
   3324 	for (j = i; j < ecol; j++) {
   3325 	    m_add_val(t->matrix, i, j, w);
   3326 	    m_set_val(t->matrix, j, i, m_entry(t->matrix, i, j));
   3327 	}
   3328     }
   3329     return i;
   3330 }
   3331 
   3332 static void
   3333 correct_table_matrix2(struct table *t, int col, int cspan, double s, double b)
   3334 {
   3335     int i, j;
   3336     int ecol = col + cspan;
   3337     int size = t->maxcol + 1;
   3338     double w = 1. / (b * b);
   3339     double ss;
   3340 
   3341     for (i = 0; i < size; i++) {
   3342 	for (j = i; j < size; j++) {
   3343 	    if (i >= col && i < ecol && j >= col && j < ecol)
   3344 		ss = (1. - s) * (1. - s);
   3345 	    else if ((i >= col && i < ecol) || (j >= col && j < ecol))
   3346 		ss = -(1. - s) * s;
   3347 	    else
   3348 		ss = s * s;
   3349 	    m_add_val(t->matrix, i, j, w * ss);
   3350 	}
   3351     }
   3352 }
   3353 
   3354 static void
   3355 correct_table_matrix3(struct table *t, int col, char *flags, double s,
   3356 		      double b)
   3357 {
   3358     int i, j;
   3359     double ss;
   3360     int size = t->maxcol + 1;
   3361     double w = 1. / (b * b);
   3362     int flg = (flags[col] == 0);
   3363 
   3364     for (i = 0; i < size; i++) {
   3365 	if (!((flg && flags[i] == 0) || (!flg && flags[i] != 0)))
   3366 	    continue;
   3367 	for (j = i; j < size; j++) {
   3368 	    if (!((flg && flags[j] == 0) || (!flg && flags[j] != 0)))
   3369 		continue;
   3370 	    if (i == col && j == col)
   3371 		ss = (1. - s) * (1. - s);
   3372 	    else if (i == col || j == col)
   3373 		ss = -(1. - s) * s;
   3374 	    else
   3375 		ss = s * s;
   3376 	    m_add_val(t->matrix, i, j, w * ss);
   3377 	}
   3378     }
   3379 }
   3380 
   3381 static void
   3382 correct_table_matrix4(struct table *t, int col, int cspan, char *flags,
   3383 		      double s, double b)
   3384 {
   3385     int i, j;
   3386     double ss;
   3387     int ecol = col + cspan;
   3388     int size = t->maxcol + 1;
   3389     double w = 1. / (b * b);
   3390 
   3391     for (i = 0; i < size; i++) {
   3392 	if (flags[i] && !(i >= col && i < ecol))
   3393 	    continue;
   3394 	for (j = i; j < size; j++) {
   3395 	    if (flags[j] && !(j >= col && j < ecol))
   3396 		continue;
   3397 	    if (i >= col && i < ecol && j >= col && j < ecol)
   3398 		ss = (1. - s) * (1. - s);
   3399 	    else if ((i >= col && i < ecol) || (j >= col && j < ecol))
   3400 		ss = -(1. - s) * s;
   3401 	    else
   3402 		ss = s * s;
   3403 	    m_add_val(t->matrix, i, j, w * ss);
   3404 	}
   3405     }
   3406 }
   3407 
   3408 static void
   3409 set_table_matrix0(struct table *t, int maxwidth)
   3410 {
   3411     int size = t->maxcol + 1;
   3412     int i, j, k, bcol, ecol;
   3413     int width;
   3414     double w0, w1, w, s, b;
   3415 #ifdef __GNUC__
   3416     double we[size];
   3417     char expand[size];
   3418 #else				/* not __GNUC__ */
   3419     double we[MAXCOL];
   3420     char expand[MAXCOL];
   3421 #endif				/* not __GNUC__ */
   3422     struct table_cell *cell = &t->cell;
   3423 
   3424     w0 = 0.;
   3425     for (i = 0; i < size; i++) {
   3426 	we[i] = weight(t->tabwidth[i]);
   3427 	w0 += we[i];
   3428     }
   3429     if (w0 <= 0.)
   3430 	w0 = 1.;
   3431 
   3432     if (cell->necell == 0) {
   3433 	for (i = 0; i < size; i++) {
   3434 	    s = we[i] / w0;
   3435 	    b = sigma_td_nw((int)(s * maxwidth));
   3436 	    correct_table_matrix2(t, i, 1, s, b);
   3437 	}
   3438 	return;
   3439     }
   3440 
   3441     bzero(expand, size);
   3442 
   3443     for (k = 0; k < cell->necell; k++) {
   3444 	j = cell->eindex[k];
   3445 	bcol = cell->col[j];
   3446 	ecol = bcol + cell->colspan[j];
   3447 	width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
   3448 	w1 = 0.;
   3449 	for (i = bcol; i < ecol; i++) {
   3450 	    w1 += t->tabwidth[i] + 0.1;
   3451 	    expand[i]++;
   3452 	}
   3453 	for (i = bcol; i < ecol; i++) {
   3454 	    w = weight(width * (t->tabwidth[i] + 0.1) / w1);
   3455 	    if (w > we[i])
   3456 		we[i] = w;
   3457 	}
   3458     }
   3459 
   3460     w0 = 0.;
   3461     w1 = 0.;
   3462     for (i = 0; i < size; i++) {
   3463 	w0 += we[i];
   3464 	if (expand[i] == 0)
   3465 	    w1 += we[i];
   3466     }
   3467     if (w0 <= 0.)
   3468 	w0 = 1.;
   3469 
   3470     for (k = 0; k < cell->necell; k++) {
   3471 	j = cell->eindex[k];
   3472 	bcol = cell->col[j];
   3473 	width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
   3474 	w = weight(width);
   3475 	s = w / (w1 + w);
   3476 	b = sigma_td_nw((int)(s * maxwidth));
   3477 	correct_table_matrix4(t, bcol, cell->colspan[j], expand, s, b);
   3478     }
   3479 
   3480     for (i = 0; i < size; i++) {
   3481 	if (expand[i] == 0) {
   3482 	    s = we[i] / max(w1, 1.);
   3483 	    b = sigma_td_nw((int)(s * maxwidth));
   3484 	}
   3485 	else {
   3486 	    s = we[i] / max(w0 - w1, 1.);
   3487 	    b = sigma_td_nw(maxwidth);
   3488 	}
   3489 	correct_table_matrix3(t, i, expand, s, b);
   3490     }
   3491 }
   3492 
   3493 void
   3494 check_relative_width(struct table *t, int maxwidth)
   3495 {
   3496     int i;
   3497     double rel_total = 0;
   3498     int size = t->maxcol + 1;
   3499     double *rcolwidth = New_N(double, size);
   3500     struct table_cell *cell = &t->cell;
   3501     int n_leftcol = 0;
   3502 
   3503     for (i = 0; i < size; i++)
   3504 	rcolwidth[i] = 0;
   3505 
   3506     for (i = 0; i < size; i++) {
   3507 	if (t->fixed_width[i] < 0)
   3508 	    rcolwidth[i] = -(double)t->fixed_width[i] / 100.0;
   3509 	else if (t->fixed_width[i] > 0)
   3510 	    rcolwidth[i] = (double)t->fixed_width[i] / maxwidth;
   3511 	else
   3512 	    n_leftcol++;
   3513     }
   3514     for (i = 0; i <= cell->maxcell; i++) {
   3515 	if (cell->fixed_width[i] < 0) {
   3516 	    double w = -(double)cell->fixed_width[i] / 100.0;
   3517 	    double r;
   3518 	    int j, k;
   3519 	    int n_leftcell = 0;
   3520 	    k = cell->col[i];
   3521 	    r = 0.0;
   3522 	    for (j = 0; j < cell->colspan[i]; j++) {
   3523 		if (rcolwidth[j + k] > 0)
   3524 		    r += rcolwidth[j + k];
   3525 		else
   3526 		    n_leftcell++;
   3527 	    }
   3528 	    if (n_leftcell == 0) {
   3529 		/* w must be identical to r */
   3530 		if (w != r)
   3531 		    cell->fixed_width[i] = -100 * r;
   3532 	    }
   3533 	    else {
   3534 		if (w <= r) {
   3535 		    /* make room for the left(width-unspecified) cell */
   3536 		    /* the next formula is an estimation of required width */
   3537 		    w = r * cell->colspan[i] / (cell->colspan[i] - n_leftcell);
   3538 		    cell->fixed_width[i] = -100 * w;
   3539 		}
   3540 		for (j = 0; j < cell->colspan[i]; j++) {
   3541 		    if (rcolwidth[j + k] == 0)
   3542 			rcolwidth[j + k] = (w - r) / n_leftcell;
   3543 		}
   3544 	    }
   3545 	}
   3546 	else if (cell->fixed_width[i] > 0) {
   3547 	    /* todo */
   3548 	}
   3549     }
   3550     /* sanity check */
   3551     for (i = 0; i < size; i++)
   3552 	rel_total += rcolwidth[i];
   3553 
   3554     if ((n_leftcol == 0 && rel_total < 0.9) || 1.1 < rel_total) {
   3555 	for (i = 0; i < size; i++) {
   3556 	    rcolwidth[i] /= rel_total;
   3557 	}
   3558 	for (i = 0; i < size; i++) {
   3559 	    if (t->fixed_width[i] < 0)
   3560 		t->fixed_width[i] = -rcolwidth[i] * 100;
   3561 	}
   3562 	for (i = 0; i <= cell->maxcell; i++) {
   3563 	    if (cell->fixed_width[i] < 0) {
   3564 		double r;
   3565 		int j, k;
   3566 		k = cell->col[i];
   3567 		r = 0.0;
   3568 		for (j = 0; j < cell->colspan[i]; j++)
   3569 		    r += rcolwidth[j + k];
   3570 		cell->fixed_width[i] = -r * 100;
   3571 	    }
   3572 	}
   3573     }
   3574 }
   3575 
   3576 void
   3577 set_table_matrix(struct table *t, int width)
   3578 {
   3579     int size = t->maxcol + 1;
   3580     int i, j;
   3581     double b, s;
   3582     int a;
   3583     struct table_cell *cell = &t->cell;
   3584 
   3585     if (size < 1)
   3586 	return;
   3587 
   3588     t->matrix = m_get(size, size);
   3589     t->vector = v_get(size);
   3590     for (i = 0; i < size; i++) {
   3591 	for (j = i; j < size; j++)
   3592 	    m_set_val(t->matrix, i, j, 0.);
   3593 	v_set_val(t->vector, i, 0.);
   3594     }
   3595 
   3596     check_relative_width(t, width);
   3597 
   3598     for (i = 0; i < size; i++) {
   3599 	if (t->fixed_width[i] > 0) {
   3600 	    a = max(t->fixed_width[i], t->minimum_width[i]);
   3601 	    b = sigma_td(a);
   3602 	    correct_table_matrix(t, i, 1, a, b);
   3603 	}
   3604 	else if (t->fixed_width[i] < 0) {
   3605 	    s = -(double)t->fixed_width[i] / 100.;
   3606 	    b = sigma_td((int)(s * width));
   3607 	    correct_table_matrix2(t, i, 1, s, b);
   3608 	}
   3609     }
   3610 
   3611     for (j = 0; j <= cell->maxcell; j++) {
   3612 	if (cell->fixed_width[j] > 0) {
   3613 	    a = max(cell->fixed_width[j], cell->minimum_width[j]);
   3614 	    b = sigma_td(a);
   3615 	    correct_table_matrix(t, cell->col[j], cell->colspan[j], a, b);
   3616 	}
   3617 	else if (cell->fixed_width[j] < 0) {
   3618 	    s = -(double)cell->fixed_width[j] / 100.;
   3619 	    b = sigma_td((int)(s * width));
   3620 	    correct_table_matrix2(t, cell->col[j], cell->colspan[j], s, b);
   3621 	}
   3622     }
   3623 
   3624     set_table_matrix0(t, width);
   3625 
   3626     if (t->total_width > 0) {
   3627 	b = sigma_table(width);
   3628     }
   3629     else {
   3630 	b = sigma_table_nw(width);
   3631     }
   3632     correct_table_matrix(t, 0, size, width, b);
   3633 }
   3634 #endif				/* MATRIX */
   3635 
   3636 /* Local Variables:    */
   3637 /* c-basic-offset: 4   */
   3638 /* tab-width: 8        */
   3639 /* End:                */