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