i18n: Introduce C_ENCODING as replacement for LEGACY_NATIVE.
[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/dictionary.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 "error.h"
42 #include "minmax.h"
43 #include "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 dictionary *dict, 
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, dict_get_encoding (dict), f, table->container);
383
384   table->cc[c + r * table->cf] = contents;
385   table->ct[c + r * table->cf] = opt;
386 }
387
388 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
389    with NDEC decimal places. */
390 void
391 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
392            double val, int w, int d)
393 {
394   struct fmt_spec f;
395   union value double_value;
396   char *s;
397
398   assert (c >= 0);
399   assert (c < tab_nc (table));
400   assert (r >= 0);
401   assert (r < tab_nr (table));
402
403   f = fmt_for_output (FMT_F, w, d);
404
405 #if DEBUGGING
406   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
407       || c + table->col_ofs >= tab_nc (table)
408       || r + table->row_ofs >= tab_nr (table))
409     {
410       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
411               "(%d,%d)\n",
412               c, table->col_ofs, c + table->col_ofs,
413               r, table->row_ofs, r + table->row_ofs,
414               tab_nc (table), tab_nr (table));
415       return;
416     }
417 #endif
418
419   double_value.f = val;
420   s = data_out_pool (&double_value, C_ENCODING, &f, table->container);
421
422   table->cc[c + r * table->cf] = s + strspn (s, " ");
423   table->ct[c + r * table->cf] = opt;
424 }
425
426 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
427    formatted by FMT.
428    If FMT is null, then the default print format will be used.
429 */
430 void
431 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
432            double val, const struct fmt_spec *fmt)
433 {
434   union value double_value ;
435   char *s;
436
437   assert (c >= 0);
438   assert (c < tab_nc (table));
439   assert (r >= 0);
440   assert (r < tab_nr (table));
441
442   if ( fmt == NULL)
443     fmt = settings_get_format ();
444
445   fmt_check_output (fmt);
446
447 #if DEBUGGING
448   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
449       || c + table->col_ofs >= tab_nc (table)
450       || r + table->row_ofs >= tab_nr (table))
451     {
452       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
453               "(%d,%d)\n",
454               c, table->col_ofs, c + table->col_ofs,
455               r, table->row_ofs, r + table->row_ofs,
456               tab_nc (table), tab_nr (table));
457       return;
458     }
459 #endif
460
461   double_value.f = val;
462   s = data_out_pool (&double_value, C_ENCODING, fmt, table->container);
463   table->cc[c + r * table->cf] = s + strspn (s, " ");
464   table->ct[c + r * table->cf] = opt;
465 }
466
467
468 static void
469 do_tab_text (struct tab_table *table, int c, int r, unsigned opt, char *text)
470 {
471   assert (c >= 0 );
472   assert (r >= 0 );
473   assert (c < tab_nc (table));
474   assert (r < tab_nr (table));
475
476 #if DEBUGGING
477   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
478       || c + table->col_ofs >= tab_nc (table)
479       || r + table->row_ofs >= tab_nr (table))
480     {
481       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
482               "(%d,%d)\n",
483               c, table->col_ofs, c + table->col_ofs,
484               r, table->row_ofs, r + table->row_ofs,
485               tab_nc (table), tab_nr (table));
486       return;
487     }
488 #endif
489
490   table->cc[c + r * table->cf] = text;
491   table->ct[c + r * table->cf] = opt;
492 }
493
494 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
495    TEXT. */
496 void
497 tab_text (struct tab_table *table, int c, int r, unsigned opt,
498           const char *text)
499 {
500   do_tab_text (table, c, r, opt, pool_strdup (table->container, text));
501 }
502
503 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
504    FORMAT, which is formatted as if passed to printf. */
505 void
506 tab_text_format (struct tab_table *table, int c, int r, unsigned opt,
507                  const char *format, ...)
508 {
509   va_list args;
510
511   va_start (args, format);
512   do_tab_text (table, c, r, opt,
513                pool_vasprintf (table->container, format, args));
514   va_end (args);
515 }
516
517 static void
518 do_tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
519                    unsigned opt, char *text)
520 {
521   struct tab_joined_cell *j;
522
523   assert (x1 + table->col_ofs >= 0);
524   assert (y1 + table->row_ofs >= 0);
525   assert (y2 >= y1);
526   assert (x2 >= x1);
527   assert (y2 + table->row_ofs < tab_nr (table));
528   assert (x2 + table->col_ofs < tab_nc (table));
529
530 #if DEBUGGING
531   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= tab_nc (table)
532       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= tab_nr (table)
533       || x2 < x1 || x2 + table->col_ofs >= tab_nc (table)
534       || y2 < y2 || y2 + table->row_ofs >= tab_nr (table))
535     {
536       printf ("tab_joint_text(): bad cell "
537               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
538               x1, table->col_ofs, x1 + table->col_ofs,
539               y1, table->row_ofs, y1 + table->row_ofs,
540               x2, table->col_ofs, x2 + table->col_ofs,
541               y2, table->row_ofs, y2 + table->row_ofs,
542               tab_nc (table), tab_nr (table));
543       return;
544     }
545 #endif
546
547   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
548
549   j = pool_alloc (table->container, sizeof *j);
550   j->d[TABLE_HORZ][0] = x1 + table->col_ofs;
551   j->d[TABLE_VERT][0] = y1 + table->row_ofs;
552   j->d[TABLE_HORZ][1] = ++x2 + table->col_ofs;
553   j->d[TABLE_VERT][1] = ++y2 + table->row_ofs;
554   j->contents = text;
555
556   opt |= TAB_JOIN;
557
558   {
559     void **cc = &table->cc[x1 + y1 * table->cf];
560     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
561     const int ofs = table->cf - (x2 - x1);
562
563     int y;
564
565     for (y = y1; y < y2; y++)
566       {
567         int x;
568
569         for (x = x1; x < x2; x++)
570           {
571             *cc++ = j;
572             *ct++ = opt;
573           }
574
575         cc += ofs;
576         ct += ofs;
577       }
578   }
579 }
580
581 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
582    options OPT to have text value TEXT. */
583 void
584 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
585                 unsigned opt, const char *text)
586 {
587   do_tab_joint_text (table, x1, y1, x2, y2, opt,
588                      pool_strdup (table->container, text));
589 }
590
591 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them
592    with options OPT to have text value FORMAT, which is formatted
593    as if passed to printf. */
594 void
595 tab_joint_text_format (struct tab_table *table, int x1, int y1, int x2, int y2,
596                        unsigned opt, const char *format, ...)
597 {
598   va_list args;
599
600   va_start (args, format);
601   do_tab_joint_text (table, x1, y1, x2, y2, opt,
602                      pool_vasprintf (table->container, format, args));
603   va_end (args);
604 }
605
606 bool
607 tab_cell_is_empty (const struct tab_table *table, int c, int r)
608 {
609   return table->cc[c + r * table->cf] == NULL;
610 }
611 \f
612 /* Miscellaneous. */
613
614 /* Set the title of table T to TITLE, which is formatted as if
615    passed to printf(). */
616 void
617 tab_title (struct tab_table *t, const char *title, ...)
618 {
619   va_list args;
620
621   free (t->title);
622   va_start (args, title);
623   t->title = xvasprintf (title, args);
624   va_end (args);
625 }
626
627 /* Easy, type-safe way to submit a tab table to som. */
628 void
629 tab_submit (struct tab_table *t)
630 {
631   table_item_submit (table_item_create (&t->table, t->title));
632 }
633 \f
634 /* Editing. */
635
636 /* Set table row and column offsets for all functions that affect
637    cells or rules. */
638 void
639 tab_offset (struct tab_table *t, int col, int row)
640 {
641   int diff = 0;
642
643 #if DEBUGGING
644   if (row < -1 || row > tab_nr (t))
645     {
646       printf ("tab_offset(): row=%d in %d-row table\n", row, tab_nr (t));
647       NOT_REACHED ();
648     }
649   if (col < -1 || col > tab_nc (t))
650     {
651       printf ("tab_offset(): col=%d in %d-column table\n", col, tab_nc (t));
652       NOT_REACHED ();
653     }
654 #endif
655
656   if (row != -1)
657     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
658   if (col != -1)
659     diff += (col - t->col_ofs), t->col_ofs = col;
660
661   t->cc += diff;
662   t->ct += diff;
663 }
664
665 /* Increment the row offset by one. If the table is too small,
666    increase its size. */
667 void
668 tab_next_row (struct tab_table *t)
669 {
670   t->cc += t->cf;
671   t->ct += t->cf;
672   if (++t->row_ofs >= tab_nr (t))
673     tab_realloc (t, -1, tab_nr (t) * 4 / 3);
674 }
675 \f
676 /* Writes STRING to the output.  OPTIONS may be any valid combination of TAB_*
677    bits.
678
679    This function is obsolete.  Please do not add new uses of it.  Instead, use
680    a text_item (see output/text-item.h). */
681 void
682 tab_output_text (int options, const char *string)
683 {
684   enum text_item_type type = (options & TAB_EMPH ? TEXT_ITEM_SUBHEAD
685                               : options & TAB_FIX ? TEXT_ITEM_MONOSPACE
686                               : TEXT_ITEM_PARAGRAPH);
687   text_item_submit (text_item_create (type, string));
688 }
689
690 /* Same as tab_output_text(), but FORMAT is passed through printf-like
691    formatting before output. */
692 void
693 tab_output_text_format (int options, const char *format, ...)
694 {
695   va_list args;
696   char *text;
697
698   va_start (args, format);
699   text = xvasprintf (format, args);
700   va_end (args);
701
702   tab_output_text (options, text);
703
704   free (text);
705 }
706 \f
707 /* Table class implementation. */
708
709 static void
710 tab_destroy (struct table *table)
711 {
712   struct tab_table *t = tab_cast (table);
713   free (t->title);
714   t->title = NULL;
715   pool_destroy (t->container);
716 }
717
718 static void
719 tab_get_cell (const struct table *table, int x, int y, struct table_cell *cell)
720 {
721   const struct tab_table *t = tab_cast (table);
722   int index = x + y * t->cf;
723   unsigned char opt = t->ct[index];
724   const void *content = t->cc[index];
725
726   cell->options = opt;
727   if (opt & TAB_JOIN)
728     {
729       const struct tab_joined_cell *jc = content;
730       cell->d[TABLE_HORZ][0] = jc->d[TABLE_HORZ][0];
731       cell->d[TABLE_HORZ][1] = jc->d[TABLE_HORZ][1];
732       cell->d[TABLE_VERT][0] = jc->d[TABLE_VERT][0];
733       cell->d[TABLE_VERT][1] = jc->d[TABLE_VERT][1];
734       cell->contents = jc->contents;
735     }
736   else
737     {
738       cell->d[TABLE_HORZ][0] = x;
739       cell->d[TABLE_HORZ][1] = x + 1;
740       cell->d[TABLE_VERT][0] = y;
741       cell->d[TABLE_VERT][1] = y + 1;
742       cell->contents = content != NULL ? content : "";
743     }
744   cell->destructor = NULL;
745 }
746
747 static int
748 tab_get_rule (const struct table *table, enum table_axis axis, int x, int y)
749 {
750   const struct tab_table *t = tab_cast (table);
751   return (axis == TABLE_VERT
752           ? t->rh[x + t->cf * y]
753           : t->rv[x + (t->cf + 1) * y]);
754 }
755
756 static const struct table_class tab_table_class =
757   {
758     tab_destroy,
759     tab_get_cell,
760     tab_get_rule,
761     NULL,                       /* paste */
762     NULL,                       /* select */
763   };
764
765 struct tab_table *
766 tab_cast (const struct table *table)
767 {
768   assert (table->class == &tab_table_class);
769   return UP_CAST (table, struct tab_table, table);
770 }