Added bounds checking to tab.c
[pspp-builds.git] / 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 }
119
120 /* Sets the width and height of a table, in columns and rows,
121    respectively.  Use only to reduce the size of a table, since it
122    does not change the amount of allocated memory. */
123 void
124 tab_resize (struct tab_table *t, int nc, int nr)
125 {
126   assert (t != NULL);
127   if (nc != -1)
128     {
129       assert (nc + t->col_ofs <= t->cf);
130       t->nc = nc + t->col_ofs;
131     }
132   if (nr != -1)
133     {
134       assert (nr + t->row_ofs <= t->nr);
135       t->nr = nr + t->row_ofs;
136     }
137 }
138
139 /* Changes either or both dimensions of a table.  Consider using the
140    above routine instead if it won't waste a lot of space.
141
142    Changing the number of columns in a table is particularly expensive
143    in space and time.  Avoid doing such.  FIXME: In fact, transferring
144    of rules isn't even implemented yet. */
145 void
146 tab_realloc (struct tab_table *t, int nc, int nr)
147 {
148   int ro, co;
149   
150   assert (t != NULL);
151 #if GLOBAL_DEBUGGING
152   assert (t->reallocable);
153 #endif
154   ro = t->row_ofs;
155   co = t->col_ofs;
156   if (ro || co)
157     tab_offset (t, 0, 0);
158
159   if (nc == -1)
160     nc = t->nc;
161   if (nr == -1)
162     nr = t->nr;
163   
164   assert (nc == t->nc);
165   
166   if (nc > t->cf)
167     {
168       int mr1 = min (nr, t->nr);
169       int mc1 = min (nc, t->nc);
170       
171       struct len_string *new_cc;
172       unsigned char *new_ct;
173       int r;
174
175       new_cc = pool_malloc (t->container, nr * nc * sizeof *new_cc);
176       new_ct = pool_malloc (t->container, nr * nc);
177       for (r = 0; r < mr1; r++)
178         {
179           memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
180           memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
181           memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
182         }
183       pool_free (t->container, t->cc);
184       pool_free (t->container, t->ct);
185       t->cc = new_cc;
186       t->ct = new_ct;
187       t->cf = nc;
188     }
189   else if (nr != t->nr)
190     {
191       t->cc = pool_realloc (t->container, t->cc, nr * nc * sizeof *t->cc);
192       t->ct = pool_realloc (t->container, t->ct, nr * nc);
193
194       t->rh = pool_realloc (t->container, t->rh, nc * (nr + 1));
195       t->rv = pool_realloc (t->container, t->rv, (nc + 1) * nr);
196       t->trh = pool_realloc (t->container, t->trh, nr + 1);
197       t->hrh = pool_realloc (t->container, t->hrh,
198                              sizeof *t->hrh * (nr + 1));
199       
200       if (nr > t->nr)
201         {
202           memset (&t->rh[nc * (t->nr + 1)], 0, (nr - t->nr) * nc);
203           memset (&t->rv[(nc + 1) * t->nr], 0, (nr - t->nr) * (nc + 1));
204           memset (&t->trh[t->nr + 1], 0, nr - t->nr);
205         }
206     }
207
208   memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
209   
210   t->nr = nr;
211   t->nc = nc;
212
213   if (ro || co)
214     tab_offset (t, co, ro);
215 }
216
217 /* Sets the number of header rows on each side of TABLE to L on the
218    left, R on the right, T on the top, B on the bottom.  Header rows
219    are repeated when a table is broken across multiple columns or
220    multiple pages. */
221 void
222 tab_headers (struct tab_table *table, int l, int r, int t, int b)
223 {
224   assert (table != NULL);
225   table->l = l;
226   table->r = r;
227   table->t = t;
228   table->b = b;
229 }
230
231 /* Set up table T so that, when it is an appropriate size, it will be
232    displayed across the page in columns.
233
234    STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
235    as a unit. */
236 void
237 tab_columns (struct tab_table *t, int style, int group)
238 {
239   assert (t != NULL);
240   t->col_style = style;
241   t->col_group = group;
242 }
243 \f
244 /* Rules. */
245
246 /* Draws a vertical line to the left of cells at horizontal position X
247    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
248 void
249 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
250 {
251   int y;
252
253   assert (t != NULL);
254   assert (x  > 0);
255   assert (x  < t->nc);
256   assert (y1 >= 0);
257   assert (y2 >= y1);
258   assert (y2 <=  t->nr);
259
260 #if GLOBAL_DEBUGGING
261   if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
262       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
263       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
264     {
265       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
266                 "table size (%d,%d)\n"),
267               x, t->col_ofs, x + t->col_ofs,
268               y1, t->row_ofs, y1 + t->row_ofs,
269               y2, t->row_ofs, y2 + t->row_ofs,
270               t->nc, t->nr);
271       return;
272     }
273 #endif
274
275   x += t->col_ofs;
276   y1 += t->row_ofs;
277   y2 += t->row_ofs;
278
279   if (style != -1)
280     {
281       if ((style & TAL_SPACING) == 0)
282         for (y = y1; y <= y2; y++)
283           t->rv[x + (t->cf + 1) * y] = style;
284       t->trv[x] |= (1 << (style & ~TAL_SPACING));
285     }
286 }
287
288 /* Draws a horizontal line above cells at vertical position Y from X1
289    to X2 inclusive in style STYLE, if style is not -1. */
290 void
291 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
292 {
293   int x;
294
295   assert (t != NULL);
296
297   assert (y >= 0);
298   assert (y < t->nr);
299   assert (x2 >= x1 );
300   assert (x1 >= 0 );
301   assert (x2 < t->nc);
302
303   x1 += t->col_ofs;
304   x2 += t->col_ofs;
305   y += t->row_ofs;
306
307   if (style != -1)
308     {
309       if ((style & TAL_SPACING) == 0)
310         for (x = x1; x <= x2; x++)
311           t->rh[x + t->cf * y] = style;
312       t->trh[y] |= (1 << (style & ~TAL_SPACING));
313     }
314 }
315
316 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
317    lines of style F_H and vertical lines of style F_V.  Fills the
318    interior of the box with horizontal lines of style I_H and vertical
319    lines of style I_V.  Any of the line styles may be -1 to avoid
320    drawing those lines.  This is distinct from 0, which draws a null
321    line. */
322 void
323 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
324          int x1, int y1, int x2, int y2)
325 {
326   assert (t != NULL);
327
328   assert (x2 >= x1);
329   assert (y2 >= y1);
330   assert (x1 >= 0);
331   assert (y1 >= 0);
332   assert (x2 < t->nc);
333   assert (y2 < t->nr);
334
335 #if GLOBAL_DEBUGGING
336   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc 
337       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
338       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr 
339       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
340     {
341       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
342                 "in table size (%d,%d)\n"),
343               x1, t->col_ofs, x1 + t->col_ofs,
344               y1, t->row_ofs, y1 + t->row_ofs,
345               x2, t->col_ofs, x2 + t->col_ofs,
346               y2, t->row_ofs, y2 + t->row_ofs,
347               t->nc, t->nr);
348       abort ();
349     }
350 #endif
351
352   x1 += t->col_ofs;
353   x2 += t->col_ofs;
354   y1 += t->row_ofs;
355   y2 += t->row_ofs;
356
357   if (f_h != -1)
358     {
359       int x;
360       if ((f_h & TAL_SPACING) == 0)
361         for (x = x1; x <= x2; x++)
362           {
363             t->rh[x + t->cf * y1] = f_h;
364             t->rh[x + t->cf * (y2 + 1)] = f_h;
365           }
366       t->trh[y1] |= (1 << (f_h & ~TAL_SPACING));
367       t->trh[y2 + 1] |= (1 << (f_h & ~TAL_SPACING));
368     }
369   if (f_v != -1)
370     {
371       int y;
372       if ((f_v & TAL_SPACING) == 0)
373         for (y = y1; y <= y2; y++)
374           {
375             t->rv[x1 + (t->cf + 1) * y] = f_v;
376             t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
377           }
378       t->trv[x1] |= (1 << (f_v & ~TAL_SPACING));
379       t->trv[x2 + 1] |= (1 << (f_v & ~TAL_SPACING));
380     }
381
382   if (i_h != -1)
383     {
384       int y;
385       
386       for (y = y1 + 1; y <= y2; y++)
387         {
388           int x;
389
390           if ((i_h & TAL_SPACING) == 0)
391             for (x = x1; x <= x2; x++)
392               t->rh[x + t->cf * y] = i_h;
393
394           t->trh[y] |= (1 << (i_h & ~TAL_SPACING));
395         }
396     }
397   if (i_v != -1)
398     {
399       int x;
400       
401       for (x = x1 + 1; x <= x2; x++)
402         {
403           int y;
404           
405           if ((i_v & TAL_SPACING) == 0)
406             for (y = y1; y <= y2; y++)
407               t->rv[x + (t->cf + 1) * y] = i_v;
408
409           t->trv[x] |= (1 << (i_v & ~TAL_SPACING));
410         }
411     }
412 }
413
414 /* Formats text TEXT and arguments ARGS as indicated in OPT and sets
415    the resultant string into S in TABLE's pool. */
416 static void
417 text_format (struct tab_table *table, int opt, const char *text, va_list args,
418              struct len_string *s)
419 {
420   int len;
421   
422   assert (table != NULL && text != NULL && s != NULL);
423   
424   if (opt & TAT_PRINTF)
425     {
426       char *temp_buf = local_alloc (1024);
427       
428       len = nvsprintf (temp_buf, text, args);
429       text = temp_buf;
430     }
431   else
432     len = strlen (text);
433
434   ls_create_buffer (table->container, s, text, len);
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   union value temp_val;
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   if (formats[f->type].cat & FCAT_STRING)
594     {
595       temp_val.c = (char *) v->s;
596       v = &temp_val;
597     }
598   data_out (contents, f, v);
599 }
600
601 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
602    with NDEC decimal places. */
603 void
604 tab_float (struct tab_table *table, int c, int r, unsigned char opt,
605            double val, int w, int d)
606 {
607   char *contents;
608   char buf[40], *cp;
609   
610   struct fmt_spec f;
611
612   assert (table != NULL && w <= 40);
613   
614   f.type = FMT_F;
615   f.w = w;
616   f.d = d;
617   
618 #if GLOBAL_DEBUGGING
619   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
620       || c + table->col_ofs >= table->nc
621       || r + table->row_ofs >= table->nr)
622     {
623       printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
624               "(%d,%d)\n",
625               c, table->col_ofs, c + table->col_ofs,
626               r, table->row_ofs, r + table->row_ofs,
627               table->nc, table->nr);
628       return;
629     }
630 #endif
631
632   data_out (buf, &f, (union value *) &val);
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 >= 0);
690   assert (y1 >= 0);
691   assert (y2 >= y1);
692   assert (x2 >= x1);
693   assert (y2 < table->nr);
694   assert (x2 < 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 len_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 len_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_table s;
874
875   assert (t != NULL);
876   s.class = &tab_table_class;
877   s.ext = t;
878   som_submit (&s);
879   tab_destroy (t);
880 }
881 \f
882 /* Editing. */
883
884 /* Set table row and column offsets for all functions that affect
885    cells or rules. */
886 void
887 tab_offset (struct tab_table *t, int col, int row)
888 {
889   int diff = 0;
890
891   assert (t != NULL);
892 #if GLOBAL_DEBUGGING
893   if (row < -1 || row >= t->nr)
894     {
895       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
896       abort ();
897     }
898   if (col < -1 || col >= t->nc)
899     {
900       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
901       abort ();
902     }
903 #endif
904
905   if (row != -1)
906     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
907   if (col != -1)
908     diff += (col - t->col_ofs), t->col_ofs = col;
909
910   t->cc += diff;
911   t->ct += diff;
912 }
913
914 /* Increment the row offset by one. If the table is too small,
915    increase its size. */
916 void
917 tab_next_row (struct tab_table *t)
918 {
919   assert (t != NULL);
920   t->cc += t->cf;
921   t->ct += t->cf;
922   if (++t->row_ofs >= t->nr)
923     tab_realloc (t, -1, t->nr * 4 / 3);
924 }
925 \f
926 static struct tab_table *t;
927 static struct outp_driver *d;
928 int tab_hit;
929
930 /* Set the current table to TABLE. */
931 static void
932 tabi_table (struct som_table *table)
933 {
934   assert (table != NULL);
935   t = table->ext;
936   tab_offset (t, 0, 0);
937   
938   assert (t->w == NULL && t->h == NULL);
939   t->w = pool_alloc (t->container, sizeof *t->w * t->nc);
940   t->h = pool_alloc (t->container, sizeof *t->h * t->nr);
941 }
942
943 /* Set the current output device to DRIVER. */
944 static void
945 tabi_driver (struct outp_driver *driver)
946 {
947   int i;
948
949   assert (driver != NULL);
950   d = driver;
951   
952   /* Figure out sizes of rules. */
953   for (t->hr_tot = i = 0; i <= t->nr; i++)
954     t->hr_tot += t->hrh[i] = d->horiz_line_spacing[t->trh[i]];
955   for (t->vr_tot = i = 0; i <= t->nc; i++)
956     t->vr_tot += t->wrv[i] = d->vert_line_spacing[t->trv[i]];
957
958 #if GLOBAL_DEBUGGING
959   for (i = 0; i < t->nr; i++)
960     t->h[i] = -1;
961   for (i = 0; i < t->nc; i++)
962     t->w[i] = -1;
963 #endif
964
965   assert (t->dim != NULL);
966   t->dim (t, d);
967
968 #if GLOBAL_DEBUGGING
969   {
970     int error = 0;
971
972     for (i = 0; i < t->nr; i++)
973       {
974         if (t->h[i] == -1)
975           {
976             printf ("Table row %d height not initialized.\n", i);
977             error = 1;
978           }
979         assert (t->h[i] > 0);
980       }
981     
982     for (i = 0; i < t->nc; i++)
983       {
984         if (t->w[i] == -1)
985           {
986             printf ("Table column %d width not initialized.\n", i);
987             error = 1;
988           }
989         assert (t->w[i] > 0);
990       }
991   }
992 #endif
993     
994   /* Add up header sizes. */
995   for (i = 0, t->wl = t->wrv[0]; i < t->l; i++)
996     t->wl += t->w[i] + t->wrv[i + 1];
997   for (i = 0, t->ht = t->hrh[0]; i < t->t; i++)
998     t->ht += t->h[i] + t->hrh[i + 1];
999   for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++)
1000     t->wr += t->w[i] + t->wrv[i + 1];
1001   for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++)
1002     t->hb += t->h[i] + t->hrh[i + 1];
1003   
1004   /* Title. */
1005   if (!(t->flags & SOMF_NO_TITLE))
1006     t->ht += d->font_height;
1007 }
1008
1009 /* Return the number of columns and rows in the table into N_COLUMNS
1010    and N_ROWS, respectively. */
1011 static void
1012 tabi_count (int *n_columns, int *n_rows)
1013 {
1014   assert (n_columns != NULL && n_rows != NULL);
1015   *n_columns = t->nc;
1016   *n_rows = t->nr;
1017 }
1018
1019 static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual);
1020
1021 /* Return the horizontal and vertical size of the entire table,
1022    including headers, for the current output device, into HORIZ and
1023    VERT. */
1024 static void
1025 tabi_area (int *horiz, int *vert)
1026 {
1027   assert (horiz != NULL && vert != NULL);
1028   
1029   {
1030     int w, c;
1031     
1032     for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l];
1033          c < t->nc - t->r; c++)
1034       w += t->w[c] + t->wrv[c];
1035     *horiz = w;
1036   }
1037   
1038   {
1039     int h, r;
1040     for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t];
1041          r < t->nr - t->b; r++)
1042       h += t->h[r] + t->hrh[r];
1043     *vert = h;
1044   }
1045 }
1046
1047 /* Return the column style for this table into STYLE. */
1048 static void
1049 tabi_columns (int *style)
1050 {
1051   assert (style != NULL);
1052   *style = t->col_style;
1053 }
1054
1055 /* Return the number of header rows/columns on the left, right, top,
1056    and bottom sides into HL, HR, HT, and HB, respectively. */
1057 static void
1058 tabi_headers (int *hl, int *hr, int *ht, int *hb)
1059 {
1060   assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL);
1061   *hl = t->l;
1062   *hr = t->r;
1063   *ht = t->t;
1064   *hb = t->b;
1065 }
1066
1067 /* Determines the number of rows or columns (including appropriate
1068    headers), depending on CUMTYPE, that will fit into the space
1069    specified.  Takes rows/columns starting at index START and attempts
1070    to fill up available space MAX.  Returns in END the index of the
1071    last row/column plus one; returns in ACTUAL the actual amount of
1072    space the selected rows/columns (including appropriate headers)
1073    filled. */
1074 static void
1075 tabi_cumulate (int cumtype, int start, int *end, int max, int *actual)
1076 {
1077   int n;
1078   int *d;
1079   int *r;
1080   int total;
1081   
1082   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1083   if (cumtype == SOM_ROWS)
1084     {
1085       assert (start >= 0 && start < t->nr);
1086       n = t->nr - t->b;
1087       d = &t->h[start];
1088       r = &t->hrh[start + 1];
1089       total = t->ht + t->hb;
1090     } else {
1091       assert (start >= 0 && start < t->nc);
1092       n = t->nc - t->r;
1093       d = &t->w[start];
1094       r = &t->wrv[start + 1];
1095       total = t->wl + t->wr;
1096     }
1097   
1098   total += *d++;
1099   if (total > max)
1100     {
1101       if (end)
1102         *end = start;
1103       if (actual)
1104         *actual = 0;
1105       return;
1106     }
1107     
1108   {
1109     int x;
1110       
1111     for (x = start + 1; x < n; x++)
1112       {
1113         int amt = *d++ + *r++;
1114         
1115         total += amt;
1116         if (total > max)
1117           {
1118             total -= amt;
1119             break;
1120           }
1121       }
1122
1123     if (end)
1124       *end = x;
1125     
1126     if (actual)
1127       *actual = total;
1128   }
1129 }
1130
1131 /* Return flags set for the current table into FLAGS. */
1132 static void
1133 tabi_flags (unsigned *flags)
1134 {
1135   assert (flags != NULL);
1136   *flags = t->flags;
1137 }
1138
1139 /* Render title for current table, with major index X and minor index
1140    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1141    if Y is nonzero. */
1142 static void
1143 tabi_title (int x, int y)
1144 {
1145   char buf[1024];
1146   char *cp;
1147
1148   if (t->flags & SOMF_NO_TITLE)
1149     return;
1150   
1151   cp = spprintf (buf, "%d.%d", table_num, subtable_num);
1152   if (x && y)
1153     cp = spprintf (cp, "(%d:%d)", x, y);
1154   else if (x)
1155     cp = spprintf (cp, "(%d)", x);
1156   if (cur_proc)
1157     cp = spprintf (cp, " %s", cur_proc);
1158   cp = stpcpy (cp, ".  ");
1159   if (!ls_empty_p (&t->title))
1160     {
1161       memcpy (cp, ls_value (&t->title), ls_length (&t->title));
1162       cp += ls_length (&t->title);
1163     }
1164   *cp = 0;
1165   
1166   {
1167     struct outp_text text;
1168
1169     text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT;
1170     ls_init (&text.s, buf, cp - buf);
1171     text.h = d->width;
1172     text.v = d->font_height;
1173     text.x = 0;
1174     text.y = d->cp_y;
1175     d->class->text_draw (d, &text);
1176   }
1177 }
1178
1179 static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2);
1180
1181 /* Draws the table region in rectangle (X1,Y1)-(X2,Y2), where column
1182    X2 and row Y2 are not included in the rectangle, at the current
1183    position on the current output device.  Draws headers as well. */
1184 static void
1185 tabi_render (int x1, int y1, int x2, int y2)
1186 {
1187   int i, y;
1188   int ranges[3][2];
1189   
1190   tab_hit++;
1191
1192   y = d->cp_y;
1193   if (!(t->flags & SOMF_NO_TITLE))
1194     y += d->font_height;
1195
1196   /* Top headers. */
1197   ranges[0][0] = 0;
1198   ranges[0][1] = t->t * 2 + 1;
1199
1200   /* Requested rows. */
1201   ranges[1][0] = y1 * 2 + 1;
1202   ranges[1][1] = y2 * 2;
1203
1204   /* Bottom headers. */
1205   ranges[2][0] = (t->nr - t->b) * 2;
1206   ranges[2][1] = t->nr * 2 + 1;
1207
1208   for (i = 0; i < 3; i++) 
1209     {
1210       int r;
1211
1212       for (r = ranges[i][0]; r < ranges[i][1]; r++) 
1213         {
1214           int x = d->cp_x;
1215           x += render_strip (x, y, r, 0, t->l * 2 + 1, y1, y2);
1216           x += render_strip (x, y, r, x1 * 2 + 1, x2 * 2, y1, y2);
1217           x += render_strip (x, y, r, (t->nc - t->r) * 2,
1218                              t->nc * 2 + 1, y1, y2);
1219           y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; 
1220         }
1221     }
1222 }
1223
1224 struct som_table_class tab_table_class =
1225   {
1226     tabi_table,
1227     tabi_driver,
1228     
1229     tabi_count,
1230     tabi_area,
1231     NULL,
1232     NULL,
1233     tabi_columns,
1234     NULL,
1235     tabi_headers,
1236     NULL,
1237     tabi_cumulate,
1238     tabi_flags,
1239     
1240     NULL,
1241     NULL,
1242
1243     tabi_title,
1244     tabi_render,
1245   };
1246 \f
1247 /* Render contiguous strip consisting of columns C1...C2, exclusive,
1248    on row R, at location (X,Y).  Return width of the strip thus
1249    rendered.
1250
1251    Renders joined cells, even those outside the strip, within the
1252    rendering region (C1,R1)-(C2,R2).
1253
1254    For the purposes of counting rows and columns in this function
1255    only, horizontal rules are considered rows and vertical rules are
1256    considered columns.
1257
1258    FIXME: Doesn't use r1?  Huh?  */
1259 static int
1260 render_strip (int x, int y, int r, int c1, int c2, int r1 unused, int r2)
1261 {
1262   int x_origin = x;
1263
1264   /* Horizontal rules. */
1265   if ((r & 1) == 0)
1266     {
1267       int hrh = t->hrh[r / 2];
1268       int c;
1269
1270       for (c = c1; c < c2; c++)
1271         {
1272           if (c & 1)
1273             {
1274               int style = t->rh[(c / 2) + (r / 2 * t->cf)];
1275
1276               if (style != TAL_0)
1277                 {
1278                   const struct color clr = {0, 0, 0, 0};
1279                   struct rect rct;
1280
1281                   rct.x1 = x;
1282                   rct.y1 = y;
1283                   rct.x2 = x + t->w[c / 2];
1284                   rct.y2 = y + hrh;
1285                   d->class->line_horz (d, &rct, &clr, style);
1286                 }
1287               x += t->w[c / 2];
1288             } else {
1289               const struct color clr = {0, 0, 0, 0};
1290               struct rect rct;
1291               struct outp_styles s;
1292
1293               rct.x1 = x;
1294               rct.y1 = y;
1295               rct.x2 = x + t->wrv[c / 2];
1296               rct.y2 = y + hrh;
1297
1298               s.t = r > 0 ? t->rv[(c / 2) + (t->cf + 1) * (r / 2 - 1)] : 0;
1299               s.b = r < 2 * t->nr ? t->rv[(c / 2) + (t->cf + 1) * (r / 2)] : 0;
1300               s.l = c > 0 ? t->rh[(c / 2 - 1) + t->cf * (r / 2)] : 0;
1301               s.r = c < 2 * t->nc ? t->rh[(c / 2) + t->cf * (r / 2)] : 0;
1302
1303               if (s.t | s.b | s.l | s.r)
1304                 d->class->line_intersection (d, &rct, &clr, &s);
1305               
1306               x += t->wrv[c / 2];
1307             }
1308         }
1309     } else {
1310       int c;
1311
1312       for (c = c1; c < c2; c++)
1313         {
1314           if (c & 1)
1315             {
1316               const int index = (c / 2) + (r / 2 * t->cf);
1317
1318               if (!(t->ct[index] & TAB_JOIN))
1319                 {
1320                   struct outp_text text;
1321
1322                   text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1323                                   | OUTP_T_HORZ | OUTP_T_VERT);
1324                   if ((t->ct[index] & TAB_EMPTY) == 0)
1325                     {
1326                       text.s = t->cc[index];
1327                       assert (!ls_null_p (&text.s));
1328                       text.h = t->w[c / 2];
1329                       text.v = t->h[r / 2];
1330                       text.x = x;
1331                       text.y = y;
1332                       d->class->text_draw (d, &text);
1333                     }
1334                 } else {
1335                   struct tab_joined_cell *j =
1336                     (struct tab_joined_cell *) ls_value (&t->cc[index]);
1337
1338                   if (j->hit != tab_hit)
1339                     {
1340                       j->hit = tab_hit;
1341
1342                       if (j->x1 == c / 2 && j->y1 == r / 2
1343                           && j->x2 <= c2 && j->y2 <= r2)
1344                         {
1345                           struct outp_text text;
1346
1347                           text.options = ((t->ct[index] & OUTP_T_JUST_MASK)
1348                                           | OUTP_T_HORZ | OUTP_T_VERT);
1349                           text.s = j->contents;
1350                           text.x = x;
1351                           text.y = y;
1352                           
1353                           {
1354                             int c;
1355
1356                             for (c = j->x1, text.h = -t->wrv[j->x2];
1357                                  c < j->x2; c++)
1358                               text.h += t->w[c] + t->wrv[c + 1];
1359                           }
1360                           
1361                           {
1362                             int r;
1363
1364                             for (r = j->y1, text.v = -t->hrh[j->y2];
1365                                  r < j->y2; r++)
1366                               text.v += t->h[r] + t->hrh[r + 1];
1367                           }
1368                           d->class->text_draw (d, &text);
1369                         }
1370                     }
1371                 }
1372               x += t->w[c / 2];
1373             } else {
1374               int style = t->rv[(c / 2) + (r / 2 * (t->cf + 1))];
1375
1376               if (style != TAL_0)
1377                 {
1378                   const struct color clr = {0, 0, 0, 0};
1379                   struct rect rct;
1380
1381                   rct.x1 = x;
1382                   rct.y1 = y;
1383                   rct.x2 = x + t->wrv[c / 2];
1384                   rct.y2 = y + t->h[r / 2];
1385                   d->class->line_vert (d, &rct, &clr, style);
1386                 }
1387               x += t->wrv[c / 2];
1388             }
1389         }
1390     }
1391
1392   return x - x_origin;
1393 }
1394