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