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