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