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