output: Get rid of write-only "hit" member in struct tab_joined_cell.
[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->x1 = x1 + table->col_ofs;
736   j->y1 = y1 + table->row_ofs;
737   j->x2 = ++x2 + table->col_ofs;
738   j->y2 = ++y2 + table->row_ofs;
739
740   {
741     va_list args;
742
743     va_start (args, text);
744     j->contents = text_format (table, opt, text, args);
745     va_end (args);
746   }
747
748   opt |= TAB_JOIN;
749
750   {
751     struct substring *cc = &table->cc[x1 + y1 * table->cf];
752     unsigned char *ct = &table->ct[x1 + y1 * table->cf];
753     const int ofs = table->cf - (x2 - x1);
754
755     int y;
756
757     for (y = y1; y < y2; y++)
758       {
759         int x;
760
761         for (x = x1; x < x2; x++)
762           {
763             *cc++ = ss_buffer ((char *) j, 0);
764             *ct++ = opt;
765           }
766
767         cc += ofs;
768         ct += ofs;
769       }
770   }
771 }
772
773 /* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */
774 void
775 tab_raw (struct tab_table *table, int c, int r, unsigned opt,
776          struct substring *string)
777 {
778   assert (table != NULL && string != NULL);
779
780 #if DEBUGGING
781   if (c + table->col_ofs < 0 || r + table->row_ofs < 0
782       || c + table->col_ofs >= table->nc
783       || r + table->row_ofs >= table->nr)
784     {
785       printf ("tab_raw(): bad cell (%d+%d=%d,%d+%d=%d) in table size "
786               "(%d,%d)\n",
787               c, table->col_ofs, c + table->col_ofs,
788               r, table->row_ofs, r + table->row_ofs,
789               table->nc, table->nr);
790       return;
791     }
792 #endif
793
794   table->cc[c + r * table->cf] = *string;
795   table->ct[c + r * table->cf] = opt;
796 }
797 \f
798 /* Miscellaneous. */
799
800 /* Sets the widths of all the columns and heights of all the rows in
801    table T for driver D. */
802 static void
803 nowrap_dim (struct tab_rendering *r, void *aux UNUSED)
804 {
805   r->w[0] = tab_natural_width (r, 0);
806   r->h[0] = r->driver->font_height;
807 }
808
809 /* Sets the widths of all the columns and heights of all the rows in
810    table T for driver D. */
811 static void
812 wrap_dim (struct tab_rendering *r, void *aux UNUSED)
813 {
814   r->w[0] = tab_natural_width (r, 0);
815   r->h[0] = tab_natural_height (r, 0);
816 }
817
818 /* Outputs text BUF as a table with a single cell having cell options
819    OPTIONS, which is a combination of the TAB_* and TAT_*
820    constants. */
821 void
822 tab_output_text (int options, const char *buf, ...)
823 {
824   struct tab_table *t = tab_create (1, 1, 0);
825   char *tmp_buf = NULL;
826
827   if (options & TAT_PRINTF)
828     {
829       va_list args;
830
831       va_start (args, buf);
832       buf = tmp_buf = xvasprintf (buf, args);
833       va_end (args);
834     }
835
836   tab_text (t, 0, 0, options & ~TAT_PRINTF, buf);
837   tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING);
838   tab_dim (t, options & TAT_NOWRAP ? nowrap_dim : wrap_dim, NULL, NULL);
839   tab_submit (t);
840
841   free (tmp_buf);
842 }
843
844 /* Set table flags to FLAGS. */
845 void
846 tab_flags (struct tab_table *t, unsigned flags)
847 {
848   assert (t != NULL);
849   t->flags = flags;
850 }
851
852 /* Easy, type-safe way to submit a tab table to som. */
853 void
854 tab_submit (struct tab_table *t)
855 {
856   struct som_entity s;
857
858   assert (t != NULL);
859   s.class = &tab_table_class;
860   s.ext = t;
861   s.type = SOM_TABLE;
862   som_submit (&s);
863   tab_destroy (t);
864 }
865 \f
866 /* Editing. */
867
868 /* Set table row and column offsets for all functions that affect
869    cells or rules. */
870 void
871 tab_offset (struct tab_table *t, int col, int row)
872 {
873   int diff = 0;
874
875   assert (t != NULL);
876 #if DEBUGGING
877   if (row < -1 || row > t->nr)
878     {
879       printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr);
880       NOT_REACHED ();
881     }
882   if (col < -1 || col > t->nc)
883     {
884       printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc);
885       NOT_REACHED ();
886     }
887 #endif
888
889   if (row != -1)
890     diff += (row - t->row_ofs) * t->cf, t->row_ofs = row;
891   if (col != -1)
892     diff += (col - t->col_ofs), t->col_ofs = col;
893
894   t->cc += diff;
895   t->ct += diff;
896 }
897
898 /* Increment the row offset by one. If the table is too small,
899    increase its size. */
900 void
901 tab_next_row (struct tab_table *t)
902 {
903   assert (t != NULL);
904   t->cc += t->cf;
905   t->ct += t->cf;
906   if (++t->row_ofs >= t->nr)
907     tab_realloc (t, -1, t->nr * 4 / 3);
908 }
909 \f
910 /* Return the number of columns and rows in the table into N_COLUMNS
911    and N_ROWS, respectively. */
912 static void
913 tabi_count (struct som_entity *t_, int *n_columns, int *n_rows)
914 {
915   struct tab_table *t = t_->ext;
916   *n_columns = t->nc;
917   *n_rows = t->nr;
918 }
919
920 /* Return the column style for this table into STYLE. */
921 static void
922 tabi_columns (struct som_entity *t_, int *style)
923 {
924   struct tab_table *t = t_->ext;
925   *style = t->col_style;
926 }
927
928 /* Return the number of header rows/columns on the left, right, top,
929    and bottom sides into HL, HR, HT, and HB, respectively. */
930 static void
931 tabi_headers (struct som_entity *t_, int *hl, int *hr, int *ht, int *hb)
932 {
933   struct tab_table *t = t_->ext;
934   *hl = t->l;
935   *hr = t->r;
936   *ht = t->t;
937   *hb = t->b;
938 }
939
940 /* Return flags set for the current table into FLAGS. */
941 static void
942 tabi_flags (struct som_entity *t_, unsigned *flags)
943 {
944   struct tab_table *t = t_->ext;
945   *flags = t->flags;
946 }
947
948 /* Returns the line style to use for spacing purposes for a rule
949    of the given TYPE. */
950 static enum outp_line_style
951 rule_to_spacing_type (unsigned char type)
952 {
953   switch (type)
954     {
955     case TAL_0:
956       return OUTP_L_NONE;
957     case TAL_GAP:
958     case TAL_1:
959       return OUTP_L_SINGLE;
960     case TAL_2:
961       return OUTP_L_DOUBLE;
962     default:
963       NOT_REACHED ();
964     }
965 }
966
967 static void *
968 tabi_render_init (struct som_entity *t_, struct outp_driver *driver,
969                   int hl, int hr, int ht, int hb)
970 {
971   const struct tab_table *t = t_->ext;
972   struct tab_rendering *r;
973   int col, row;
974   int i;
975
976   tab_offset (t_->ext, 0, 0);
977
978   r = xmalloc (sizeof *r);
979   r->table = t;
980   r->driver = driver;
981   r->w = xnmalloc (t->nc, sizeof *r->w);
982   r->h = xnmalloc (t->nr, sizeof *r->h);
983   r->hrh = xnmalloc (t->nr + 1, sizeof *r->hrh);
984   r->wrv = xnmalloc (t->nc + 1, sizeof *r->wrv);
985   r->l = hl;
986   r->r = hr;
987   r->t = ht;
988   r->b = hb;
989
990   /* Figure out sizes of rules. */
991   for (row = 0; row <= t->nr; row++)
992     {
993       int width = 0;
994       for (col = 0; col < t->nc; col++)
995         {
996           unsigned char rh = t->rh[col + row * t->cf];
997           int w = driver->horiz_line_width[rule_to_spacing_type (rh)];
998           if (w > width)
999             width = w;
1000         }
1001       r->hrh[row] = width;
1002     }
1003
1004   for (col = 0; col <= t->nc; col++)
1005     {
1006       int width = 0;
1007       for (row = 0; row < t->nr; row++)
1008         {
1009           unsigned char *rv = &t->rv[col + row * (t->cf + 1)];
1010           int w;
1011           if (*rv == UCHAR_MAX)
1012             *rv = col != 0 && col != t->nc ? TAL_GAP : TAL_0;
1013           w = driver->vert_line_width[rule_to_spacing_type (*rv)];
1014           if (w > width)
1015             width = w;
1016         }
1017       r->wrv[col] = width;
1018     }
1019
1020   /* Determine row heights and columns widths. */
1021   for (i = 0; i < t->nr; i++)
1022     r->h[i] = -1;
1023   for (i = 0; i < t->nc; i++)
1024     r->w[i] = -1;
1025
1026   t->dim (r, t->dim_aux);
1027
1028   for (i = 0; i < t->nr; i++)
1029     if (r->h[i] < 0)
1030       error (0, 0, "height of table row %d is %d (not initialized?)",
1031              i, r->h[i]);
1032   for (i = 0; i < t->nc; i++)
1033     if (r->w[i] < 0)
1034       error (0, 0, "width of table column %d is %d (not initialized?)",
1035              i, r->w[i]);
1036
1037   /* Add up header sizes. */
1038   for (i = 0, r->wl = r->wrv[0]; i < r->l; i++)
1039     r->wl += r->w[i] + r->wrv[i + 1];
1040   for (i = 0, r->ht = r->hrh[0]; i < r->t; i++)
1041     r->ht += r->h[i] + r->hrh[i + 1];
1042   for (i = t->nc - r->r, r->wr = r->wrv[i]; i < t->nc; i++)
1043     r->wr += r->w[i] + r->wrv[i + 1];
1044   for (i = t->nr - r->b, r->hb = r->hrh[i]; i < t->nr; i++)
1045     r->hb += r->h[i] + r->hrh[i + 1];
1046
1047   /* Title. */
1048   if (!(t->flags & SOMF_NO_TITLE))
1049     r->ht += driver->font_height;
1050
1051   return r;
1052 }
1053
1054 static void
1055 tabi_render_free (void *r_)
1056 {
1057   struct tab_rendering *r = r_;
1058
1059   free (r->w);
1060   free (r->h);
1061   free (r->hrh);
1062   free (r->wrv);
1063   free (r);
1064 }
1065
1066 /* Return the horizontal and vertical size of the entire table,
1067    including headers, for the current output device, into HORIZ and
1068    VERT. */
1069 static void
1070 tabi_area (void *r_, int *horiz, int *vert)
1071 {
1072   struct tab_rendering *r = r_;
1073   const struct tab_table *t = r->table;
1074   int width, col;
1075   int height, row;
1076
1077   width = 0;
1078   for (col = r->l + 1, width = r->wl + r->wr + r->w[t->l];
1079        col < t->nc - r->r; col++)
1080     width += r->w[col] + r->wrv[col];
1081   *horiz = width;
1082
1083   height = 0;
1084   for (row = r->t + 1, height = r->ht + r->hb + r->h[t->t];
1085        row < t->nr - t->b; row++)
1086     height += r->h[row] + r->hrh[row];
1087   *vert = height;
1088 }
1089
1090 /* Determines the number of rows or columns (including appropriate
1091    headers), depending on CUMTYPE, that will fit into the space
1092    specified.  Takes rows/columns starting at index START and attempts
1093    to fill up available space MAX.  Returns in END the index of the
1094    last row/column plus one; returns in ACTUAL the actual amount of
1095    space the selected rows/columns (including appropriate headers)
1096    filled. */
1097 static void
1098 tabi_cumulate (void *r_, int cumtype, int start, int *end,
1099                int max, int *actual)
1100 {
1101   const struct tab_rendering *r = r_;
1102   const struct tab_table *t = r->table;
1103   int limit;
1104   int *cells, *rules;
1105   int total;
1106   int idx;
1107
1108   assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS));
1109   if (cumtype == SOM_ROWS)
1110     {
1111       assert (start >= 0 && start < t->nr);
1112       limit = t->nr - r->b;
1113       cells = &r->h[start];
1114       rules = &r->hrh[start + 1];
1115       total = r->ht + r->hb;
1116     }
1117   else
1118     {
1119       assert (start >= 0 && start < t->nc);
1120       limit = t->nc - t->r;
1121       cells = &r->w[start];
1122       rules = &r->wrv[start + 1];
1123       total = r->wl + r->wr;
1124     }
1125
1126   total += *cells++;
1127   if (total > max)
1128     {
1129       if (end)
1130         *end = start;
1131       if (actual)
1132         *actual = 0;
1133       return;
1134     }
1135
1136   for (idx = start + 1; idx < limit; idx++)
1137     {
1138       int amt = *cells++ + *rules++;
1139
1140       total += amt;
1141       if (total > max)
1142         {
1143           total -= amt;
1144           break;
1145         }
1146     }
1147
1148   if (end)
1149     *end = idx;
1150
1151   if (actual)
1152     *actual = total;
1153 }
1154
1155 /* Render title for current table, with major index X and minor index
1156    Y.  Y may be zero, or X and Y may be zero, but X should be nonzero
1157    if Y is nonzero. */
1158 static void
1159 tabi_title (void *r_, int x, int y, int table_num, int subtable_num,
1160             const char *command_name)
1161 {
1162   const struct tab_rendering *r = r_;
1163   const struct tab_table *t = r->table;
1164   struct outp_text text;
1165   struct string title;
1166
1167   if (t->flags & SOMF_NO_TITLE)
1168     return;
1169
1170   ds_init_empty (&title);
1171   ds_put_format (&title,"%d.%d", table_num, subtable_num);
1172   if (x && y)
1173     ds_put_format (&title, "(%d:%d)", x, y);
1174   else if (x)
1175     ds_put_format (&title, "(%d)", x);
1176   if (command_name != NULL)
1177     ds_put_format (&title, " %s", command_name);
1178   ds_put_cstr (&title, ".  ");
1179   if (t->title != NULL)
1180     ds_put_cstr (&title, t->title);
1181
1182   text.font = OUTP_PROPORTIONAL;
1183   text.justification = OUTP_LEFT;
1184   text.string = ds_ss (&title);
1185   text.h = r->driver->width;
1186   text.v = r->driver->font_height;
1187   text.x = 0;
1188   text.y = r->driver->cp_y;
1189   r->driver->class->text_draw (r->driver, &text);
1190
1191   ds_destroy (&title);
1192 }
1193
1194 static int render_strip (const struct tab_rendering *,
1195                          int x, int y, int r, int c1, int c2, int r1, int r2);
1196
1197 static void
1198 add_range (int ranges[][2], int *np, int start, int end)
1199 {
1200   int n = *np;
1201   if (n == 0 || start > ranges[n - 1][1])
1202     {
1203       ranges[n][0] = start;
1204       ranges[n][1] = end;
1205       ++*np;
1206     }
1207   else
1208     ranges[n - 1][1] = end;
1209 }
1210
1211 /* Draws table region (C0,R0)-(C1,R1), plus headers, at the
1212    current position on the current output device.  */
1213 static void
1214 tabi_render (void *r_, int c0, int r0, int c1, int r1)
1215 {
1216   const struct tab_rendering *r = r_;
1217   const struct tab_table *t = r->table;
1218   int rows[3][2], cols[3][2];
1219   int n_row_ranges, n_col_ranges;
1220   int y, i;
1221
1222   /* Rows to render, counting horizontal rules as rows.  */
1223   n_row_ranges = 0;
1224   add_range (rows, &n_row_ranges, 0, t->t * 2 + 1);
1225   add_range (rows, &n_row_ranges, r0 * 2 + 1, r1 * 2);
1226   add_range (rows, &n_row_ranges, (t->nr - t->b) * 2, t->nr * 2 + 1);
1227
1228   /* Columns to render, counting vertical rules as columns. */
1229   n_col_ranges = 0;
1230   add_range (cols, &n_col_ranges, 0, r->l * 2 + 1);
1231   add_range (cols, &n_col_ranges, c0 * 2 + 1, c1 * 2);
1232   add_range (cols, &n_col_ranges, (t->nc - r->r) * 2, t->nc * 2 + 1);
1233
1234   y = r->driver->cp_y;
1235   if (!(t->flags & SOMF_NO_TITLE))
1236     y += r->driver->font_height;
1237   for (i = 0; i < n_row_ranges; i++)
1238     {
1239       int row;
1240
1241       for (row = rows[i][0]; row < rows[i][1]; row++)
1242         {
1243           int x, j;
1244
1245           x = r->driver->cp_x;
1246           for (j = 0; j < n_col_ranges; j++)
1247             x = render_strip (r, x, y, row,
1248                               cols[j][0], cols[j][1],
1249                               rows[i][0], rows[i][1]);
1250
1251           y += (row & 1) ? r->h[row / 2] : r->hrh[row / 2];
1252         }
1253     }
1254 }
1255
1256 const struct som_table_class tab_table_class =
1257   {
1258     tabi_count,
1259     tabi_columns,
1260     tabi_headers,
1261     tabi_flags,
1262
1263     tabi_render_init,
1264     tabi_render_free,
1265
1266     tabi_area,
1267     tabi_cumulate,
1268     tabi_title,
1269     tabi_render,
1270   };
1271 \f
1272 static enum outp_justification
1273 translate_justification (unsigned int opt)
1274 {
1275   switch (opt & TAB_ALIGN_MASK)
1276     {
1277     case TAB_RIGHT:
1278       return OUTP_RIGHT;
1279     case TAB_LEFT:
1280       return OUTP_LEFT;
1281     case TAB_CENTER:
1282       return OUTP_CENTER;
1283     default:
1284       NOT_REACHED ();
1285     }
1286 }
1287
1288 /* Returns the line style to use for drawing a rule of the given
1289    TYPE. */
1290 static enum outp_line_style
1291 rule_to_draw_type (unsigned char type)
1292 {
1293   switch (type)
1294     {
1295     case TAL_0:
1296     case TAL_GAP:
1297       return OUTP_L_NONE;
1298     case TAL_1:
1299       return OUTP_L_SINGLE;
1300     case TAL_2:
1301       return OUTP_L_DOUBLE;
1302     default:
1303       NOT_REACHED ();
1304     }
1305 }
1306
1307 /* Returns the horizontal rule at the given column and row. */
1308 static int
1309 get_hrule (const struct tab_table *t, int col, int row)
1310 {
1311   return t->rh[col + row * t->cf];
1312 }
1313
1314 /* Returns the vertical rule at the given column and row. */
1315 static int
1316 get_vrule (const struct tab_table *t, int col, int row)
1317 {
1318   return t->rv[col + row * (t->cf + 1)];
1319 }
1320
1321 /* Renders the horizontal rule at the given column and row
1322    at (X,Y) on the page. */
1323 static void
1324 render_horz_rule (const struct tab_rendering *r,
1325                   int x, int y, int col, int row)
1326 {
1327   enum outp_line_style style;
1328   style = rule_to_draw_type (get_hrule (r->table, col, row));
1329   if (style != OUTP_L_NONE)
1330     r->driver->class->line (r->driver, x, y, x + r->w[col], y + r->hrh[row],
1331                             OUTP_L_NONE, style, OUTP_L_NONE, style);
1332 }
1333
1334 /* Renders the vertical rule at the given column and row
1335    at (X,Y) on the page. */
1336 static void
1337 render_vert_rule (const struct tab_rendering *r,
1338                   int x, int y, int col, int row)
1339 {
1340   enum outp_line_style style;
1341   style = rule_to_draw_type (get_vrule (r->table, col, row));
1342   if (style != OUTP_L_NONE)
1343     r->driver->class->line (r->driver, x, y, x + r->wrv[col], y + r->h[row],
1344                             style, OUTP_L_NONE, style, OUTP_L_NONE);
1345 }
1346
1347 /* Renders the rule intersection at the given column and row
1348    at (X,Y) on the page. */
1349 static void
1350 render_rule_intersection (const struct tab_rendering *r,
1351                           int x, int y, int col, int row)
1352 {
1353   const struct tab_table *t = r->table;
1354
1355   /* Bounds of intersection. */
1356   int x0 = x;
1357   int y0 = y;
1358   int x1 = x + r->wrv[col];
1359   int y1 = y + r->hrh[row];
1360
1361   /* Lines on each side of intersection. */
1362   int top = row > 0 ? get_vrule (t, col, row - 1) : TAL_0;
1363   int left = col > 0 ? get_hrule (t, col - 1, row) : TAL_0;
1364   int bottom = row < t->nr ? get_vrule (t, col, row) : TAL_0;
1365   int right = col < t->nc ? get_hrule (t, col, row) : TAL_0;
1366
1367   /* Output style for each line. */
1368   enum outp_line_style o_top = rule_to_draw_type (top);
1369   enum outp_line_style o_left = rule_to_draw_type (left);
1370   enum outp_line_style o_bottom = rule_to_draw_type (bottom);
1371   enum outp_line_style o_right = rule_to_draw_type (right);
1372
1373   if (o_top != OUTP_L_NONE || o_left != OUTP_L_NONE
1374       || o_bottom != OUTP_L_NONE || o_right != OUTP_L_NONE)
1375     r->driver->class->line (r->driver, x0, y0, x1, y1,
1376                             o_top, o_left, o_bottom, o_right);
1377 }
1378
1379 /* Returns the width of columns C1...C2 exclusive,
1380    including interior but not exterior rules. */
1381 static int
1382 strip_width (const struct tab_rendering *r, int c1, int c2)
1383 {
1384   int width = 0;
1385   int c;
1386
1387   for (c = c1; c < c2; c++)
1388     width += r->w[c] + r->wrv[c + 1];
1389   if (c1 < c2)
1390     width -= r->wrv[c2];
1391   return width;
1392 }
1393
1394 /* Returns the height of rows R1...R2 exclusive,
1395    including interior but not exterior rules. */
1396 static int
1397 strip_height (const struct tab_rendering *r, int r1, int r2)
1398 {
1399   int height = 0;
1400   int row;
1401
1402   for (row = r1; row < r2; row++)
1403     height += r->h[row] + r->hrh[row + 1];
1404   if (r1 < r2)
1405     height -= r->hrh[r2];
1406   return height;
1407 }
1408
1409 /* Renders the cell at the given column and row at (X,Y) on the
1410    page.  Also renders joined cells that extend as far to the
1411    right as C1 and as far down as R1. */
1412 static void
1413 render_cell (const struct tab_rendering *r,
1414              int x, int y, int col, int row, int c1, int r1)
1415 {
1416   const struct tab_table *t = r->table;
1417   const int index = col + (row * t->cf);
1418   unsigned char type = t->ct[index];
1419   struct substring *content = &t->cc[index];
1420
1421   if (!(type & TAB_JOIN))
1422     {
1423       if (!(type & TAB_EMPTY))
1424         {
1425           struct outp_text text;
1426           text.font = options_to_font (type);
1427           text.justification = translate_justification (type);
1428           text.string = *content;
1429           text.h = r->w[col];
1430           text.v = r->h[row];
1431           text.x = x;
1432           text.y = y;
1433           r->driver->class->text_draw (r->driver, &text);
1434         }
1435     }
1436   else
1437     {
1438       struct tab_joined_cell *j
1439         = (struct tab_joined_cell *) ss_data (*content);
1440
1441       if (j->x1 == col && j->y1 == row)
1442         {
1443           struct outp_text text;
1444           text.font = options_to_font (type);
1445           text.justification = translate_justification (type);
1446           text.string = j->contents;
1447           text.x = x;
1448           text.y = y;
1449           text.h = strip_width (r, j->x1, MIN (j->x2, c1));
1450           text.v = strip_height (r, j->y1, MIN (j->y2, r1));
1451           r->driver->class->text_draw (r->driver, &text);
1452         }
1453     }
1454 }
1455
1456 /* Render contiguous strip consisting of columns C0...C1, exclusive,
1457    on row ROW, at (X,Y).  Returns X position after rendering.
1458    Also renders joined cells that extend beyond that strip,
1459    cropping them to lie within rendering region (C0,R0)-(C1,R1).
1460    C0 and C1 count vertical rules as columns.
1461    ROW counts horizontal rules as rows, but R0 and R1 do not. */
1462 static int
1463 render_strip (const struct tab_rendering *r,
1464               int x, int y, int row, int c0, int c1, int r0 UNUSED, int r1)
1465 {
1466   int col;
1467
1468   for (col = c0; col < c1; col++)
1469     if (col & 1)
1470       {
1471         if (row & 1)
1472           render_cell (r, x, y, col / 2, row / 2, c1 / 2, r1);
1473         else
1474           render_horz_rule (r, x, y, col / 2, row / 2);
1475         x += r->w[col / 2];
1476       }
1477     else
1478       {
1479         if (row & 1)
1480           render_vert_rule (r, x, y, col / 2, row / 2);
1481         else
1482           render_rule_intersection (r, x, y, col / 2, row / 2);
1483         x += r->wrv[col / 2];
1484       }
1485
1486   return x;
1487 }