Rewrite and improve formatted output routines.
[pspp-builds.git] / src / output / table.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000, 2006 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21
22 #include "table.h"
23
24 #include <ctype.h>
25 #include <stdarg.h>
26 #include <limits.h>
27 #include <stdlib.h>
28
29 #include "output.h"
30 #include "manager.h"
31
32 #include <data/data-out.h>
33 #include <data/format.h>
34 #include <data/variable.h>
35 #include <libpspp/alloc.h>
36 #include <libpspp/assertion.h>
37 #include <libpspp/compiler.h>
38 #include <libpspp/magic.h>
39 #include <libpspp/misc.h>
40 #include <libpspp/pool.h>
41
42 #include "minmax.h"
43
44 #include "gettext.h"
45 #define _(msgid) gettext (msgid)
46 \f
47 const struct som_table_class tab_table_class;
48 static char *command_name;
49
50 /* Returns the font to use for a cell with the given OPTIONS. */
51 static enum outp_font
52 options_to_font (unsigned options) 
53 {
54   return (options & TAB_FIX ? OUTP_FIXED
55           : options & TAB_EMPH ? OUTP_EMPHASIS
56           : OUTP_PROPORTIONAL);
57 }
58
59 /* Creates a table with NC columns and NR rows. */
60 struct tab_table *
61 tab_create (int nc, int nr, int reallocable UNUSED)
62 {
63   struct tab_table *t;
64
65   t = pool_create_container (struct tab_table, container);
66   t->col_style = TAB_COL_NONE;
67   t->col_group = 0;
68   t->title = NULL;
69   t->flags = SOMF_NONE;
70   t->nr = nr;
71   t->nc = t->cf = nc;
72   t->l = t->r = t->t = t->b = 0;
73
74   t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
75   t->ct = pool_malloc (t->container, nr * nc);
76   memset (t->ct, TAB_EMPTY, nc * nr);
77
78   t->rh = pool_nmalloc (t->container, nc, nr + 1);
79   memset (t->rh, 0, nc * (nr + 1));
80
81   t->rv = pool_nmalloc (t->container, nr, nc + 1);
82   memset (t->rv, UCHAR_MAX, nr * (nc + 1));
83
84   t->dim = NULL;
85   t->w = t->h = NULL;
86   t->col_ofs = t->row_ofs = 0;
87
88   return t;
89 }
90
91 /* Destroys table T. */
92 void
93 tab_destroy (struct tab_table *t)
94 {
95   assert (t != NULL);
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 as the dimension function for table T. */
403 void
404 tab_dim (struct tab_table *t, tab_dim_func *dim_func)
405 {
406   assert (t != NULL && t->dim == NULL);
407   t->dim = dim_func;
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         assert (t->w[c] != NOT_INT);
481         if (opt & (TAB_JOIN | TAB_EMPTY))
482           continue;
483
484         text.string = t->cc[c + r * t->cf];
485         text.justification = OUTP_LEFT;
486         text.font = options_to_font (opt);
487         text.h = t->w[c];
488         text.v = INT_MAX;
489         d->class->text_metrics (d, &text, NULL, &h);
490
491         if (h > height)
492           height = h;
493       }
494   }
495
496   return height;
497 }
498
499 /* Callback function to set all columns and rows to their natural
500    dimensions.  Not really meant to be called directly.  */
501 void
502 tab_natural_dimensions (struct tab_table *t, struct outp_driver *d)
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 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_float (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_float(): 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 text value
598    TEXT. */
599 void
600 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
601 {
602   va_list args;
603
604   assert (table != NULL && text != NULL);
605
606   assert (c >= 0 );
607   assert (r >= 0 );
608   assert (c < table->nc);
609   assert (r < table->nr);
610   
611
612 #if DEBUGGING
613   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
614       || c + table->col_ofs >= table->nc
615       || r + table->row_ofs >= table->nr)
616     {
617       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
618               "(%d,%d)\n",
619               c, table->col_ofs, c + table->col_ofs,
620               r, table->row_ofs, r + table->row_ofs,
621               table->nc, table->nr);
622       return;
623     }
624 #endif
625
626   va_start (args, text);
627   table->cc[c + r * table->cf] = text_format (table, opt, text, args);
628   table->ct[c + r * table->cf] = opt;
629   va_end (args);
630 }
631
632 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
633    options OPT to have text value TEXT. */
634 void
635 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
636                 unsigned opt, const char *text, ...)
637 {
638   struct tab_joined_cell *j;
639
640   assert (table != NULL && text != NULL);
641
642   assert (x1 + table->col_ofs >= 0);
643   assert (y1 + table->row_ofs >= 0);
644   assert (y2 >= y1);
645   assert (x2 >= x1);
646   assert (y2 + table->row_ofs < table->nr);
647   assert (x2 + table->col_ofs < table->nc);
648
649 #if DEBUGGING
650   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
651       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
652       || x2 < x1 || x2 + table->col_ofs >= table->nc
653       || y2 < y2 || y2 + table->row_ofs >= table->nr)
654     {
655       printf ("tab_joint_text(): bad cell "
656               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
657               x1, table->col_ofs, x1 + table->col_ofs,
658               y1, table->row_ofs, y1 + table->row_ofs,
659               x2, table->col_ofs, x2 + table->col_ofs,
660               y2, table->row_ofs, y2 + table->row_ofs,
661               table->nc, table->nr);
662       return;
663     }
664 #endif
665
666   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
667   
668   j = pool_alloc (table->container, sizeof *j);
669   j->hit = 0;
670   j->x1 = x1 + table->col_ofs;
671   j->y1 = y1 + table->row_ofs;
672   j->x2 = ++x2 + table->col_ofs;
673   j->y2 = ++y2 + table->row_ofs;
674   
675   {
676     va_list args;
677     
678     va_start (args, text);
679     j->contents = text_format (table, opt, text, args);
680     va_end (args);
681   }
682   
683   opt |= TAB_JOIN;
684   
685   {
686     struct substring *cc = &table->cc[x1 + y1 * table->cf];
687     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
688     const int ofs = table->cf - (x2 - x1);
689
690     int y;
691     
692     for (y = y1; y < y2; y++)
693       {
694         int x;
695         
696         for (x = x1; x < x2; x++)
697           {
698             *cc++ = ss_buffer ((char *) j, 0);
699             *ct++ = opt;
700           }
701         
702         cc += ofs;
703         ct += ofs;
704       }
705   }
706 }
707
708 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
709 void
710 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
711          struct substring *string)
712 {
713   assert (table != NULL && string != NULL);
714   
715 #if DEBUGGING
716   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
717       || c + table->col_ofs >= table->nc
718       || r + table->row_ofs >= table->nr)
719     {
720       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
721               "(%d,%d)\n",
722               c, table->col_ofs, c + table->col_ofs,
723               r, table->row_ofs, r + table->row_ofs,
724               table->nc, table->nr);
725       return;
726     }
727 #endif
728
729   table->cc[c + r * table->cf] = *string;
730   table->ct[c + r * table->cf] = opt;
731 }
732 \f
733 /* Miscellaneous. */
734
735 /* Sets the widths of all the columns and heights of all the rows in
736    table T for driver D. */
737 static void
738 nowrap_dim (struct tab_table *t, struct outp_driver *d)
739 {
740   t->w[0] = tab_natural_width (t, d, 0);
741   t->h[0] = d->font_height;
742 }
743
744 /* Sets the widths of all the columns and heights of all the rows in
745    table T for driver D. */
746 static void
747 wrap_dim (struct tab_table *t, struct outp_driver *d)
748 {
749   t->w[0] = tab_natural_width (t, d, 0);
750   t->h[0] = tab_natural_height (t, d, 0);
751 }
752
753 /* Outputs text BUF as a table with a single cell having cell options
754    OPTIONS, which is a combination of the TAB_* and TAT_*
755    constants. */
756 void
757 tab_output_text (int options, const char *buf, ...)
758 {
759   struct tab_table *t = tab_create (1, 1, 0);
760   char *tmp_buf = NULL;
761
762   if (options & TAT_PRINTF)
763     {
764       va_list args;
765       
766       va_start (args, buf);
767       buf = tmp_buf = xvasprintf (buf, args);
768       va_end (args);
769     }
770   
771   tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
772   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
773   tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim);
774   tab_submit (t);
775   
776   free (tmp_buf);
777 }
778
779 /* Set table flags to FLAGS. */
780 void
781 tab_flags (struct tab_table *t, unsigned flags)
782 {
783   assert (t != NULL);
784   t->flags = flags;
785 }
786
787 /* Easy, type-safe way to submit a tab table to som. */
788 void
789 tab_submit (struct tab_table *t)
790 {
791   struct som_entity s;
792
793   assert (t != NULL);
794   s.class = &tab_table_class;
795   s.ext = t;
796   s.type = SOM_TABLE;
797   som_submit (&s);
798   tab_destroy (t);
799 }
800 \f
801 /* Editing. */
802
803 /* Set table row and column offsets for all functions that affect
804    cells or rules. */
805 void
806 tab_offset (struct tab_table *t, int col, int row)
807 {
808   int diff = 0;
809
810   assert (t != NULL);
811 #if DEBUGGING
812   if (row < -1 || row > t->nr)
813     {
814       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
815       NOT_REACHED ();
816     }
817   if (col < -1 || col > t->nc)
818     {
819       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
820       NOT_REACHED ();
821     }
822 #endif
823
824   if (row != -1)
825     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
826   if (col != -1)
827     diff += (col - t->col_ofs), t->col_ofs = col;
828
829   t->cc += diff;
830   t->ct += diff;
831 }
832
833 /* Increment the row offset by one. If the table is too small,
834    increase its size. */
835 void
836 tab_next_row (struct tab_table *t)
837 {
838   assert (t != NULL);
839   t->cc += t->cf;
840   t->ct += t->cf;
841   if (++t->row_ofs >= t->nr)
842     tab_realloc (t, -1, t->nr * 4 / 3);
843 }
844 \f
845 static struct tab_table *t;
846 static struct outp_driver *d;
847 static int tab_hit;
848
849 /* Set the current table to TABLE. */
850 static void
851 tabi_table (struct som_entity *table)
852 {
853   assert (table != NULL);
854   assert (table->type == SOM_TABLE);
855
856   t = table->ext;
857   tab_offset (t, 0, 0);
858   
859   assert (t->w == NULL && t->h == NULL);
860   t->w = pool_nalloc (t->container, t->nc, sizeof *t->w);
861   t->h = pool_nalloc (t->container, t->nr, sizeof *t->h);
862   t->hrh = pool_nmalloc (t->container, t->nr + 1, sizeof *t->hrh);
863   t->wrv = pool_nmalloc (t->container, t->nc + 1, sizeof *t->wrv);
864 }
865
866 /* Returns the line style to use for spacing purposes for a rule
867    of the given TYPE. */
868 static enum outp_line_style
869 rule_to_spacing_type (unsigned char type) 
870 {
871   switch (type) 
872     {
873     case TAL_0:
874       return OUTP_L_NONE;
875     case TAL_GAP:
876     case TAL_1:
877       return OUTP_L_SINGLE;
878     case TAL_2:
879       return OUTP_L_DOUBLE;
880     default:
881       NOT_REACHED ();
882     }
883 }
884
885 /* Set the current output device to DRIVER. */
886 static void
887 tabi_driver (struct outp_driver *driver)
888 {
889   int c, r;
890   int i;
891   
892   assert (driver != NULL);
893   d = driver;
894   
895   /* Figure out sizes of rules. */
896   for (r = 0; r <= t->nr; r++) 
897     {
898       int width = 0;
899       for (c = 0; c < t->nc; c++) 
900         {
901           unsigned char rh = t->rh[c + r * t->cf];
902           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
903           if (w > width)
904             width = w; 
905         }
906       t->hrh[r] = width; 
907     }
908
909   for (c = 0; c <= t->nc; c++) 
910     {
911       int width = 0;
912       for (r = 0; r < t->nr; r++) 
913         {
914           unsigned char *rv = &t->rv[c + r * (t->cf + 1)];
915           int w;
916           if (*rv == UCHAR_MAX)
917             *rv = c != 0 && c != t->nc ? TAL_GAP : TAL_0;
918           w = driver->vert_line_width[rule_to_spacing_type (*rv)];
919           if (w > width)
920             width = w;
921         }
922       t->wrv[c] = width; 
923     }
924
925 #if DEBUGGING
926   for (i = 0; i < t->nr; i++)
927     t->h[i] = -1;
928   for (i = 0; i < t->nc; i++)
929     t->w[i] = -1;
930 #endif
931
932   assert (t->dim != NULL);
933   t->dim (t, d);
934
935 #if DEBUGGING
936   {
937     int error = 0;
938
939     for (i = 0; i < t->nr; i++)
940       {
941         if (t->h[i] == -1)
942           {
943             printf ("Table row %d height not initialized.\n", i);
944             error = 1;
945           }
946         assert (t->h[i] > 0);
947       }
948     
949     for (i = 0; i < t->nc; i++)
950       {
951         if (t->w[i] == -1)
952           {
953             printf ("Table column %d width not initialized.\n", i);
954             error = 1;
955           }
956         assert (t->w[i] > 0);
957       }
958   }
959 #endif
960     
961   /* Add up header sizes. */
962   for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
963     t->wl += t->w[i] + t->wrv[i + 1];
964   for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
965     t->ht += t->h[i] + t->hrh[i + 1];
966   for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
967     t->wr += t->w[i] + t->wrv[i + 1];
968   for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
969     t->hb += t->h[i] + t->hrh[i + 1];
970   
971   /* Title. */
972   if (!(t->flags & SOMF_NO_TITLE))
973     t->ht += d->font_height;
974 }
975
976 /* Return the number of columns and rows in the table into N_COLUMNS
977    and N_ROWS, respectively. */
978 static void
979 tabi_count (int *n_columns, int *n_rows)
980 {
981   assert (n_columns != NULL && n_rows != NULL);
982   *n_columns = t->nc;
983   *n_rows = t->nr;
984 }
985
986 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
987
988 /* Return the horizontal and vertical size of the entire table,
989    including headers, for the current output device, into HORIZ and
990    VERT. */
991 static void
992 tabi_area (int *horiz, int *vert)
993 {
994   assert (horiz != NULL && vert != NULL);
995   
996   {
997     int w, c;
998     
999     for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1000          c < t->nc - t->r; c++)
1001       w += t->w[c] + t->wrv[c];
1002     *horiz = w;
1003   }
1004   
1005   {
1006     int h, r;
1007     for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1008          r < t->nr - t->b; r++)
1009       h += t->h[r] + t->hrh[r];
1010     *vert = h;
1011   }
1012 }
1013
1014 /* Return the column style for this table into STYLE. */
1015 static void
1016 tabi_columns (int *style)
1017 {
1018   assert (style != NULL);
1019   *style = t->col_style;
1020 }
1021
1022 /* Return the number of header rows/columns on the left, right, top,
1023    and bottom sides into HL, HR, HT, and HB, respectively. */
1024 static void
1025 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1026 {
1027   assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1028   *hl = t->l;
1029   *hr = t->r;
1030   *ht = t->t;
1031   *hb = t->b;
1032 }
1033
1034 /* Determines the number of rows or columns (including appropriate
1035    headers), depending on CUMTYPE, that will fit into the space
1036    specified.  Takes rows/columns starting at index START and attempts
1037    to fill up available space MAX.  Returns in END the index of the
1038    last row/column plus one; returns in ACTUAL the actual amount of
1039    space the selected rows/columns (including appropriate headers)
1040    filled. */
1041 static void
1042 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1043 {
1044   int n;
1045   int *d;
1046   int *r;
1047   int total;
1048   
1049   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1050   if (cumtype == SOM_ROWS)
1051     {
1052       assert (start >= 0 && start < t->nr);
1053       n = t->nr - t->b;
1054       d = &t->h[start];
1055       r = &t->hrh[start + 1];
1056       total = t->ht + t->hb;
1057     }
1058   else
1059     {
1060       assert (start >= 0 && start < t->nc);
1061       n = t->nc - t->r;
1062       d = &t->w[start];
1063       r = &t->wrv[start + 1];
1064       total = t->wl + t->wr;
1065     }
1066   
1067   total += *d++;
1068   if (total > max)
1069     {
1070       if (end)
1071         *end = start;
1072       if (actual)
1073         *actual = 0;
1074       return;
1075     }
1076     
1077   {
1078     int x;
1079       
1080     for (x = start + 1; x < n; x++)
1081       {
1082         int amt = *d++ + *r++;
1083         
1084         total += amt;
1085         if (total > max)
1086           {
1087             total -= amt;
1088             break;
1089           }
1090       }
1091
1092     if (end)
1093       *end = x;
1094     
1095     if (actual)
1096       *actual = total;
1097   }
1098 }
1099
1100 /* Return flags set for the current table into FLAGS. */
1101 static void
1102 tabi_flags (unsigned *flags)
1103 {
1104   assert (flags != NULL);
1105   *flags = t->flags;
1106 }
1107
1108 /* Returns true if the table will fit in the given page WIDTH,
1109    false otherwise. */
1110 static bool
1111 tabi_fits_width (int width) 
1112 {
1113   int i;
1114
1115   for (i = t->l; i < t->nc - t->r; i++)
1116     if (t->wl + t->wr + t->w[i] > width)
1117       return false;
1118
1119   return true;
1120 }
1121
1122 /* Returns true if the table will fit in the given page LENGTH,
1123    false otherwise. */
1124 static bool
1125 tabi_fits_length (int length) 
1126 {
1127   int i;
1128
1129   for (i = t->t; i < t->nr - t->b; i++)
1130     if (t->ht + t->hb + t->h[i] > length)
1131       return false;
1132
1133   return true;
1134 }
1135
1136 /* Sets the number of header rows/columns on the left, right, top,
1137    and bottom sides to HL, HR, HT, and HB, respectively. */
1138 static void
1139 tabi_set_headers (int hl, int hr, int ht, int hb)
1140 {
1141   t->l = hl;
1142   t->r = hr;
1143   t->t = ht;
1144   t->b = hb;
1145 }
1146
1147 /* Render title for current table, with major index X and minor index
1148    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1149    if Y is nonzero. */
1150 static void
1151 tabi_title (int x, int y)
1152 {
1153   char buf[1024];
1154   char *cp;
1155
1156   if (t->flags & SOMF_NO_TITLE)
1157     return;
1158   
1159   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1160   if (x && y)
1161     cp = spprintf (cp, "(%d:%d)", x, y);
1162   else if (x)
1163     cp = spprintf (cp, "(%d)", x);
1164   if (command_name != NULL)
1165     cp = spprintf (cp, " %s", command_name);
1166   cp = stpcpy (cp, ".  ");
1167   if (t->title != NULL)
1168     {
1169       size_t length = strlen (t->title);
1170       memcpy (cp, t->title, length);
1171       cp += length;
1172     }
1173   *cp = 0;
1174   
1175   {
1176     struct outp_text text;
1177
1178     text.font = OUTP_PROPORTIONAL;
1179     text.justification = OUTP_LEFT;
1180     text.string = ss_buffer (buf, cp - buf);
1181     text.h = d->width;
1182     text.v = d->font_height;
1183     text.x = 0;
1184     text.y = d->cp_y;
1185     d->class->text_draw (d, &text);
1186   }
1187 }
1188
1189 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1190
1191 /* Renders columns C0...C1, plus headers, of rows R0...R1,
1192    at the given vertical position Y.
1193    C0 and C1 count vertical rules as columns,
1194    but R0 and R1 do not count horizontal rules as rows.
1195    Returns the vertical position after rendering. */
1196 static int
1197 render_rows (int y, int c0, int c1, int r0, int r1)
1198 {
1199   int r;
1200   for (r = r0; r < r1; r++) 
1201     {
1202       int x = d->cp_x;
1203       x = render_strip (x, y, r, 0, t->l * 2 + 1, r0, r1);
1204       x = render_strip (x, y, r, c0 * 2 + 1, c1 * 2, r0, r1);
1205       x = render_strip (x, y, r, (t->nc - t->r) * 2, t->nc * 2 + 1, r0, r1);
1206       y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
1207     }
1208   return y;
1209 }
1210
1211 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1212    current position on the current output device.  */
1213 static void
1214 tabi_render (int c0, int r0, int c1, int r1)
1215 {
1216   int y;
1217   
1218   tab_hit++;
1219
1220   y = d->cp_y;
1221   if (!(t->flags & SOMF_NO_TITLE))
1222     y += d->font_height;
1223
1224   y = render_rows (y, c0, c1, 0, t->t * 2 + 1);
1225   y = render_rows (y, c0, c1, r0 * 2 + 1, r1 * 2);
1226   y = render_rows (y, c0, c1, (t->nr - t->b) * 2, t->nr * 2 + 1);
1227 }
1228
1229 const struct som_table_class tab_table_class =
1230   {
1231     tabi_table,
1232     tabi_driver,
1233     
1234     tabi_count,
1235     tabi_area,
1236     NULL,
1237     NULL,
1238     tabi_columns,
1239     NULL,
1240     tabi_headers,
1241     NULL,
1242     tabi_cumulate,
1243     tabi_flags,
1244     tabi_fits_width,
1245     tabi_fits_length,
1246     
1247     NULL,
1248     NULL,
1249     tabi_set_headers,
1250
1251     tabi_title,
1252     tabi_render,
1253   };
1254 \f
1255 static enum outp_justification
1256 translate_justification (unsigned int opt)
1257 {
1258   switch (opt & TAB_ALIGN_MASK) 
1259     {
1260     case TAB_RIGHT:
1261       return OUTP_RIGHT;
1262     case TAB_LEFT:
1263       return OUTP_LEFT;
1264     case TAB_CENTER:
1265       return OUTP_CENTER;
1266     default:
1267       NOT_REACHED ();
1268     }
1269 }
1270
1271 /* Returns the line style to use for drawing a rule of the given
1272    TYPE. */
1273 static enum outp_line_style
1274 rule_to_draw_type (unsigned char type) 
1275 {
1276   switch (type) 
1277     {
1278     case TAL_0:
1279     case TAL_GAP:
1280       return OUTP_L_NONE;
1281     case TAL_1:
1282       return OUTP_L_SINGLE;
1283     case TAL_2:
1284       return OUTP_L_DOUBLE;
1285     default:
1286       NOT_REACHED ();
1287     }
1288 }
1289
1290 /* Returns the horizontal rule at the given column and row. */
1291 static int
1292 get_hrule (int c, int r) 
1293 {
1294   return t->rh[c + r * t->cf];
1295 }
1296
1297 /* Returns the vertical rule at the given column and row. */
1298 static int
1299 get_vrule (int c, int r) 
1300 {
1301   return t->rv[c + r * (t->cf + 1)];
1302 }
1303
1304 /* Renders the horizontal rule at the given column and row
1305    at (X,Y) on the page. */
1306 static void
1307 render_horz_rule (int x, int y, int c, int r)
1308 {
1309   enum outp_line_style style = rule_to_draw_type (get_hrule (c, r));
1310   if (style != OUTP_L_NONE)
1311     d->class->line (d, x, y, x + t->w[c], y + t->hrh[r],
1312                     OUTP_L_NONE, style, OUTP_L_NONE, style);
1313 }
1314
1315 /* Renders the vertical rule at the given column and row
1316    at (X,Y) on the page. */
1317 static void
1318 render_vert_rule (int x, int y, int c, int r)
1319 {
1320   enum outp_line_style style = rule_to_draw_type (get_vrule (c, r));
1321   if (style != OUTP_L_NONE)
1322     d->class->line (d, x, y, x + t->wrv[c], y + t->h[r],
1323                     style, OUTP_L_NONE, style, OUTP_L_NONE);
1324 }
1325
1326 /* Renders the rule intersection at the given column and row
1327    at (X,Y) on the page. */
1328 static void
1329 render_rule_intersection (int x, int y, int c, int r)
1330 {
1331   /* Bounds of intersection. */
1332   int x0 = x;
1333   int y0 = y;
1334   int x1 = x + t->wrv[c];
1335   int y1 = y + t->hrh[r];
1336
1337   /* Lines on each side of intersection. */
1338   int top = r > 0 ? get_vrule (c, r - 1) : TAL_0;
1339   int left = c > 0 ? get_hrule (c - 1, r) : TAL_0;
1340   int bottom = r < t->nr ? get_vrule (c, r) : TAL_0;
1341   int right = c < t->nc ? get_hrule (c, r) : TAL_0;
1342
1343   /* Output style for each line. */
1344   enum outp_line_style o_top = rule_to_draw_type (top);
1345   enum outp_line_style o_left = rule_to_draw_type (left);
1346   enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1347   enum outp_line_style o_right = rule_to_draw_type (right);
1348
1349   if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1350       || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1351     d->class->line (d, x0, y0, x1, y1, o_top, o_left, o_bottom, o_right);
1352 }
1353
1354 /* Returns the width of columns C1...C2 exclusive,
1355    including interior but not exterior rules. */
1356 static int
1357 strip_width (int c1, int c2)
1358 {
1359   int width = 0;
1360   int c;
1361
1362   for (c = c1; c < c2; c++) 
1363     width += t->w[c] + t->wrv[c + 1];
1364   if (c1 < c2)
1365     width -= t->wrv[c2];
1366   return width;
1367 }
1368
1369 /* Returns the height of rows R1...R2 exclusive,
1370    including interior but not exterior rules. */
1371 static int
1372 strip_height (int r1, int r2)
1373 {
1374   int height = 0;
1375   int r;
1376
1377   for (r = r1; r < r2; r++) 
1378     height += t->h[r] + t->hrh[r + 1];
1379   if (r1 < r2)
1380     height -= t->hrh[r2];
1381   return height;
1382 }
1383
1384 /* Renders the cell at the given column and row at (X,Y) on the
1385    page.  Also renders joined cells that extend as far to the
1386    right as C1 and as far down as R1. */
1387 static void
1388 render_cell (int x, int y, int c, int r, int c1, int r1)
1389 {
1390   const int index = c + (r * t->cf);
1391   unsigned char type = t->ct[index];
1392   struct substring *content = &t->cc[index];
1393   
1394   if (!(type & TAB_JOIN))
1395     {
1396       if (!(type & TAB_EMPTY))
1397         {
1398           struct outp_text text;
1399           text.font = options_to_font (type);
1400           text.justification = translate_justification (type);
1401           text.string = *content;
1402           text.h = t->w[c];
1403           text.v = t->h[r];
1404           text.x = x;
1405           text.y = y;
1406           d->class->text_draw (d, &text);
1407         }
1408     }
1409   else
1410     {
1411       struct tab_joined_cell *j
1412         = (struct tab_joined_cell *) ss_data (*content);
1413
1414       if (j->hit != tab_hit)
1415         {
1416           j->hit = tab_hit;
1417
1418           if (j->x1 == c && j->y1 == r)
1419             {
1420               struct outp_text text;
1421               text.font = options_to_font (type);
1422               text.justification = translate_justification (type);
1423               text.string = j->contents;
1424               text.x = x;
1425               text.y = y;
1426               text.h = strip_width (j->x1, MIN (j->x2, c1));
1427               text.v = strip_height (j->y1, MIN (j->y2, r1));
1428               d->class->text_draw (d, &text);
1429             }
1430         }
1431     }
1432 }
1433
1434 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1435    on row R, at (X,Y).  Returns X position after rendering.
1436    Also renders joined cells that extend beyond that strip,
1437    cropping them to lie within rendering region (C0,R0)-(C1,R1).
1438    C0 and C1 count vertical rules as columns.
1439    R counts horizontal rules as rows, but R0 and R1 do not. */
1440 static int
1441 render_strip (int x, int y, int r, int c0, int c1, int r0 UNUSED, int r1)
1442 {
1443   int c;
1444
1445   for (c = c0; c < c1; c++)
1446     if (c & 1) 
1447       {
1448         if (r & 1)
1449           render_cell (x, y, c / 2, r / 2, c1 / 2, r1);
1450         else
1451           render_horz_rule (x, y, c / 2, r / 2);
1452         x += t->w[c / 2];
1453       }
1454     else
1455       {
1456         if (r & 1)
1457           render_vert_rule (x, y, c / 2, r / 2);
1458         else
1459           render_rule_intersection (x, y, c / 2, r / 2);
1460         x += t->wrv[c / 2];
1461       }
1462   
1463   return x;
1464 }
1465
1466 /* Sets COMMAND_NAME as the name of the current command,
1467    for embedding in output. */
1468 void
1469 tab_set_command_name (const char *command_name_) 
1470 {
1471   free (command_name);
1472   command_name = command_name_ ? xstrdup (command_name_) : NULL;
1473 }