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