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