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