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