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