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