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