tab: Expand space for table options from 8 to 16 bits each.
[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   const struct footnote **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_calloc (t->container, nr * nc, sizeof *t->ct);
99
100   t->rh = pool_nmalloc (t->container, nc, nr + 1);
101   memset (t->rh, TAL_0, nc * (nr + 1));
102
103   t->rv = pool_nmalloc (t->container, nr, nc + 1);
104   memset (t->rv, TAL_0, nr * (nc + 1));
105
106   memset (t->fmtmap, 0, sizeof (*t->fmtmap) * n_RC);
107   t->fmtmap[RC_PVALUE] = ugly[RC_PVALUE];
108   t->fmtmap[RC_INTEGER] = ugly[RC_INTEGER];
109   t->fmtmap[RC_OTHER] = *settings_get_format ();
110
111   t->col_ofs = t->row_ofs = 0;
112
113   return t;
114 }
115
116
117 void
118 tab_set_format (struct tab_table *t, enum result_class rc,
119                 const struct fmt_spec *fmt)
120 {
121   t->fmtmap[rc] = *fmt;
122 }
123
124
125 /* Sets the width and height of a table, in columns and rows,
126    respectively.  Use only to reduce the size of a table, since it
127    does not change the amount of allocated memory.
128
129    This function is obsolete.  Please do not add new uses of it.  (Instead, use
130    table_select() or one of its helper functions.) */
131 void
132 tab_resize (struct tab_table *t, int nc, int nr)
133 {
134   if (nc != -1)
135     {
136       assert (nc + t->col_ofs <= t->cf);
137       table_set_nc (&t->table, nc + t->col_ofs);
138     }
139   if (nr != -1)
140     {
141       assert (nr + t->row_ofs <= tab_nr (t));
142       table_set_nr (&t->table, nr + t->row_ofs);
143     }
144 }
145
146 /* Changes either or both dimensions of a table and reallocates memory as
147    necessary.
148
149    This function is obsolete.  Please do not add new uses of it.  (Instead, use
150    table_paste() or one of its helper functions to paste multiple tables
151    together into a larger one.) */
152 void
153 tab_realloc (struct tab_table *t, int nc, int nr)
154 {
155   int ro, co;
156
157   ro = t->row_ofs;
158   co = t->col_ofs;
159   if (ro || co)
160     tab_offset (t, 0, 0);
161
162   if (nc == -1)
163     nc = tab_nc (t);
164   if (nr == -1)
165     nr = tab_nr (t);
166
167   assert (nc == tab_nc (t));
168
169   if (nc > t->cf)
170     {
171       int mr1 = MIN (nr, tab_nr (t));
172       int mc1 = MIN (nc, tab_nc (t));
173
174       void **new_cc;
175       unsigned short *new_ct;
176       int r;
177
178       new_cc = pool_calloc (t->container, nr * nc, sizeof *new_cc);
179       new_ct = pool_malloc (t->container, nr * nc);
180       for (r = 0; r < mr1; r++)
181         {
182           memcpy (&new_cc[r * nc], &t->cc[r * tab_nc (t)],
183                   mc1 * sizeof *t->cc);
184           memcpy (&new_ct[r * nc], &t->ct[r * tab_nc (t)],
185                   mc1 * sizeof *t->ct);
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_nrealloc (t->container, t->ct, nr * nc, sizeof *t->ct);
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)) * sizeof *t->ct);
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 short 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 short 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 short *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   if (x1 == x2 && y1 == y2)
597     do_tab_text (table, x1, y1, opt, s);
598   else
599     add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
600 }
601
602 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
603    with options OPT to have text value FORMAT, which is formatted
604    as if passed to printf. */
605 void
606 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2,
607                        int y2, unsigned opt, const char *format, ...)
608 {
609   va_list args;
610   char *s;
611
612   va_start (args, format);
613   s = pool_vasprintf (table->container, format, args);
614   va_end (args);
615
616   add_joined_cell (table, x1, y1, x2, y2, opt)->u.text = s;
617 }
618
619 struct footnote *
620 tab_create_footnote (struct tab_table *table, size_t idx, const char *content,
621                      const char *marker)
622 {
623   struct footnote *f = pool_alloc (table->container, sizeof *f);
624   f->idx = idx;
625   f->content = pool_strdup (table->container, content);
626   f->marker = pool_strdup (table->container, marker);
627   return f;
628 }
629
630 void
631 tab_add_footnote (struct tab_table *table, int x, int y,
632                   const struct footnote *f)
633 {
634   int index = x + y * table->cf;
635   unsigned short opt = table->ct[index];
636   struct tab_joined_cell *j;
637
638   if (opt & TAB_JOIN)
639     j = table->cc[index];
640   else
641     {
642       char *text = table->cc[index];
643
644       j = add_joined_cell (table, x, y, x, y, table->ct[index]);
645       j->u.text = text ? text : xstrdup ("");
646     }
647
648   j->footnotes = pool_realloc (table->container, j->footnotes,
649                                (j->n_footnotes + 1) * sizeof *j->footnotes);
650
651   j->footnotes[j->n_footnotes++] = f;
652 }
653
654 bool
655 tab_cell_is_empty (const struct tab_table *table, int c, int r)
656 {
657   return table->cc[c + r * table->cf] == NULL;
658 }
659 \f
660 /* Miscellaneous. */
661
662 /* Set the title of table T to TITLE, which is formatted as if
663    passed to printf(). */
664 void
665 tab_title (struct tab_table *t, const char *title, ...)
666 {
667   va_list args;
668
669   free (t->title);
670   va_start (args, title);
671   t->title = xvasprintf (title, args);
672   va_end (args);
673 }
674
675 /* Set the caption of table T to CAPTION, which is formatted as if
676    passed to printf(). */
677 void
678 tab_caption (struct tab_table *t, const char *caption, ...)
679 {
680   va_list args;
681
682   free (t->caption);
683   va_start (args, caption);
684   t->caption = xvasprintf (caption, args);
685   va_end (args);
686 }
687
688 /* Easy, type-safe way to submit a tab table to som. */
689 void
690 tab_submit (struct tab_table *t)
691 {
692   table_item_submit (table_item_create (&t->table, t->title, t->caption));
693 }
694 \f
695 /* Editing. */
696
697 /* Set table row and column offsets for all functions that affect
698    cells or rules. */
699 void
700 tab_offset (struct tab_table *t, int col, int row)
701 {
702   int diff = 0;
703
704   if (debugging)
705     {
706       if (row < -1 || row > tab_nr (t))
707         {
708           printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
709           NOT_REACHED ();
710         }
711       if (col < -1 || col > tab_nc (t))
712         {
713           printf ("tab_offset(): col=%d in %d-column table\n", col,
714                   tab_nc (t));
715           NOT_REACHED ();
716         }
717     }
718
719   if (row != -1)
720     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
721   if (col != -1)
722     diff += (col - t->col_ofs), t->col_ofs = col;
723
724   t->cc += diff;
725   t->ct += diff;
726 }
727
728 /* Increment the row offset by one. If the table is too small,
729    increase its size. */
730 void
731 tab_next_row (struct tab_table *t)
732 {
733   t->cc += t->cf;
734   t->ct += t->cf;
735   if (++t->row_ofs >= tab_nr (t))
736     tab_realloc (t, -1, tab_nr (t) * 4 / 3);
737 }
738 \f
739 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
740    bits.
741
742    This function is obsolete.  Please do not add new uses of it.  Instead, use
743    a text_item (see output/text-item.h). */
744 void
745 tab_output_text (int options, const char *string)
746 {
747   enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD
748                               : options & TAB_FIX ? TEXT_ITEM_MONOSPACE
749                               : TEXT_ITEM_PARAGRAPH);
750   text_item_submit (text_item_create (type, string));
751 }
752
753 /* Same as tab_output_text(), but FORMAT is passed through printf-like
754    formatting before output. */
755 void
756 tab_output_text_format (int options, const char *format, ...)
757 {
758   va_list args;
759   char *text;
760
761   va_start (args, format);
762   text = xvasprintf (format, args);
763   va_end (args);
764
765   tab_output_text (options, text);
766
767   free (text);
768 }
769 \f
770 /* Table class implementation. */
771
772 static void
773 tab_destroy (struct table *table)
774 {
775   struct tab_table *t = tab_cast (table);
776   free (t->title);
777   t->title = NULL;
778   free (t->caption);
779   t->caption = NULL;
780   pool_destroy (t->container);
781 }
782
783 static void
784 tab_get_cell (const struct table *table, int x, int y,
785               struct table_cell *cell)
786 {
787   const struct tab_table *t = tab_cast (table);
788   int index = x + y * t->cf;
789   unsigned short opt = t->ct[index];
790   const void *cc = t->cc[index];
791
792   cell->inline_contents.options = opt;
793   cell->inline_contents.n_footnotes = 0;
794   cell->destructor = NULL;
795
796   if (opt & TAB_JOIN)
797     {
798       const struct tab_joined_cell *jc = cc;
799       cell->contents = &cell->inline_contents;
800       cell->n_contents = 1;
801       cell->inline_contents.text = jc->u.text;
802
803       cell->inline_contents.footnotes = jc->footnotes;
804       cell->inline_contents.n_footnotes = jc->n_footnotes;
805
806       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
807       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
808       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
809       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
810     }
811   else
812     {
813       cell->d[TABLE_HORZ][0] = x;
814       cell->d[TABLE_HORZ][1] = x + 1;
815       cell->d[TABLE_VERT][0] = y;
816       cell->d[TABLE_VERT][1] = y + 1;
817       if (cc != NULL)
818         {
819           cell->contents = &cell->inline_contents;
820           cell->n_contents = 1;
821           cell->inline_contents.text = CONST_CAST (char *, cc);
822         }
823       else
824         {
825           cell->contents = NULL;
826           cell->n_contents = 0;
827         }
828     }
829 }
830
831 static int
832 tab_get_rule (const struct table *table, enum table_axis axis, int x, int y)
833 {
834   const struct tab_table *t = tab_cast (table);
835   return (axis == TABLE_VERT
836           ? t->rh[x + t->cf * y] : t->rv[x + (t->cf + 1) * y]);
837 }
838
839 static const struct table_class tab_table_class = {
840   tab_destroy,
841   tab_get_cell,
842   tab_get_rule,
843   NULL,                         /* paste */
844   NULL,                         /* select */
845 };
846
847 struct tab_table *
848 tab_cast (const struct table *table)
849 {
850   assert (table->klass == &tab_table_class);
851   return UP_CAST (table, struct tab_table, table);
852 }