435fa609ce06d4547f885f07aad3ad5c6e336e08
[pspp] / src / output / tab.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2006, 2009, 2010, 2011, 2013, 2014, 2016 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 "output/tab.h"
20
21 #include <ctype.h>
22 #include <stdarg.h>
23 #include <limits.h>
24 #include <stdlib.h>
25
26 #include "data/data-out.h"
27 #include "data/format.h"
28 #include "data/settings.h"
29 #include "data/value.h"
30 #include "data/variable.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/i18n.h"
34 #include "libpspp/misc.h"
35 #include "libpspp/pool.h"
36 #include "output/driver.h"
37 #include "output/table-item.h"
38 #include "output/table-provider.h"
39 #include "output/text-item.h"
40
41 #include "gl/minmax.h"
42 #include "gl/xalloc.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46 \f
47
48 static const bool debugging = true;
49
50
51 /* Cell options. */
52 #define TAB_JOIN     (1u << TAB_FIRST_AVAILABLE)
53
54 /* Joined cell. */
55 struct tab_joined_cell
56 {
57   int d[TABLE_N_AXES][2];       /* Table region, same as struct table_cell. */
58   union
59   {
60     char *text;
61     struct table_item *subtable;
62   }
63     u;
64
65   size_t n_footnotes;
66   const struct footnote **footnotes;
67
68   const struct cell_style *style;
69 };
70
71 static const struct table_class tab_table_class;
72
73 struct fmt_spec ugly[n_RC] = {
74   {FMT_F, 8, 0},                /* INTEGER */
75   {FMT_F, 8, 3},                /* WEIGHT (ignored) */
76   {FMT_F, 8, 3},                /* PVALUE */
77   {FMT_F, 8, 3}                 /* OTHER (ignored) */
78 };
79
80
81 /* Creates and returns a new table with NC columns and NR rows and initially no
82    header rows or columns.  The table's cells are initially empty. */
83 struct tab_table *
84 tab_create (int nc, int nr)
85 {
86   struct tab_table *t;
87
88   t = pool_create_container (struct tab_table, container);
89   table_init (&t->table, &tab_table_class);
90   table_set_nc (&t->table, nc);
91   table_set_nr (&t->table, nr);
92
93   t->title = NULL;
94   t->caption = NULL;
95   t->cf = nc;
96   t->cc = pool_calloc (t->container, nr * nc, sizeof *t->cc);
97   t->ct = pool_calloc (t->container, nr * nc, sizeof *t->ct);
98
99   t->rh = pool_nmalloc (t->container, nc, nr + 1);
100   memset (t->rh, TAL_0, nc * (nr + 1));
101
102   t->rv = pool_nmalloc (t->container, nr, nc + 1);
103   memset (t->rv, TAL_0, nr * (nc + 1));
104
105   memset (t->fmtmap, 0, sizeof (*t->fmtmap) * n_RC);
106   t->fmtmap[RC_PVALUE] = ugly[RC_PVALUE];
107   t->fmtmap[RC_INTEGER] = ugly[RC_INTEGER];
108   t->fmtmap[RC_OTHER] = *settings_get_format ();
109
110   memset (t->styles, 0, sizeof t->styles);
111   memset (t->rule_colors, 0, sizeof t->rule_colors);
112
113   t->col_ofs = t->row_ofs = 0;
114
115   return t;
116 }
117
118
119 void
120 tab_set_format (struct tab_table *t, enum result_class rc,
121                 const struct fmt_spec *fmt)
122 {
123   t->fmtmap[rc] = *fmt;
124 }
125
126
127 /* Sets the width and height of a table, in columns and rows,
128    respectively.  Use only to reduce the size of a table, since it
129    does not change the amount of allocated memory.
130
131    This function is obsolete.  Please do not add new uses of it.  (Instead, use
132    table_select() or one of its helper functions.) */
133 void
134 tab_resize (struct tab_table *t, int nc, int nr)
135 {
136   if (nc != -1)
137     {
138       assert (nc + t->col_ofs <= t->cf);
139       table_set_nc (&t->table, nc + t->col_ofs);
140     }
141   if (nr != -1)
142     {
143       assert (nr + t->row_ofs <= tab_nr (t));
144       table_set_nr (&t->table, nr + t->row_ofs);
145     }
146 }
147
148 /* Changes either or both dimensions of a table and reallocates memory as
149    necessary.
150
151    This function is obsolete.  Please do not add new uses of it.  (Instead, use
152    table_paste() or one of its helper functions to paste multiple tables
153    together into a larger one.) */
154 void
155 tab_realloc (struct tab_table *t, int nc, int nr)
156 {
157   int ro, co;
158
159   ro = t->row_ofs;
160   co = t->col_ofs;
161   if (ro || co)
162     tab_offset (t, 0, 0);
163
164   if (nc == -1)
165     nc = tab_nc (t);
166   if (nr == -1)
167     nr = tab_nr (t);
168
169   assert (nc == tab_nc (t));
170
171   if (nc > t->cf)
172     {
173       int mr1 = MIN (nr, tab_nr (t));
174       int mc1 = MIN (nc, tab_nc (t));
175
176       void **new_cc;
177       unsigned short *new_ct;
178       int r;
179
180       new_cc = pool_calloc (t->container, nr * nc, sizeof *new_cc);
181       new_ct = pool_malloc (t->container, nr * nc);
182       for (r = 0; r < mr1; r++)
183         {
184           memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)],
185                   mc1 * sizeof *t->cc);
186           memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)],
187                   mc1 * sizeof *t->ct);
188           memset (&new_ct[r * nc + tab_nc (t)], 0, nc - tab_nc (t));
189         }
190       pool_free (t->container, t->cc);
191       pool_free (t->container, t->ct);
192       t->cc = new_cc;
193       t->ct = new_ct;
194       t->cf = nc;
195     }
196   else if (nr != tab_nr (t))
197     {
198       t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
199       t->ct = pool_nrealloc (t->container, t->ct, nr * nc, sizeof *t->ct);
200
201       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
202       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
203
204       if (nr > tab_nr (t))
205         {
206           memset (&t->rh[nc * (tab_nr (t) + 1)], TAL_0,
207                   (nr - tab_nr (t)) * nc);
208           memset (&t->rv[(nc + 1) * tab_nr (t)], TAL_0,
209                   (nr - tab_nr (t)) * (nc + 1));
210         }
211     }
212
213   memset (&t->ct[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)) * sizeof *t->ct);
214   memset (&t->cc[nc * tab_nr (t)], 0, nc * (nr - tab_nr (t)) * sizeof *t->cc);
215
216   table_set_nr (&t->table, nr);
217   table_set_nc (&t->table, nc);
218
219   if (ro || co)
220     tab_offset (t, co, ro);
221 }
222
223 /* Sets the number of header rows on each side of TABLE to L on the
224    left, R on the right, T on the top, B on the bottom.  Header rows
225    are repeated when a table is broken across multiple columns or
226    multiple pages. */
227 void
228 tab_headers (struct tab_table *table, int l, int r, int t, int b)
229 {
230   table_set_hl (&table->table, l);
231   table_set_hr (&table->table, r);
232   table_set_ht (&table->table, t);
233   table_set_hb (&table->table, b);
234 }
235 \f
236 /* Rules. */
237
238 /* Draws a vertical line to the left of cells at horizontal position X
239    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
240 void
241 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
242 {
243   if (debugging)
244     {
245       if (x + t->col_ofs < 0 || x + t->col_ofs > tab_nc (t)
246           || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
247           || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
248         {
249           printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
250                     "table size (%d,%d)\n"),
251                   x, t->col_ofs, x + t->col_ofs,
252                   y1, t->row_ofs, y1 + t->row_ofs,
253                   y2, t->row_ofs, y2 + t->row_ofs, tab_nc (t), tab_nr (t));
254           return;
255         }
256     }
257
258   x += t->col_ofs;
259   y1 += t->row_ofs;
260   y2 += t->row_ofs;
261
262   assert (x >= 0);
263   assert (x <= tab_nc (t));
264   assert (y1 >= 0);
265   assert (y2 >= y1);
266   assert (y2 <= tab_nr (t));
267
268   if (style != -1)
269     {
270       int y;
271       for (y = y1; y <= y2; y++)
272         t->rv[x + (t->cf + 1) * y] = style;
273     }
274 }
275
276 /* Draws a horizontal line above cells at vertical position Y from X1
277    to X2 inclusive in style STYLE, if style is not -1. */
278 void
279 tab_hline (struct tab_table *t, int style, int x1, int x2, int y)
280 {
281   if (debugging)
282     {
283       if (y + t->row_ofs < 0 || y + t->row_ofs > tab_nr (t)
284           || x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
285           || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t))
286         {
287           printf (_("bad hline: x=(%d+%d=%d,%d+%d=%d) y=%d+%d=%d in "
288                     "table size (%d,%d)\n"),
289                   x1, t->col_ofs, x1 + t->col_ofs,
290                   x2, t->col_ofs, x2 + t->col_ofs,
291                   y, t->row_ofs, y + t->row_ofs, tab_nc (t), tab_nr (t));
292           return;
293         }
294     }
295
296   x1 += t->col_ofs;
297   x2 += t->col_ofs;
298   y += t->row_ofs;
299
300   assert (y >= 0);
301   assert (y <= tab_nr (t));
302   assert (x2 >= x1);
303   assert (x1 >= 0);
304   assert (x2 < tab_nc (t));
305
306   if (style != -1)
307     {
308       int x;
309       for (x = x1; x <= x2; x++)
310         t->rh[x + t->cf * y] = style;
311     }
312 }
313
314 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
315    lines of style F_H and vertical lines of style F_V.  Fills the
316    interior of the box with horizontal lines of style I_H and vertical
317    lines of style I_V.  Any of the line styles may be -1 to avoid
318    drawing those lines.  This is distinct from 0, which draws a null
319    line. */
320 void
321 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
322          int x1, int y1, int x2, int y2)
323 {
324   if (debugging)
325     {
326       if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= tab_nc (t)
327           || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= tab_nc (t)
328           || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= tab_nr (t)
329           || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= tab_nr (t))
330         {
331           printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
332                     "in table size (%d,%d)\n"),
333                   x1, t->col_ofs, x1 + t->col_ofs,
334                   y1, t->row_ofs, y1 + t->row_ofs,
335                   x2, t->col_ofs, x2 + t->col_ofs,
336                   y2, t->row_ofs, y2 + t->row_ofs, tab_nc (t), tab_nr (t));
337           NOT_REACHED ();
338         }
339     }
340
341   x1 += t->col_ofs;
342   x2 += t->col_ofs;
343   y1 += t->row_ofs;
344   y2 += t->row_ofs;
345
346   assert (x2 >= x1);
347   assert (y2 >= y1);
348   assert (x1 >= 0);
349   assert (y1 >= 0);
350   assert (x2 < tab_nc (t));
351   assert (y2 < tab_nr (t));
352
353   if (f_h != -1)
354     {
355       int x;
356       for (x = x1; x <= x2; x++)
357         {
358           t->rh[x + t->cf * y1] = f_h;
359           t->rh[x + t->cf * (y2 + 1)] = f_h;
360         }
361     }
362   if (f_v != -1)
363     {
364       int y;
365       for (y = y1; y <= y2; y++)
366         {
367           t->rv[x1 + (t->cf + 1) * y] = f_v;
368           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
369         }
370     }
371
372   if (i_h != -1)
373     {
374       int y;
375
376       for (y = y1 + 1; y <= y2; y++)
377         {
378           int x;
379
380           for (x = x1; x <= x2; x++)
381             t->rh[x + t->cf * y] = i_h;
382         }
383     }
384   if (i_v != -1)
385     {
386       int x;
387
388       for (x = x1 + 1; x <= x2; x++)
389         {
390           int y;
391
392           for (y = y1; y <= y2; y++)
393             t->rv[x + (t->cf + 1) * y] = i_v;
394         }
395     }
396 }
397 \f
398 /* Cells. */
399
400 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
401    from V, displayed with format spec F. */
402 void
403 tab_value (struct tab_table *table, int c, int r, unsigned short opt,
404            const union value *v, const struct variable *var,
405            const struct fmt_spec *f)
406 {
407   char *contents;
408
409   if (debugging)
410     {
411       if (c + table->col_ofs < 0 || r + table->row_ofs < 0
412           || c + table->col_ofs >= tab_nc (table)
413           || r + table->row_ofs >= tab_nr (table))
414         {
415           printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
416                   "(%d,%d)\n",
417                   c, table->col_ofs, c + table->col_ofs,
418                   r, table->row_ofs, r + table->row_ofs,
419                   tab_nc (table), tab_nr (table));
420           return;
421         }
422     }
423
424   contents = data_out_stretchy (v, var_get_encoding (var),
425                                 f != NULL ? f : var_get_print_format (var),
426                                 table->container);
427
428   table->cc[c + r * table->cf] = contents;
429   table->ct[c + r * table->cf] = opt;
430 }
431
432 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
433    formatted by FMT.
434    If FMT is null, then the default print format will be used.
435 */
436 void
437 tab_double (struct tab_table *table, int c, int r, unsigned short opt,
438             double val, const struct fmt_spec *fmt, enum result_class rc)
439 {
440   union value double_value;
441   char *s;
442
443   assert (c >= 0);
444   assert (c < tab_nc (table));
445   assert (r >= 0);
446   assert (r < tab_nr (table));
447
448   if (fmt == NULL)
449     fmt = &table->fmtmap[rc];
450
451   fmt_check_output (fmt);
452
453   if (debugging)
454     {
455       if (c + table->col_ofs < 0 || r + table->row_ofs < 0
456           || c + table->col_ofs >= tab_nc (table)
457           || r + table->row_ofs >= tab_nr (table))
458         {
459           printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
460                   "(%d,%d)\n",
461                   c, table->col_ofs, c + table->col_ofs,
462                   r, table->row_ofs, r + table->row_ofs,
463                   tab_nc (table), tab_nr (table));
464           return;
465         }
466     }
467
468   double_value.f = val;
469   s = data_out_stretchy (&double_value, C_ENCODING, fmt, table->container);
470   table->cc[c + r * table->cf] = s + strspn (s, " ");
471   table->ct[c + r * table->cf] = opt;
472 }
473
474
475 static void
476 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
477 {
478   assert (c >= 0);
479   assert (r >= 0);
480   assert (c < tab_nc (table));
481   assert (r < tab_nr (table));
482
483   if (debugging)
484     {
485       if (c + table->col_ofs < 0 || r + table->row_ofs < 0
486           || c + table->col_ofs >= tab_nc (table)
487           || r + table->row_ofs >= tab_nr (table))
488         {
489           printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
490                   "(%d,%d)\n",
491                   c, table->col_ofs, c + table->col_ofs,
492                   r, table->row_ofs, r + table->row_ofs,
493                   tab_nc (table), tab_nr (table));
494           return;
495         }
496     }
497
498   table->cc[c + r * table->cf] = text;
499   table->ct[c + r * table->cf] = opt;
500 }
501
502 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
503    TEXT. */
504 void
505 tab_text (struct tab_table *table, int c, int r, unsigned opt,
506           const char *text)
507 {
508   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
509 }
510
511 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
512    FORMAT, which is formatted as if passed to printf. */
513 void
514 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
515                  const char *format, ...)
516 {
517   va_list args;
518
519   va_start (args, format);
520   do_tab_text (table, c, r, opt,
521                pool_vasprintf (table->container, format, args));
522   va_end (args);
523 }
524
525 static struct tab_joined_cell *
526 add_joined_cell (struct tab_table *table, int x1, int y1, int x2, int y2,
527                  unsigned opt)
528 {
529   struct tab_joined_cell *j;
530
531   assert (x1 + table->col_ofs >= 0);
532   assert (y1 + table->row_ofs >= 0);
533   assert (y2 >= y1);
534   assert (x2 >= x1);
535   assert (y2 + table->row_ofs < tab_nr (table));
536   assert (x2 + table->col_ofs < tab_nc (table));
537
538   if (debugging)
539     {
540       if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
541           || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
542           || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
543           || y2 < y1 || y2 + table->row_ofs >= tab_nr (table))
544         {
545           printf ("tab_joint_text(): bad cell "
546                   "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
547                   x1, table->col_ofs, x1 + table->col_ofs,
548                   y1, table->row_ofs, y1 + table->row_ofs,
549                   x2, table->col_ofs, x2 + table->col_ofs,
550                   y2, table->row_ofs, y2 + table->row_ofs,
551                   tab_nc (table), tab_nr (table));
552           return NULL;
553         }
554     }
555
556   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
557
558   j = pool_alloc (table->container, sizeof *j);
559   j->d[TABLE_HORZ][0] = x1 + table->col_ofs;
560   j->d[TABLE_VERT][0] = y1 + table->row_ofs;
561   j->d[TABLE_HORZ][1] = ++x2 + table->col_ofs;
562   j->d[TABLE_VERT][1] = ++y2 + table->row_ofs;
563   j->n_footnotes = 0;
564   j->footnotes = NULL;
565   j->style = NULL;
566
567   {
568     void **cc = &table->cc[x1 + y1 * table->cf];
569     unsigned short *ct = &table->ct[x1 + y1 * table->cf];
570     const int ofs = table->cf - (x2 - x1);
571
572     int y;
573
574     for (y = y1; y < y2; y++)
575       {
576         int x;
577
578         for (x = x1; x < x2; x++)
579           {
580             *cc++ = j;
581             *ct++ = opt | TAB_JOIN;
582           }
583
584         cc += ofs;
585         ct += ofs;
586       }
587   }
588
589   return j;
590 }
591
592 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
593    options OPT to have text value TEXT. */
594 void
595 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
596                 unsigned opt, const char *text)
597 {
598   char *s = pool_strdup (table->container, text);
599   if (x1 == x2 && y1 == y2)
600     do_tab_text (table, x1, y1, opt, s);
601   else
602     add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
603 }
604
605 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
606    with options OPT to have text value FORMAT, which is formatted
607    as if passed to printf. */
608 void
609 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2,
610                        int y2, unsigned opt, const char *format, ...)
611 {
612   va_list args;
613   char *s;
614
615   va_start (args, format);
616   s = pool_vasprintf (table->container, format, args);
617   va_end (args);
618
619   add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
620 }
621
622 struct footnote *
623 tab_create_footnote (struct tab_table *table, size_t idx, const char *content,
624                      const char *marker, struct cell_style *style)
625 {
626   struct footnote *f = pool_alloc (table->container, sizeof *f);
627   f->idx = idx;
628   f->content = pool_strdup (table->container, content);
629   f->marker = pool_strdup (table->container, marker);
630   f->style = style;
631   return f;
632 }
633
634 void
635 tab_add_footnote (struct tab_table *table, int x, int y,
636                   const struct footnote *f)
637 {
638   int index = x + y * table->cf;
639   unsigned short opt = table->ct[index];
640   struct tab_joined_cell *j;
641
642   if (opt & TAB_JOIN)
643     j = table->cc[index];
644   else
645     {
646       char *text = table->cc[index];
647
648       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
649       j->u.text = text ? text : xstrdup ("");
650     }
651
652   j->footnotes = pool_realloc (table->container, j->footnotes,
653                                (j->n_footnotes + 1) * sizeof *j->footnotes);
654
655   j->footnotes[j->n_footnotes++] = f;
656 }
657
658 void
659 tab_add_style (struct tab_table *table, int x, int y,
660                const struct cell_style *style)
661 {
662   int index = x + y * table->cf;
663   unsigned short opt = table->ct[index];
664   struct tab_joined_cell *j;
665
666   if (opt & TAB_JOIN)
667     j = table->cc[index];
668   else
669     {
670       char *text = table->cc[index];
671
672       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
673       j->u.text = text ? text : xstrdup ("");
674     }
675
676   j->style = style;
677 }
678
679 bool
680 tab_cell_is_empty (const struct tab_table *table, int c, int r)
681 {
682   return table->cc[c + r * table->cf] == NULL;
683 }
684 \f
685 /* Miscellaneous. */
686
687 /* Set the title of table T to TITLE, which is formatted as if
688    passed to printf(). */
689 void
690 tab_title (struct tab_table *t, const char *title, ...)
691 {
692   va_list args;
693
694   free (t->title);
695   va_start (args, title);
696   t->title = xvasprintf (title, args);
697   va_end (args);
698 }
699
700 /* Set the caption of table T to CAPTION, which is formatted as if
701    passed to printf(). */
702 void
703 tab_caption (struct tab_table *t, const char *caption, ...)
704 {
705   va_list args;
706
707   free (t->caption);
708   va_start (args, caption);
709   t->caption = xvasprintf (caption, args);
710   va_end (args);
711 }
712
713 /* Easy, type-safe way to submit a tab table to som. */
714 void
715 tab_submit (struct tab_table *t)
716 {
717   table_item_submit (table_item_create (&t->table, t->title, t->caption));
718 }
719 \f
720 /* Editing. */
721
722 /* Set table row and column offsets for all functions that affect
723    cells or rules. */
724 void
725 tab_offset (struct tab_table *t, int col, int row)
726 {
727   int diff = 0;
728
729   if (debugging)
730     {
731       if (row < -1 || row > tab_nr (t))
732         {
733           printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
734           NOT_REACHED ();
735         }
736       if (col < -1 || col > tab_nc (t))
737         {
738           printf ("tab_offset(): col=%d in %d-column table\n", col,
739                   tab_nc (t));
740           NOT_REACHED ();
741         }
742     }
743
744   if (row != -1)
745     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
746   if (col != -1)
747     diff += (col - t->col_ofs), t->col_ofs = col;
748
749   t->cc += diff;
750   t->ct += diff;
751 }
752
753 /* Increment the row offset by one. If the table is too small,
754    increase its size. */
755 void
756 tab_next_row (struct tab_table *t)
757 {
758   t->cc += t->cf;
759   t->ct += t->cf;
760   if (++t->row_ofs >= tab_nr (t))
761     tab_realloc (t, -1, tab_nr (t) * 4 / 3);
762 }
763 \f
764 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
765    bits.
766
767    This function is obsolete.  Please do not add new uses of it.  Instead, use
768    a text_item (see output/text-item.h). */
769 void
770 tab_output_text (int options, const char *string)
771 {
772   enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD
773                               : options & TAB_FIX ? TEXT_ITEM_MONOSPACE
774                               : TEXT_ITEM_PARAGRAPH);
775   text_item_submit (text_item_create (type, string));
776 }
777
778 /* Same as tab_output_text(), but FORMAT is passed through printf-like
779    formatting before output. */
780 void
781 tab_output_text_format (int options, const char *format, ...)
782 {
783   va_list args;
784   char *text;
785
786   va_start (args, format);
787   text = xvasprintf (format, args);
788   va_end (args);
789
790   tab_output_text (options, text);
791
792   free (text);
793 }
794 \f
795 /* Table class implementation. */
796
797 static void
798 tab_destroy (struct table *table)
799 {
800   struct tab_table *t = tab_cast (table);
801   free (t->title);
802   t->title = NULL;
803   free (t->caption);
804   t->caption = NULL;
805   pool_destroy (t->container);
806 }
807
808 static void
809 tab_get_cell (const struct table *table, int x, int y,
810               struct table_cell *cell)
811 {
812   const struct tab_table *t = tab_cast (table);
813   int index = x + y * t->cf;
814   unsigned short opt = t->ct[index];
815   const void *cc = t->cc[index];
816
817   cell->inline_contents.options = opt;
818   cell->inline_contents.n_footnotes = 0;
819   cell->destructor = NULL;
820
821   int style_idx = (opt & TAB_STYLE_MASK) >> TAB_STYLE_SHIFT;
822   const struct cell_style *style = t->styles[style_idx];
823   if (style)
824     cell->style = style;
825
826   if (opt & TAB_JOIN)
827     {
828       const struct tab_joined_cell *jc = cc;
829       cell->contents = &cell->inline_contents;
830       cell->n_contents = 1;
831       cell->inline_contents.text = jc->u.text;
832
833       cell->inline_contents.footnotes = jc->footnotes;
834       cell->inline_contents.n_footnotes = jc->n_footnotes;
835
836       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
837       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
838       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
839       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
840
841       if (jc->style)
842         cell->style = jc->style;
843     }
844   else
845     {
846       cell->d[TABLE_HORZ][0] = x;
847       cell->d[TABLE_HORZ][1] = x + 1;
848       cell->d[TABLE_VERT][0] = y;
849       cell->d[TABLE_VERT][1] = y + 1;
850       if (cc != NULL)
851         {
852           cell->contents = &cell->inline_contents;
853           cell->n_contents = 1;
854           cell->inline_contents.text = CONST_CAST (char *, cc);
855         }
856       else
857         {
858           cell->contents = NULL;
859           cell->n_contents = 0;
860         }
861     }
862 }
863
864 static int
865 tab_get_rule (const struct table *table, enum table_axis axis, int x, int y,
866               struct cell_color *color)
867 {
868   const struct tab_table *t = tab_cast (table);
869   uint8_t raw = (axis == TABLE_VERT
870                  ? t->rh[x + t->cf * y] : t->rv[x + (t->cf + 1) * y]);
871   struct cell_color *p = t->rule_colors[(raw & TAB_RULE_STYLE_MASK)
872                                         >> TAB_RULE_STYLE_SHIFT];
873   if (p)
874     *color = *p;
875   return (raw & TAB_RULE_TYPE_MASK) >> TAB_RULE_TYPE_SHIFT;
876 }
877
878 static const struct table_class tab_table_class = {
879   tab_destroy,
880   tab_get_cell,
881   tab_get_rule,
882   NULL,                         /* paste */
883   NULL,                         /* select */
884 };
885
886 struct tab_table *
887 tab_cast (const struct table *table)
888 {
889   assert (table->klass == &tab_table_class);
890   return UP_CAST (table, struct tab_table, table);
891 }