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