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