084c5873b91f118f1f914341024ba05b49c3f917
[pspp-builds.git] / src / tab.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include "tab.h"
22 #include <ctype.h>
23 #include <stdarg.h>
24 #include <limits.h>
25 #include <stdlib.h>
26 #include "error.h"
27 #include "alloc.h"
28 #include "command.h"
29 #include "format.h"
30 #include "magic.h"
31 #include "misc.h"
32 #include "output.h"
33 #include "pool.h"
34 #include "som.h"
35 #include "var.h"
36
37 #include "debug-print.h"
38 \f
39 struct som_table_class tab_table_class;
40
41 #if DEBUGGING
42 #define DEFFIRST(NAME, LABEL) LABEL,
43 #define DEFTAB(NAME, LABEL) LABEL,
44 /*
45 static const char *tab_names[] =
46   {
47 #include "tab.def"
48   };
49 */
50 #undef DEFFIRST
51 #undef DEFTAB
52 #endif
53
54 /* Creates a table with NC columns and NR rows.  If REALLOCABLE is
55    nonzero then the table's size can be increased later; otherwise,
56    its size can only be reduced. */
57 struct tab_table *
58 tab_create (int nc, int nr, int reallocable)
59 {
60   void *(*alloc_func) (struct pool *, size_t);
61
62   struct tab_table *t;
63   
64   {
65     struct pool *container = pool_create ();
66     t = pool_alloc (container, sizeof *t);
67     t->container = container;
68   }
69   
70   t->col_style = TAB_COL_NONE;
71   t->col_group = 0;
72   ls_null (&t->title);
73   t->flags = SOMF_NONE;
74   t->nr = nr;
75   t->nc = t->cf = nc;
76   t->l = t->r = t->t = t->b = 0;
77
78   alloc_func = reallocable ? pool_malloc : pool_alloc;
79 #if GLOBAL_DEBUGGING
80   t->reallocable = reallocable;
81 #endif
82
83   t->cc = alloc_func (t->container, nr * nc * sizeof *t->cc);
84   t->ct = alloc_func (t->container, nr * nc);
85   memset (t->ct, TAB_EMPTY, nc * nr);
86
87   t->rh = alloc_func (t->container, nc * (nr + 1));
88   memset (t->rh, 0, nc * (nr + 1));
89
90   t->hrh = alloc_func (t->container, sizeof *t->hrh * (nr + 1));
91   memset (t->hrh, 0, sizeof *t->hrh * (nr + 1));
92
93   t->trh = alloc_func (t->container, nr + 1);
94   memset (t->trh, 0, nr + 1);
95
96   t->rv = alloc_func (t->container, (nc + 1) * nr);
97   memset (t->rv, 0, (nc + 1) * nr);
98
99   t->wrv = alloc_func (t->container, sizeof *t->wrv * (nc + 1));
100   memset (t->wrv, 0, sizeof *t->wrv * (nc + 1));
101
102   t->trv = alloc_func (t->container, nc + 1);
103   memset (t->trv, 0, nc + 1);
104
105   t->dim = NULL;
106   t->w = t->h = NULL;
107   t->col_ofs = t->row_ofs = 0;
108   
109   return t;
110 }
111
112 /* Destroys table T. */
113 void
114 tab_destroy (struct tab_table *t)
115 {
116   assert (t != NULL);
117   pool_destroy (t->container);
118   t=0;
119 }
120
121 /* Sets the width and height of a table, in columns and rows,
122    respectively.  Use only to reduce the size of a table, since it
123    does not change the amount of allocated memory. */
124 void
125 tab_resize (struct tab_table *t, int nc, int nr)
126 {
127   assert (t != NULL);
128   if (nc != -1)
129     {
130       assert (nc + t->col_ofs <= t->cf);
131       t->nc = nc + t->col_ofs;
132     }
133   if (nr != -1)
134     {
135       assert (nr + t->row_ofs <= t->nr);
136       t->nr = nr + t->row_ofs;
137     }
138 }
139
140 /* Changes either or both dimensions of a table.  Consider using the
141    above routine instead if it won't waste a lot of space.
142
143    Changing the number of columns in a table is particularly expensive
144    in space and time.  Avoid doing such.  FIXME: In fact, transferring
145    of rules isn't even implemented yet. */
146 void
147 tab_realloc (struct tab_table *t, int nc, int nr)
148 {
149   int ro, co;
150   
151   assert (t != NULL);
152 #if GLOBAL_DEBUGGING
153   assert (t->reallocable);
154 #endif
155   ro = t->row_ofs;
156   co = t->col_ofs;
157   if (ro || co)
158     tab_offset (t, 0, 0);
159
160   if (nc == -1)
161     nc = t->nc;
162   if (nr == -1)
163     nr = t->nr;
164   
165   assert (nc == t->nc);
166   
167   if (nc > t->cf)
168     {
169       int mr1 = min (nr, t->nr);
170       int mc1 = min (nc, t->nc);
171       
172       struct len_string *new_cc;
173       unsigned char *new_ct;
174       int r;
175
176       new_cc = pool_malloc (t->container, nr * nc * sizeof *new_cc);
177       new_ct = pool_malloc (t->container, nr * nc);
178       for (r = 0; r < mr1; r++)
179         {
180           memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
181           memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
182           memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
183         }
184       pool_free (t->container, t->cc);
185       pool_free (t->container, t->ct);
186       t->cc = new_cc;
187       t->ct = new_ct;
188       t->cf = nc;
189     }
190   else if (nr != t->nr)
191     {
192       t->cc = pool_realloc (t->container, t->cc, nr * nc * sizeof *t->cc);
193       t->ct = pool_realloc (t->container, t->ct, nr * nc);
194
195       t->rh = pool_realloc (t->container, t->rh, nc * (nr + 1));
196       t->rv = pool_realloc (t->container, t->rv, (nc + 1) * nr);
197       t->trh = pool_realloc (t->container, t->trh, nr + 1);
198       t->hrh = pool_realloc (t->container, t->hrh,
199                              sizeof *t->hrh * (nr + 1));
200       
201       if (nr > t->nr)
202         {
203           memset (&t->rh[nc * (t->nr + 1)], 0, (nr - t->nr) * nc);
204           memset (&t->rv[(nc + 1) * t->nr], 0, (nr - t->nr) * (nc + 1));
205           memset (&t->trh[t->nr + 1], 0, nr - t->nr);
206         }
207     }
208
209   memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
210   
211   t->nr = nr;
212   t->nc = nc;
213
214   if (ro || co)
215     tab_offset (t, co, ro);
216 }
217
218 /* Sets the number of header rows on each side of TABLE to L on the
219    left, R on the right, T on the top, B on the bottom.  Header rows
220    are repeated when a table is broken across multiple columns or
221    multiple pages. */
222 void
223 tab_headers (struct tab_table *table, int l, int r, int t, int b)
224 {
225   assert (table != NULL);
226   table->l = l;
227   table->r = r;
228   table->t = t;
229   table->b = b;
230 }
231
232 /* Set up table T so that, when it is an appropriate size, it will be
233    displayed across the page in columns.
234
235    STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
236    as a unit. */
237 void
238 tab_columns (struct tab_table *t, int style, int group)
239 {
240   assert (t != NULL);
241   t->col_style = style;
242   t->col_group = group;
243 }
244 \f
245 /* Rules. */
246
247 /* Draws a vertical line to the left of cells at horizontal position X
248    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
249 void
250 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
251 {
252   int y;
253
254   assert (t != NULL);
255
256 #if GLOBAL_DEBUGGING
257   if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
258       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
259       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
260     {
261       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
262                 "table size (%d,%d)\n"),
263               x, t->col_ofs, x + t->col_ofs,
264               y1, t->row_ofs, y1 + t->row_ofs,
265               y2, t->row_ofs, y2 + t->row_ofs,
266               t->nc, t->nr);
267       return;
268     }
269 #endif
270
271   x += t->col_ofs;
272   y1 += t->row_ofs;
273   y2 += t->row_ofs;
274
275   assert (x  > 0);
276   assert (x  < t->nc);
277   assert (y1 >= 0);
278   assert (y2 >= y1);
279   assert (y2 <=  t->nr);
280
281   if (style != -1)
282     {
283       if ((style & TAL_SPACING) == 0)
284         for (y = y1; y <= y2; y++)
285           t->rv[x + (t->cf + 1) * y] = style;
286       t->trv[x] |= (1 << (style & ~TAL_SPACING));
287     }
288 }
289
290 /* Draws a horizontal line above cells at vertical position Y from X1
291    to X2 inclusive in style STYLE, if style is not -1. */
292 void
293 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
294 {
295   int x;
296
297   assert (t != NULL);
298
299   x1 += t->col_ofs;
300   x2 += t->col_ofs;
301   y += t->row_ofs;
302
303   assert (y >= 0);
304   assert (y < t->nr);
305   assert (x2 >= x1 );
306   assert (x1 >= 0 );
307   assert (x2 < t->nc);
308
309   if (style != -1)
310     {
311       if ((style & TAL_SPACING) == 0)
312         for (x = x1; x <= x2; x++)
313           t->rh[x + t->cf * y] = style;
314       t->trh[y] |= (1 << (style & ~TAL_SPACING));
315     }
316 }
317
318 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
319    lines of style F_H and vertical lines of style F_V.  Fills the
320    interior of the box with horizontal lines of style I_H and vertical
321    lines of style I_V.  Any of the line styles may be -1 to avoid
322    drawing those lines.  This is distinct from 0, which draws a null
323    line. */
324 void
325 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
326          int x1, int y1, int x2, int y2)
327 {
328   assert (t != NULL);
329
330 #if GLOBAL_DEBUGGING
331   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc 
332       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
333       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr 
334       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
335     {
336       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
337                 "in table size (%d,%d)\n"),
338               x1, t->col_ofs, x1 + t->col_ofs,
339               y1, t->row_ofs, y1 + t->row_ofs,
340               x2, t->col_ofs, x2 + t->col_ofs,
341               y2, t->row_ofs, y2 + t->row_ofs,
342               t->nc, t->nr);
343       abort ();
344     }
345 #endif
346
347   x1 += t->col_ofs;
348   x2 += t->col_ofs;
349   y1 += t->row_ofs;
350   y2 += t->row_ofs;
351
352   assert (x2 >= x1);
353   assert (y2 >= y1);
354   assert (x1 >= 0);
355   assert (y1 >= 0);
356   assert (x2 < t->nc);
357   assert (y2 < t->nr);
358
359   if (f_h != -1)
360     {
361       int x;
362       if ((f_h & TAL_SPACING) == 0)
363         for (x = x1; x <= x2; x++)
364           {
365             t->rh[x + t->cf * y1] = f_h;
366             t->rh[x + t->cf * (y2 + 1)] = f_h;
367           }
368       t->trh[y1] |= (1 << (f_h & ~TAL_SPACING));
369       t->trh[y2 + 1] |= (1 << (f_h & ~TAL_SPACING));
370     }
371   if (f_v != -1)
372     {
373       int y;
374       if ((f_v & TAL_SPACING) == 0)
375         for (y = y1; y <= y2; y++)
376           {
377             t->rv[x1 + (t->cf + 1) * y] = f_v;
378             t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
379           }
380       t->trv[x1] |= (1 << (f_v & ~TAL_SPACING));
381       t->trv[x2 + 1] |= (1 << (f_v & ~TAL_SPACING));
382     }
383
384   if (i_h != -1)
385     {
386       int y;
387       
388       for (y = y1 + 1; y <= y2; y++)
389         {
390           int x;
391
392           if ((i_h & TAL_SPACING) == 0)
393             for (x = x1; x <= x2; x++)
394               t->rh[x + t->cf * y] = i_h;
395
396           t->trh[y] |= (1 << (i_h & ~TAL_SPACING));
397         }
398     }
399   if (i_v != -1)
400     {
401       int x;
402       
403       for (x = x1 + 1; x <= x2; x++)
404         {
405           int y;
406           
407           if ((i_v & TAL_SPACING) == 0)
408             for (y = y1; y <= y2; y++)
409               t->rv[x + (t->cf + 1) * y] = i_v;
410
411           t->trv[x] |= (1 << (i_v & ~TAL_SPACING));
412         }
413     }
414 }
415
416 /* Formats text TEXT and arguments ARGS as indicated in OPT and sets
417    the resultant string into S in TABLE's pool. */
418 static void
419 text_format (struct tab_table *table, int opt, const char *text, va_list args,
420              struct len_string *s)
421 {
422   int len;
423   
424   assert (table != NULL && text != NULL && s != NULL);
425   
426   if (opt & TAT_PRINTF)
427     {
428       char *temp_buf = local_alloc (1024);
429       
430       len = nvsprintf (temp_buf, text, args);
431       text = temp_buf;
432     }
433   else
434     len = strlen (text);
435
436   ls_create_buffer (s, text, len);
437   pool_register (table->container, free, s->string);
438   
439   if (opt & TAT_PRINTF)
440     local_free (text);
441 }
442
443 /* Set the title of table T to TITLE, which is formatted with printf
444    if FORMAT is nonzero. */
445 void
446 tab_title (struct tab_table *t, int format, const char *title, ...)
447 {
448   va_list args;
449
450   assert (t != NULL && title != NULL);
451   va_start (args, title);
452   text_format (t, format ? TAT_PRINTF : TAT_NONE, title, args, &t->title);
453   va_end (args);
454 }
455
456 /* Set DIM_FUNC as the dimension function for table T. */
457 void
458 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
459 {
460   assert (t != NULL && t->dim == NULL);
461   t->dim = dim_func;
462 }
463
464 /* Returns the natural width of column C in table T for driver D, that
465    is, the smallest width necessary to display all its cells without
466    wrapping.  The width will be no larger than the page width minus
467    left and right rule widths. */
468 int
469 tab_natural_width (struct tab_table *t, struct outp_driver *d, int c)
470 {
471   int width;
472
473   assert (t != NULL && c >= 0 && c < t->nc);
474   {
475     int r;
476
477     for (width = r = 0; r < t->nr; r++)
478       {
479         struct outp_text text;
480         unsigned char opt = t->ct[c + r * t->cf];
481                 
482         if (opt & (TAB_JOIN | TAB_EMPTY))
483           continue;
484
485         text.s = t->cc[c + r * t->cf];
486         assert (!ls_null_p (&text.s));
487         text.options = OUTP_T_JUST_LEFT;
488
489         d->class->text_metrics (d, &text);
490         if (text.h > width)
491           width = text.h;
492       }
493   }
494
495   if (width == 0)
496     {
497       width = d->prop_em_width * 8;
498 #if GLOBAL_DEBUGGING
499       printf ("warning: table column %d contains no data.\n", c);
500 #endif
501     }
502   
503   {
504     const int clamp = d->width - t->wrv[0] - t->wrv[t->nc];
505     
506     if (width > clamp)
507       width = clamp;
508   }
509
510   return width;
511 }
512
513 /* Returns the natural height of row R in table T for driver D, that
514    is, the minimum height necessary to display the information in the
515    cell at the widths set for each column. */
516 int
517 tab_natural_height (struct tab_table *t, struct outp_driver *d, int r)
518 {
519   int height;
520
521   assert (t != NULL && r >= 0 && r < t->nr);
522   
523   {
524     int c;
525     
526     for (height = d->font_height, c = 0; c < t->nc; c++)
527       {
528         struct outp_text text;
529         unsigned char opt = t->ct[c + r * t->cf];
530
531         assert (t->w[c] != NOT_INT);
532         if (opt & (TAB_JOIN | TAB_EMPTY))
533           continue;
534
535         text.s = t->cc[c + r * t->cf];
536         assert (!ls_null_p (&text.s));
537         text.options = OUTP_T_HORZ | OUTP_T_JUST_LEFT;
538         text.h = t->w[c];
539         d->class->text_metrics (d, &text);
540
541         if (text.v > height)
542           height = text.v;
543       }
544   }
545
546   return height;
547 }
548
549 /* Callback function to set all columns and rows to their natural
550    dimensions.  Not really meant to be called directly.  */
551 void
552 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
553 {
554   int i;
555
556   assert (t != NULL);
557   
558   for (i = 0; i < t->nc; i++)
559     t->w[i] = tab_natural_width (t, d, i);
560   
561   for (i = 0; i < t->nr; i++)
562     t->h[i] = tab_natural_height (t, d, i);
563 }
564
565 \f
566 /* Cells. */
567
568 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
569    from V, displayed with format spec F. */
570 void
571 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
572            const union value *v, const struct fmt_spec *f)
573 {
574   char *contents;
575
576   assert (table != NULL && v != NULL && f != NULL);
577 #if GLOBAL_DEBUGGING
578   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
579       || c + table->col_ofs >= table->nc
580       || r + table->row_ofs >= table->nr)
581     {
582       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
583               "(%d,%d)\n",
584               c, table->col_ofs, c + table->col_ofs,
585               r, table->row_ofs, r + table->row_ofs,
586               table->nc, table->nr);
587       return;
588     }
589 #endif
590
591   contents = pool_alloc (table->container, f->w);
592   ls_init (&table->cc[c + r * table->cf], contents, f->w);
593   table->ct[c + r * table->cf] = opt;
594   
595   data_out (contents, f, v);
596 }
597
598 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
599    with NDEC decimal places. */
600 void
601 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
602            double val, int w, int d)
603 {
604   char *contents;
605   char buf[40], *cp;
606   
607   struct fmt_spec f;
608   union value double_value;
609
610   assert (table != NULL && w <= 40);
611   
612   assert (c >= 0);
613   assert (c < table->nc);
614   assert (r >= 0);
615   assert (r < table->nr);
616
617   f.type = FMT_F;
618   f.w = w;
619   f.d = d;
620   
621 #if GLOBAL_DEBUGGING
622   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
623       || c + table->col_ofs >= table->nc
624       || r + table->row_ofs >= table->nr)
625     {
626       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
627               "(%d,%d)\n",
628               c, table->col_ofs, c + table->col_ofs,
629               r, table->row_ofs, r + table->row_ofs,
630               table->nc, table->nr);
631       return;
632     }
633 #endif
634
635   double_value.f = val;
636   data_out (buf, &f, &double_value);
637
638   cp = buf;
639   while (isspace ((unsigned char) *cp) && cp < &buf[w])
640     cp++;
641   f.w = w - (cp - buf);
642
643   contents = pool_alloc (table->container, f.w);
644   ls_init (&table->cc[c + r * table->cf], contents, f.w);
645   table->ct[c + r * table->cf] = opt;
646   memcpy (contents, cp, f.w);
647 }
648
649 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
650    TEXT. */
651 void
652 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
653 {
654   va_list args;
655
656   assert (table != NULL && text != NULL);
657
658   assert (c >= 0 );
659   assert (r >= 0 );
660   assert (c < table->nc);
661   assert (r < table->nr);
662   
663
664 #if GLOBAL_DEBUGGING
665   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
666       || c + table->col_ofs >= table->nc
667       || r + table->row_ofs >= table->nr)
668     {
669       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
670               "(%d,%d)\n",
671               c, table->col_ofs, c + table->col_ofs,
672               r, table->row_ofs, r + table->row_ofs,
673               table->nc, table->nr);
674       return;
675     }
676 #endif
677     
678   va_start (args, text);
679   text_format (table, opt, text, args, &table->cc[c + r * table->cf]);
680   table->ct[c + r * table->cf] = opt;
681   va_end (args);
682 }
683
684 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
685    options OPT to have text value TEXT. */
686 void
687 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
688                 unsigned opt, const char *text, ...)
689 {
690   struct tab_joined_cell *j;
691
692   assert (table != NULL && text != NULL);
693
694   assert (x1 + table->col_ofs >= 0);
695   assert (y1 + table->row_ofs >= 0);
696   assert (y2 >= y1);
697   assert (x2 >= x1);
698   assert (y2 + table->row_ofs < table->nr);
699   assert (x2 + table->col_ofs < table->nc);
700
701 #if GLOBAL_DEBUGGING
702   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
703       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
704       || x2 < x1 || x2 + table->col_ofs >= table->nc
705       || y2 < y2 || y2 + table->row_ofs >= table->nr)
706     {
707       printf ("tab_joint_text(): bad cell "
708               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
709               x1, table->col_ofs, x1 + table->col_ofs,
710               y1, table->row_ofs, y1 + table->row_ofs,
711               x2, table->col_ofs, x2 + table->col_ofs,
712               y2, table->row_ofs, y2 + table->row_ofs,
713               table->nc, table->nr);
714       return;
715     }
716 #endif
717   
718   j = pool_alloc (table->container, sizeof *j);
719   j->hit = 0;
720   j->x1 = x1 + table->col_ofs;
721   j->y1 = y1 + table->row_ofs;
722   j->x2 = ++x2 + table->col_ofs;
723   j->y2 = ++y2 + table->row_ofs;
724   
725   {
726     va_list args;
727     
728     va_start (args, text);
729     text_format (table, opt, text, args, &j->contents);
730     va_end (args);
731   }
732   
733   opt |= TAB_JOIN;
734   
735   {
736     struct len_string *cc = &table->cc[x1 + y1 * table->cf];
737     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
738     const int ofs = table->cf - (x2 - x1);
739
740     int y;
741     
742     for (y = y1; y < y2; y++)
743       {
744         int x;
745         
746         for (x = x1; x < x2; x++)
747           {
748             ls_init (cc++, (char *) j, 0);
749             *ct++ = opt;
750           }
751         
752         cc += ofs;
753         ct += ofs;
754       }
755   }
756 }
757
758 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
759 void
760 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
761          struct len_string *string)
762 {
763   assert (table != NULL && string != NULL);
764   
765 #if GLOBAL_DEBUGGING
766   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
767       || c + table->col_ofs >= table->nc
768       || r + table->row_ofs >= table->nr)
769     {
770       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
771               "(%d,%d)\n",
772               c, table->col_ofs, c + table->col_ofs,
773               r, table->row_ofs, r + table->row_ofs,
774               table->nc, table->nr);
775       return;
776     }
777 #endif
778
779   table->cc[c + r * table->cf] = *string;
780   table->ct[c + r * table->cf] = opt;
781 }
782 \f
783 /* Miscellaneous. */
784
785 /* Sets the widths of all the columns and heights of all the rows in
786    table T for driver D. */
787 static void
788 nowrap_dim (struct tab_table *t, struct outp_driver *d)
789 {
790   t->w[0] = tab_natural_width (t, d, 0);
791   t->h[0] = d->font_height;
792 }
793
794 /* Sets the widths of all the columns and heights of all the rows in
795    table T for driver D. */
796 static void
797 wrap_dim (struct tab_table *t, struct outp_driver *d)
798 {
799   t->w[0] = tab_natural_width (t, d, 0);
800   t->h[0] = tab_natural_height (t, d, 0);
801 }
802
803 /* Outputs text BUF as a table with a single cell having cell options
804    OPTIONS, which is a combination of the TAB_* and TAT_*
805    constants. */
806 void
807 tab_output_text (int options, const char *buf, ...)
808 {
809   struct tab_table *t = tab_create (1, 1, 0);
810
811   assert (buf != NULL);
812   if (options & TAT_PRINTF)
813     {
814       va_list args;
815       char *temp_buf = local_alloc (4096);
816       
817       va_start (args, buf);
818       nvsprintf (temp_buf, buf, args);
819       buf = temp_buf;
820       va_end (args);
821     }
822   
823   if (options & TAT_FIX)
824     {
825       struct outp_driver *d;
826
827       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
828         {
829           if (!d->page_open)
830             d->class->open_page (d);
831
832           if (d->class->text_set_font_by_name != NULL)
833             d->class->text_set_font_by_name (d, "FIXED");
834           else 
835             {
836               /* FIXME */
837             }
838         }
839     }
840
841   tab_text (t, 0, 0, options &~ TAT_PRINTF, buf);
842   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
843   if (options & TAT_NOWRAP)
844     tab_dim (t, nowrap_dim);
845   else
846     tab_dim (t, wrap_dim);
847   tab_submit (t);
848
849   if (options & TAT_FIX)
850     {
851       struct outp_driver *d;
852
853       for (d = outp_drivers (NULL); d; d = outp_drivers (d))
854         if (d->class->text_set_font_by_name != NULL)
855           d->class->text_set_font_by_name (d, "PROP");
856         else 
857           {
858             /* FIXME */
859           }
860     }
861   
862   if (options & TAT_PRINTF)
863     local_free (buf);
864 }
865
866 /* Set table flags to FLAGS. */
867 void
868 tab_flags (struct tab_table *t, unsigned flags)
869 {
870   assert (t != NULL);
871   t->flags = flags;
872 }
873
874 /* Easy, type-safe way to submit a tab table to som. */
875 void
876 tab_submit (struct tab_table *t)
877 {
878   struct som_table s;
879
880   assert (t != NULL);
881   s.class = &tab_table_class;
882   s.ext = t;
883   som_submit (&s);
884   tab_destroy (t);
885 }
886 \f
887 /* Editing. */
888
889 /* Set table row and column offsets for all functions that affect
890    cells or rules. */
891 void
892 tab_offset (struct tab_table *t, int col, int row)
893 {
894   int diff = 0;
895
896   assert (t != NULL);
897 #if GLOBAL_DEBUGGING
898   if (row < -1 || row >= t->nr)
899     {
900       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
901       abort ();
902     }
903   if (col < -1 || col >= t->nc)
904     {
905       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
906       abort ();
907     }
908 #endif
909
910   if (row != -1)
911     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
912   if (col != -1)
913     diff += (col - t->col_ofs), t->col_ofs = col;
914
915   t->cc += diff;
916   t->ct += diff;
917 }
918
919 /* Increment the row offset by one. If the table is too small,
920    increase its size. */
921 void
922 tab_next_row (struct tab_table *t)
923 {
924   assert (t != NULL);
925   t->cc += t->cf;
926   t->ct += t->cf;
927   if (++t->row_ofs >= t->nr)
928     tab_realloc (t, -1, t->nr * 4 / 3);
929 }
930 \f
931 static struct tab_table *t;
932 static struct outp_driver *d;
933 int tab_hit;
934
935 /* Set the current table to TABLE. */
936 static void
937 tabi_table (struct som_table *table)
938 {
939   assert (table != NULL);
940   t = table->ext;
941   tab_offset (t, 0, 0);
942   
943   assert (t->w == NULL && t->h == NULL);
944   t->w = pool_alloc (t->container, sizeof *t->w * t->nc);
945   t->h = pool_alloc (t->container, sizeof *t->h * t->nr);
946 }
947
948 /* Set the current output device to DRIVER. */
949 static void
950 tabi_driver (struct outp_driver *driver)
951 {
952   int i;
953
954   assert (driver != NULL);
955   d = driver;
956   
957   /* Figure out sizes of rules. */
958   for (t->hr_tot = i = 0; i <= t->nr; i++)
959     t->hr_tot += t->hrh[i] = d->horiz_line_spacing[t->trh[i]];
960   for (t->vr_tot = i = 0; i <= t->nc; i++)
961     t->vr_tot += t->wrv[i] = d->vert_line_spacing[t->trv[i]];
962
963 #if GLOBAL_DEBUGGING
964   for (i = 0; i < t->nr; i++)
965     t->h[i] = -1;
966   for (i = 0; i < t->nc; i++)
967     t->w[i] = -1;
968 #endif
969
970   assert (t->dim != NULL);
971   t->dim (t, d);
972
973 #if GLOBAL_DEBUGGING
974   {
975     int error = 0;
976
977     for (i = 0; i < t->nr; i++)
978       {
979         if (t->h[i] == -1)
980           {
981             printf ("Table row %d height not initialized.\n", i);
982             error = 1;
983           }
984         assert (t->h[i] > 0);
985       }
986     
987     for (i = 0; i < t->nc; i++)
988       {
989         if (t->w[i] == -1)
990           {
991             printf ("Table column %d width not initialized.\n", i);
992             error = 1;
993           }
994         assert (t->w[i] > 0);
995       }
996   }
997 #endif
998     
999   /* Add up header sizes. */
1000   for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
1001     t->wl += t->w[i] + t->wrv[i + 1];
1002   for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
1003     t->ht += t->h[i] + t->hrh[i + 1];
1004   for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1005     t->wr += t->w[i] + t->wrv[i + 1];
1006   for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1007     t->hb += t->h[i] + t->hrh[i + 1];
1008   
1009   /* Title. */
1010   if (!(t->flags & SOMF_NO_TITLE))
1011     t->ht += d->font_height;
1012 }
1013
1014 /* Return the number of columns and rows in the table into N_COLUMNS
1015    and N_ROWS, respectively. */
1016 static void
1017 tabi_count (int *n_columns, int *n_rows)
1018 {
1019   assert (n_columns != NULL && n_rows != NULL);
1020   *n_columns = t->nc;
1021   *n_rows = t->nr;
1022 }
1023
1024 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1025
1026 /* Return the horizontal and vertical size of the entire table,
1027    including headers, for the current output device, into HORIZ and
1028    VERT. */
1029 static void
1030 tabi_area (int *horiz, int *vert)
1031 {
1032   assert (horiz != NULL && vert != NULL);
1033   
1034   {
1035     int w, c;
1036     
1037     for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1038          c < t->nc - t->r; c++)
1039       w += t->w[c] + t->wrv[c];
1040     *horiz = w;
1041   }
1042   
1043   {
1044     int h, r;
1045     for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1046          r < t->nr - t->b; r++)
1047       h += t->h[r] + t->hrh[r];
1048     *vert = h;
1049   }
1050 }
1051
1052 /* Return the column style for this table into STYLE. */
1053 static void
1054 tabi_columns (int *style)
1055 {
1056   assert (style != NULL);
1057   *style = t->col_style;
1058 }
1059
1060 /* Return the number of header rows/columns on the left, right, top,
1061    and bottom sides into HL, HR, HT, and HB, respectively. */
1062 static void
1063 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1064 {
1065   assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1066   *hl = t->l;
1067   *hr = t->r;
1068   *ht = t->t;
1069   *hb = t->b;
1070 }
1071
1072 /* Determines the number of rows or columns (including appropriate
1073    headers), depending on CUMTYPE, that will fit into the space
1074    specified.  Takes rows/columns starting at index START and attempts
1075    to fill up available space MAX.  Returns in END the index of the
1076    last row/column plus one; returns in ACTUAL the actual amount of
1077    space the selected rows/columns (including appropriate headers)
1078    filled. */
1079 static void
1080 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1081 {
1082   int n;
1083   int *d;
1084   int *r;
1085   int total;
1086   
1087   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1088   if (cumtype == SOM_ROWS)
1089     {
1090       assert (start >= 0 && start < t->nr);
1091       n = t->nr - t->b;
1092       d = &t->h[start];
1093       r = &t->hrh[start + 1];
1094       total = t->ht + t->hb;
1095     } else {
1096       assert (start >= 0 && start < t->nc);
1097       n = t->nc - t->r;
1098       d = &t->w[start];
1099       r = &t->wrv[start + 1];
1100       total = t->wl + t->wr;
1101     }
1102   
1103   total += *d++;
1104   if (total > max)
1105     {
1106       if (end)
1107         *end = start;
1108       if (actual)
1109         *actual = 0;
1110       return;
1111     }
1112     
1113   {
1114     int x;
1115       
1116     for (x = start + 1; x < n; x++)
1117       {
1118         int amt = *d++ + *r++;
1119         
1120         total += amt;
1121         if (total > max)
1122           {
1123             total -= amt;
1124             break;
1125           }
1126       }
1127
1128     if (end)
1129       *end = x;
1130     
1131     if (actual)
1132       *actual = total;
1133   }
1134 }
1135
1136 /* Return flags set for the current table into FLAGS. */
1137 static void
1138 tabi_flags (unsigned *flags)
1139 {
1140   assert (flags != NULL);
1141   *flags = t->flags;
1142 }
1143
1144 /* Render title for current table, with major index X and minor index
1145    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1146    if Y is nonzero. */
1147 static void
1148 tabi_title (int x, int y)
1149 {
1150   char buf[1024];
1151   char *cp;
1152
1153   if (t->flags & SOMF_NO_TITLE)
1154     return;
1155   
1156   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1157   if (x && y)
1158     cp = spprintf (cp, "(%d:%d)", x, y);
1159   else if (x)
1160     cp = spprintf (cp, "(%d)", x);
1161   if (cur_proc)
1162     cp = spprintf (cp, " %s", cur_proc);
1163   cp = stpcpy (cp, ".  ");
1164   if (!ls_empty_p (&t->title))
1165     {
1166       memcpy (cp, ls_c_str (&t->title), ls_length (&t->title));
1167       cp += ls_length (&t->title);
1168     }
1169   *cp = 0;
1170   
1171   {
1172     struct outp_text text;
1173
1174     text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
1175     ls_init (&text.s, buf, cp - buf);
1176     text.h = d->width;
1177     text.v = d->font_height;
1178     text.x = 0;
1179     text.y = d->cp_y;
1180     d->class->text_draw (d, &text);
1181   }
1182 }
1183
1184 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1185
1186 /* Draws the table region in rectangle (X1,Y1)-(X2,Y2), where column
1187    X2 and row Y2 are not included in the rectangle, at the current
1188    position on the current output device.  Draws headers as well. */
1189 static void
1190 tabi_render (int x1, int y1, int x2, int y2)
1191 {
1192   int i, y;
1193   int ranges[3][2];
1194   
1195   tab_hit++;
1196
1197   y = d->cp_y;
1198   if (!(t->flags & SOMF_NO_TITLE))
1199     y += d->font_height;
1200
1201   /* Top headers. */
1202   ranges[0][0] = 0;
1203   ranges[0][1] = t->t * 2 + 1;
1204
1205   /* Requested rows. */
1206   ranges[1][0] = y1 * 2 + 1;
1207   ranges[1][1] = y2 * 2;
1208
1209   /* Bottom headers. */
1210   ranges[2][0] = (t->nr - t->b) * 2;
1211   ranges[2][1] = t->nr * 2 + 1;
1212
1213   for (i = 0; i < 3; i++) 
1214     {
1215       int r;
1216
1217       for (r = ranges[i][0]; r < ranges[i][1]; r++) 
1218         {
1219           int x = d->cp_x;
1220           x += render_strip (x, y, r, 0, t->l * 2 + 1, y1, y2);
1221           x += render_strip (x, y, r, x1 * 2 + 1, x2 * 2, y1, y2);
1222           x += render_strip (x, y, r, (t->nc - t->r) * 2,
1223                              t->nc * 2 + 1, y1, y2);
1224           y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
1225         }
1226     }
1227 }
1228
1229 struct som_table_class tab_table_class =
1230   {
1231     tabi_table,
1232     tabi_driver,
1233     
1234     tabi_count,
1235     tabi_area,
1236     NULL,
1237     NULL,
1238     tabi_columns,
1239     NULL,
1240     tabi_headers,
1241     NULL,
1242     tabi_cumulate,
1243     tabi_flags,
1244     
1245     NULL,
1246     NULL,
1247
1248     tabi_title,
1249     tabi_render,
1250   };
1251 \f
1252 /* Render contiguous strip consisting of columns C1...C2, exclusive,
1253    on row R, at location (X,Y).  Return width of the strip thus
1254    rendered.
1255
1256    Renders joined cells, even those outside the strip, within the
1257    rendering region (C1,R1)-(C2,R2).
1258
1259    For the purposes of counting rows and columns in this function
1260    only, horizontal rules are considered rows and vertical rules are
1261    considered columns.
1262
1263    FIXME: Doesn't use r1?  Huh?  */
1264 static int
1265 render_strip (int x, int y, int r, int c1, int c2, int r1 UNUSED, int r2)
1266 {
1267   int x_origin = x;
1268
1269   /* Horizontal rules. */
1270   if ((r & 1) == 0)
1271     {
1272       int hrh = t->hrh[r / 2];
1273       int c;
1274
1275       for (c = c1; c < c2; c++)
1276         {
1277           if (c & 1)
1278             {
1279               int style = t->rh[(c / 2) + (r / 2 * t->cf)];
1280
1281               if (style != TAL_0)
1282                 {
1283                   const struct color clr = {0, 0, 0, 0};
1284                   struct rect rct;
1285
1286                   rct.x1 = x;
1287                   rct.y1 = y;
1288                   rct.x2 = x + t->w[c / 2];
1289                   rct.y2 = y + hrh;
1290                   d->class->line_horz (d, &rct, &clr, style);
1291                 }
1292               x += t->w[c / 2];
1293             } else {
1294               const struct color clr = {0, 0, 0, 0};
1295               struct rect rct;
1296               struct outp_styles s;
1297
1298               rct.x1 = x;
1299               rct.y1 = y;
1300               rct.x2 = x + t->wrv[c / 2];
1301               rct.y2 = y + hrh;
1302
1303               s.t = r > 0 ? t->rv[(c / 2) + (t->cf + 1) * (r / 2 - 1)] : 0;
1304               s.b = r < 2 * t->nr ? t->rv[(c / 2) + (t->cf + 1) * (r / 2)] : 0;
1305               s.l = c > 0 ? t->rh[(c / 2 - 1) + t->cf * (r / 2)] : 0;
1306               s.r = c < 2 * t->nc ? t->rh[(c / 2) + t->cf * (r / 2)] : 0;
1307
1308               if (s.t | s.b | s.l | s.r)
1309                 d->class->line_intersection (d, &rct, &clr, &s);
1310               
1311               x += t->wrv[c / 2];
1312             }
1313         }
1314     } else {
1315       int c;
1316
1317       for (c = c1; c < c2; c++)
1318         {
1319           if (c & 1)
1320             {
1321               const int index = (c / 2) + (r / 2 * t->cf);
1322
1323               if (!(t->ct[index] & TAB_JOIN))
1324                 {
1325                   struct outp_text text;
1326
1327                   text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1328                                   | OUTP_T_HORZ | OUTP_T_VERT);
1329                   if ((t->ct[index] & TAB_EMPTY) == 0)
1330                     {
1331                       text.s = t->cc[index];
1332                       assert (!ls_null_p (&text.s));
1333                       text.h = t->w[c / 2];
1334                       text.v = t->h[r / 2];
1335                       text.x = x;
1336                       text.y = y;
1337                       d->class->text_draw (d, &text);
1338                     }
1339                 } else {
1340                   struct tab_joined_cell *j =
1341                     (struct tab_joined_cell *) ls_c_str (&t->cc[index]);
1342
1343                   if (j->hit != tab_hit)
1344                     {
1345                       j->hit = tab_hit;
1346
1347                       if (j->x1 == c / 2 && j->y1 == r / 2)
1348                         {
1349                           struct outp_text text;
1350
1351                           text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1352                                           | OUTP_T_HORZ | OUTP_T_VERT);
1353                           text.s = j->contents;
1354                           text.x = x;
1355                           text.y = y;
1356                           
1357                           {
1358                             int c;
1359
1360                             for (c = j->x1, text.h = -t->wrv[j->x2];
1361                                  c < j->x2 && c < c2 / 2; c++) 
1362                                 text.h += t->w[c] + t->wrv[c + 1]; 
1363                           }
1364                           
1365                           {
1366                             int r;
1367
1368                             for (r = j->y1, text.v = -t->hrh[j->y2];
1369                                  r < j->y2 && r < r2 / 2; r++)
1370                               text.v += t->h[r] + t->hrh[r + 1];
1371                           }
1372                           d->class->text_draw (d, &text);
1373                         }
1374                     }
1375                 }
1376               x += t->w[c / 2];
1377             } else {
1378               int style = t->rv[(c / 2) + (r / 2 * (t->cf + 1))];
1379
1380               if (style != TAL_0)
1381                 {
1382                   const struct color clr = {0, 0, 0, 0};
1383                   struct rect rct;
1384
1385                   rct.x1 = x;
1386                   rct.y1 = y;
1387                   rct.x2 = x + t->wrv[c / 2];
1388                   rct.y2 = y + t->h[r / 2];
1389                   d->class->line_vert (d, &rct, &clr, style);
1390                 }
1391               x += t->wrv[c / 2];
1392             }
1393         }
1394     }
1395
1396   return x - x_origin;
1397 }
1398