output: Improve warnings about negative table dimensions.
[pspp-builds.git] / src / output / table.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 "table.h"
20
21 #include <ctype.h>
22 #include <stdarg.h>
23 #include <limits.h>
24 #include <stdlib.h>
25
26 #include "output.h"
27 #include "manager.h"
28
29 #include <data/data-out.h>
30 #include <data/format.h>
31 #include <data/value.h>
32 #include <libpspp/assertion.h>
33 #include <libpspp/compiler.h>
34 #include <libpspp/misc.h>
35 #include <libpspp/pool.h>
36
37 #include <data/settings.h>
38
39 #include "error.h"
40 #include "minmax.h"
41 #include "xalloc.h"
42
43 #include "gettext.h"
44 #define _(msgid) gettext (msgid)
45 \f
46 const struct som_table_class tab_table_class;
47 static char *command_name;
48
49 /* Returns the font to use for a cell with the given OPTIONS. */
50 static enum outp_font
51 options_to_font (unsigned options)
52 {
53   return (options & TAB_FIX ? OUTP_FIXED
54           : options & TAB_EMPH ? OUTP_EMPHASIS
55           : OUTP_PROPORTIONAL);
56 }
57
58 /* Creates a table with NC columns and NR rows. */
59 struct tab_table *
60 tab_create (int nc, int nr, int reallocable UNUSED)
61 {
62   struct tab_table *t;
63
64   t = pool_create_container (struct tab_table, container);
65   t->col_style = TAB_COL_NONE;
66   t->col_group = 0;
67   t->title = NULL;
68   t->flags = SOMF_NONE;
69   t->nr = nr;
70   t->nc = t->cf = nc;
71   t->l = t->r = t->t = t->b = 0;
72
73   t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
74   t->ct = pool_malloc (t->container, nr * nc);
75   memset (t->ct, TAB_EMPTY, 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, UCHAR_MAX, nr * (nc + 1));
82
83   t->dim = NULL;
84   t->col_ofs = t->row_ofs = 0;
85
86   return t;
87 }
88
89 /* Destroys table T. */
90 void
91 tab_destroy (struct tab_table *t)
92 {
93   assert (t != NULL);
94   if (t->dim_free != NULL)
95     t->dim_free (t->dim_aux);
96   free (t->title);
97   pool_destroy (t->container);
98 }
99
100 /* Sets the width and height of a table, in columns and rows,
101    respectively.  Use only to reduce the size of a table, since it
102    does not change the amount of allocated memory. */
103 void
104 tab_resize (struct tab_table *t, int nc, int nr)
105 {
106   assert (t != NULL);
107   if (nc != -1)
108     {
109       assert (nc + t->col_ofs <= t->cf);
110       t->nc = nc + t->col_ofs;
111     }
112   if (nr != -1)
113     {
114       assert (nr + t->row_ofs <= t->nr);
115       t->nr = nr + t->row_ofs;
116     }
117 }
118
119 /* Changes either or both dimensions of a table.  Consider using the
120    above routine instead if it won't waste a lot of space.
121
122    Changing the number of columns in a table is particularly expensive
123    in space and time.  Avoid doing such.  FIXME: In fact, transferring
124    of rules isn't even implemented yet. */
125 void
126 tab_realloc (struct tab_table *t, int nc, int nr)
127 {
128   int ro, co;
129
130   assert (t != NULL);
131   ro = t->row_ofs;
132   co = t->col_ofs;
133   if (ro || co)
134     tab_offset (t, 0, 0);
135
136   if (nc == -1)
137     nc = t->nc;
138   if (nr == -1)
139     nr = t->nr;
140
141   assert (nc == t->nc);
142
143   if (nc > t->cf)
144     {
145       int mr1 = MIN (nr, t->nr);
146       int mc1 = MIN (nc, t->nc);
147
148       struct substring *new_cc;
149       unsigned char *new_ct;
150       int r;
151
152       new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
153       new_ct = pool_malloc (t->container, nr * nc);
154       for (r = 0; r < mr1; r++)
155         {
156           memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
157           memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
158           memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
159         }
160       pool_free (t->container, t->cc);
161       pool_free (t->container, t->ct);
162       t->cc = new_cc;
163       t->ct = new_ct;
164       t->cf = nc;
165     }
166   else if (nr != t->nr)
167     {
168       t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
169       t->ct = pool_realloc (t->container, t->ct, nr * nc);
170
171       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
172       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
173
174       if (nr > t->nr)
175         {
176           memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
177           memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
178                   (nr - t->nr) * (nc + 1));
179         }
180     }
181
182   memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
183
184   t->nr = nr;
185   t->nc = nc;
186
187   if (ro || co)
188     tab_offset (t, co, ro);
189 }
190
191 /* Sets the number of header rows on each side of TABLE to L on the
192    left, R on the right, T on the top, B on the bottom.  Header rows
193    are repeated when a table is broken across multiple columns or
194    multiple pages. */
195 void
196 tab_headers (struct tab_table *table, int l, int r, int t, int b)
197 {
198   assert (table != NULL);
199   assert (l < table->nc);
200   assert (r < table->nc);
201   assert (t < table->nr);
202   assert (b < table->nr);
203
204
205   table->l = l;
206   table->r = r;
207   table->t = t;
208   table->b = b;
209 }
210
211 /* Set up table T so that, when it is an appropriate size, it will be
212    displayed across the page in columns.
213
214    STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
215    as a unit. */
216 void
217 tab_columns (struct tab_table *t, int style, int group)
218 {
219   assert (t != NULL);
220   t->col_style = style;
221   t->col_group = group;
222 }
223 \f
224 /* Rules. */
225
226 /* Draws a vertical line to the left of cells at horizontal position X
227    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
228 void
229 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
230 {
231   assert (t != NULL);
232
233 #if DEBUGGING
234   if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
235       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
236       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
237     {
238       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
239                 "table size (%d,%d)\n"),
240               x, t->col_ofs, x + t->col_ofs,
241               y1, t->row_ofs, y1 + t->row_ofs,
242               y2, t->row_ofs, y2 + t->row_ofs,
243               t->nc, t->nr);
244       return;
245     }
246 #endif
247
248   x += t->col_ofs;
249   y1 += t->row_ofs;
250   y2 += t->row_ofs;
251
252   assert (x  > 0);
253   assert (x  < t->nc);
254   assert (y1 >= 0);
255   assert (y2 >= y1);
256   assert (y2 <=  t->nr);
257
258   if (style != -1)
259     {
260       int y;
261       for (y = y1; y <= y2; y++)
262         t->rv[x + (t->cf + 1) * y] = style;
263     }
264 }
265
266 /* Draws a horizontal line above cells at vertical position Y from X1
267    to X2 inclusive in style STYLE, if style is not -1. */
268 void
269 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
270 {
271   assert (t != NULL);
272
273   x1 += t->col_ofs;
274   x2 += t->col_ofs;
275   y += t->row_ofs;
276
277   assert (y >= 0);
278   assert (y <= t->nr);
279   assert (x2 >= x1 );
280   assert (x1 >= 0 );
281   assert (x2 < t->nc);
282
283   if (style != -1)
284     {
285       int x;
286       for (x = x1; x <= x2; x++)
287         t->rh[x + t->cf * y] = style;
288     }
289 }
290
291 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
292    lines of style F_H and vertical lines of style F_V.  Fills the
293    interior of the box with horizontal lines of style I_H and vertical
294    lines of style I_V.  Any of the line styles may be -1 to avoid
295    drawing those lines.  This is distinct from 0, which draws a null
296    line. */
297 void
298 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
299          int x1, int y1, int x2, int y2)
300 {
301   assert (t != NULL);
302
303 #if DEBUGGING
304   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
305       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
306       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
307       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
308     {
309       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
310                 "in table size (%d,%d)\n"),
311               x1, t->col_ofs, x1 + t->col_ofs,
312               y1, t->row_ofs, y1 + t->row_ofs,
313               x2, t->col_ofs, x2 + t->col_ofs,
314               y2, t->row_ofs, y2 + t->row_ofs,
315               t->nc, t->nr);
316       NOT_REACHED ();
317     }
318 #endif
319
320   x1 += t->col_ofs;
321   x2 += t->col_ofs;
322   y1 += t->row_ofs;
323   y2 += t->row_ofs;
324
325   assert (x2 >= x1);
326   assert (y2 >= y1);
327   assert (x1 >= 0);
328   assert (y1 >= 0);
329   assert (x2 < t->nc);
330   assert (y2 < t->nr);
331
332   if (f_h != -1)
333     {
334       int x;
335       for (x = x1; x <= x2; x++)
336         {
337           t->rh[x + t->cf * y1] = f_h;
338           t->rh[x + t->cf * (y2 + 1)] = f_h;
339         }
340     }
341   if (f_v != -1)
342     {
343       int y;
344       for (y = y1; y <= y2; y++)
345         {
346           t->rv[x1 + (t->cf + 1) * y] = f_v;
347           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
348         }
349     }
350
351   if (i_h != -1)
352     {
353       int y;
354
355       for (y = y1 + 1; y <= y2; y++)
356         {
357           int x;
358
359           for (x = x1; x <= x2; x++)
360             t->rh[x + t->cf * y] = i_h;
361         }
362     }
363   if (i_v != -1)
364     {
365       int x;
366
367       for (x = x1 + 1; x <= x2; x++)
368         {
369           int y;
370
371           for (y = y1; y <= y2; y++)
372             t->rv[x + (t->cf + 1) * y] = i_v;
373         }
374     }
375 }
376
377 /* Formats text TEXT and arguments ARGS as indicated in OPT in
378    TABLE's pool and returns the resultant string. */
379 static struct substring
380 text_format (struct tab_table *table, int opt, const char *text, va_list args)
381 {
382   assert (table != NULL && text != NULL);
383
384   return ss_cstr (opt & TAT_PRINTF
385                   ? pool_vasprintf (table->container, text, args)
386                   : pool_strdup (table->container, text));
387 }
388
389 /* Set the title of table T to TITLE, which is formatted as if
390    passed to printf(). */
391 void
392 tab_title (struct tab_table *t, const char *title, ...)
393 {
394   va_list args;
395
396   assert (t != NULL && title != NULL);
397   va_start (args, title);
398   t->title = xvasprintf (title, args);
399   va_end (args);
400 }
401
402 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
403    dimension function for table T.
404
405    DIM_FUNC must not assume that it is called from the same
406    context as tab_dim; for example, table T might be kept in
407    memory and, thus, DIM_FUNC might be called after the currently
408    running command completes.  If it is non-null, FREE_FUNC is
409    called when the table is destroyed, to allow any data
410    allocated for use by DIM_FUNC to be freed.  */
411 void
412 tab_dim (struct tab_table *t,
413          tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
414 {
415   assert (t->dim == NULL);
416   t->dim = dim_func;
417   t->dim_free = free_func;
418   t->dim_aux = aux;
419 }
420
421 /* Returns the natural width of column C in table T for driver D, that
422    is, the smallest width necessary to display all its cells without
423    wrapping.  The width will be no larger than the page width minus
424    left and right rule widths. */
425 int
426 tab_natural_width (const struct tab_rendering *r, int col)
427 {
428   const struct tab_table *t = r->table;
429   int width, row, max_width;
430
431   assert (col >= 0 && col < t->nc);
432
433   width = 0;
434   for (row = 0; row < t->nr; row++)
435     {
436       struct outp_text text;
437       unsigned char opt = t->ct[col + row * t->cf];
438       int w;
439
440       if (opt & (TAB_JOIN | TAB_EMPTY))
441         continue;
442
443       text.string = t->cc[col + row * t->cf];
444       text.justification = OUTP_LEFT;
445       text.font = options_to_font (opt);
446       text.h = text.v = INT_MAX;
447
448       r->driver->class->text_metrics (r->driver, &text, &w, NULL);
449       if (w > width)
450         width = w;
451     }
452
453   if (width == 0)
454     {
455       /* FIXME: This is an ugly kluge to compensate for the fact
456          that we don't let joined cells contribute to column
457          widths. */
458       width = r->driver->prop_em_width * 8;
459     }
460
461   max_width = r->driver->width - r->wrv[0] - r->wrv[t->nc];
462   return MIN (width, max_width);
463 }
464
465 /* Returns the natural height of row R in table T for driver D, that
466    is, the minimum height necessary to display the information in the
467    cell at the widths set for each column. */
468 int
469 tab_natural_height (const struct tab_rendering *r, int row)
470 {
471   const struct tab_table *t = r->table;
472   int height, col;
473
474   assert (row >= 0 && row < t->nr);
475
476   height = r->driver->font_height;
477   for (col = 0; col < t->nc; col++)
478     {
479       struct outp_text text;
480       unsigned char opt = t->ct[col + row * t->cf];
481       int h;
482
483       if (opt & (TAB_JOIN | TAB_EMPTY))
484         continue;
485
486       text.string = t->cc[col + row * t->cf];
487       text.justification = OUTP_LEFT;
488       text.font = options_to_font (opt);
489       text.h = r->w[col];
490       text.v = INT_MAX;
491       r->driver->class->text_metrics (r->driver, &text, NULL, &h);
492
493       if (h > height)
494         height = h;
495     }
496
497   return height;
498 }
499
500 /* Callback function to set all columns and rows to their natural
501    dimensions.  Not really meant to be called directly.  */
502 void
503 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
504 {
505   const struct tab_table *t = r->table;
506   int i;
507
508   for (i = 0; i < t->nc; i++)
509     r->w[i] = tab_natural_width (r, i);
510
511   for (i = 0; i < t->nr; i++)
512     r->h[i] = tab_natural_height (r, i);
513 }
514
515 \f
516 /* Cells. */
517
518 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
519    from V, displayed with format spec F. */
520 void
521 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
522            const union value *v, const struct fmt_spec *f)
523 {
524   char *contents;
525
526   assert (table != NULL && v != NULL && f != NULL);
527 #if DEBUGGING
528   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
529       || c + table->col_ofs >= table->nc
530       || r + table->row_ofs >= table->nr)
531     {
532       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
533               "(%d,%d)\n",
534               c, table->col_ofs, c + table->col_ofs,
535               r, table->row_ofs, r + table->row_ofs,
536               table->nc, table->nr);
537       return;
538     }
539 #endif
540
541   contents = pool_alloc (table->container, f->w);
542   table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
543   table->ct[c + r * table->cf] = opt;
544
545   data_out (v, f, contents);
546 }
547
548 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
549    with NDEC decimal places. */
550 void
551 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
552            double val, int w, int d)
553 {
554   char *contents;
555   char buf[40], *cp;
556
557   struct fmt_spec f;
558   union value double_value;
559
560   assert (table != NULL && w <= 40);
561
562   assert (c >= 0);
563   assert (c < table->nc);
564   assert (r >= 0);
565   assert (r < table->nr);
566
567   f = fmt_for_output (FMT_F, w, d);
568
569 #if DEBUGGING
570   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
571       || c + table->col_ofs >= table->nc
572       || r + table->row_ofs >= table->nr)
573     {
574       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
575               "(%d,%d)\n",
576               c, table->col_ofs, c + table->col_ofs,
577               r, table->row_ofs, r + table->row_ofs,
578               table->nc, table->nr);
579       return;
580     }
581 #endif
582
583   double_value.f = val;
584   data_out (&double_value, &f, buf);
585
586   cp = buf;
587   while (isspace ((unsigned char) *cp) && cp < &buf[w])
588     cp++;
589   f.w = w - (cp - buf);
590
591   contents = pool_alloc (table->container, f.w);
592   table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
593   table->ct[c + r * table->cf] = opt;
594   memcpy (contents, cp, f.w);
595 }
596
597 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
598    formatted by FMT.
599    If FMT is null, then the default print format will be used.
600 */
601 void
602 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
603            double val, const struct fmt_spec *fmt)
604 {
605   int w;
606   char *contents;
607   char buf[40], *cp;
608
609   union value double_value;
610
611   assert (table != NULL);
612
613   assert (c >= 0);
614   assert (c < table->nc);
615   assert (r >= 0);
616   assert (r < table->nr);
617
618   if ( fmt == NULL)
619     fmt = settings_get_format ();
620
621   fmt_check_output (fmt);
622
623 #if DEBUGGING
624   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
625       || c + table->col_ofs >= table->nc
626       || r + table->row_ofs >= table->nr)
627     {
628       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
629               "(%d,%d)\n",
630               c, table->col_ofs, c + table->col_ofs,
631               r, table->row_ofs, r + table->row_ofs,
632               table->nc, table->nr);
633       return;
634     }
635 #endif
636
637   double_value.f = val;
638   data_out (&double_value, fmt, buf);
639
640   cp = buf;
641   while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
642     cp++;
643   w = fmt->w - (cp - buf);
644
645   contents = pool_alloc (table->container, w);
646   table->cc[c + r * table->cf] = ss_buffer (contents, w);
647   table->ct[c + r * table->cf] = opt;
648   memcpy (contents, cp, w);
649 }
650
651
652 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
653    TEXT. */
654 void
655 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
656 {
657   va_list args;
658
659   assert (table != NULL && text != NULL);
660
661   assert (c >= 0 );
662   assert (r >= 0 );
663   assert (c < table->nc);
664   assert (r < table->nr);
665
666
667 #if DEBUGGING
668   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
669       || c + table->col_ofs >= table->nc
670       || r + table->row_ofs >= table->nr)
671     {
672       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
673               "(%d,%d)\n",
674               c, table->col_ofs, c + table->col_ofs,
675               r, table->row_ofs, r + table->row_ofs,
676               table->nc, table->nr);
677       return;
678     }
679 #endif
680
681   va_start (args, text);
682   table->cc[c + r * table->cf] = text_format (table, opt, text, args);
683   table->ct[c + r * table->cf] = opt;
684   va_end (args);
685 }
686
687 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
688    options OPT to have text value TEXT. */
689 void
690 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
691                 unsigned opt, const char *text, ...)
692 {
693   struct tab_joined_cell *j;
694
695   assert (table != NULL && text != NULL);
696
697   assert (x1 + table->col_ofs >= 0);
698   assert (y1 + table->row_ofs >= 0);
699   assert (y2 >= y1);
700   assert (x2 >= x1);
701   assert (y2 + table->row_ofs < table->nr);
702   assert (x2 + table->col_ofs < table->nc);
703
704 #if DEBUGGING
705   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
706       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
707       || x2 < x1 || x2 + table->col_ofs >= table->nc
708       || y2 < y2 || y2 + table->row_ofs >= table->nr)
709     {
710       printf ("tab_joint_text(): bad cell "
711               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
712               x1, table->col_ofs, x1 + table->col_ofs,
713               y1, table->row_ofs, y1 + table->row_ofs,
714               x2, table->col_ofs, x2 + table->col_ofs,
715               y2, table->row_ofs, y2 + table->row_ofs,
716               table->nc, table->nr);
717       return;
718     }
719 #endif
720
721   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
722
723   j = pool_alloc (table->container, sizeof *j);
724   j->hit = 0;
725   j->x1 = x1 + table->col_ofs;
726   j->y1 = y1 + table->row_ofs;
727   j->x2 = ++x2 + table->col_ofs;
728   j->y2 = ++y2 + table->row_ofs;
729
730   {
731     va_list args;
732
733     va_start (args, text);
734     j->contents = text_format (table, opt, text, args);
735     va_end (args);
736   }
737
738   opt |= TAB_JOIN;
739
740   {
741     struct substring *cc = &table->cc[x1 + y1 * table->cf];
742     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
743     const int ofs = table->cf - (x2 - x1);
744
745     int y;
746
747     for (y = y1; y < y2; y++)
748       {
749         int x;
750
751         for (x = x1; x < x2; x++)
752           {
753             *cc++ = ss_buffer ((char *) j, 0);
754             *ct++ = opt;
755           }
756
757         cc += ofs;
758         ct += ofs;
759       }
760   }
761 }
762
763 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
764 void
765 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
766          struct substring *string)
767 {
768   assert (table != NULL && string != NULL);
769
770 #if DEBUGGING
771   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
772       || c + table->col_ofs >= table->nc
773       || r + table->row_ofs >= table->nr)
774     {
775       printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
776               "(%d,%d)\n",
777               c, table->col_ofs, c + table->col_ofs,
778               r, table->row_ofs, r + table->row_ofs,
779               table->nc, table->nr);
780       return;
781     }
782 #endif
783
784   table->cc[c + r * table->cf] = *string;
785   table->ct[c + r * table->cf] = opt;
786 }
787 \f
788 /* Miscellaneous. */
789
790 /* Sets the widths of all the columns and heights of all the rows in
791    table T for driver D. */
792 static void
793 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
794 {
795   r->w[0] = tab_natural_width (r, 0);
796   r->h[0] = r->driver->font_height;
797 }
798
799 /* Sets the widths of all the columns and heights of all the rows in
800    table T for driver D. */
801 static void
802 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
803 {
804   r->w[0] = tab_natural_width (r, 0);
805   r->h[0] = tab_natural_height (r, 0);
806 }
807
808 /* Outputs text BUF as a table with a single cell having cell options
809    OPTIONS, which is a combination of the TAB_* and TAT_*
810    constants. */
811 void
812 tab_output_text (int options, const char *buf, ...)
813 {
814   struct tab_table *t = tab_create (1, 1, 0);
815   char *tmp_buf = NULL;
816
817   if (options & TAT_PRINTF)
818     {
819       va_list args;
820
821       va_start (args, buf);
822       buf = tmp_buf = xvasprintf (buf, args);
823       va_end (args);
824     }
825
826   tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
827   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
828   tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
829   tab_submit (t);
830
831   free (tmp_buf);
832 }
833
834 /* Set table flags to FLAGS. */
835 void
836 tab_flags (struct tab_table *t, unsigned flags)
837 {
838   assert (t != NULL);
839   t->flags = flags;
840 }
841
842 /* Easy, type-safe way to submit a tab table to som. */
843 void
844 tab_submit (struct tab_table *t)
845 {
846   struct som_entity s;
847
848   assert (t != NULL);
849   s.class = &tab_table_class;
850   s.ext = t;
851   s.type = SOM_TABLE;
852   som_submit (&s);
853   tab_destroy (t);
854 }
855 \f
856 /* Editing. */
857
858 /* Set table row and column offsets for all functions that affect
859    cells or rules. */
860 void
861 tab_offset (struct tab_table *t, int col, int row)
862 {
863   int diff = 0;
864
865   assert (t != NULL);
866 #if DEBUGGING
867   if (row < -1 || row > t->nr)
868     {
869       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
870       NOT_REACHED ();
871     }
872   if (col < -1 || col > t->nc)
873     {
874       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
875       NOT_REACHED ();
876     }
877 #endif
878
879   if (row != -1)
880     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
881   if (col != -1)
882     diff += (col - t->col_ofs), t->col_ofs = col;
883
884   t->cc += diff;
885   t->ct += diff;
886 }
887
888 /* Increment the row offset by one. If the table is too small,
889    increase its size. */
890 void
891 tab_next_row (struct tab_table *t)
892 {
893   assert (t != NULL);
894   t->cc += t->cf;
895   t->ct += t->cf;
896   if (++t->row_ofs >= t->nr)
897     tab_realloc (t, -1, t->nr * 4 / 3);
898 }
899 \f
900 /* Return the number of columns and rows in the table into N_COLUMNS
901    and N_ROWS, respectively. */
902 static void
903 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
904 {
905   struct tab_table *t = t_->ext;
906   *n_columns = t->nc;
907   *n_rows = t->nr;
908 }
909
910 /* Return the column style for this table into STYLE. */
911 static void
912 tabi_columns (struct som_entity *t_, int *style)
913 {
914   struct tab_table *t = t_->ext;
915   *style = t->col_style;
916 }
917
918 /* Return the number of header rows/columns on the left, right, top,
919    and bottom sides into HL, HR, HT, and HB, respectively. */
920 static void
921 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
922 {
923   struct tab_table *t = t_->ext;
924   *hl = t->l;
925   *hr = t->r;
926   *ht = t->t;
927   *hb = t->b;
928 }
929
930 /* Return flags set for the current table into FLAGS. */
931 static void
932 tabi_flags (struct som_entity *t_, unsigned *flags)
933 {
934   struct tab_table *t = t_->ext;
935   *flags = t->flags;
936 }
937
938 /* Returns the line style to use for spacing purposes for a rule
939    of the given TYPE. */
940 static enum outp_line_style
941 rule_to_spacing_type (unsigned char type)
942 {
943   switch (type)
944     {
945     case TAL_0:
946       return OUTP_L_NONE;
947     case TAL_GAP:
948     case TAL_1:
949       return OUTP_L_SINGLE;
950     case TAL_2:
951       return OUTP_L_DOUBLE;
952     default:
953       NOT_REACHED ();
954     }
955 }
956
957 static void *
958 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
959                   int hl, int hr, int ht, int hb)
960 {
961   const struct tab_table *t = t_->ext;
962   struct tab_rendering *r;
963   int col, row;
964   int i;
965
966   tab_offset (t_->ext, 0, 0);
967
968   r = xmalloc (sizeof *r);
969   r->table = t;
970   r->driver = driver;
971   r->w = xnmalloc (t->nc, sizeof *r->w);
972   r->h = xnmalloc (t->nr, sizeof *r->h);
973   r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
974   r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
975   r->l = hl;
976   r->r = hr;
977   r->t = ht;
978   r->b = hb;
979
980   /* Figure out sizes of rules. */
981   for (row = 0; row <= t->nr; row++)
982     {
983       int width = 0;
984       for (col = 0; col < t->nc; col++)
985         {
986           unsigned char rh = t->rh[col + row * t->cf];
987           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
988           if (w > width)
989             width = w;
990         }
991       r->hrh[row] = width;
992     }
993
994   for (col = 0; col <= t->nc; col++)
995     {
996       int width = 0;
997       for (row = 0; row < t->nr; row++)
998         {
999           unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1000           int w;
1001           if (*rv == UCHAR_MAX)
1002             *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
1003           w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1004           if (w > width)
1005             width = w;
1006         }
1007       r->wrv[col] = width;
1008     }
1009
1010   /* Determine row heights and columns widths. */
1011   for (i = 0; i < t->nr; i++)
1012     r->h[i] = -1;
1013   for (i = 0; i < t->nc; i++)
1014     r->w[i] = -1;
1015
1016   t->dim (r, t->dim_aux);
1017
1018   for (i = 0; i < t->nr; i++)
1019     if (r->h[i] < 0)
1020       error (0, 0, "height of table row %d is %d (not initialized?)",
1021              i, r->h[i]);
1022   for (i = 0; i < t->nc; i++)
1023     if (r->w[i] < 0)
1024       error (0, 0, "width of table column %d is %d (not initialized?)",
1025              i, r->w[i]);
1026
1027   /* Add up header sizes. */
1028   for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1029     r->wl += r->w[i] + r->wrv[i + 1];
1030   for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1031     r->ht += r->h[i] + r->hrh[i + 1];
1032   for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1033     r->wr += r->w[i] + r->wrv[i + 1];
1034   for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1035     r->hb += r->h[i] + r->hrh[i + 1];
1036
1037   /* Title. */
1038   if (!(t->flags & SOMF_NO_TITLE))
1039     r->ht += driver->font_height;
1040
1041   return r;
1042 }
1043
1044 static void
1045 tabi_render_free (void *r_)
1046 {
1047   struct tab_rendering *r = r_;
1048
1049   free (r->w);
1050   free (r->h);
1051   free (r->hrh);
1052   free (r->wrv);
1053   free (r);
1054 }
1055
1056 /* Return the horizontal and vertical size of the entire table,
1057    including headers, for the current output device, into HORIZ and
1058    VERT. */
1059 static void
1060 tabi_area (void *r_, int *horiz, int *vert)
1061 {
1062   struct tab_rendering *r = r_;
1063   const struct tab_table *t = r->table;
1064   int width, col;
1065   int height, row;
1066
1067   width = 0;
1068   for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1069        col < t->nc - r->r; col++)
1070     width += r->w[col] + r->wrv[col];
1071   *horiz = width;
1072
1073   height = 0;
1074   for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1075        row < t->nr - t->b; row++)
1076     height += r->h[row] + r->hrh[row];
1077   *vert = height;
1078 }
1079
1080 /* Determines the number of rows or columns (including appropriate
1081    headers), depending on CUMTYPE, that will fit into the space
1082    specified.  Takes rows/columns starting at index START and attempts
1083    to fill up available space MAX.  Returns in END the index of the
1084    last row/column plus one; returns in ACTUAL the actual amount of
1085    space the selected rows/columns (including appropriate headers)
1086    filled. */
1087 static void
1088 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1089                int max, int *actual)
1090 {
1091   const struct tab_rendering *r = r_;
1092   const struct tab_table *t = r->table;
1093   int limit;
1094   int *cells, *rules;
1095   int total;
1096   int idx;
1097
1098   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1099   if (cumtype == SOM_ROWS)
1100     {
1101       assert (start >= 0 && start < t->nr);
1102       limit = t->nr - r->b;
1103       cells = &r->h[start];
1104       rules = &r->hrh[start + 1];
1105       total = r->ht + r->hb;
1106     }
1107   else
1108     {
1109       assert (start >= 0 && start < t->nc);
1110       limit = t->nc - t->r;
1111       cells = &r->w[start];
1112       rules = &r->wrv[start + 1];
1113       total = r->wl + r->wr;
1114     }
1115
1116   total += *cells++;
1117   if (total > max)
1118     {
1119       if (end)
1120         *end = start;
1121       if (actual)
1122         *actual = 0;
1123       return;
1124     }
1125
1126   for (idx = start + 1; idx < limit; idx++)
1127     {
1128       int amt = *cells++ + *rules++;
1129
1130       total += amt;
1131       if (total > max)
1132         {
1133           total -= amt;
1134           break;
1135         }
1136     }
1137
1138   if (end)
1139     *end = idx;
1140
1141   if (actual)
1142     *actual = total;
1143 }
1144
1145 /* Render title for current table, with major index X and minor index
1146    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1147    if Y is nonzero. */
1148 static void
1149 tabi_title (void *r_, int x, int y, int table_num, int subtable_num)
1150 {
1151   const struct tab_rendering *r = r_;
1152   const struct tab_table *t = r->table;
1153   struct outp_text text;
1154   struct string title;
1155
1156   if (t->flags & SOMF_NO_TITLE)
1157     return;
1158
1159   ds_init_empty (&title);
1160   ds_put_format (&title,"%d.%d", table_num, subtable_num);
1161   if (x && y)
1162     ds_put_format (&title, "(%d:%d)", x, y);
1163   else if (x)
1164     ds_put_format (&title, "(%d)", x);
1165   if (command_name != NULL)
1166     ds_put_format (&title, " %s", command_name);
1167   ds_put_cstr (&title, ".  ");
1168   if (t->title != NULL)
1169     ds_put_cstr (&title, t->title);
1170
1171   text.font = OUTP_PROPORTIONAL;
1172   text.justification = OUTP_LEFT;
1173   text.string = ds_ss (&title);
1174   text.h = r->driver->width;
1175   text.v = r->driver->font_height;
1176   text.x = 0;
1177   text.y = r->driver->cp_y;
1178   r->driver->class->text_draw (r->driver, &text);
1179
1180   ds_destroy (&title);
1181 }
1182
1183 static int render_strip (const struct tab_rendering *,
1184                          int x, int y, int r, int c1, int c2, int r1, int r2);
1185
1186 static void
1187 add_range (int ranges[][2], int *np, int start, int end)
1188 {
1189   int n = *np;
1190   if (n == 0 || start > ranges[n - 1][1])
1191     {
1192       ranges[n][0] = start;
1193       ranges[n][1] = end;
1194       ++*np;
1195     }
1196   else
1197     ranges[n - 1][1] = end;
1198 }
1199
1200 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1201    current position on the current output device.  */
1202 static void
1203 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1204 {
1205   const struct tab_rendering *r = r_;
1206   const struct tab_table *t = r->table;
1207   int rows[3][2], cols[3][2];
1208   int n_row_ranges, n_col_ranges;
1209   int y, i;
1210
1211   /* Rows to render, counting horizontal rules as rows.  */
1212   n_row_ranges = 0;
1213   add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1214   add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1215   add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1216
1217   /* Columns to render, counting vertical rules as columns. */
1218   n_col_ranges = 0;
1219   add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1220   add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1221   add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1222
1223   y = r->driver->cp_y;
1224   if (!(t->flags & SOMF_NO_TITLE))
1225     y += r->driver->font_height;
1226   for (i = 0; i < n_row_ranges; i++)
1227     {
1228       int row;
1229
1230       for (row = rows[i][0]; row < rows[i][1]; row++)
1231         {
1232           int x, j;
1233
1234           x = r->driver->cp_x;
1235           for (j = 0; j < n_col_ranges; j++)
1236             x = render_strip (r, x, y, row,
1237                               cols[j][0], cols[j][1],
1238                               rows[i][0], rows[i][1]);
1239
1240           y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1241         }
1242     }
1243 }
1244
1245 const struct som_table_class tab_table_class =
1246   {
1247     tabi_count,
1248     tabi_columns,
1249     tabi_headers,
1250     tabi_flags,
1251
1252     tabi_render_init,
1253     tabi_render_free,
1254
1255     tabi_area,
1256     tabi_cumulate,
1257     tabi_title,
1258     tabi_render,
1259   };
1260 \f
1261 static enum outp_justification
1262 translate_justification (unsigned int opt)
1263 {
1264   switch (opt & TAB_ALIGN_MASK)
1265     {
1266     case TAB_RIGHT:
1267       return OUTP_RIGHT;
1268     case TAB_LEFT:
1269       return OUTP_LEFT;
1270     case TAB_CENTER:
1271       return OUTP_CENTER;
1272     default:
1273       NOT_REACHED ();
1274     }
1275 }
1276
1277 /* Returns the line style to use for drawing a rule of the given
1278    TYPE. */
1279 static enum outp_line_style
1280 rule_to_draw_type (unsigned char type)
1281 {
1282   switch (type)
1283     {
1284     case TAL_0:
1285     case TAL_GAP:
1286       return OUTP_L_NONE;
1287     case TAL_1:
1288       return OUTP_L_SINGLE;
1289     case TAL_2:
1290       return OUTP_L_DOUBLE;
1291     default:
1292       NOT_REACHED ();
1293     }
1294 }
1295
1296 /* Returns the horizontal rule at the given column and row. */
1297 static int
1298 get_hrule (const struct tab_table *t, int col, int row)
1299 {
1300   return t->rh[col + row * t->cf];
1301 }
1302
1303 /* Returns the vertical rule at the given column and row. */
1304 static int
1305 get_vrule (const struct tab_table *t, int col, int row)
1306 {
1307   return t->rv[col + row * (t->cf + 1)];
1308 }
1309
1310 /* Renders the horizontal rule at the given column and row
1311    at (X,Y) on the page. */
1312 static void
1313 render_horz_rule (const struct tab_rendering *r,
1314                   int x, int y, int col, int row)
1315 {
1316   enum outp_line_style style;
1317   style = rule_to_draw_type (get_hrule (r->table, col, row));
1318   if (style != OUTP_L_NONE)
1319     r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1320                             OUTP_L_NONE, style, OUTP_L_NONE, style);
1321 }
1322
1323 /* Renders the vertical rule at the given column and row
1324    at (X,Y) on the page. */
1325 static void
1326 render_vert_rule (const struct tab_rendering *r,
1327                   int x, int y, int col, int row)
1328 {
1329   enum outp_line_style style;
1330   style = rule_to_draw_type (get_vrule (r->table, col, row));
1331   if (style != OUTP_L_NONE)
1332     r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1333                             style, OUTP_L_NONE, style, OUTP_L_NONE);
1334 }
1335
1336 /* Renders the rule intersection at the given column and row
1337    at (X,Y) on the page. */
1338 static void
1339 render_rule_intersection (const struct tab_rendering *r,
1340                           int x, int y, int col, int row)
1341 {
1342   const struct tab_table *t = r->table;
1343
1344   /* Bounds of intersection. */
1345   int x0 = x;
1346   int y0 = y;
1347   int x1 = x + r->wrv[col];
1348   int y1 = y + r->hrh[row];
1349
1350   /* Lines on each side of intersection. */
1351   int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1352   int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1353   int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1354   int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1355
1356   /* Output style for each line. */
1357   enum outp_line_style o_top = rule_to_draw_type (top);
1358   enum outp_line_style o_left = rule_to_draw_type (left);
1359   enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1360   enum outp_line_style o_right = rule_to_draw_type (right);
1361
1362   if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1363       || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1364     r->driver->class->line (r->driver, x0, y0, x1, y1,
1365                             o_top, o_left, o_bottom, o_right);
1366 }
1367
1368 /* Returns the width of columns C1...C2 exclusive,
1369    including interior but not exterior rules. */
1370 static int
1371 strip_width (const struct tab_rendering *r, int c1, int c2)
1372 {
1373   int width = 0;
1374   int c;
1375
1376   for (c = c1; c < c2; c++)
1377     width += r->w[c] + r->wrv[c + 1];
1378   if (c1 < c2)
1379     width -= r->wrv[c2];
1380   return width;
1381 }
1382
1383 /* Returns the height of rows R1...R2 exclusive,
1384    including interior but not exterior rules. */
1385 static int
1386 strip_height (const struct tab_rendering *r, int r1, int r2)
1387 {
1388   int height = 0;
1389   int row;
1390
1391   for (row = r1; row < r2; row++)
1392     height += r->h[row] + r->hrh[row + 1];
1393   if (r1 < r2)
1394     height -= r->hrh[r2];
1395   return height;
1396 }
1397
1398 /* Renders the cell at the given column and row at (X,Y) on the
1399    page.  Also renders joined cells that extend as far to the
1400    right as C1 and as far down as R1. */
1401 static void
1402 render_cell (const struct tab_rendering *r,
1403              int x, int y, int col, int row, int c1, int r1)
1404 {
1405   const struct tab_table *t = r->table;
1406   const int index = col + (row * t->cf);
1407   unsigned char type = t->ct[index];
1408   struct substring *content = &t->cc[index];
1409
1410   if (!(type & TAB_JOIN))
1411     {
1412       if (!(type & TAB_EMPTY))
1413         {
1414           struct outp_text text;
1415           text.font = options_to_font (type);
1416           text.justification = translate_justification (type);
1417           text.string = *content;
1418           text.h = r->w[col];
1419           text.v = r->h[row];
1420           text.x = x;
1421           text.y = y;
1422           r->driver->class->text_draw (r->driver, &text);
1423         }
1424     }
1425   else
1426     {
1427       struct tab_joined_cell *j
1428         = (struct tab_joined_cell *) ss_data (*content);
1429
1430       if (j->x1 == col && j->y1 == row)
1431         {
1432           struct outp_text text;
1433           text.font = options_to_font (type);
1434           text.justification = translate_justification (type);
1435           text.string = j->contents;
1436           text.x = x;
1437           text.y = y;
1438           text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1439           text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1440           r->driver->class->text_draw (r->driver, &text);
1441         }
1442     }
1443 }
1444
1445 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1446    on row ROW, at (X,Y).  Returns X position after rendering.
1447    Also renders joined cells that extend beyond that strip,
1448    cropping them to lie within rendering region (C0,R0)-(C1,R1).
1449    C0 and C1 count vertical rules as columns.
1450    ROW counts horizontal rules as rows, but R0 and R1 do not. */
1451 static int
1452 render_strip (const struct tab_rendering *r,
1453               int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1454 {
1455   int col;
1456
1457   for (col = c0; col < c1; col++)
1458     if (col & 1)
1459       {
1460         if (row & 1)
1461           render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1462         else
1463           render_horz_rule (r, x, y, col / 2, row / 2);
1464         x += r->w[col / 2];
1465       }
1466     else
1467       {
1468         if (row & 1)
1469           render_vert_rule (r, x, y, col / 2, row / 2);
1470         else
1471           render_rule_intersection (r, x, y, col / 2, row / 2);
1472         x += r->wrv[col / 2];
1473       }
1474
1475   return x;
1476 }
1477
1478 /* Sets COMMAND_NAME as the name of the current command,
1479    for embedding in output. */
1480 void
1481 tab_set_command_name (const char *command_name_)
1482 {
1483   free (command_name);
1484   command_name = command_name_ ? xstrdup (command_name_) : NULL;
1485 }