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