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