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