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