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