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