output: Include command names in titles in GUI output.
[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
48 /* Returns the font to use for a cell with the given OPTIONS. */
49 static enum outp_font
50 options_to_font (unsigned options)
51 {
52   return (options & TAB_FIX ? OUTP_FIXED
53           : options & TAB_EMPH ? OUTP_EMPHASIS
54           : OUTP_PROPORTIONAL);
55 }
56
57 /* Creates a table with NC columns and NR rows. */
58 struct tab_table *
59 tab_create (int nc, int nr, int reallocable UNUSED)
60 {
61   struct tab_table *t;
62
63   t = pool_create_container (struct tab_table, container);
64   t->ref_cnt = 1;
65   t->col_style = TAB_COL_NONE;
66   t->col_group = 0;
67   t->title = NULL;
68   t->flags = SOMF_NONE;
69   t->nr = nr;
70   t->nc = t->cf = nc;
71   t->l = t->r = t->t = t->b = 0;
72
73   t->cc = pool_nmalloc (t->container, nr * nc, sizeof *t->cc);
74   t->ct = pool_malloc (t->container, nr * nc);
75   memset (t->ct, TAB_EMPTY, nc * nr);
76
77   t->rh = pool_nmalloc (t->container, nc, nr + 1);
78   memset (t->rh, 0, nc * (nr + 1));
79
80   t->rv = pool_nmalloc (t->container, nr, nc + 1);
81   memset (t->rv, UCHAR_MAX, nr * (nc + 1));
82
83   t->dim = NULL;
84   t->col_ofs = t->row_ofs = 0;
85
86   return t;
87 }
88
89 /* Increases T's reference count and, if this causes T's
90    reference count to reach 0, destroys T. */
91 void
92 tab_destroy (struct tab_table *t)
93 {
94   assert (t->ref_cnt > 0);
95   if (--t->ref_cnt > 0)
96     return;
97   if (t->dim_free != NULL)
98     t->dim_free (t->dim_aux);
99   free (t->title);
100   pool_destroy (t->container);
101 }
102
103 /* Increases T's reference count. */
104 void
105 tab_ref (struct tab_table *t)
106 {
107   assert (t->ref_cnt > 0);
108   t->ref_cnt++;
109 }
110
111 /* Sets the width and height of a table, in columns and rows,
112    respectively.  Use only to reduce the size of a table, since it
113    does not change the amount of allocated memory. */
114 void
115 tab_resize (struct tab_table *t, int nc, int nr)
116 {
117   assert (t != NULL);
118   if (nc != -1)
119     {
120       assert (nc + t->col_ofs <= t->cf);
121       t->nc = nc + t->col_ofs;
122     }
123   if (nr != -1)
124     {
125       assert (nr + t->row_ofs <= t->nr);
126       t->nr = nr + t->row_ofs;
127     }
128 }
129
130 /* Changes either or both dimensions of a table.  Consider using the
131    above routine instead if it won't waste a lot of space.
132
133    Changing the number of columns in a table is particularly expensive
134    in space and time.  Avoid doing such.  FIXME: In fact, transferring
135    of rules isn't even implemented yet. */
136 void
137 tab_realloc (struct tab_table *t, int nc, int nr)
138 {
139   int ro, co;
140
141   assert (t != NULL);
142   ro = t->row_ofs;
143   co = t->col_ofs;
144   if (ro || co)
145     tab_offset (t, 0, 0);
146
147   if (nc == -1)
148     nc = t->nc;
149   if (nr == -1)
150     nr = t->nr;
151
152   assert (nc == t->nc);
153
154   if (nc > t->cf)
155     {
156       int mr1 = MIN (nr, t->nr);
157       int mc1 = MIN (nc, t->nc);
158
159       struct substring *new_cc;
160       unsigned char *new_ct;
161       int r;
162
163       new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc);
164       new_ct = pool_malloc (t->container, nr * nc);
165       for (r = 0; r < mr1; r++)
166         {
167           memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc);
168           memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1);
169           memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc);
170         }
171       pool_free (t->container, t->cc);
172       pool_free (t->container, t->ct);
173       t->cc = new_cc;
174       t->ct = new_ct;
175       t->cf = nc;
176     }
177   else if (nr != t->nr)
178     {
179       t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc);
180       t->ct = pool_realloc (t->container, t->ct, nr * nc);
181
182       t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1);
183       t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1);
184
185       if (nr > t->nr)
186         {
187           memset (&t->rh[nc * (t->nr + 1)], TAL_0, (nr - t->nr) * nc);
188           memset (&t->rv[(nc + 1) * t->nr], UCHAR_MAX,
189                   (nr - t->nr) * (nc + 1));
190         }
191     }
192
193   memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr));
194
195   t->nr = nr;
196   t->nc = nc;
197
198   if (ro || co)
199     tab_offset (t, co, ro);
200 }
201
202 /* Sets the number of header rows on each side of TABLE to L on the
203    left, R on the right, T on the top, B on the bottom.  Header rows
204    are repeated when a table is broken across multiple columns or
205    multiple pages. */
206 void
207 tab_headers (struct tab_table *table, int l, int r, int t, int b)
208 {
209   assert (table != NULL);
210   assert (l < table->nc);
211   assert (r < table->nc);
212   assert (t < table->nr);
213   assert (b < table->nr);
214
215
216   table->l = l;
217   table->r = r;
218   table->t = t;
219   table->b = b;
220 }
221
222 /* Set up table T so that, when it is an appropriate size, it will be
223    displayed across the page in columns.
224
225    STYLE is a TAB_COL_* constant.  GROUP is the number of rows to take
226    as a unit. */
227 void
228 tab_columns (struct tab_table *t, int style, int group)
229 {
230   assert (t != NULL);
231   t->col_style = style;
232   t->col_group = group;
233 }
234 \f
235 /* Rules. */
236
237 /* Draws a vertical line to the left of cells at horizontal position X
238    from Y1 to Y2 inclusive in style STYLE, if style is not -1. */
239 void
240 tab_vline (struct tab_table *t, int style, int x, int y1, int y2)
241 {
242   assert (t != NULL);
243
244 #if DEBUGGING
245   if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc
246       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
247       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
248     {
249       printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in "
250                 "table size (%d,%d)\n"),
251               x, t->col_ofs, x + t->col_ofs,
252               y1, t->row_ofs, y1 + t->row_ofs,
253               y2, t->row_ofs, y2 + t->row_ofs,
254               t->nc, t->nr);
255       return;
256     }
257 #endif
258
259   x += t->col_ofs;
260   y1 += t->row_ofs;
261   y2 += t->row_ofs;
262
263   assert (x  > 0);
264   assert (x  < t->nc);
265   assert (y1 >= 0);
266   assert (y2 >= y1);
267   assert (y2 <=  t->nr);
268
269   if (style != -1)
270     {
271       int y;
272       for (y = y1; y <= y2; y++)
273         t->rv[x + (t->cf + 1) * y] = style;
274     }
275 }
276
277 /* Draws a horizontal line above cells at vertical position Y from X1
278    to X2 inclusive in style STYLE, if style is not -1. */
279 void
280 tab_hline (struct tab_table * t, int style, int x1, int x2, int y)
281 {
282   assert (t != NULL);
283
284   x1 += t->col_ofs;
285   x2 += t->col_ofs;
286   y += t->row_ofs;
287
288   assert (y >= 0);
289   assert (y <= t->nr);
290   assert (x2 >= x1 );
291   assert (x1 >= 0 );
292   assert (x2 < t->nc);
293
294   if (style != -1)
295     {
296       int x;
297       for (x = x1; x <= x2; x++)
298         t->rh[x + t->cf * y] = style;
299     }
300 }
301
302 /* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal
303    lines of style F_H and vertical lines of style F_V.  Fills the
304    interior of the box with horizontal lines of style I_H and vertical
305    lines of style I_V.  Any of the line styles may be -1 to avoid
306    drawing those lines.  This is distinct from 0, which draws a null
307    line. */
308 void
309 tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v,
310          int x1, int y1, int x2, int y2)
311 {
312   assert (t != NULL);
313
314 #if DEBUGGING
315   if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc
316       || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc
317       || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr
318       || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr)
319     {
320       printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) "
321                 "in table size (%d,%d)\n"),
322               x1, t->col_ofs, x1 + t->col_ofs,
323               y1, t->row_ofs, y1 + t->row_ofs,
324               x2, t->col_ofs, x2 + t->col_ofs,
325               y2, t->row_ofs, y2 + t->row_ofs,
326               t->nc, t->nr);
327       NOT_REACHED ();
328     }
329 #endif
330
331   x1 += t->col_ofs;
332   x2 += t->col_ofs;
333   y1 += t->row_ofs;
334   y2 += t->row_ofs;
335
336   assert (x2 >= x1);
337   assert (y2 >= y1);
338   assert (x1 >= 0);
339   assert (y1 >= 0);
340   assert (x2 < t->nc);
341   assert (y2 < t->nr);
342
343   if (f_h != -1)
344     {
345       int x;
346       for (x = x1; x <= x2; x++)
347         {
348           t->rh[x + t->cf * y1] = f_h;
349           t->rh[x + t->cf * (y2 + 1)] = f_h;
350         }
351     }
352   if (f_v != -1)
353     {
354       int y;
355       for (y = y1; y <= y2; y++)
356         {
357           t->rv[x1 + (t->cf + 1) * y] = f_v;
358           t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v;
359         }
360     }
361
362   if (i_h != -1)
363     {
364       int y;
365
366       for (y = y1 + 1; y <= y2; y++)
367         {
368           int x;
369
370           for (x = x1; x <= x2; x++)
371             t->rh[x + t->cf * y] = i_h;
372         }
373     }
374   if (i_v != -1)
375     {
376       int x;
377
378       for (x = x1 + 1; x <= x2; x++)
379         {
380           int y;
381
382           for (y = y1; y <= y2; y++)
383             t->rv[x + (t->cf + 1) * y] = i_v;
384         }
385     }
386 }
387
388 /* Formats text TEXT and arguments ARGS as indicated in OPT in
389    TABLE's pool and returns the resultant string. */
390 static struct substring
391 text_format (struct tab_table *table, int opt, const char *text, va_list args)
392 {
393   assert (table != NULL && text != NULL);
394
395   return ss_cstr (opt & TAT_PRINTF
396                   ? pool_vasprintf (table->container, text, args)
397                   : pool_strdup (table->container, text));
398 }
399
400 /* Set the title of table T to TITLE, which is formatted as if
401    passed to printf(). */
402 void
403 tab_title (struct tab_table *t, const char *title, ...)
404 {
405   va_list args;
406
407   assert (t != NULL && title != NULL);
408   va_start (args, title);
409   t->title = xvasprintf (title, args);
410   va_end (args);
411 }
412
413 /* Set DIM_FUNC, which will be passed auxiliary data AUX, as the
414    dimension function for table T.
415
416    DIM_FUNC must not assume that it is called from the same
417    context as tab_dim; for example, table T might be kept in
418    memory and, thus, DIM_FUNC might be called after the currently
419    running command completes.  If it is non-null, FREE_FUNC is
420    called when the table is destroyed, to allow any data
421    allocated for use by DIM_FUNC to be freed.  */
422 void
423 tab_dim (struct tab_table *t,
424          tab_dim_func *dim_func, tab_dim_free_func *free_func, void *aux)
425 {
426   assert (t->dim == NULL);
427   t->dim = dim_func;
428   t->dim_free = free_func;
429   t->dim_aux = aux;
430 }
431
432 /* Returns the natural width of column C in table T for driver D, that
433    is, the smallest width necessary to display all its cells without
434    wrapping.  The width will be no larger than the page width minus
435    left and right rule widths. */
436 int
437 tab_natural_width (const struct tab_rendering *r, int col)
438 {
439   const struct tab_table *t = r->table;
440   int width, row, max_width;
441
442   assert (col >= 0 && col < t->nc);
443
444   width = 0;
445   for (row = 0; row < t->nr; row++)
446     {
447       struct outp_text text;
448       unsigned char opt = t->ct[col + row * t->cf];
449       int w;
450
451       if (opt & (TAB_JOIN | TAB_EMPTY))
452         continue;
453
454       text.string = t->cc[col + row * t->cf];
455       text.justification = OUTP_LEFT;
456       text.font = options_to_font (opt);
457       text.h = text.v = INT_MAX;
458
459       r->driver->class->text_metrics (r->driver, &text, &w, NULL);
460       if (w > width)
461         width = w;
462     }
463
464   if (width == 0)
465     {
466       /* FIXME: This is an ugly kluge to compensate for the fact
467          that we don't let joined cells contribute to column
468          widths. */
469       width = r->driver->prop_em_width * 8;
470     }
471
472   max_width = r->driver->width - r->wrv[0] - r->wrv[t->nc];
473   return MIN (width, max_width);
474 }
475
476 /* Returns the natural height of row R in table T for driver D, that
477    is, the minimum height necessary to display the information in the
478    cell at the widths set for each column. */
479 int
480 tab_natural_height (const struct tab_rendering *r, int row)
481 {
482   const struct tab_table *t = r->table;
483   int height, col;
484
485   assert (row >= 0 && row < t->nr);
486
487   height = r->driver->font_height;
488   for (col = 0; col < t->nc; col++)
489     {
490       struct outp_text text;
491       unsigned char opt = t->ct[col + row * t->cf];
492       int h;
493
494       if (opt & (TAB_JOIN | TAB_EMPTY))
495         continue;
496
497       text.string = t->cc[col + row * t->cf];
498       text.justification = OUTP_LEFT;
499       text.font = options_to_font (opt);
500       text.h = r->w[col];
501       text.v = INT_MAX;
502       r->driver->class->text_metrics (r->driver, &text, NULL, &h);
503
504       if (h > height)
505         height = h;
506     }
507
508   return height;
509 }
510
511 /* Callback function to set all columns and rows to their natural
512    dimensions.  Not really meant to be called directly.  */
513 void
514 tab_natural_dimensions (struct tab_rendering *r, void *aux UNUSED)
515 {
516   const struct tab_table *t = r->table;
517   int i;
518
519   for (i = 0; i < t->nc; i++)
520     r->w[i] = tab_natural_width (r, i);
521
522   for (i = 0; i < t->nr; i++)
523     r->h[i] = tab_natural_height (r, i);
524 }
525
526 \f
527 /* Cells. */
528
529 /* Sets cell (C,R) in TABLE, with options OPT, to have a value taken
530    from V, displayed with format spec F. */
531 void
532 tab_value (struct tab_table *table, int c, int r, unsigned char opt,
533            const union value *v, const struct fmt_spec *f)
534 {
535   char *contents;
536
537   assert (table != NULL && v != NULL && f != NULL);
538 #if DEBUGGING
539   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
540       || c + table->col_ofs >= table->nc
541       || r + table->row_ofs >= table->nr)
542     {
543       printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
544               "(%d,%d)\n",
545               c, table->col_ofs, c + table->col_ofs,
546               r, table->row_ofs, r + table->row_ofs,
547               table->nc, table->nr);
548       return;
549     }
550 #endif
551
552   contents = pool_alloc (table->container, f->w);
553   table->cc[c + r * table->cf] = ss_buffer (contents, f->w);
554   table->ct[c + r * table->cf] = opt;
555
556   data_out (v, f, contents);
557 }
558
559 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL
560    with NDEC decimal places. */
561 void
562 tab_fixed (struct tab_table *table, int c, int r, unsigned char opt,
563            double val, int w, int d)
564 {
565   char *contents;
566   char buf[40], *cp;
567
568   struct fmt_spec f;
569   union value double_value;
570
571   assert (table != NULL && w <= 40);
572
573   assert (c >= 0);
574   assert (c < table->nc);
575   assert (r >= 0);
576   assert (r < table->nr);
577
578   f = fmt_for_output (FMT_F, w, d);
579
580 #if DEBUGGING
581   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
582       || c + table->col_ofs >= table->nc
583       || r + table->row_ofs >= table->nr)
584     {
585       printf ("tab_fixed(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
586               "(%d,%d)\n",
587               c, table->col_ofs, c + table->col_ofs,
588               r, table->row_ofs, r + table->row_ofs,
589               table->nc, table->nr);
590       return;
591     }
592 #endif
593
594   double_value.f = val;
595   data_out (&double_value, &f, buf);
596
597   cp = buf;
598   while (isspace ((unsigned char) *cp) && cp < &buf[w])
599     cp++;
600   f.w = w - (cp - buf);
601
602   contents = pool_alloc (table->container, f.w);
603   table->cc[c + r * table->cf] = ss_buffer (contents, f.w);
604   table->ct[c + r * table->cf] = opt;
605   memcpy (contents, cp, f.w);
606 }
607
608 /* Sets cell (C,R) in TABLE, with options OPT, to have value VAL as
609    formatted by FMT.
610    If FMT is null, then the default print format will be used.
611 */
612 void
613 tab_double (struct tab_table *table, int c, int r, unsigned char opt,
614            double val, const struct fmt_spec *fmt)
615 {
616   int w;
617   char *contents;
618   char buf[40], *cp;
619
620   union value double_value;
621
622   assert (table != NULL);
623
624   assert (c >= 0);
625   assert (c < table->nc);
626   assert (r >= 0);
627   assert (r < table->nr);
628
629   if ( fmt == NULL)
630     fmt = settings_get_format ();
631
632   fmt_check_output (fmt);
633
634 #if DEBUGGING
635   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
636       || c + table->col_ofs >= table->nc
637       || r + table->row_ofs >= table->nr)
638     {
639       printf ("tab_double(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
640               "(%d,%d)\n",
641               c, table->col_ofs, c + table->col_ofs,
642               r, table->row_ofs, r + table->row_ofs,
643               table->nc, table->nr);
644       return;
645     }
646 #endif
647
648   double_value.f = val;
649   data_out (&double_value, fmt, buf);
650
651   cp = buf;
652   while (isspace ((unsigned char) *cp) && cp < &buf[fmt->w])
653     cp++;
654   w = fmt->w - (cp - buf);
655
656   contents = pool_alloc (table->container, w);
657   table->cc[c + r * table->cf] = ss_buffer (contents, w);
658   table->ct[c + r * table->cf] = opt;
659   memcpy (contents, cp, w);
660 }
661
662
663 /* Sets cell (C,R) in TABLE, with options OPT, to have text value
664    TEXT. */
665 void
666 tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...)
667 {
668   va_list args;
669
670   assert (table != NULL && text != NULL);
671
672   assert (c >= 0 );
673   assert (r >= 0 );
674   assert (c < table->nc);
675   assert (r < table->nr);
676
677
678 #if DEBUGGING
679   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
680       || c + table->col_ofs >= table->nc
681       || r + table->row_ofs >= table->nr)
682     {
683       printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
684               "(%d,%d)\n",
685               c, table->col_ofs, c + table->col_ofs,
686               r, table->row_ofs, r + table->row_ofs,
687               table->nc, table->nr);
688       return;
689     }
690 #endif
691
692   va_start (args, text);
693   table->cc[c + r * table->cf] = text_format (table, opt, text, args);
694   table->ct[c + r * table->cf] = opt;
695   va_end (args);
696 }
697
698 /* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with
699    options OPT to have text value TEXT. */
700 void
701 tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2,
702                 unsigned opt, const char *text, ...)
703 {
704   struct tab_joined_cell *j;
705
706   assert (table != NULL && text != NULL);
707
708   assert (x1 + table->col_ofs >= 0);
709   assert (y1 + table->row_ofs >= 0);
710   assert (y2 >= y1);
711   assert (x2 >= x1);
712   assert (y2 + table->row_ofs < table->nr);
713   assert (x2 + table->col_ofs < table->nc);
714
715 #if DEBUGGING
716   if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc
717       || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr
718       || x2 < x1 || x2 + table->col_ofs >= table->nc
719       || y2 < y2 || y2 + table->row_ofs >= table->nr)
720     {
721       printf ("tab_joint_text(): bad cell "
722               "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n",
723               x1, table->col_ofs, x1 + table->col_ofs,
724               y1, table->row_ofs, y1 + table->row_ofs,
725               x2, table->col_ofs, x2 + table->col_ofs,
726               y2, table->row_ofs, y2 + table->row_ofs,
727               table->nc, table->nr);
728       return;
729     }
730 #endif
731
732   tab_box (table, -1, -1, TAL_0, TAL_0, x1, y1, x2, y2);
733
734   j = pool_alloc (table->container, sizeof *j);
735   j->hit = 0;
736   j->x1 = x1 + table->col_ofs;
737   j->y1 = y1 + table->row_ofs;
738   j->x2 = ++x2 + table->col_ofs;
739   j->y2 = ++y2 + table->row_ofs;
740
741   {
742     va_list args;
743
744     va_start (args, text);
745     j->contents = text_format (table, opt, text, args);
746     va_end (args);
747   }
748
749   opt |= TAB_JOIN;
750
751   {
752     struct substring *cc = &table->cc[x1 + y1 * table->cf];
753     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
754     const int ofs = table->cf - (x2 - x1);
755
756     int y;
757
758     for (y = y1; y < y2; y++)
759       {
760         int x;
761
762         for (x = x1; x < x2; x++)
763           {
764             *cc++ = ss_buffer ((char *) j, 0);
765             *ct++ = opt;
766           }
767
768         cc += ofs;
769         ct += ofs;
770       }
771   }
772 }
773
774 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
775 void
776 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
777          struct substring *string)
778 {
779   assert (table != NULL && string != NULL);
780
781 #if DEBUGGING
782   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
783       || c + table->col_ofs >= table->nc
784       || r + table->row_ofs >= table->nr)
785     {
786       printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
787               "(%d,%d)\n",
788               c, table->col_ofs, c + table->col_ofs,
789               r, table->row_ofs, r + table->row_ofs,
790               table->nc, table->nr);
791       return;
792     }
793 #endif
794
795   table->cc[c + r * table->cf] = *string;
796   table->ct[c + r * table->cf] = opt;
797 }
798 \f
799 /* Miscellaneous. */
800
801 /* Sets the widths of all the columns and heights of all the rows in
802    table T for driver D. */
803 static void
804 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
805 {
806   r->w[0] = tab_natural_width (r, 0);
807   r->h[0] = r->driver->font_height;
808 }
809
810 /* Sets the widths of all the columns and heights of all the rows in
811    table T for driver D. */
812 static void
813 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
814 {
815   r->w[0] = tab_natural_width (r, 0);
816   r->h[0] = tab_natural_height (r, 0);
817 }
818
819 /* Outputs text BUF as a table with a single cell having cell options
820    OPTIONS, which is a combination of the TAB_* and TAT_*
821    constants. */
822 void
823 tab_output_text (int options, const char *buf, ...)
824 {
825   struct tab_table *t = tab_create (1, 1, 0);
826   char *tmp_buf = NULL;
827
828   if (options & TAT_PRINTF)
829     {
830       va_list args;
831
832       va_start (args, buf);
833       buf = tmp_buf = xvasprintf (buf, args);
834       va_end (args);
835     }
836
837   tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
838   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
839   tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
840   tab_submit (t);
841
842   free (tmp_buf);
843 }
844
845 /* Set table flags to FLAGS. */
846 void
847 tab_flags (struct tab_table *t, unsigned flags)
848 {
849   assert (t != NULL);
850   t->flags = flags;
851 }
852
853 /* Easy, type-safe way to submit a tab table to som. */
854 void
855 tab_submit (struct tab_table *t)
856 {
857   struct som_entity s;
858
859   assert (t != NULL);
860   s.class = &tab_table_class;
861   s.ext = t;
862   s.type = SOM_TABLE;
863   som_submit (&s);
864   tab_destroy (t);
865 }
866 \f
867 /* Editing. */
868
869 /* Set table row and column offsets for all functions that affect
870    cells or rules. */
871 void
872 tab_offset (struct tab_table *t, int col, int row)
873 {
874   int diff = 0;
875
876   assert (t != NULL);
877 #if DEBUGGING
878   if (row < -1 || row > t->nr)
879     {
880       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
881       NOT_REACHED ();
882     }
883   if (col < -1 || col > t->nc)
884     {
885       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
886       NOT_REACHED ();
887     }
888 #endif
889
890   if (row != -1)
891     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
892   if (col != -1)
893     diff += (col - t->col_ofs), t->col_ofs = col;
894
895   t->cc += diff;
896   t->ct += diff;
897 }
898
899 /* Increment the row offset by one. If the table is too small,
900    increase its size. */
901 void
902 tab_next_row (struct tab_table *t)
903 {
904   assert (t != NULL);
905   t->cc += t->cf;
906   t->ct += t->cf;
907   if (++t->row_ofs >= t->nr)
908     tab_realloc (t, -1, t->nr * 4 / 3);
909 }
910 \f
911 /* Return the number of columns and rows in the table into N_COLUMNS
912    and N_ROWS, respectively. */
913 static void
914 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
915 {
916   struct tab_table *t = t_->ext;
917   *n_columns = t->nc;
918   *n_rows = t->nr;
919 }
920
921 /* Return the column style for this table into STYLE. */
922 static void
923 tabi_columns (struct som_entity *t_, int *style)
924 {
925   struct tab_table *t = t_->ext;
926   *style = t->col_style;
927 }
928
929 /* Return the number of header rows/columns on the left, right, top,
930    and bottom sides into HL, HR, HT, and HB, respectively. */
931 static void
932 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
933 {
934   struct tab_table *t = t_->ext;
935   *hl = t->l;
936   *hr = t->r;
937   *ht = t->t;
938   *hb = t->b;
939 }
940
941 /* Return flags set for the current table into FLAGS. */
942 static void
943 tabi_flags (struct som_entity *t_, unsigned *flags)
944 {
945   struct tab_table *t = t_->ext;
946   *flags = t->flags;
947 }
948
949 /* Returns the line style to use for spacing purposes for a rule
950    of the given TYPE. */
951 static enum outp_line_style
952 rule_to_spacing_type (unsigned char type)
953 {
954   switch (type)
955     {
956     case TAL_0:
957       return OUTP_L_NONE;
958     case TAL_GAP:
959     case TAL_1:
960       return OUTP_L_SINGLE;
961     case TAL_2:
962       return OUTP_L_DOUBLE;
963     default:
964       NOT_REACHED ();
965     }
966 }
967
968 static void *
969 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
970                   int hl, int hr, int ht, int hb)
971 {
972   const struct tab_table *t = t_->ext;
973   struct tab_rendering *r;
974   int col, row;
975   int i;
976
977   tab_offset (t_->ext, 0, 0);
978
979   r = xmalloc (sizeof *r);
980   r->table = t;
981   r->driver = driver;
982   r->w = xnmalloc (t->nc, sizeof *r->w);
983   r->h = xnmalloc (t->nr, sizeof *r->h);
984   r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
985   r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
986   r->l = hl;
987   r->r = hr;
988   r->t = ht;
989   r->b = hb;
990
991   /* Figure out sizes of rules. */
992   for (row = 0; row <= t->nr; row++)
993     {
994       int width = 0;
995       for (col = 0; col < t->nc; col++)
996         {
997           unsigned char rh = t->rh[col + row * t->cf];
998           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
999           if (w > width)
1000             width = w;
1001         }
1002       r->hrh[row] = width;
1003     }
1004
1005   for (col = 0; col <= t->nc; col++)
1006     {
1007       int width = 0;
1008       for (row = 0; row < t->nr; row++)
1009         {
1010           unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1011           int w;
1012           if (*rv == UCHAR_MAX)
1013             *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
1014           w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1015           if (w > width)
1016             width = w;
1017         }
1018       r->wrv[col] = width;
1019     }
1020
1021   /* Determine row heights and columns widths. */
1022   for (i = 0; i < t->nr; i++)
1023     r->h[i] = -1;
1024   for (i = 0; i < t->nc; i++)
1025     r->w[i] = -1;
1026
1027   t->dim (r, t->dim_aux);
1028
1029   for (i = 0; i < t->nr; i++)
1030     if (r->h[i] < 0)
1031       error (0, 0, "height of table row %d is %d (not initialized?)",
1032              i, r->h[i]);
1033   for (i = 0; i < t->nc; i++)
1034     if (r->w[i] < 0)
1035       error (0, 0, "width of table column %d is %d (not initialized?)",
1036              i, r->w[i]);
1037
1038   /* Add up header sizes. */
1039   for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1040     r->wl += r->w[i] + r->wrv[i + 1];
1041   for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1042     r->ht += r->h[i] + r->hrh[i + 1];
1043   for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1044     r->wr += r->w[i] + r->wrv[i + 1];
1045   for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1046     r->hb += r->h[i] + r->hrh[i + 1];
1047
1048   /* Title. */
1049   if (!(t->flags & SOMF_NO_TITLE))
1050     r->ht += driver->font_height;
1051
1052   return r;
1053 }
1054
1055 static void
1056 tabi_render_free (void *r_)
1057 {
1058   struct tab_rendering *r = r_;
1059
1060   free (r->w);
1061   free (r->h);
1062   free (r->hrh);
1063   free (r->wrv);
1064   free (r);
1065 }
1066
1067 /* Return the horizontal and vertical size of the entire table,
1068    including headers, for the current output device, into HORIZ and
1069    VERT. */
1070 static void
1071 tabi_area (void *r_, int *horiz, int *vert)
1072 {
1073   struct tab_rendering *r = r_;
1074   const struct tab_table *t = r->table;
1075   int width, col;
1076   int height, row;
1077
1078   width = 0;
1079   for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1080        col < t->nc - r->r; col++)
1081     width += r->w[col] + r->wrv[col];
1082   *horiz = width;
1083
1084   height = 0;
1085   for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1086        row < t->nr - t->b; row++)
1087     height += r->h[row] + r->hrh[row];
1088   *vert = height;
1089 }
1090
1091 /* Determines the number of rows or columns (including appropriate
1092    headers), depending on CUMTYPE, that will fit into the space
1093    specified.  Takes rows/columns starting at index START and attempts
1094    to fill up available space MAX.  Returns in END the index of the
1095    last row/column plus one; returns in ACTUAL the actual amount of
1096    space the selected rows/columns (including appropriate headers)
1097    filled. */
1098 static void
1099 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1100                int max, int *actual)
1101 {
1102   const struct tab_rendering *r = r_;
1103   const struct tab_table *t = r->table;
1104   int limit;
1105   int *cells, *rules;
1106   int total;
1107   int idx;
1108
1109   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1110   if (cumtype == SOM_ROWS)
1111     {
1112       assert (start >= 0 && start < t->nr);
1113       limit = t->nr - r->b;
1114       cells = &r->h[start];
1115       rules = &r->hrh[start + 1];
1116       total = r->ht + r->hb;
1117     }
1118   else
1119     {
1120       assert (start >= 0 && start < t->nc);
1121       limit = t->nc - t->r;
1122       cells = &r->w[start];
1123       rules = &r->wrv[start + 1];
1124       total = r->wl + r->wr;
1125     }
1126
1127   total += *cells++;
1128   if (total > max)
1129     {
1130       if (end)
1131         *end = start;
1132       if (actual)
1133         *actual = 0;
1134       return;
1135     }
1136
1137   for (idx = start + 1; idx < limit; idx++)
1138     {
1139       int amt = *cells++ + *rules++;
1140
1141       total += amt;
1142       if (total > max)
1143         {
1144           total -= amt;
1145           break;
1146         }
1147     }
1148
1149   if (end)
1150     *end = idx;
1151
1152   if (actual)
1153     *actual = total;
1154 }
1155
1156 /* Render title for current table, with major index X and minor index
1157    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1158    if Y is nonzero. */
1159 static void
1160 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1161             const char *command_name)
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 }