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