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