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