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