Intial commit
[profile/ivi/w3m.git] / table.c
1 /* $Id: table.c,v 1.55 2007/05/23 13:07:44 inu Exp $ */
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 static int
2412 feed_table_tag(struct table *tbl, char *line, struct table_mode *mode,
2413                int width, struct parsed_tag *tag)
2414 {
2415     int cmd;
2416 #ifdef ID_EXT
2417     char *p;
2418 #endif
2419     struct table_cell *cell = &tbl->cell;
2420     int colspan, rowspan;
2421     int col, prev_col;
2422     int i, j, k, v, v0, w, id;
2423     Str tok, tmp, anchor;
2424     table_attr align, valign;
2425
2426     cmd = tag->tagid;
2427
2428     if (mode->pre_mode & TBLM_PLAIN) {
2429         if (mode->end_tag == cmd) {
2430             mode->pre_mode &= ~TBLM_PLAIN;
2431             mode->end_tag = 0;
2432             feed_table_block_tag(tbl, line, mode, 0, cmd);
2433             return TAG_ACTION_NONE;
2434         }
2435         return TAG_ACTION_PLAIN;
2436     }
2437     if (mode->pre_mode & TBLM_INTXTA) {
2438         switch (cmd) {
2439         CASE_TABLE_TAG:
2440         case HTML_N_TEXTAREA:
2441             table_close_textarea(tbl, mode, width);
2442             if (cmd == HTML_N_TEXTAREA)
2443                 return TAG_ACTION_NONE;
2444             break;
2445         default:
2446             return TAG_ACTION_FEED;
2447         }
2448     }
2449     if (mode->pre_mode & TBLM_SCRIPT) {
2450         if (mode->end_tag == cmd) {
2451             mode->pre_mode &= ~TBLM_SCRIPT;
2452             mode->end_tag = 0;
2453             return TAG_ACTION_NONE;
2454         }
2455         return TAG_ACTION_PLAIN;
2456     }
2457     if (mode->pre_mode & TBLM_STYLE) {
2458         if (mode->end_tag == cmd) {
2459             mode->pre_mode &= ~TBLM_STYLE;
2460             mode->end_tag = 0;
2461             return TAG_ACTION_NONE;
2462         }
2463         return TAG_ACTION_PLAIN;
2464     }
2465     /* failsafe: a tag other than <option></option>and </select> in *
2466      * <select> environment is regarded as the end of <select>. */
2467     if (mode->pre_mode & TBLM_INSELECT) {
2468         switch (cmd) {
2469           CASE_TABLE_TAG:
2470         case HTML_N_FORM:
2471         case HTML_N_SELECT:     /* mode->end_tag */
2472             table_close_select(tbl, mode, width);
2473             if (cmd == HTML_N_SELECT)
2474                 return TAG_ACTION_NONE;
2475             break;
2476         default:
2477             return TAG_ACTION_FEED;
2478         }
2479     }
2480     if (mode->caption) {
2481         switch (cmd) {
2482           CASE_TABLE_TAG:
2483         case HTML_N_CAPTION:
2484             mode->caption = 0;
2485             if (cmd == HTML_N_CAPTION)
2486                 return TAG_ACTION_NONE;
2487             break;
2488         default:
2489             return TAG_ACTION_FEED;
2490         }
2491     }
2492
2493     if (mode->pre_mode & TBLM_PRE) {
2494         switch (cmd) {
2495         case HTML_NOBR:
2496         case HTML_N_NOBR:
2497         case HTML_PRE_INT:
2498         case HTML_N_PRE_INT:
2499             return TAG_ACTION_NONE;
2500         }
2501     }
2502
2503     switch (cmd) {
2504     case HTML_TABLE:
2505         check_rowcol(tbl, mode);
2506         return TAG_ACTION_TABLE;
2507     case HTML_N_TABLE:
2508         if (tbl->suspended_data)
2509             check_rowcol(tbl, mode);
2510         return TAG_ACTION_N_TABLE;
2511     case HTML_TR:
2512         if (tbl->col >= 0 && tbl->tabcontentssize > 0)
2513             setwidth(tbl, mode);
2514         tbl->col = -1;
2515         tbl->row++;
2516         tbl->flag |= TBL_IN_ROW;
2517         tbl->flag &= ~TBL_IN_COL;
2518         align = 0;
2519         valign = 0;
2520         if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) {
2521             switch (i) {
2522             case ALIGN_LEFT:
2523                 align = (HTT_LEFT | HTT_TRSET);
2524                 break;
2525             case ALIGN_RIGHT:
2526                 align = (HTT_RIGHT | HTT_TRSET);
2527                 break;
2528             case ALIGN_CENTER:
2529                 align = (HTT_CENTER | HTT_TRSET);
2530                 break;
2531             }
2532         }
2533         if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) {
2534             switch (i) {
2535             case VALIGN_TOP:
2536                 valign = (HTT_TOP | HTT_VTRSET);
2537                 break;
2538             case VALIGN_MIDDLE:
2539                 valign = (HTT_MIDDLE | HTT_VTRSET);
2540                 break;
2541             case VALIGN_BOTTOM:
2542                 valign = (HTT_BOTTOM | HTT_VTRSET);
2543                 break;
2544             }
2545         }
2546 #ifdef ID_EXT
2547         if (parsedtag_get_value(tag, ATTR_ID, &p))
2548             tbl->tridvalue[tbl->row] = Strnew_charp(p);
2549 #endif                          /* ID_EXT */
2550         tbl->trattr = align | valign;
2551         break;
2552     case HTML_TH:
2553     case HTML_TD:
2554         prev_col = tbl->col;
2555         if (tbl->col >= 0 && tbl->tabcontentssize > 0)
2556             setwidth(tbl, mode);
2557         if (tbl->row == -1) {
2558             /* for broken HTML... */
2559             tbl->row = -1;
2560             tbl->col = -1;
2561             tbl->maxrow = tbl->row;
2562         }
2563         if (tbl->col == -1) {
2564             if (!(tbl->flag & TBL_IN_ROW)) {
2565                 tbl->row++;
2566                 tbl->flag |= TBL_IN_ROW;
2567             }
2568             if (tbl->row > tbl->maxrow)
2569                 tbl->maxrow = tbl->row;
2570         }
2571         tbl->col++;
2572         check_row(tbl, tbl->row);
2573         while (tbl->tabattr[tbl->row][tbl->col]) {
2574             tbl->col++;
2575         }
2576         if (tbl->col > MAXCOL - 1) {
2577             tbl->col = prev_col;
2578             return TAG_ACTION_NONE;
2579         }
2580         if (tbl->col > tbl->maxcol) {
2581             tbl->maxcol = tbl->col;
2582         }
2583         colspan = rowspan = 1;
2584         if (tbl->trattr & HTT_TRSET)
2585             align = (tbl->trattr & HTT_ALIGN);
2586         else if (cmd == HTML_TH)
2587             align = HTT_CENTER;
2588         else
2589             align = HTT_LEFT;
2590         if (tbl->trattr & HTT_VTRSET)
2591             valign = (tbl->trattr & HTT_VALIGN);
2592         else
2593             valign = HTT_MIDDLE;
2594         if (parsedtag_get_value(tag, ATTR_ROWSPAN, &rowspan)) {
2595             if ((tbl->row + rowspan) >= tbl->max_rowsize)
2596                 check_row(tbl, tbl->row + rowspan);
2597         }
2598         if (parsedtag_get_value(tag, ATTR_COLSPAN, &colspan)) {
2599             if ((tbl->col + colspan) >= MAXCOL) {
2600                 /* Can't expand column */
2601                 colspan = MAXCOL - tbl->col;
2602             }
2603         }
2604         if (parsedtag_get_value(tag, ATTR_ALIGN, &i)) {
2605             switch (i) {
2606             case ALIGN_LEFT:
2607                 align = HTT_LEFT;
2608                 break;
2609             case ALIGN_RIGHT:
2610                 align = HTT_RIGHT;
2611                 break;
2612             case ALIGN_CENTER:
2613                 align = HTT_CENTER;
2614                 break;
2615             }
2616         }
2617         if (parsedtag_get_value(tag, ATTR_VALIGN, &i)) {
2618             switch (i) {
2619             case VALIGN_TOP:
2620                 valign = HTT_TOP;
2621                 break;
2622             case VALIGN_MIDDLE:
2623                 valign = HTT_MIDDLE;
2624                 break;
2625             case VALIGN_BOTTOM:
2626                 valign = HTT_BOTTOM;
2627                 break;
2628             }
2629         }
2630 #ifdef NOWRAP
2631         if (parsedtag_exists(tag, ATTR_NOWRAP))
2632             tbl->tabattr[tbl->row][tbl->col] |= HTT_NOWRAP;
2633 #endif                          /* NOWRAP */
2634         v = 0;
2635         if (parsedtag_get_value(tag, ATTR_WIDTH, &v)) {
2636 #ifdef TABLE_EXPAND
2637             if (v > 0) {
2638                 if (tbl->real_width > 0)
2639                     v = -(v * 100) / (tbl->real_width * pixel_per_char);
2640                 else
2641                     v = (int)(v / pixel_per_char);
2642             }
2643 #else
2644             v = RELATIVE_WIDTH(v);
2645 #endif                          /* not TABLE_EXPAND */
2646         }
2647 #ifdef ID_EXT
2648         if (parsedtag_get_value(tag, ATTR_ID, &p))
2649             tbl->tabidvalue[tbl->row][tbl->col] = Strnew_charp(p);
2650 #endif                          /* ID_EXT */
2651 #ifdef NOWRAP
2652         if (v != 0) {
2653             /* NOWRAP and WIDTH= conflicts each other */
2654             tbl->tabattr[tbl->row][tbl->col] &= ~HTT_NOWRAP;
2655         }
2656 #endif                          /* NOWRAP */
2657         tbl->tabattr[tbl->row][tbl->col] &= ~(HTT_ALIGN | HTT_VALIGN);
2658         tbl->tabattr[tbl->row][tbl->col] |= (align | valign);
2659         if (colspan > 1) {
2660             col = tbl->col;
2661
2662             cell->icell = cell->maxcell + 1;
2663             k = bsearch_2short(colspan, cell->colspan, col, cell->col, MAXCOL,
2664                                cell->index, cell->icell);
2665             if (k <= cell->maxcell) {
2666                 i = cell->index[k];
2667                 if (cell->col[i] == col && cell->colspan[i] == colspan)
2668                     cell->icell = i;
2669             }
2670             if (cell->icell > cell->maxcell && cell->icell < MAXCELL) {
2671                 cell->maxcell++;
2672                 cell->col[cell->maxcell] = col;
2673                 cell->colspan[cell->maxcell] = colspan;
2674                 cell->width[cell->maxcell] = 0;
2675                 cell->minimum_width[cell->maxcell] = 0;
2676                 cell->fixed_width[cell->maxcell] = 0;
2677                 if (cell->maxcell > k) {
2678                     int ii;
2679                     for (ii = cell->maxcell; ii > k; ii--)
2680                         cell->index[ii] = cell->index[ii - 1];
2681                 }
2682                 cell->index[k] = cell->maxcell;
2683             }
2684             if (cell->icell > cell->maxcell)
2685                 cell->icell = -1;
2686         }
2687         if (v != 0) {
2688             if (colspan == 1) {
2689                 v0 = tbl->fixed_width[tbl->col];
2690                 if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0)) {
2691 #ifdef FEED_TABLE_DEBUG
2692                     fprintf(stderr, "width(%d) = %d\n", tbl->col, v);
2693 #endif                          /* TABLE_DEBUG */
2694                     tbl->fixed_width[tbl->col] = v;
2695                 }
2696             }
2697             else if (cell->icell >= 0) {
2698                 v0 = cell->fixed_width[cell->icell];
2699                 if (v0 == 0 || (v0 > 0 && v > v0) || (v0 < 0 && v < v0))
2700                     cell->fixed_width[cell->icell] = v;
2701             }
2702         }
2703         for (i = 0; i < rowspan; i++) {
2704             check_row(tbl, tbl->row + i);
2705             for (j = 0; j < colspan; j++) {
2706 #if 0
2707                 tbl->tabattr[tbl->row + i][tbl->col + j] &= ~(HTT_X | HTT_Y);
2708 #endif
2709                 if (!(tbl->tabattr[tbl->row + i][tbl->col + j] &
2710                       (HTT_X | HTT_Y))) {
2711                     tbl->tabattr[tbl->row + i][tbl->col + j] |=
2712                         ((i > 0) ? HTT_Y : 0) | ((j > 0) ? HTT_X : 0);
2713                 }
2714                 if (tbl->col + j > tbl->maxcol) {
2715                     tbl->maxcol = tbl->col + j;
2716                 }
2717             }
2718             if (tbl->row + i > tbl->maxrow) {
2719                 tbl->maxrow = tbl->row + i;
2720             }
2721         }
2722         begin_cell(tbl, mode);
2723         break;
2724     case HTML_N_TR:
2725         setwidth(tbl, mode);
2726         tbl->col = -1;
2727         tbl->flag &= ~(TBL_IN_ROW | TBL_IN_COL);
2728         return TAG_ACTION_NONE;
2729     case HTML_N_TH:
2730     case HTML_N_TD:
2731         setwidth(tbl, mode);
2732         tbl->flag &= ~TBL_IN_COL;
2733 #ifdef FEED_TABLE_DEBUG
2734         {
2735             TextListItem *it;
2736             int i = tbl->col, j = tbl->row;
2737             fprintf(stderr, "(a) row,col: %d, %d\n", j, i);
2738             if (tbl->tabdata[j] && tbl->tabdata[j][i]) {
2739                 for (it = ((TextList *)tbl->tabdata[j][i])->first;
2740                      it; it = it->next)
2741                     fprintf(stderr, "  [%s] \n", it->ptr);
2742             }
2743         }
2744 #endif
2745         return TAG_ACTION_NONE;
2746     case HTML_P:
2747     case HTML_BR:
2748     case HTML_CENTER:
2749     case HTML_N_CENTER:
2750     case HTML_DIV:
2751     case HTML_N_DIV:
2752         if (!(tbl->flag & TBL_IN_ROW))
2753             break;
2754     case HTML_DT:
2755     case HTML_DD:
2756     case HTML_H:
2757     case HTML_N_H:
2758     case HTML_LI:
2759     case HTML_PRE:
2760     case HTML_N_PRE:
2761     case HTML_HR:
2762     case HTML_LISTING:
2763     case HTML_XMP:
2764     case HTML_PLAINTEXT:
2765     case HTML_PRE_PLAIN:
2766     case HTML_N_PRE_PLAIN:
2767         feed_table_block_tag(tbl, line, mode, 0, cmd);
2768         switch (cmd) {
2769         case HTML_PRE:
2770         case HTML_PRE_PLAIN:
2771             mode->pre_mode |= TBLM_PRE;
2772             break;
2773         case HTML_N_PRE:
2774         case HTML_N_PRE_PLAIN:
2775             mode->pre_mode &= ~TBLM_PRE;
2776             break;
2777         case HTML_LISTING:
2778             mode->pre_mode |= TBLM_PLAIN;
2779             mode->end_tag = HTML_N_LISTING;
2780             break;
2781         case HTML_XMP:
2782             mode->pre_mode |= TBLM_PLAIN;
2783             mode->end_tag = HTML_N_XMP;
2784             break;
2785         case HTML_PLAINTEXT:
2786             mode->pre_mode |= TBLM_PLAIN;
2787             mode->end_tag = MAX_HTMLTAG;
2788             break;
2789         }
2790         break;
2791     case HTML_DL:
2792     case HTML_BLQ:
2793     case HTML_OL:
2794     case HTML_UL:
2795         feed_table_block_tag(tbl, line, mode, 1, cmd);
2796         break;
2797     case HTML_N_DL:
2798     case HTML_N_BLQ:
2799     case HTML_N_OL:
2800     case HTML_N_UL:
2801         feed_table_block_tag(tbl, line, mode, -1, cmd);
2802         break;
2803     case HTML_NOBR:
2804     case HTML_WBR:
2805         if (!(tbl->flag & TBL_IN_ROW))
2806             break;
2807     case HTML_PRE_INT:
2808         feed_table_inline_tag(tbl, line, mode, -1);
2809         switch (cmd) {
2810         case HTML_NOBR:
2811             mode->nobr_level++;
2812             if (mode->pre_mode & TBLM_NOBR)
2813                 return TAG_ACTION_NONE;
2814             mode->pre_mode |= TBLM_NOBR;
2815             break;
2816         case HTML_PRE_INT:
2817             if (mode->pre_mode & TBLM_PRE_INT)
2818                 return TAG_ACTION_NONE;
2819             mode->pre_mode |= TBLM_PRE_INT;
2820             tbl->linfo.prev_spaces = 0;
2821             break;
2822         }
2823         mode->nobr_offset = -1;
2824         if (tbl->linfo.length > 0) {
2825             check_minimum0(tbl, tbl->linfo.length);
2826             tbl->linfo.length = 0;
2827         }
2828         break;
2829     case HTML_N_NOBR:
2830         if (!(tbl->flag & TBL_IN_ROW))
2831             break;
2832         feed_table_inline_tag(tbl, line, mode, -1);
2833         if (mode->nobr_level > 0)
2834             mode->nobr_level--;
2835         if (mode->nobr_level == 0)
2836             mode->pre_mode &= ~TBLM_NOBR;
2837         break;
2838     case HTML_N_PRE_INT:
2839         feed_table_inline_tag(tbl, line, mode, -1);
2840         mode->pre_mode &= ~TBLM_PRE_INT;
2841         break;
2842     case HTML_IMG:
2843         check_rowcol(tbl, mode);
2844         w = tbl->fixed_width[tbl->col];
2845         if (w < 0) {
2846             if (tbl->total_width > 0)
2847                 w = -tbl->total_width * w / 100;
2848             else if (width > 0)
2849                 w = -width * w / 100;
2850             else
2851                 w = 0;
2852         }
2853         else if (w == 0) {
2854             if (tbl->total_width > 0)
2855                 w = tbl->total_width;
2856             else if (width > 0)
2857                 w = width;
2858         }
2859         tok = process_img(tag, w);
2860         feed_table1(tbl, tok, mode, width);
2861         break;
2862     case HTML_FORM:
2863         feed_table_block_tag(tbl, "", mode, 0, cmd);
2864         tmp = process_form(tag);
2865         if (tmp)
2866             feed_table1(tbl, tmp, mode, width);
2867         break;
2868     case HTML_N_FORM:
2869         feed_table_block_tag(tbl, "", mode, 0, cmd);
2870         process_n_form();
2871         break;
2872     case HTML_INPUT:
2873         tmp = process_input(tag);
2874         feed_table1(tbl, tmp, mode, width);
2875         break;
2876     case HTML_SELECT:
2877         tmp = process_select(tag);
2878         if (tmp)
2879             feed_table1(tbl, tmp, mode, width);
2880         mode->pre_mode |= TBLM_INSELECT;
2881         mode->end_tag = HTML_N_SELECT;
2882         break;
2883     case HTML_N_SELECT:
2884     case HTML_OPTION:
2885         /* nothing */
2886         break;
2887     case HTML_TEXTAREA:
2888         w = 0;
2889         check_rowcol(tbl, mode);
2890         if (tbl->col + 1 <= tbl->maxcol &&
2891             tbl->tabattr[tbl->row][tbl->col + 1] & HTT_X) {
2892             if (cell->icell >= 0 && cell->fixed_width[cell->icell] > 0)
2893                 w = cell->fixed_width[cell->icell];
2894         }
2895         else {
2896             if (tbl->fixed_width[tbl->col] > 0)
2897                 w = tbl->fixed_width[tbl->col];
2898         }
2899         tmp = process_textarea(tag, w);
2900         if (tmp)
2901             feed_table1(tbl, tmp, mode, width);
2902         mode->pre_mode |= TBLM_INTXTA;
2903         mode->end_tag = HTML_N_TEXTAREA;
2904         break;
2905     case HTML_A:
2906         table_close_anchor0(tbl, mode);
2907         anchor = NULL;
2908         i = 0;
2909         parsedtag_get_value(tag, ATTR_HREF, &anchor);
2910         parsedtag_get_value(tag, ATTR_HSEQ, &i);
2911         if (anchor) {
2912             check_rowcol(tbl, mode);
2913             if (i == 0) {
2914                 Str tmp = process_anchor(tag, line);
2915                 pushdata(tbl, tbl->row, tbl->col, tmp->ptr);
2916             }
2917             else
2918                 pushdata(tbl, tbl->row, tbl->col, line);
2919             if (i >= 0) {
2920                 mode->pre_mode |= TBLM_ANCHOR;
2921                 mode->anchor_offset = tbl->tabcontentssize;
2922             }
2923         }
2924         else
2925             suspend_or_pushdata(tbl, line);
2926         break;
2927     case HTML_DEL:
2928         switch (displayInsDel) {
2929         case DISPLAY_INS_DEL_SIMPLE:
2930             mode->pre_mode |= TBLM_DEL;
2931             break;
2932         case DISPLAY_INS_DEL_NORMAL:
2933             feed_table_inline_tag(tbl, line, mode, 5);  /* [DEL: */
2934             break;
2935         case DISPLAY_INS_DEL_FONTIFY:
2936             feed_table_inline_tag(tbl, line, mode, -1);
2937             break;
2938         }
2939         break;
2940     case HTML_N_DEL:
2941         switch (displayInsDel) {
2942         case DISPLAY_INS_DEL_SIMPLE:
2943             mode->pre_mode &= ~TBLM_DEL;
2944             break;
2945         case DISPLAY_INS_DEL_NORMAL:
2946             feed_table_inline_tag(tbl, line, mode, 5);  /* :DEL] */
2947             break;
2948         case DISPLAY_INS_DEL_FONTIFY:
2949             feed_table_inline_tag(tbl, line, mode, -1);
2950             break;
2951         }
2952         break;
2953     case HTML_S:
2954         switch (displayInsDel) {
2955         case DISPLAY_INS_DEL_SIMPLE:
2956             mode->pre_mode |= TBLM_S;
2957             break;
2958         case DISPLAY_INS_DEL_NORMAL:
2959             feed_table_inline_tag(tbl, line, mode, 3);  /* [S: */
2960             break;
2961         case DISPLAY_INS_DEL_FONTIFY:
2962             feed_table_inline_tag(tbl, line, mode, -1);
2963             break;
2964         }
2965         break;
2966     case HTML_N_S:
2967         switch (displayInsDel) {
2968         case DISPLAY_INS_DEL_SIMPLE:
2969             mode->pre_mode &= ~TBLM_S;
2970             break;
2971         case DISPLAY_INS_DEL_NORMAL:
2972             feed_table_inline_tag(tbl, line, mode, 3);  /* :S] */
2973             break;
2974         case DISPLAY_INS_DEL_FONTIFY:
2975             feed_table_inline_tag(tbl, line, mode, -1);
2976             break;
2977         }
2978         break;
2979     case HTML_INS:
2980     case HTML_N_INS:
2981         switch (displayInsDel) {
2982         case DISPLAY_INS_DEL_SIMPLE:
2983             break;
2984         case DISPLAY_INS_DEL_NORMAL:
2985             feed_table_inline_tag(tbl, line, mode, 5);  /* [INS:, :INS] */
2986             break;
2987         case DISPLAY_INS_DEL_FONTIFY:
2988             feed_table_inline_tag(tbl, line, mode, -1);
2989             break;
2990         }
2991         break;
2992     case HTML_SUP:
2993     case HTML_SUB:
2994     case HTML_N_SUB:
2995         if (!(mode->pre_mode & (TBLM_DEL | TBLM_S)))
2996             feed_table_inline_tag(tbl, line, mode, 1);  /* ^, [, ] */
2997         break;
2998     case HTML_N_SUP:
2999         break;
3000     case HTML_TABLE_ALT:
3001         id = -1;
3002         w = 0;
3003         parsedtag_get_value(tag, ATTR_TID, &id);
3004         if (id >= 0 && id < tbl->ntable) {
3005             struct table *tbl1 = tbl->tables[id].ptr;
3006             feed_table_block_tag(tbl, line, mode, 0, cmd);
3007             addcontentssize(tbl, maximum_table_width(tbl1));
3008             check_minimum0(tbl, tbl1->sloppy_width);
3009 #ifdef TABLE_EXPAND
3010             w = tbl1->total_width;
3011             v = 0;
3012             colspan = table_colspan(tbl, tbl->row, tbl->col);
3013             if (colspan > 1) {
3014                 if (cell->icell >= 0)
3015                     v = cell->fixed_width[cell->icell];
3016             }
3017             else
3018                 v = tbl->fixed_width[tbl->col];
3019             if (v < 0 && tbl->real_width > 0 && tbl1->real_width > 0)
3020                 w = -(tbl1->real_width * 100) / tbl->real_width;
3021             else
3022                 w = tbl1->real_width;
3023             if (w > 0)
3024                 check_minimum0(tbl, w);
3025             else if (w < 0 && v < w) {
3026                 if (colspan > 1) {
3027                     if (cell->icell >= 0)
3028                         cell->fixed_width[cell->icell] = w;
3029                 }
3030                 else
3031                     tbl->fixed_width[tbl->col] = w;
3032             }
3033 #endif
3034             setwidth0(tbl, mode);
3035             clearcontentssize(tbl, mode);
3036         }
3037         break;
3038     case HTML_CAPTION:
3039         mode->caption = 1;
3040         break;
3041     case HTML_N_CAPTION:
3042     case HTML_THEAD:
3043     case HTML_N_THEAD:
3044     case HTML_TBODY:
3045     case HTML_N_TBODY:
3046     case HTML_TFOOT:
3047     case HTML_N_TFOOT:
3048     case HTML_COLGROUP:
3049     case HTML_N_COLGROUP:
3050     case HTML_COL:
3051         break;
3052     case HTML_SCRIPT:
3053         mode->pre_mode |= TBLM_SCRIPT;
3054         mode->end_tag = HTML_N_SCRIPT;
3055         break;
3056     case HTML_STYLE:
3057         mode->pre_mode |= TBLM_STYLE;
3058         mode->end_tag = HTML_N_STYLE;
3059         break;
3060     case HTML_N_A:
3061         table_close_anchor0(tbl, mode);
3062     case HTML_FONT:
3063     case HTML_N_FONT:
3064     case HTML_NOP:
3065         suspend_or_pushdata(tbl, line);
3066         break;
3067     case HTML_INTERNAL:
3068     case HTML_N_INTERNAL:
3069     case HTML_FORM_INT:
3070     case HTML_N_FORM_INT:
3071     case HTML_INPUT_ALT:
3072     case HTML_N_INPUT_ALT:
3073     case HTML_SELECT_INT:
3074     case HTML_N_SELECT_INT:
3075     case HTML_OPTION_INT:
3076     case HTML_TEXTAREA_INT:
3077     case HTML_N_TEXTAREA_INT:
3078     case HTML_IMG_ALT:
3079     case HTML_SYMBOL:
3080     case HTML_N_SYMBOL:
3081     default:
3082         /* unknown tag: put into table */
3083         return TAG_ACTION_FEED;
3084     }
3085     return TAG_ACTION_NONE;
3086 }
3087
3088
3089 int
3090 feed_table(struct table *tbl, char *line, struct table_mode *mode,
3091            int width, int internal)
3092 {
3093     int i;
3094     char *p;
3095     Str tmp;
3096     struct table_linfo *linfo = &tbl->linfo;
3097
3098     if (*line == '<' && line[1] && REALLY_THE_BEGINNING_OF_A_TAG(line)) {
3099         struct parsed_tag *tag;
3100         p = line;
3101         tag = parse_tag(&p, internal);
3102         if (tag) {
3103             switch (feed_table_tag(tbl, line, mode, width, tag)) {
3104             case TAG_ACTION_NONE:
3105                 return -1;
3106             case TAG_ACTION_N_TABLE:
3107                 return 0;
3108             case TAG_ACTION_TABLE:
3109                 return 1;
3110             case TAG_ACTION_PLAIN:
3111                 break;
3112             case TAG_ACTION_FEED:
3113             default:
3114                 if (parsedtag_need_reconstruct(tag))
3115                     line = parsedtag2str(tag)->ptr;
3116             }
3117         }
3118         else {
3119             if (!(mode->pre_mode & (TBLM_PLAIN | TBLM_INTXTA | TBLM_INSELECT |
3120                                     TBLM_SCRIPT | TBLM_STYLE)))
3121                 return -1;
3122         }
3123     }
3124     else {
3125         if (mode->pre_mode & (TBLM_DEL | TBLM_S))
3126             return -1;
3127     }
3128     if (mode->caption) {
3129         Strcat_charp(tbl->caption, line);
3130         return -1;
3131     }
3132     if (mode->pre_mode & TBLM_SCRIPT)
3133         return -1;
3134     if (mode->pre_mode & TBLM_STYLE)
3135         return -1;
3136     if (mode->pre_mode & TBLM_INTXTA) {
3137         feed_textarea(line);
3138         return -1;
3139     }
3140     if (mode->pre_mode & TBLM_INSELECT) {
3141         feed_select(line);
3142         return -1;
3143     }
3144     if (!(mode->pre_mode & TBLM_PLAIN) &&
3145         !(*line == '<' && line[strlen(line) - 1] == '>') &&
3146         strchr(line, '&') != NULL) {
3147         tmp = Strnew();
3148         for (p = line; *p;) {
3149             char *q, *r;
3150             if (*p == '&') {
3151                 if (!strncasecmp(p, "&amp;", 5) ||
3152                     !strncasecmp(p, "&gt;", 4) || !strncasecmp(p, "&lt;", 4)) {
3153                     /* do not convert */
3154                     Strcat_char(tmp, *p);
3155                     p++;
3156                 }
3157                 else {
3158                     int ec;
3159                     q = p;
3160                     switch (ec = getescapechar(&p)) {
3161                     case '<':
3162                         Strcat_charp(tmp, "&lt;");
3163                         break;
3164                     case '>':
3165                         Strcat_charp(tmp, "&gt;");
3166                         break;
3167                     case '&':
3168                         Strcat_charp(tmp, "&amp;");
3169                         break;
3170                     case '\r':
3171                         Strcat_char(tmp, '\n');
3172                         break;
3173                     default:
3174                         r = conv_entity(ec);
3175                         if (r != NULL && strlen(r) == 1 &&
3176                             ec == (unsigned char)*r) {
3177                             Strcat_char(tmp, *r);
3178                             break;
3179                         }
3180                     case -1:
3181                         Strcat_char(tmp, *q);
3182                         p = q + 1;
3183                         break;
3184                     }
3185                 }
3186             }
3187             else {
3188                 Strcat_char(tmp, *p);
3189                 p++;
3190             }
3191         }
3192         line = tmp->ptr;
3193     }
3194     if (!(mode->pre_mode & (TBLM_SPECIAL & ~TBLM_NOBR))) {
3195         if (!(tbl->flag & TBL_IN_COL) || linfo->prev_spaces != 0)
3196             while (IS_SPACE(*line))
3197                 line++;
3198         if (*line == '\0')
3199             return -1;
3200         check_rowcol(tbl, mode);
3201         if (mode->pre_mode & TBLM_NOBR && mode->nobr_offset < 0)
3202             mode->nobr_offset = tbl->tabcontentssize;
3203
3204         /* count of number of spaces skipped in normal mode */
3205         i = skip_space(tbl, line, linfo, !(mode->pre_mode & TBLM_NOBR));
3206         addcontentssize(tbl, visible_length(line) - i);
3207         setwidth(tbl, mode);
3208         pushdata(tbl, tbl->row, tbl->col, line);
3209     }
3210     else if (mode->pre_mode & TBLM_PRE_INT) {
3211         check_rowcol(tbl, mode);
3212         if (mode->nobr_offset < 0)
3213             mode->nobr_offset = tbl->tabcontentssize;
3214         addcontentssize(tbl, maximum_visible_length(line, tbl->tabcontentssize));
3215         setwidth(tbl, mode);
3216         pushdata(tbl, tbl->row, tbl->col, line);
3217     }
3218     else {
3219         /* <pre> mode or something like it */
3220         check_rowcol(tbl, mode);
3221         while (*line) {
3222             int nl = FALSE;
3223             if ((p = strchr(line, '\r')) || (p = strchr(line, '\n'))) {
3224                 if (*p == '\r' && p[1] == '\n')
3225                     p++;
3226                 if (p[1]) {
3227                     p++;
3228                     tmp = Strnew_charp_n(line, p - line);
3229                     line = p;
3230                     p = tmp->ptr;
3231                 }
3232                 else {
3233                     p = line;
3234                     line = "";
3235                 }
3236                 nl = TRUE;
3237             }
3238             else {
3239                 p = line;
3240                 line = "";
3241             }
3242             if (mode->pre_mode & TBLM_PLAIN)
3243                 i = maximum_visible_length_plain(p, tbl->tabcontentssize);
3244             else
3245                 i = maximum_visible_length(p, tbl->tabcontentssize);
3246             addcontentssize(tbl, i);
3247             setwidth(tbl, mode);
3248             if (nl)
3249                 clearcontentssize(tbl, mode);
3250             pushdata(tbl, tbl->row, tbl->col, p);
3251         }
3252     }
3253     return -1;
3254 }
3255
3256 void
3257 feed_table1(struct table *tbl, Str tok, struct table_mode *mode, int width)
3258 {
3259     Str tokbuf;
3260     int status;
3261     char *line;
3262     if (!tok)
3263         return;
3264     tokbuf = Strnew();
3265     status = R_ST_NORMAL;
3266     line = tok->ptr;
3267     while (read_token
3268            (tokbuf, &line, &status, mode->pre_mode & TBLM_PREMODE, 0))
3269         feed_table(tbl, tokbuf->ptr, mode, width, TRUE);
3270 }
3271
3272 void
3273 pushTable(struct table *tbl, struct table *tbl1)
3274 {
3275     int col;
3276     int row;
3277
3278     col = tbl->col;
3279     row = tbl->row;
3280
3281     if (tbl->ntable >= tbl->tables_size) {
3282         struct table_in *tmp;
3283         tbl->tables_size += MAX_TABLE_N;
3284         tmp = New_N(struct table_in, tbl->tables_size);
3285         if (tbl->tables)
3286             bcopy(tbl->tables, tmp, tbl->ntable * sizeof(struct table_in));
3287         tbl->tables = tmp;
3288     }
3289
3290     tbl->tables[tbl->ntable].ptr = tbl1;
3291     tbl->tables[tbl->ntable].col = col;
3292     tbl->tables[tbl->ntable].row = row;
3293     tbl->tables[tbl->ntable].indent = tbl->indent;
3294     tbl->tables[tbl->ntable].buf = newTextLineList();
3295     check_row(tbl, row);
3296     if (col + 1 <= tbl->maxcol && tbl->tabattr[row][col + 1] & HTT_X)
3297         tbl->tables[tbl->ntable].cell = tbl->cell.icell;
3298     else
3299         tbl->tables[tbl->ntable].cell = -1;
3300     tbl->ntable++;
3301 }
3302
3303 #ifdef MATRIX
3304 int
3305 correct_table_matrix(struct table *t, int col, int cspan, int a, double b)
3306 {
3307     int i, j;
3308     int ecol = col + cspan;
3309     double w = 1. / (b * b);
3310
3311     for (i = col; i < ecol; i++) {
3312         v_add_val(t->vector, i, w * a);
3313         for (j = i; j < ecol; j++) {
3314             m_add_val(t->matrix, i, j, w);
3315             m_set_val(t->matrix, j, i, m_entry(t->matrix, i, j));
3316         }
3317     }
3318     return i;
3319 }
3320
3321 static void
3322 correct_table_matrix2(struct table *t, int col, int cspan, double s, double b)
3323 {
3324     int i, j;
3325     int ecol = col + cspan;
3326     int size = t->maxcol + 1;
3327     double w = 1. / (b * b);
3328     double ss;
3329
3330     for (i = 0; i < size; i++) {
3331         for (j = i; j < size; j++) {
3332             if (i >= col && i < ecol && j >= col && j < ecol)
3333                 ss = (1. - s) * (1. - s);
3334             else if ((i >= col && i < ecol) || (j >= col && j < ecol))
3335                 ss = -(1. - s) * s;
3336             else
3337                 ss = s * s;
3338             m_add_val(t->matrix, i, j, w * ss);
3339         }
3340     }
3341 }
3342
3343 static void
3344 correct_table_matrix3(struct table *t, int col, char *flags, double s,
3345                       double b)
3346 {
3347     int i, j;
3348     double ss;
3349     int size = t->maxcol + 1;
3350     double w = 1. / (b * b);
3351     int flg = (flags[col] == 0);
3352
3353     for (i = 0; i < size; i++) {
3354         if (!((flg && flags[i] == 0) || (!flg && flags[i] != 0)))
3355             continue;
3356         for (j = i; j < size; j++) {
3357             if (!((flg && flags[j] == 0) || (!flg && flags[j] != 0)))
3358                 continue;
3359             if (i == col && j == col)
3360                 ss = (1. - s) * (1. - s);
3361             else if (i == col || j == col)
3362                 ss = -(1. - s) * s;
3363             else
3364                 ss = s * s;
3365             m_add_val(t->matrix, i, j, w * ss);
3366         }
3367     }
3368 }
3369
3370 static void
3371 correct_table_matrix4(struct table *t, int col, int cspan, char *flags,
3372                       double s, double b)
3373 {
3374     int i, j;
3375     double ss;
3376     int ecol = col + cspan;
3377     int size = t->maxcol + 1;
3378     double w = 1. / (b * b);
3379
3380     for (i = 0; i < size; i++) {
3381         if (flags[i] && !(i >= col && i < ecol))
3382             continue;
3383         for (j = i; j < size; j++) {
3384             if (flags[j] && !(j >= col && j < ecol))
3385                 continue;
3386             if (i >= col && i < ecol && j >= col && j < ecol)
3387                 ss = (1. - s) * (1. - s);
3388             else if ((i >= col && i < ecol) || (j >= col && j < ecol))
3389                 ss = -(1. - s) * s;
3390             else
3391                 ss = s * s;
3392             m_add_val(t->matrix, i, j, w * ss);
3393         }
3394     }
3395 }
3396
3397 static void
3398 set_table_matrix0(struct table *t, int maxwidth)
3399 {
3400     int size = t->maxcol + 1;
3401     int i, j, k, bcol, ecol;
3402     int width;
3403     double w0, w1, w, s, b;
3404 #ifdef __GNUC__
3405     double we[size];
3406     char expand[size];
3407 #else                           /* not __GNUC__ */
3408     double we[MAXCOL];
3409     char expand[MAXCOL];
3410 #endif                          /* not __GNUC__ */
3411     struct table_cell *cell = &t->cell;
3412
3413     w0 = 0.;
3414     for (i = 0; i < size; i++) {
3415         we[i] = weight(t->tabwidth[i]);
3416         w0 += we[i];
3417     }
3418     if (w0 <= 0.)
3419         w0 = 1.;
3420
3421     if (cell->necell == 0) {
3422         for (i = 0; i < size; i++) {
3423             s = we[i] / w0;
3424             b = sigma_td_nw((int)(s * maxwidth));
3425             correct_table_matrix2(t, i, 1, s, b);
3426         }
3427         return;
3428     }
3429
3430     bzero(expand, size);
3431
3432     for (k = 0; k < cell->necell; k++) {
3433         j = cell->eindex[k];
3434         bcol = cell->col[j];
3435         ecol = bcol + cell->colspan[j];
3436         width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
3437         w1 = 0.;
3438         for (i = bcol; i < ecol; i++) {
3439             w1 += t->tabwidth[i] + 0.1;
3440             expand[i]++;
3441         }
3442         for (i = bcol; i < ecol; i++) {
3443             w = weight(width * (t->tabwidth[i] + 0.1) / w1);
3444             if (w > we[i])
3445                 we[i] = w;
3446         }
3447     }
3448
3449     w0 = 0.;
3450     w1 = 0.;
3451     for (i = 0; i < size; i++) {
3452         w0 += we[i];
3453         if (expand[i] == 0)
3454             w1 += we[i];
3455     }
3456     if (w0 <= 0.)
3457         w0 = 1.;
3458
3459     for (k = 0; k < cell->necell; k++) {
3460         j = cell->eindex[k];
3461         bcol = cell->col[j];
3462         width = cell->width[j] - (cell->colspan[j] - 1) * t->cellspacing;
3463         w = weight(width);
3464         s = w / (w1 + w);
3465         b = sigma_td_nw((int)(s * maxwidth));
3466         correct_table_matrix4(t, bcol, cell->colspan[j], expand, s, b);
3467     }
3468
3469     for (i = 0; i < size; i++) {
3470         if (expand[i] == 0) {
3471             s = we[i] / max(w1, 1.);
3472             b = sigma_td_nw((int)(s * maxwidth));
3473         }
3474         else {
3475             s = we[i] / max(w0 - w1, 1.);
3476             b = sigma_td_nw(maxwidth);
3477         }
3478         correct_table_matrix3(t, i, expand, s, b);
3479     }
3480 }
3481
3482 void
3483 check_relative_width(struct table *t, int maxwidth)
3484 {
3485     int i;
3486     double rel_total = 0;
3487     int size = t->maxcol + 1;
3488     double *rcolwidth = New_N(double, size);
3489     struct table_cell *cell = &t->cell;
3490     int n_leftcol = 0;
3491
3492     for (i = 0; i < size; i++)
3493         rcolwidth[i] = 0;
3494
3495     for (i = 0; i < size; i++) {
3496         if (t->fixed_width[i] < 0)
3497             rcolwidth[i] = -(double)t->fixed_width[i] / 100.0;
3498         else if (t->fixed_width[i] > 0)
3499             rcolwidth[i] = (double)t->fixed_width[i] / maxwidth;
3500         else
3501             n_leftcol++;
3502     }
3503     for (i = 0; i <= cell->maxcell; i++) {
3504         if (cell->fixed_width[i] < 0) {
3505             double w = -(double)cell->fixed_width[i] / 100.0;
3506             double r;
3507             int j, k;
3508             int n_leftcell = 0;
3509             k = cell->col[i];
3510             r = 0.0;
3511             for (j = 0; j < cell->colspan[i]; j++) {
3512                 if (rcolwidth[j + k] > 0)
3513                     r += rcolwidth[j + k];
3514                 else
3515                     n_leftcell++;
3516             }
3517             if (n_leftcell == 0) {
3518                 /* w must be identical to r */
3519                 if (w != r)
3520                     cell->fixed_width[i] = -100 * r;
3521             }
3522             else {
3523                 if (w <= r) {
3524                     /* make room for the left(width-unspecified) cell */
3525                     /* the next formula is an estimation of required width */
3526                     w = r * cell->colspan[i] / (cell->colspan[i] - n_leftcell);
3527                     cell->fixed_width[i] = -100 * w;
3528                 }
3529                 for (j = 0; j < cell->colspan[i]; j++) {
3530                     if (rcolwidth[j + k] == 0)
3531                         rcolwidth[j + k] = (w - r) / n_leftcell;
3532                 }
3533             }
3534         }
3535         else if (cell->fixed_width[i] > 0) {
3536             /* todo */
3537         }
3538     }
3539     /* sanity check */
3540     for (i = 0; i < size; i++)
3541         rel_total += rcolwidth[i];
3542
3543     if ((n_leftcol == 0 && rel_total < 0.9) || 1.1 < rel_total) {
3544         for (i = 0; i < size; i++) {
3545             rcolwidth[i] /= rel_total;
3546         }
3547         for (i = 0; i < size; i++) {
3548             if (t->fixed_width[i] < 0)
3549                 t->fixed_width[i] = -rcolwidth[i] * 100;
3550         }
3551         for (i = 0; i <= cell->maxcell; i++) {
3552             if (cell->fixed_width[i] < 0) {
3553                 double r;
3554                 int j, k;
3555                 k = cell->col[i];
3556                 r = 0.0;
3557                 for (j = 0; j < cell->colspan[i]; j++)
3558                     r += rcolwidth[j + k];
3559                 cell->fixed_width[i] = -r * 100;
3560             }
3561         }
3562     }
3563 }
3564
3565 void
3566 set_table_matrix(struct table *t, int width)
3567 {
3568     int size = t->maxcol + 1;
3569     int i, j;
3570     double b, s;
3571     int a;
3572     struct table_cell *cell = &t->cell;
3573
3574     if (size < 1)
3575         return;
3576
3577     t->matrix = m_get(size, size);
3578     t->vector = v_get(size);
3579     for (i = 0; i < size; i++) {
3580         for (j = i; j < size; j++)
3581             m_set_val(t->matrix, i, j, 0.);
3582         v_set_val(t->vector, i, 0.);
3583     }
3584
3585     check_relative_width(t, width);
3586
3587     for (i = 0; i < size; i++) {
3588         if (t->fixed_width[i] > 0) {
3589             a = max(t->fixed_width[i], t->minimum_width[i]);
3590             b = sigma_td(a);
3591             correct_table_matrix(t, i, 1, a, b);
3592         }
3593         else if (t->fixed_width[i] < 0) {
3594             s = -(double)t->fixed_width[i] / 100.;
3595             b = sigma_td((int)(s * width));
3596             correct_table_matrix2(t, i, 1, s, b);
3597         }
3598     }
3599
3600     for (j = 0; j <= cell->maxcell; j++) {
3601         if (cell->fixed_width[j] > 0) {
3602             a = max(cell->fixed_width[j], cell->minimum_width[j]);
3603             b = sigma_td(a);
3604             correct_table_matrix(t, cell->col[j], cell->colspan[j], a, b);
3605         }
3606         else if (cell->fixed_width[j] < 0) {
3607             s = -(double)cell->fixed_width[j] / 100.;
3608             b = sigma_td((int)(s * width));
3609             correct_table_matrix2(t, cell->col[j], cell->colspan[j], s, b);
3610         }
3611     }
3612
3613     set_table_matrix0(t, width);
3614
3615     if (t->total_width > 0) {
3616         b = sigma_table(width);
3617     }
3618     else {
3619         b = sigma_table_nw(width);
3620     }
3621     correct_table_matrix(t, 0, size, width, b);
3622 }
3623 #endif                          /* MATRIX */
3624
3625 /* Local Variables:    */
3626 /* c-basic-offset: 4   */
3627 /* tab-width: 8        */
3628 /* End:                */