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