Get rid of most global variables in outputting tables.
[pspp-builds.git] / 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   char buf[1024];
1141   char *cp;
1142
1143   if (t->flags & SOMF_NO_TITLE)
1144     return;
1145
1146   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1147   if (x && y)
1148     cp = spprintf (cp, "(%d:%d)", x, y);
1149   else if (x)
1150     cp = spprintf (cp, "(%d)", x);
1151   if (command_name != NULL)
1152     cp = spprintf (cp, " %s", command_name);
1153   cp = stpcpy (cp, ".  ");
1154   if (t->title != NULL)
1155     {
1156       size_t length = strlen (t->title);
1157       memcpy (cp, t->title, length);
1158       cp += length;
1159     }
1160   *cp = 0;
1161
1162   text.font = OUTP_PROPORTIONAL;
1163   text.justification = OUTP_LEFT;
1164   text.string = ss_buffer (buf, cp - buf);
1165   text.h = r->driver->width;
1166   text.v = r->driver->font_height;
1167   text.x = 0;
1168   text.y = r->driver->cp_y;
1169   r->driver->class->text_draw (r->driver, &text);
1170 }
1171
1172 static int render_strip (const struct tab_rendering *,
1173                          int x, int y, int r, int c1, int c2, int r1, int r2);
1174
1175 static void
1176 add_range (int ranges[][2], int *np, int start, int end)
1177 {
1178   int n = *np;
1179   if (n == 0 || start > ranges[n - 1][1])
1180     {
1181       ranges[n][0] = start;
1182       ranges[n][1] = end;
1183       ++*np;
1184     }
1185   else
1186     ranges[n - 1][1] = end;
1187 }
1188
1189 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1190    current position on the current output device.  */
1191 static void
1192 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1193 {
1194   const struct tab_rendering *r = r_;
1195   const struct tab_table *t = r->table;
1196   int rows[3][2], cols[3][2];
1197   int n_row_ranges, n_col_ranges;
1198   int y, i;
1199
1200   /* Rows to render, counting horizontal rules as rows.  */
1201   n_row_ranges = 0;
1202   add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1203   add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1204   add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1205
1206   /* Columns to render, counting vertical rules as columns. */
1207   n_col_ranges = 0;
1208   add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1209   add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1210   add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1211
1212   y = r->driver->cp_y;
1213   if (!(t->flags & SOMF_NO_TITLE))
1214     y += r->driver->font_height;
1215   for (i = 0; i < n_row_ranges; i++)
1216     {
1217       int row;
1218
1219       for (row = rows[i][0]; row < rows[i][1]; row++)
1220         {
1221           int x, j;
1222
1223           x = r->driver->cp_x;
1224           for (j = 0; j < n_col_ranges; j++)
1225             x = render_strip (r, x, y, row,
1226                               cols[j][0], cols[j][1],
1227                               rows[i][0], rows[i][1]);
1228
1229           y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1230         }
1231     }
1232 }
1233
1234 const struct som_table_class tab_table_class =
1235   {
1236     tabi_count,
1237     tabi_columns,
1238     tabi_headers,
1239     tabi_flags,
1240
1241     tabi_render_init,
1242     tabi_render_free,
1243
1244     tabi_area,
1245     tabi_cumulate,
1246     tabi_title,
1247     tabi_render,
1248   };
1249 \f
1250 static enum outp_justification
1251 translate_justification (unsigned int opt)
1252 {
1253   switch (opt & TAB_ALIGN_MASK)
1254     {
1255     case TAB_RIGHT:
1256       return OUTP_RIGHT;
1257     case TAB_LEFT:
1258       return OUTP_LEFT;
1259     case TAB_CENTER:
1260       return OUTP_CENTER;
1261     default:
1262       NOT_REACHED ();
1263     }
1264 }
1265
1266 /* Returns the line style to use for drawing a rule of the given
1267    TYPE. */
1268 static enum outp_line_style
1269 rule_to_draw_type (unsigned char type)
1270 {
1271   switch (type)
1272     {
1273     case TAL_0:
1274     case TAL_GAP:
1275       return OUTP_L_NONE;
1276     case TAL_1:
1277       return OUTP_L_SINGLE;
1278     case TAL_2:
1279       return OUTP_L_DOUBLE;
1280     default:
1281       NOT_REACHED ();
1282     }
1283 }
1284
1285 /* Returns the horizontal rule at the given column and row. */
1286 static int
1287 get_hrule (const struct tab_table *t, int col, int row)
1288 {
1289   return t->rh[col + row * t->cf];
1290 }
1291
1292 /* Returns the vertical rule at the given column and row. */
1293 static int
1294 get_vrule (const struct tab_table *t, int col, int row)
1295 {
1296   return t->rv[col + row * (t->cf + 1)];
1297 }
1298
1299 /* Renders the horizontal rule at the given column and row
1300    at (X,Y) on the page. */
1301 static void
1302 render_horz_rule (const struct tab_rendering *r,
1303                   int x, int y, int col, int row)
1304 {
1305   enum outp_line_style style;
1306   style = rule_to_draw_type (get_hrule (r->table, col, row));
1307   if (style != OUTP_L_NONE)
1308     r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1309                             OUTP_L_NONE, style, OUTP_L_NONE, style);
1310 }
1311
1312 /* Renders the vertical rule at the given column and row
1313    at (X,Y) on the page. */
1314 static void
1315 render_vert_rule (const struct tab_rendering *r,
1316                   int x, int y, int col, int row)
1317 {
1318   enum outp_line_style style;
1319   style = rule_to_draw_type (get_vrule (r->table, col, row));
1320   if (style != OUTP_L_NONE)
1321     r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1322                             style, OUTP_L_NONE, style, OUTP_L_NONE);
1323 }
1324
1325 /* Renders the rule intersection at the given column and row
1326    at (X,Y) on the page. */
1327 static void
1328 render_rule_intersection (const struct tab_rendering *r,
1329                           int x, int y, int col, int row)
1330 {
1331   const struct tab_table *t = r->table;
1332
1333   /* Bounds of intersection. */
1334   int x0 = x;
1335   int y0 = y;
1336   int x1 = x + r->wrv[col];
1337   int y1 = y + r->hrh[row];
1338
1339   /* Lines on each side of intersection. */
1340   int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1341   int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1342   int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1343   int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1344
1345   /* Output style for each line. */
1346   enum outp_line_style o_top = rule_to_draw_type (top);
1347   enum outp_line_style o_left = rule_to_draw_type (left);
1348   enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1349   enum outp_line_style o_right = rule_to_draw_type (right);
1350
1351   if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1352       || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1353     r->driver->class->line (r->driver, x0, y0, x1, y1,
1354                             o_top, o_left, o_bottom, o_right);
1355 }
1356
1357 /* Returns the width of columns C1...C2 exclusive,
1358    including interior but not exterior rules. */
1359 static int
1360 strip_width (const struct tab_rendering *r, int c1, int c2)
1361 {
1362   int width = 0;
1363   int c;
1364
1365   for (c = c1; c < c2; c++)
1366     width += r->w[c] + r->wrv[c + 1];
1367   if (c1 < c2)
1368     width -= r->wrv[c2];
1369   return width;
1370 }
1371
1372 /* Returns the height of rows R1...R2 exclusive,
1373    including interior but not exterior rules. */
1374 static int
1375 strip_height (const struct tab_rendering *r, int r1, int r2)
1376 {
1377   int height = 0;
1378   int row;
1379
1380   for (row = r1; row < r2; row++)
1381     height += r->h[row] + r->hrh[row + 1];
1382   if (r1 < r2)
1383     height -= r->hrh[r2];
1384   return height;
1385 }
1386
1387 /* Renders the cell at the given column and row at (X,Y) on the
1388    page.  Also renders joined cells that extend as far to the
1389    right as C1 and as far down as R1. */
1390 static void
1391 render_cell (const struct tab_rendering *r,
1392              int x, int y, int col, int row, int c1, int r1)
1393 {
1394   const struct tab_table *t = r->table;
1395   const int index = col + (row * t->cf);
1396   unsigned char type = t->ct[index];
1397   struct substring *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 = r->w[col];
1408           text.v = r->h[row];
1409           text.x = x;
1410           text.y = y;
1411           r->driver->class->text_draw (r->driver, &text);
1412         }
1413     }
1414   else
1415     {
1416       struct tab_joined_cell *j
1417         = (struct tab_joined_cell *) ss_data (*content);
1418
1419       if (j->x1 == col && j->y1 == row)
1420         {
1421           struct outp_text text;
1422           text.font = options_to_font (type);
1423           text.justification = translate_justification (type);
1424           text.string = j->contents;
1425           text.x = x;
1426           text.y = y;
1427           text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1428           text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1429           r->driver->class->text_draw (r->driver, &text);
1430         }
1431     }
1432 }
1433
1434 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1435    on row ROW, at (X,Y).  Returns X position after rendering.
1436    Also renders joined cells that extend beyond that strip,
1437    cropping them to lie within rendering region (C0,R0)-(C1,R1).
1438    C0 and C1 count vertical rules as columns.
1439    ROW counts horizontal rules as rows, but R0 and R1 do not. */
1440 static int
1441 render_strip (const struct tab_rendering *r,
1442               int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1443 {
1444   int col;
1445
1446   for (col = c0; col < c1; col++)
1447     if (col & 1)
1448       {
1449         if (row & 1)
1450           render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1451         else
1452           render_horz_rule (r, x, y, col / 2, row / 2);
1453         x += r->w[col / 2];
1454       }
1455     else
1456       {
1457         if (row & 1)
1458           render_vert_rule (r, x, y, col / 2, row / 2);
1459         else
1460           render_rule_intersection (r, x, y, col / 2, row / 2);
1461         x += r->wrv[col / 2];
1462       }
1463
1464   return x;
1465 }
1466
1467 /* Sets COMMAND_NAME as the name of the current command,
1468    for embedding in output. */
1469 void
1470 tab_set_command_name (const char *command_name_)
1471 {
1472   free (command_name);
1473   command_name = command_name_ ? xstrdup (command_name_) : NULL;
1474 }