output: Use "struct string" in tabi_title in place of fixed-size buffer.
[pspp] / src / output / table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009 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 "error.h"
40 #include "minmax.h"
41 #include "xalloc.h"
42
43 #include "gettext.h"
44 #define _(msgid) gettext (msgid)
45 \f
46 const struct som_table_class tab_table_class;
47 static char *command_name;
48
49 /* Returns the font to use for a cell with the given OPTIONS. */
50 static enum outp_font
51 options_to_font (unsigned options)
52 {
53   return (options & TAB_FIX ? OUTP_FIXED
54           : options & TAB_EMPH ? OUTP_EMPHASIS
55           : OUTP_PROPORTIONAL);
56 }
57
58 /* Creates a table with NC columns and NR rows. */
59 struct tab_table *
60 tab_create (int nc, int nr, int reallocable UNUSED)
61 {
62   struct tab_table *t;
63
64   t = pool_create_container (struct tab_table, container);
65   t->col_style = TAB_COL_NONE;
66   t->col_group = 0;
67   t->title = NULL;
68   t->flags = SOMF_NONE;
69   t->nr = nr;
70   t->nc = t->cf = nc;
71   t->l = t->r = t->t = t->b = 0;
72
73   t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
74   t->ct = pool_malloc (t->container, nr * nc);
75   memset (t->ct, TAB_EMPTY, nc * nr);
76
77   t->rh = pool_nmalloc (t->container, nc, nr + 1);
78   memset (t->rh, 0, nc * (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->dim = 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, void *aux)
403 {
404   assert (t != NULL && t->dim == NULL);
405   t->dim = dim_func;
406   t->dim_aux = aux;
407 }
408
409 /* Returns the natural width of column C in table T for driver D, that
410    is, the smallest width necessary to display all its cells without
411    wrapping.  The width will be no larger than the page width minus
412    left and right rule widths. */
413 int
414 tab_natural_width (const struct tab_rendering *r, int col)
415 {
416   const struct tab_table *t = r->table;
417   int width, row, max_width;
418
419   assert (col >= 0 && col < t->nc);
420
421   width = 0;
422   for (row = 0; row < t->nr; row++)
423     {
424       struct outp_text text;
425       unsigned char opt = t->ct[col + row * t->cf];
426       int w;
427
428       if (opt & (TAB_JOIN | TAB_EMPTY))
429         continue;
430
431       text.string = t->cc[col + row * t->cf];
432       text.justification = OUTP_LEFT;
433       text.font = options_to_font (opt);
434       text.h = text.v = INT_MAX;
435
436       r->driver->class->text_metrics (r->driver, &text, &w, NULL);
437       if (w > width)
438         width = w;
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 = r->driver->prop_em_width * 8;
447     }
448
449   max_width = r->driver->width - r->wrv[0] - r->wrv[t->nc];
450   return MIN (width, max_width);
451 }
452
453 /* Returns the natural height of row R in table T for driver D, that
454    is, the minimum height necessary to display the information in the
455    cell at the widths set for each column. */
456 int
457 tab_natural_height (const struct tab_rendering *r, int row)
458 {
459   const struct tab_table *t = r->table;
460   int height, col;
461
462   assert (row >= 0 && row < t->nr);
463
464   height = r->driver->font_height;
465   for (col = 0; col < t->nc; col++)
466     {
467       struct outp_text text;
468       unsigned char opt = t->ct[col + row * t->cf];
469       int h;
470
471       if (opt & (TAB_JOIN | TAB_EMPTY))
472         continue;
473
474       text.string = t->cc[col + row * t->cf];
475       text.justification = OUTP_LEFT;
476       text.font = options_to_font (opt);
477       text.h = r->w[col];
478       text.v = INT_MAX;
479       r->driver->class->text_metrics (r->driver, &text, NULL, &h);
480
481       if (h > height)
482         height = h;
483     }
484
485   return height;
486 }
487
488 /* Callback function to set all columns and rows to their natural
489    dimensions.  Not really meant to be called directly.  */
490 void
491 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
492 {
493   const struct tab_table *t = r->table;
494   int i;
495
496   for (i = 0; i < t->nc; i++)
497     r->w[i] = tab_natural_width (r, i);
498
499   for (i = 0; i < t->nr; i++)
500     r->h[i] = tab_natural_height (r, i);
501 }
502
503 \f
504 /* Cells. */
505
506 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
507    from V, displayed with format spec F. */
508 void
509 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
510            const union value *v, const struct fmt_spec *f)
511 {
512   char *contents;
513
514   assert (table != NULL && v != NULL && f != NULL);
515 #if DEBUGGING
516   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
517       || c + table->col_ofs >= table->nc
518       || r + table->row_ofs >= table->nr)
519     {
520       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
521               "(%d,%d)\n",
522               c, table->col_ofs, c + table->col_ofs,
523               r, table->row_ofs, r + table->row_ofs,
524               table->nc, table->nr);
525       return;
526     }
527 #endif
528
529   contents = pool_alloc (table->container, f->w);
530   table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
531   table->ct[c + r * table->cf] = opt;
532
533   data_out (v, f, contents);
534 }
535
536 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
537    with NDEC decimal places. */
538 void
539 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
540            double val, int w, int d)
541 {
542   char *contents;
543   char buf[40], *cp;
544
545   struct fmt_spec f;
546   union value double_value;
547
548   assert (table != NULL && w <= 40);
549
550   assert (c >= 0);
551   assert (c < table->nc);
552   assert (r >= 0);
553   assert (r < table->nr);
554
555   f = fmt_for_output (FMT_F, w, d);
556
557 #if DEBUGGING
558   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
559       || c + table->col_ofs >= table->nc
560       || r + table->row_ofs >= table->nr)
561     {
562       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
563               "(%d,%d)\n",
564               c, table->col_ofs, c + table->col_ofs,
565               r, table->row_ofs, r + table->row_ofs,
566               table->nc, table->nr);
567       return;
568     }
569 #endif
570
571   double_value.f = val;
572   data_out (&double_value, &f, buf);
573
574   cp = buf;
575   while (isspace ((unsigned char) *cp) && cp < &buf[w])
576     cp++;
577   f.w = w - (cp - buf);
578
579   contents = pool_alloc (table->container, f.w);
580   table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
581   table->ct[c + r * table->cf] = opt;
582   memcpy (contents, cp, f.w);
583 }
584
585 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
586    formatted by FMT.
587    If FMT is null, then the default print format will be used.
588 */
589 void
590 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
591            double val, const struct fmt_spec *fmt)
592 {
593   int w;
594   char *contents;
595   char buf[40], *cp;
596
597   union value double_value;
598
599   assert (table != NULL);
600
601   assert (c >= 0);
602   assert (c < table->nc);
603   assert (r >= 0);
604   assert (r < table->nr);
605
606   if ( fmt == NULL)
607     fmt = settings_get_format ();
608
609   fmt_check_output (fmt);
610
611 #if DEBUGGING
612   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
613       || c + table->col_ofs >= table->nc
614       || r + table->row_ofs >= table->nr)
615     {
616       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
617               "(%d,%d)\n",
618               c, table->col_ofs, c + table->col_ofs,
619               r, table->row_ofs, r + table->row_ofs,
620               table->nc, table->nr);
621       return;
622     }
623 #endif
624
625   double_value.f = val;
626   data_out (&double_value, fmt, buf);
627
628   cp = buf;
629   while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
630     cp++;
631   w = fmt->w - (cp - buf);
632
633   contents = pool_alloc (table->container, w);
634   table->cc[c + r * table->cf] = ss_buffer (contents, w);
635   table->ct[c + r * table->cf] = opt;
636   memcpy (contents, cp, w);
637 }
638
639
640 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
641    TEXT. */
642 void
643 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
644 {
645   va_list args;
646
647   assert (table != NULL && text != NULL);
648
649   assert (c >= 0 );
650   assert (r >= 0 );
651   assert (c < table->nc);
652   assert (r < table->nr);
653
654
655 #if DEBUGGING
656   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
657       || c + table->col_ofs >= table->nc
658       || r + table->row_ofs >= table->nr)
659     {
660       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
661               "(%d,%d)\n",
662               c, table->col_ofs, c + table->col_ofs,
663               r, table->row_ofs, r + table->row_ofs,
664               table->nc, table->nr);
665       return;
666     }
667 #endif
668
669   va_start (args, text);
670   table->cc[c + r * table->cf] = text_format (table, opt, text, args);
671   table->ct[c + r * table->cf] = opt;
672   va_end (args);
673 }
674
675 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
676    options OPT to have text value TEXT. */
677 void
678 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
679                 unsigned opt, const char *text, ...)
680 {
681   struct tab_joined_cell *j;
682
683   assert (table != NULL && text != NULL);
684
685   assert (x1 + table->col_ofs >= 0);
686   assert (y1 + table->row_ofs >= 0);
687   assert (y2 >= y1);
688   assert (x2 >= x1);
689   assert (y2 + table->row_ofs < table->nr);
690   assert (x2 + table->col_ofs < table->nc);
691
692 #if DEBUGGING
693   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
694       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
695       || x2 < x1 || x2 + table->col_ofs >= table->nc
696       || y2 < y2 || y2 + table->row_ofs >= table->nr)
697     {
698       printf ("tab_joint_text(): bad cell "
699               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
700               x1, table->col_ofs, x1 + table->col_ofs,
701               y1, table->row_ofs, y1 + table->row_ofs,
702               x2, table->col_ofs, x2 + table->col_ofs,
703               y2, table->row_ofs, y2 + table->row_ofs,
704               table->nc, table->nr);
705       return;
706     }
707 #endif
708
709   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
710
711   j = pool_alloc (table->container, sizeof *j);
712   j->hit = 0;
713   j->x1 = x1 + table->col_ofs;
714   j->y1 = y1 + table->row_ofs;
715   j->x2 = ++x2 + table->col_ofs;
716   j->y2 = ++y2 + table->row_ofs;
717
718   {
719     va_list args;
720
721     va_start (args, text);
722     j->contents = text_format (table, opt, text, args);
723     va_end (args);
724   }
725
726   opt |= TAB_JOIN;
727
728   {
729     struct substring *cc = &table->cc[x1 + y1 * table->cf];
730     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
731     const int ofs = table->cf - (x2 - x1);
732
733     int y;
734
735     for (y = y1; y < y2; y++)
736       {
737         int x;
738
739         for (x = x1; x < x2; x++)
740           {
741             *cc++ = ss_buffer ((char *) j, 0);
742             *ct++ = opt;
743           }
744
745         cc += ofs;
746         ct += ofs;
747       }
748   }
749 }
750
751 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
752 void
753 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
754          struct substring *string)
755 {
756   assert (table != NULL && string != NULL);
757
758 #if DEBUGGING
759   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
760       || c + table->col_ofs >= table->nc
761       || r + table->row_ofs >= table->nr)
762     {
763       printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
764               "(%d,%d)\n",
765               c, table->col_ofs, c + table->col_ofs,
766               r, table->row_ofs, r + table->row_ofs,
767               table->nc, table->nr);
768       return;
769     }
770 #endif
771
772   table->cc[c + r * table->cf] = *string;
773   table->ct[c + r * table->cf] = opt;
774 }
775 \f
776 /* Miscellaneous. */
777
778 /* Sets the widths of all the columns and heights of all the rows in
779    table T for driver D. */
780 static void
781 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
782 {
783   r->w[0] = tab_natural_width (r, 0);
784   r->h[0] = r->driver->font_height;
785 }
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 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
791 {
792   r->w[0] = tab_natural_width (r, 0);
793   r->h[0] = tab_natural_height (r, 0);
794 }
795
796 /* Outputs text BUF as a table with a single cell having cell options
797    OPTIONS, which is a combination of the TAB_* and TAT_*
798    constants. */
799 void
800 tab_output_text (int options, const char *buf, ...)
801 {
802   struct tab_table *t = tab_create (1, 1, 0);
803   char *tmp_buf = NULL;
804
805   if (options & TAT_PRINTF)
806     {
807       va_list args;
808
809       va_start (args, buf);
810       buf = tmp_buf = xvasprintf (buf, args);
811       va_end (args);
812     }
813
814   tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
815   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
816   tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL);
817   tab_submit (t);
818
819   free (tmp_buf);
820 }
821
822 /* Set table flags to FLAGS. */
823 void
824 tab_flags (struct tab_table *t, unsigned flags)
825 {
826   assert (t != NULL);
827   t->flags = flags;
828 }
829
830 /* Easy, type-safe way to submit a tab table to som. */
831 void
832 tab_submit (struct tab_table *t)
833 {
834   struct som_entity s;
835
836   assert (t != NULL);
837   s.class = &tab_table_class;
838   s.ext = t;
839   s.type = SOM_TABLE;
840   som_submit (&s);
841   tab_destroy (t);
842 }
843 \f
844 /* Editing. */
845
846 /* Set table row and column offsets for all functions that affect
847    cells or rules. */
848 void
849 tab_offset (struct tab_table *t, int col, int row)
850 {
851   int diff = 0;
852
853   assert (t != NULL);
854 #if DEBUGGING
855   if (row < -1 || row > t->nr)
856     {
857       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
858       NOT_REACHED ();
859     }
860   if (col < -1 || col > t->nc)
861     {
862       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
863       NOT_REACHED ();
864     }
865 #endif
866
867   if (row != -1)
868     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
869   if (col != -1)
870     diff += (col - t->col_ofs), t->col_ofs = col;
871
872   t->cc += diff;
873   t->ct += diff;
874 }
875
876 /* Increment the row offset by one. If the table is too small,
877    increase its size. */
878 void
879 tab_next_row (struct tab_table *t)
880 {
881   assert (t != NULL);
882   t->cc += t->cf;
883   t->ct += t->cf;
884   if (++t->row_ofs >= t->nr)
885     tab_realloc (t, -1, t->nr * 4 / 3);
886 }
887 \f
888 /* Return the number of columns and rows in the table into N_COLUMNS
889    and N_ROWS, respectively. */
890 static void
891 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
892 {
893   struct tab_table *t = t_->ext;
894   *n_columns = t->nc;
895   *n_rows = t->nr;
896 }
897
898 /* Return the column style for this table into STYLE. */
899 static void
900 tabi_columns (struct som_entity *t_, int *style)
901 {
902   struct tab_table *t = t_->ext;
903   *style = t->col_style;
904 }
905
906 /* Return the number of header rows/columns on the left, right, top,
907    and bottom sides into HL, HR, HT, and HB, respectively. */
908 static void
909 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
910 {
911   struct tab_table *t = t_->ext;
912   *hl = t->l;
913   *hr = t->r;
914   *ht = t->t;
915   *hb = t->b;
916 }
917
918 /* Return flags set for the current table into FLAGS. */
919 static void
920 tabi_flags (struct som_entity *t_, unsigned *flags)
921 {
922   struct tab_table *t = t_->ext;
923   *flags = t->flags;
924 }
925
926 /* Returns the line style to use for spacing purposes for a rule
927    of the given TYPE. */
928 static enum outp_line_style
929 rule_to_spacing_type (unsigned char type)
930 {
931   switch (type)
932     {
933     case TAL_0:
934       return OUTP_L_NONE;
935     case TAL_GAP:
936     case TAL_1:
937       return OUTP_L_SINGLE;
938     case TAL_2:
939       return OUTP_L_DOUBLE;
940     default:
941       NOT_REACHED ();
942     }
943 }
944
945 static void *
946 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
947                   int hl, int hr, int ht, int hb)
948 {
949   const struct tab_table *t = t_->ext;
950   struct tab_rendering *r;
951   int col, row;
952   int i;
953
954   tab_offset (t_->ext, 0, 0);
955
956   r = xmalloc (sizeof *r);
957   r->table = t;
958   r->driver = driver;
959   r->w = xnmalloc (t->nc, sizeof *r->w);
960   r->h = xnmalloc (t->nr, sizeof *r->h);
961   r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
962   r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
963   r->l = hl;
964   r->r = hr;
965   r->t = ht;
966   r->b = hb;
967
968   /* Figure out sizes of rules. */
969   for (row = 0; row <= t->nr; row++)
970     {
971       int width = 0;
972       for (col = 0; col < t->nc; col++)
973         {
974           unsigned char rh = t->rh[col + row * t->cf];
975           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
976           if (w > width)
977             width = w;
978         }
979       r->hrh[row] = width;
980     }
981
982   for (col = 0; col <= t->nc; col++)
983     {
984       int width = 0;
985       for (row = 0; row < t->nr; row++)
986         {
987           unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
988           int w;
989           if (*rv == UCHAR_MAX)
990             *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
991           w = driver->vert_line_width[rule_to_spacing_type (*rv)];
992           if (w > width)
993             width = w;
994         }
995       r->wrv[col] = width;
996     }
997
998   /* Determine row heights and columns widths. */
999   for (i = 0; i < t->nr; i++)
1000     r->h[i] = -1;
1001   for (i = 0; i < t->nc; i++)
1002     r->w[i] = -1;
1003
1004   t->dim (r, t->dim_aux);
1005
1006   for (i = 0; i < t->nr; i++)
1007     if (r->h[i] < 0)
1008       error (0, 0, "height of table row %d not initialized", i);
1009   for (i = 0; i < t->nc; i++)
1010     if (r->w[i] < 0)
1011       error (0, 0, "width of table column %d not initialized", i);
1012
1013   /* Add up header sizes. */
1014   for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1015     r->wl += r->w[i] + r->wrv[i + 1];
1016   for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1017     r->ht += r->h[i] + r->hrh[i + 1];
1018   for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1019     r->wr += r->w[i] + r->wrv[i + 1];
1020   for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1021     r->hb += r->h[i] + r->hrh[i + 1];
1022
1023   /* Title. */
1024   if (!(t->flags & SOMF_NO_TITLE))
1025     r->ht += driver->font_height;
1026
1027   return r;
1028 }
1029
1030 static void
1031 tabi_render_free (void *r_)
1032 {
1033   struct tab_rendering *r = r_;
1034
1035   free (r->w);
1036   free (r->h);
1037   free (r->hrh);
1038   free (r->wrv);
1039   free (r);
1040 }
1041
1042 /* Return the horizontal and vertical size of the entire table,
1043    including headers, for the current output device, into HORIZ and
1044    VERT. */
1045 static void
1046 tabi_area (void *r_, int *horiz, int *vert)
1047 {
1048   struct tab_rendering *r = r_;
1049   const struct tab_table *t = r->table;
1050   int width, col;
1051   int height, row;
1052
1053   width = 0;
1054   for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1055        col < t->nc - r->r; col++)
1056     width += r->w[col] + r->wrv[col];
1057   *horiz = width;
1058
1059   height = 0;
1060   for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1061        row < t->nr - t->b; row++)
1062     height += r->h[row] + r->hrh[row];
1063   *vert = height;
1064 }
1065
1066 /* Determines the number of rows or columns (including appropriate
1067    headers), depending on CUMTYPE, that will fit into the space
1068    specified.  Takes rows/columns starting at index START and attempts
1069    to fill up available space MAX.  Returns in END the index of the
1070    last row/column plus one; returns in ACTUAL the actual amount of
1071    space the selected rows/columns (including appropriate headers)
1072    filled. */
1073 static void
1074 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1075                int max, int *actual)
1076 {
1077   const struct tab_rendering *r = r_;
1078   const struct tab_table *t = r->table;
1079   int limit;
1080   int *cells, *rules;
1081   int total;
1082   int idx;
1083
1084   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1085   if (cumtype == SOM_ROWS)
1086     {
1087       assert (start >= 0 && start < t->nr);
1088       limit = t->nr - r->b;
1089       cells = &r->h[start];
1090       rules = &r->hrh[start + 1];
1091       total = r->ht + r->hb;
1092     }
1093   else
1094     {
1095       assert (start >= 0 && start < t->nc);
1096       limit = t->nc - t->r;
1097       cells = &r->w[start];
1098       rules = &r->wrv[start + 1];
1099       total = r->wl + r->wr;
1100     }
1101
1102   total += *cells++;
1103   if (total > max)
1104     {
1105       if (end)
1106         *end = start;
1107       if (actual)
1108         *actual = 0;
1109       return;
1110     }
1111
1112   for (idx = start + 1; idx < limit; idx++)
1113     {
1114       int amt = *cells++ + *rules++;
1115
1116       total += amt;
1117       if (total > max)
1118         {
1119           total -= amt;
1120           break;
1121         }
1122     }
1123
1124   if (end)
1125     *end = idx;
1126
1127   if (actual)
1128     *actual = total;
1129 }
1130
1131 /* Render title for current table, with major index X and minor index
1132    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1133    if Y is nonzero. */
1134 static void
1135 tabi_title (void *r_, int x, int y)
1136 {
1137   const struct tab_rendering *r = r_;
1138   const struct tab_table *t = r->table;
1139   struct outp_text text;
1140   struct string title;
1141
1142   if (t->flags & SOMF_NO_TITLE)
1143     return;
1144
1145   ds_init_empty (&title);
1146   ds_put_format (&title,"%d.%d", table_num, subtable_num);
1147   if (x && y)
1148     ds_put_format (&title, "(%d:%d)", x, y);
1149   else if (x)
1150     ds_put_format (&title, "(%d)", x);
1151   if (command_name != NULL)
1152     ds_put_format (&title, " %s", command_name);
1153   ds_put_cstr (&title, ".  ");
1154   if (t->title != NULL)
1155     ds_put_cstr (&title, t->title);
1156
1157   text.font = OUTP_PROPORTIONAL;
1158   text.justification = OUTP_LEFT;
1159   text.string = ds_ss (&title);
1160   text.h = r->driver->width;
1161   text.v = r->driver->font_height;
1162   text.x = 0;
1163   text.y = r->driver->cp_y;
1164   r->driver->class->text_draw (r->driver, &text);
1165
1166   ds_destroy (&title);
1167 }
1168
1169 static int render_strip (const struct tab_rendering *,
1170                          int x, int y, int r, int c1, int c2, int r1, int r2);
1171
1172 static void
1173 add_range (int ranges[][2], int *np, int start, int end)
1174 {
1175   int n = *np;
1176   if (n == 0 || start > ranges[n - 1][1])
1177     {
1178       ranges[n][0] = start;
1179       ranges[n][1] = end;
1180       ++*np;
1181     }
1182   else
1183     ranges[n - 1][1] = end;
1184 }
1185
1186 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1187    current position on the current output device.  */
1188 static void
1189 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1190 {
1191   const struct tab_rendering *r = r_;
1192   const struct tab_table *t = r->table;
1193   int rows[3][2], cols[3][2];
1194   int n_row_ranges, n_col_ranges;
1195   int y, i;
1196
1197   /* Rows to render, counting horizontal rules as rows.  */
1198   n_row_ranges = 0;
1199   add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1200   add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1201   add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1202
1203   /* Columns to render, counting vertical rules as columns. */
1204   n_col_ranges = 0;
1205   add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1206   add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1207   add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1208
1209   y = r->driver->cp_y;
1210   if (!(t->flags & SOMF_NO_TITLE))
1211     y += r->driver->font_height;
1212   for (i = 0; i < n_row_ranges; i++)
1213     {
1214       int row;
1215
1216       for (row = rows[i][0]; row < rows[i][1]; row++)
1217         {
1218           int x, j;
1219
1220           x = r->driver->cp_x;
1221           for (j = 0; j < n_col_ranges; j++)
1222             x = render_strip (r, x, y, row,
1223                               cols[j][0], cols[j][1],
1224                               rows[i][0], rows[i][1]);
1225
1226           y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1227         }
1228     }
1229 }
1230
1231 const struct som_table_class tab_table_class =
1232   {
1233     tabi_count,
1234     tabi_columns,
1235     tabi_headers,
1236     tabi_flags,
1237
1238     tabi_render_init,
1239     tabi_render_free,
1240
1241     tabi_area,
1242     tabi_cumulate,
1243     tabi_title,
1244     tabi_render,
1245   };
1246 \f
1247 static enum outp_justification
1248 translate_justification (unsigned int opt)
1249 {
1250   switch (opt & TAB_ALIGN_MASK)
1251     {
1252     case TAB_RIGHT:
1253       return OUTP_RIGHT;
1254     case TAB_LEFT:
1255       return OUTP_LEFT;
1256     case TAB_CENTER:
1257       return OUTP_CENTER;
1258     default:
1259       NOT_REACHED ();
1260     }
1261 }
1262
1263 /* Returns the line style to use for drawing a rule of the given
1264    TYPE. */
1265 static enum outp_line_style
1266 rule_to_draw_type (unsigned char type)
1267 {
1268   switch (type)
1269     {
1270     case TAL_0:
1271     case TAL_GAP:
1272       return OUTP_L_NONE;
1273     case TAL_1:
1274       return OUTP_L_SINGLE;
1275     case TAL_2:
1276       return OUTP_L_DOUBLE;
1277     default:
1278       NOT_REACHED ();
1279     }
1280 }
1281
1282 /* Returns the horizontal rule at the given column and row. */
1283 static int
1284 get_hrule (const struct tab_table *t, int col, int row)
1285 {
1286   return t->rh[col + row * t->cf];
1287 }
1288
1289 /* Returns the vertical rule at the given column and row. */
1290 static int
1291 get_vrule (const struct tab_table *t, int col, int row)
1292 {
1293   return t->rv[col + row * (t->cf + 1)];
1294 }
1295
1296 /* Renders the horizontal rule at the given column and row
1297    at (X,Y) on the page. */
1298 static void
1299 render_horz_rule (const struct tab_rendering *r,
1300                   int x, int y, int col, int row)
1301 {
1302   enum outp_line_style style;
1303   style = rule_to_draw_type (get_hrule (r->table, col, row));
1304   if (style != OUTP_L_NONE)
1305     r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1306                             OUTP_L_NONE, style, OUTP_L_NONE, style);
1307 }
1308
1309 /* Renders the vertical rule at the given column and row
1310    at (X,Y) on the page. */
1311 static void
1312 render_vert_rule (const struct tab_rendering *r,
1313                   int x, int y, int col, int row)
1314 {
1315   enum outp_line_style style;
1316   style = rule_to_draw_type (get_vrule (r->table, col, row));
1317   if (style != OUTP_L_NONE)
1318     r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1319                             style, OUTP_L_NONE, style, OUTP_L_NONE);
1320 }
1321
1322 /* Renders the rule intersection at the given column and row
1323    at (X,Y) on the page. */
1324 static void
1325 render_rule_intersection (const struct tab_rendering *r,
1326                           int x, int y, int col, int row)
1327 {
1328   const struct tab_table *t = r->table;
1329
1330   /* Bounds of intersection. */
1331   int x0 = x;
1332   int y0 = y;
1333   int x1 = x + r->wrv[col];
1334   int y1 = y + r->hrh[row];
1335
1336   /* Lines on each side of intersection. */
1337   int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1338   int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1339   int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1340   int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1341
1342   /* Output style for each line. */
1343   enum outp_line_style o_top = rule_to_draw_type (top);
1344   enum outp_line_style o_left = rule_to_draw_type (left);
1345   enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1346   enum outp_line_style o_right = rule_to_draw_type (right);
1347
1348   if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1349       || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1350     r->driver->class->line (r->driver, x0, y0, x1, y1,
1351                             o_top, o_left, o_bottom, o_right);
1352 }
1353
1354 /* Returns the width of columns C1...C2 exclusive,
1355    including interior but not exterior rules. */
1356 static int
1357 strip_width (const struct tab_rendering *r, int c1, int c2)
1358 {
1359   int width = 0;
1360   int c;
1361
1362   for (c = c1; c < c2; c++)
1363     width += r->w[c] + r->wrv[c + 1];
1364   if (c1 < c2)
1365     width -= r->wrv[c2];
1366   return width;
1367 }
1368
1369 /* Returns the height of rows R1...R2 exclusive,
1370    including interior but not exterior rules. */
1371 static int
1372 strip_height (const struct tab_rendering *r, int r1, int r2)
1373 {
1374   int height = 0;
1375   int row;
1376
1377   for (row = r1; row < r2; row++)
1378     height += r->h[row] + r->hrh[row + 1];
1379   if (r1 < r2)
1380     height -= r->hrh[r2];
1381   return height;
1382 }
1383
1384 /* Renders the cell at the given column and row at (X,Y) on the
1385    page.  Also renders joined cells that extend as far to the
1386    right as C1 and as far down as R1. */
1387 static void
1388 render_cell (const struct tab_rendering *r,
1389              int x, int y, int col, int row, int c1, int r1)
1390 {
1391   const struct tab_table *t = r->table;
1392   const int index = col + (row * t->cf);
1393   unsigned char type = t->ct[index];
1394   struct substring *content = &t->cc[index];
1395
1396   if (!(type & TAB_JOIN))
1397     {
1398       if (!(type & TAB_EMPTY))
1399         {
1400           struct outp_text text;
1401           text.font = options_to_font (type);
1402           text.justification = translate_justification (type);
1403           text.string = *content;
1404           text.h = r->w[col];
1405           text.v = r->h[row];
1406           text.x = x;
1407           text.y = y;
1408           r->driver->class->text_draw (r->driver, &text);
1409         }
1410     }
1411   else
1412     {
1413       struct tab_joined_cell *j
1414         = (struct tab_joined_cell *) ss_data (*content);
1415
1416       if (j->x1 == col && j->y1 == row)
1417         {
1418           struct outp_text text;
1419           text.font = options_to_font (type);
1420           text.justification = translate_justification (type);
1421           text.string = j->contents;
1422           text.x = x;
1423           text.y = y;
1424           text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1425           text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1426           r->driver->class->text_draw (r->driver, &text);
1427         }
1428     }
1429 }
1430
1431 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1432    on row ROW, at (X,Y).  Returns X position after rendering.
1433    Also renders joined cells that extend beyond that strip,
1434    cropping them to lie within rendering region (C0,R0)-(C1,R1).
1435    C0 and C1 count vertical rules as columns.
1436    ROW counts horizontal rules as rows, but R0 and R1 do not. */
1437 static int
1438 render_strip (const struct tab_rendering *r,
1439               int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1440 {
1441   int col;
1442
1443   for (col = c0; col < c1; col++)
1444     if (col & 1)
1445       {
1446         if (row & 1)
1447           render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1448         else
1449           render_horz_rule (r, x, y, col / 2, row / 2);
1450         x += r->w[col / 2];
1451       }
1452     else
1453       {
1454         if (row & 1)
1455           render_vert_rule (r, x, y, col / 2, row / 2);
1456         else
1457           render_rule_intersection (r, x, y, col / 2, row / 2);
1458         x += r->wrv[col / 2];
1459       }
1460
1461   return x;
1462 }
1463
1464 /* Sets COMMAND_NAME as the name of the current command,
1465    for embedding in output. */
1466 void
1467 tab_set_command_name (const char *command_name_)
1468 {
1469   free (command_name);
1470   command_name = command_name_ ? xstrdup (command_name_) : NULL;
1471 }