X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Ftable.c;h=bd51f494033ad346d3377e3f25e00db9e536a559;hb=81579d9e9f994fb2908f50af41c3eb033d216e58;hp=4a8e036e25af1c79aaec1b3e5dae5ae523b308cb;hpb=2322678e8fddbbf158b01b2720db2636404bba3b;p=pspp-builds.git diff --git a/src/output/table.c b/src/output/table.c index 4a8e036e..bd51f494 100644 --- a/src/output/table.c +++ b/src/output/table.c @@ -1,1447 +1,339 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 2009, 2011 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include -#include "table.h" -#include -#include -#include -#include -#include "message.h" -#include "alloc.h" -#include "compiler.h" -#include "format.h" -#include "magic.h" -#include "misc.h" -#include "output.h" -#include "pool.h" -#include "manager.h" -#include "variable.h" - -#include "gettext.h" -#define _(msgid) gettext (msgid) - -#include "debug-print.h" - -struct som_table_class tab_table_class; -static char *command_name; - -/* Creates a table with NC columns and NR rows. If REALLOCABLE is - nonzero then the table's size can be increased later; otherwise, - its size can only be reduced. */ -struct tab_table * -tab_create (int nc, int nr, int reallocable) -{ - void *(*alloc_func) (struct pool *, size_t n); - void *(*nalloc_func) (struct pool *, size_t n, size_t s); - - struct tab_table *t; - - { - struct pool *container = pool_create (); - t = pool_alloc (container, sizeof *t); - t->container = container; - } - - t->col_style = TAB_COL_NONE; - t->col_group = 0; - ls_null (&t->title); - t->flags = SOMF_NONE; - t->nr = nr; - t->nc = t->cf = nc; - t->l = t->r = t->t = t->b = 0; - - nalloc_func = reallocable ? pool_nmalloc : pool_nalloc; - alloc_func = reallocable ? pool_malloc : pool_alloc; -#if GLOBAL_DEBUGGING - t->reallocable = reallocable; -#endif - t->cc = nalloc_func (t->container, nr * nc, sizeof *t->cc); - t->ct = alloc_func (t->container, nr * nc); - memset (t->ct, TAB_EMPTY, nc * nr); +#include "output/table.h" +#include "output/table-provider.h" - t->rh = nalloc_func (t->container, nc, nr + 1); - memset (t->rh, 0, nc * (nr + 1)); - - t->hrh = nalloc_func (t->container, nr + 1, sizeof *t->hrh); - memset (t->hrh, 0, sizeof *t->hrh * (nr + 1)); - - t->trh = alloc_func (t->container, nr + 1); - memset (t->trh, 0, nr + 1); - - t->rv = nalloc_func (t->container, nr, nc + 1); - memset (t->rv, 0, (nc + 1) * nr); +#include +#include - t->wrv = nalloc_func (t->container, nc + 1, sizeof *t->wrv); - memset (t->wrv, 0, sizeof *t->wrv * (nc + 1)); +#include "libpspp/cast.h" +#include "libpspp/compiler.h" - t->trv = alloc_func (t->container, nc + 1); - memset (t->trv, 0, nc + 1); +#include "gl/xalloc.h" - t->dim = NULL; - t->w = t->h = NULL; - t->col_ofs = t->row_ofs = 0; - - return t; -} - -/* Destroys table T. */ -void -tab_destroy (struct tab_table *t) +/* Increases TABLE's reference count, indicating that it has an additional + owner. An table that is shared among multiple owners must not be + modified. */ +struct table * +table_ref (const struct table *table_) { - assert (t != NULL); - pool_destroy (t->container); - t=0; + struct table *table = CONST_CAST (struct table *, table_); + table->ref_cnt++; + return table; } -/* Sets the width and height of a table, in columns and rows, - respectively. Use only to reduce the size of a table, since it - does not change the amount of allocated memory. */ +/* Decreases TABLE's reference count, indicating that it has one fewer owner. + If TABLE no longer has any owners, it is freed. */ void -tab_resize (struct tab_table *t, int nc, int nr) +table_unref (struct table *table) { - assert (t != NULL); - if (nc != -1) - { - assert (nc + t->col_ofs <= t->cf); - t->nc = nc + t->col_ofs; - } - if (nr != -1) + if (table != NULL) { - assert (nr + t->row_ofs <= t->nr); - t->nr = nr + t->row_ofs; + assert (table->ref_cnt > 0); + if (--table->ref_cnt == 0) + table->class->destroy (table); } } -/* Changes either or both dimensions of a table. Consider using the - above routine instead if it won't waste a lot of space. - - Changing the number of columns in a table is particularly expensive - in space and time. Avoid doing such. FIXME: In fact, transferring - of rules isn't even implemented yet. */ -void -tab_realloc (struct tab_table *t, int nc, int nr) +/* Returns true if TABLE has more than one owner. A table item that is shared + among multiple owners must not be modified. */ +bool +table_is_shared (const struct table *table) { - int ro, co; - - assert (t != NULL); -#if GLOBAL_DEBUGGING - assert (t->reallocable); -#endif - ro = t->row_ofs; - co = t->col_ofs; - if (ro || co) - tab_offset (t, 0, 0); - - if (nc == -1) - nc = t->nc; - if (nr == -1) - nr = t->nr; - - assert (nc == t->nc); - - if (nc > t->cf) - { - int mr1 = min (nr, t->nr); - int mc1 = min (nc, t->nc); - - struct fixed_string *new_cc; - unsigned char *new_ct; - int r; - - new_cc = pool_nmalloc (t->container, nr * nc, sizeof *new_cc); - new_ct = pool_malloc (t->container, nr * nc); - for (r = 0; r < mr1; r++) - { - memcpy (&new_cc[r * nc], &t->cc[r * t->nc], mc1 * sizeof *t->cc); - memcpy (&new_ct[r * nc], &t->ct[r * t->nc], mc1); - memset (&new_ct[r * nc + t->nc], TAB_EMPTY, nc - t->nc); - } - pool_free (t->container, t->cc); - pool_free (t->container, t->ct); - t->cc = new_cc; - t->ct = new_ct; - t->cf = nc; - } - else if (nr != t->nr) - { - t->cc = pool_nrealloc (t->container, t->cc, nr * nc, sizeof *t->cc); - t->ct = pool_realloc (t->container, t->ct, nr * nc); - - t->rh = pool_nrealloc (t->container, t->rh, nc, nr + 1); - t->rv = pool_nrealloc (t->container, t->rv, nr, nc + 1); - t->trh = pool_realloc (t->container, t->trh, nr + 1); - t->hrh = pool_nrealloc (t->container, t->hrh, nr + 1, sizeof *t->hrh); - - if (nr > t->nr) - { - memset (&t->rh[nc * (t->nr + 1)], 0, (nr - t->nr) * nc); - memset (&t->rv[(nc + 1) * t->nr], 0, (nr - t->nr) * (nc + 1)); - memset (&t->trh[t->nr + 1], 0, nr - t->nr); - } - } - - memset (&t->ct[nc * t->nr], TAB_EMPTY, nc * (nr - t->nr)); - - t->nr = nr; - t->nc = nc; - - if (ro || co) - tab_offset (t, co, ro); + return table->ref_cnt > 1; } -/* Sets the number of header rows on each side of TABLE to L on the - left, R on the right, T on the top, B on the bottom. Header rows - are repeated when a table is broken across multiple columns or - multiple pages. */ +/* Sets the number of left header columns in TABLE to HL. */ void -tab_headers (struct tab_table *table, int l, int r, int t, int b) +table_set_hl (struct table *table, int hl) { - assert (table != NULL); - assert (l < table->nc); - assert (r < table->nc); - assert (t < table->nr); - assert (b < table->nr); - - - table->l = l; - table->r = r; - table->t = t; - table->b = b; + assert (!table_is_shared (table)); + table->h[TABLE_HORZ][0] = hl; } -/* Set up table T so that, when it is an appropriate size, it will be - displayed across the page in columns. - - STYLE is a TAB_COL_* constant. GROUP is the number of rows to take - as a unit. */ +/* Sets the number of right header columns in TABLE to HR. */ void -tab_columns (struct tab_table *t, int style, int group) +table_set_hr (struct table *table, int hr) { - assert (t != NULL); - t->col_style = style; - t->col_group = group; + assert (!table_is_shared (table)); + table->h[TABLE_HORZ][1] = hr; } - -/* Rules. */ -/* Draws a vertical line to the left of cells at horizontal position X - from Y1 to Y2 inclusive in style STYLE, if style is not -1. */ +/* Sets the number of top header rows in TABLE to HT. */ void -tab_vline (struct tab_table *t, int style, int x, int y1, int y2) +table_set_ht (struct table *table, int ht) { - int y; - - assert (t != NULL); - -#if GLOBAL_DEBUGGING - if (x + t->col_ofs < 0 || x + t->col_ofs > t->nc - || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr - || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr) - { - printf (_("bad vline: x=%d+%d=%d y=(%d+%d=%d,%d+%d=%d) in " - "table size (%d,%d)\n"), - x, t->col_ofs, x + t->col_ofs, - y1, t->row_ofs, y1 + t->row_ofs, - y2, t->row_ofs, y2 + t->row_ofs, - t->nc, t->nr); - return; - } -#endif - - x += t->col_ofs; - y1 += t->row_ofs; - y2 += t->row_ofs; - - assert (x > 0); - assert (x < t->nc); - assert (y1 >= 0); - assert (y2 >= y1); - assert (y2 <= t->nr); - - if (style != -1) - { - if ((style & TAL_SPACING) == 0) - for (y = y1; y <= y2; y++) - t->rv[x + (t->cf + 1) * y] = style; - t->trv[x] |= (1 << (style & ~TAL_SPACING)); - } + assert (!table_is_shared (table)); + table->h[TABLE_VERT][0] = ht; } -/* Draws a horizontal line above cells at vertical position Y from X1 - to X2 inclusive in style STYLE, if style is not -1. */ +/* Sets the number of top header rows in TABLE to HB. */ void -tab_hline (struct tab_table * t, int style, int x1, int x2, int y) +table_set_hb (struct table *table, int hb) { - int x; - - assert (t != NULL); - - x1 += t->col_ofs; - x2 += t->col_ofs; - y += t->row_ofs; - - assert (y >= 0); - assert (y < t->nr); - assert (x2 >= x1 ); - assert (x1 >= 0 ); - assert (x2 < t->nc); - - if (style != -1) - { - if ((style & TAL_SPACING) == 0) - for (x = x1; x <= x2; x++) - t->rh[x + t->cf * y] = style; - t->trh[y] |= (1 << (style & ~TAL_SPACING)); - } + assert (!table_is_shared (table)); + table->h[TABLE_VERT][1] = hb; } + +/* Initializes TABLE as a table of the specified CLASS, initially with a + reference count of 1. -/* Draws a box around cells (X1,Y1)-(X2,Y2) inclusive with horizontal - lines of style F_H and vertical lines of style F_V. Fills the - interior of the box with horizontal lines of style I_H and vertical - lines of style I_V. Any of the line styles may be -1 to avoid - drawing those lines. This is distinct from 0, which draws a null - line. */ -void -tab_box (struct tab_table *t, int f_h, int f_v, int i_h, int i_v, - int x1, int y1, int x2, int y2) -{ - assert (t != NULL); - -#if GLOBAL_DEBUGGING - if (x1 + t->col_ofs < 0 || x1 + t->col_ofs >= t->nc - || x2 + t->col_ofs < 0 || x2 + t->col_ofs >= t->nc - || y1 + t->row_ofs < 0 || y1 + t->row_ofs >= t->nr - || y2 + t->row_ofs < 0 || y2 + t->row_ofs >= t->nr) - { - printf (_("bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) " - "in table size (%d,%d)\n"), - x1, t->col_ofs, x1 + t->col_ofs, - y1, t->row_ofs, y1 + t->row_ofs, - x2, t->col_ofs, x2 + t->col_ofs, - y2, t->row_ofs, y2 + t->row_ofs, - t->nc, t->nr); - abort (); - } -#endif - - x1 += t->col_ofs; - x2 += t->col_ofs; - y1 += t->row_ofs; - y2 += t->row_ofs; - - assert (x2 >= x1); - assert (y2 >= y1); - assert (x1 >= 0); - assert (y1 >= 0); - assert (x2 < t->nc); - assert (y2 < t->nr); - - if (f_h != -1) - { - int x; - if ((f_h & TAL_SPACING) == 0) - for (x = x1; x <= x2; x++) - { - t->rh[x + t->cf * y1] = f_h; - t->rh[x + t->cf * (y2 + 1)] = f_h; - } - t->trh[y1] |= (1 << (f_h & ~TAL_SPACING)); - t->trh[y2 + 1] |= (1 << (f_h & ~TAL_SPACING)); - } - if (f_v != -1) - { - int y; - if ((f_v & TAL_SPACING) == 0) - for (y = y1; y <= y2; y++) - { - t->rv[x1 + (t->cf + 1) * y] = f_v; - t->rv[(x2 + 1) + (t->cf + 1) * y] = f_v; - } - t->trv[x1] |= (1 << (f_v & ~TAL_SPACING)); - t->trv[x2 + 1] |= (1 << (f_v & ~TAL_SPACING)); - } - - if (i_h != -1) - { - int y; - - for (y = y1 + 1; y <= y2; y++) - { - int x; - - if ((i_h & TAL_SPACING) == 0) - for (x = x1; x <= x2; x++) - t->rh[x + t->cf * y] = i_h; - - t->trh[y] |= (1 << (i_h & ~TAL_SPACING)); - } - } - if (i_v != -1) - { - int x; - - for (x = x1 + 1; x <= x2; x++) - { - int y; - - if ((i_v & TAL_SPACING) == 0) - for (y = y1; y <= y2; y++) - t->rv[x + (t->cf + 1) * y] = i_v; - - t->trv[x] |= (1 << (i_v & ~TAL_SPACING)); - } - } -} - -/* Formats text TEXT and arguments ARGS as indicated in OPT and sets - the resultant string into S in TABLE's pool. */ -static void -text_format (struct tab_table *table, int opt, const char *text, va_list args, - struct fixed_string *s) -{ - int len; - - assert (table != NULL && text != NULL && s != NULL); - - if (opt & TAT_PRINTF) - { - char *temp_buf = local_alloc (1024); - - len = nvsprintf (temp_buf, text, args); - text = temp_buf; - } - else - len = strlen (text); - - ls_create_buffer (s, text, len); - pool_register (table->container, free, s->string); - - if (opt & TAT_PRINTF) - local_free (text); -} + TABLE initially has 0 rows and columns and no headers. The table + implementation should update the numbers of rows and columns. The table + implementation (or its client) may update the header rows and columns. -/* Set the title of table T to TITLE, which is formatted with printf - if FORMAT is nonzero. */ + A table is an abstract class, that is, a plain struct table is not useful on + its own. Thus, this function is normally called from the initialization + function of some subclass of table. */ void -tab_title (struct tab_table *t, int format, const char *title, ...) +table_init (struct table *table, const struct table_class *class) { - va_list args; - - assert (t != NULL && title != NULL); - va_start (args, title); - text_format (t, format ? TAT_PRINTF : TAT_NONE, title, args, &t->title); - va_end (args); + table->class = class; + table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0; + table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0; + table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0; + table->ref_cnt = 1; } -/* Set DIM_FUNC as the dimension function for table T. */ +/* Sets the number of columns in TABLE to NC. */ void -tab_dim (struct tab_table *t, tab_dim_func *dim_func) -{ - assert (t != NULL && t->dim == NULL); - t->dim = dim_func; -} - -/* Returns the natural width of column C in table T for driver D, that - is, the smallest width necessary to display all its cells without - wrapping. The width will be no larger than the page width minus - left and right rule widths. */ -int -tab_natural_width (struct tab_table *t, struct outp_driver *d, int c) -{ - int width; - - assert (t != NULL && c >= 0 && c < t->nc); - { - int r; - - for (width = r = 0; r < t->nr; r++) - { - struct outp_text text; - unsigned char opt = t->ct[c + r * t->cf]; - - if (opt & (TAB_JOIN | TAB_EMPTY)) - continue; - - text.s = t->cc[c + r * t->cf]; - assert (!ls_null_p (&text.s)); - text.options = OUTP_T_JUST_LEFT; - - d->class->text_metrics (d, &text); - if (text.h > width) - width = text.h; - } - } - - if (width == 0) - { - width = d->prop_em_width * 8; -#if GLOBAL_DEBUGGING - printf ("warning: table column %d contains no data.\n", c); -#endif - } - - { - const int clamp = d->width - t->wrv[0] - t->wrv[t->nc]; - - if (width > clamp) - width = clamp; - } - - return width; -} - -/* Returns the natural height of row R in table T for driver D, that - is, the minimum height necessary to display the information in the - cell at the widths set for each column. */ -int -tab_natural_height (struct tab_table *t, struct outp_driver *d, int r) +table_set_nc (struct table *table, int nc) { - int height; - - assert (t != NULL && r >= 0 && r < t->nr); - - { - int c; - - for (height = d->font_height, c = 0; c < t->nc; c++) - { - struct outp_text text; - unsigned char opt = t->ct[c + r * t->cf]; - - assert (t->w[c] != NOT_INT); - if (opt & (TAB_JOIN | TAB_EMPTY)) - continue; - - text.s = t->cc[c + r * t->cf]; - assert (!ls_null_p (&text.s)); - text.options = OUTP_T_HORZ | OUTP_T_JUST_LEFT; - text.h = t->w[c]; - d->class->text_metrics (d, &text); - - if (text.v > height) - height = text.v; - } - } - - return height; + assert (!table_is_shared (table)); + table->n[TABLE_HORZ] = nc; } -/* Callback function to set all columns and rows to their natural - dimensions. Not really meant to be called directly. */ +/* Sets the number of rows in TABLE to NR. */ void -tab_natural_dimensions (struct tab_table *t, struct outp_driver *d) +table_set_nr (struct table *table, int nr) { - int i; - - assert (t != NULL); - - for (i = 0; i < t->nc; i++) - t->w[i] = tab_natural_width (t, d, i); - - for (i = 0; i < t->nr; i++) - t->h[i] = tab_natural_height (t, d, i); + assert (!table_is_shared (table)); + table->n[TABLE_VERT] = nr; } - -/* Cells. */ +/* Initializes CELL with the contents of the table cell at column X and row Y + within TABLE. When CELL is no longer needed, the caller is responsible for + freeing it by calling table_cell_free(CELL). -/* Sets cell (C,R) in TABLE, with options OPT, to have a value taken - from V, displayed with format spec F. */ + The caller must ensure that CELL is destroyed before TABLE is unref'ed. */ void -tab_value (struct tab_table *table, int c, int r, unsigned char opt, - const union value *v, const struct fmt_spec *f) +table_get_cell (const struct table *table, int x, int y, + struct table_cell *cell) { - char *contents; - - assert (table != NULL && v != NULL && f != NULL); -#if GLOBAL_DEBUGGING - if (c + table->col_ofs < 0 || r + table->row_ofs < 0 - || c + table->col_ofs >= table->nc - || r + table->row_ofs >= table->nr) - { - printf ("tab_value(): bad cell (%d+%d=%d,%d+%d=%d) in table size " - "(%d,%d)\n", - c, table->col_ofs, c + table->col_ofs, - r, table->row_ofs, r + table->row_ofs, - table->nc, table->nr); - return; - } -#endif - - contents = pool_alloc (table->container, f->w); - ls_init (&table->cc[c + r * table->cf], contents, f->w); - table->ct[c + r * table->cf] = opt; - - data_out (contents, f, v); -} - -/* Sets cell (C,R) in TABLE, with options OPT, to have value VAL - with NDEC decimal places. */ -void -tab_float (struct tab_table *table, int c, int r, unsigned char opt, - double val, int w, int d) -{ - char *contents; - char buf[40], *cp; - - struct fmt_spec f; - union value double_value; - - assert (table != NULL && w <= 40); - - assert (c >= 0); - assert (c < table->nc); - assert (r >= 0); - assert (r < table->nr); - - f = make_output_format (FMT_F, w, d); - -#if GLOBAL_DEBUGGING - if (c + table->col_ofs < 0 || r + table->row_ofs < 0 - || c + table->col_ofs >= table->nc - || r + table->row_ofs >= table->nr) - { - printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size " - "(%d,%d)\n", - c, table->col_ofs, c + table->col_ofs, - r, table->row_ofs, r + table->row_ofs, - table->nc, table->nr); - return; - } -#endif - - double_value.f = val; - data_out (buf, &f, &double_value); - - cp = buf; - while (isspace ((unsigned char) *cp) && cp < &buf[w]) - cp++; - f.w = w - (cp - buf); - - contents = pool_alloc (table->container, f.w); - ls_init (&table->cc[c + r * table->cf], contents, f.w); - table->ct[c + r * table->cf] = opt; - memcpy (contents, cp, f.w); + assert (x >= 0 && x < table->n[TABLE_HORZ]); + assert (y >= 0 && y < table->n[TABLE_VERT]); + table->class->get_cell (table, x, y, cell); } -/* Sets cell (C,R) in TABLE, with options OPT, to have text value - TEXT. */ +/* Frees CELL, which should have been initialized by calling + table_get_cell(). */ void -tab_text (struct tab_table *table, int c, int r, unsigned opt, const char *text, ...) +table_cell_free (struct table_cell *cell) +{ + if (cell->destructor != NULL) + cell->destructor (cell->destructor_aux); +} + +/* Returns one of the TAL_* enumeration constants (declared in output/table.h) + representing a rule running alongside one of the cells in TABLE. + + Suppose NC is the number of columns in TABLE and NR is the number of rows. + Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR. If (X,Y) = + (0,0), the return value is the rule that runs vertically on the left side of + cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and + cell (1,0); and so on, up to (NC,0), which runs vertically on the right of + cell (NC-1,0). + + The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ + within a 7x7 table. The '|' characters at the intersection of the X labels + and Y labels show the rule whose style would be returned by calling + table_get_rule with those X and Y values: + + 0 1 2 3 4 5 6 7 + +--+--+--+--+--+--+--+ + 0 | | | | | | | | + +--+--+--+--+--+--+--+ + 1 | | | | | | | | + +--+--+--+--+--+--+--+ + 2 | | | | | | | | + +--+--+--+--+--+--+--+ + 3 | | | | | | | | + +--+--+--+--+--+--+--+ + 4 | | | | | | | | + +--+--+--+--+--+--+--+ + 5 | | | | | | | | + +--+--+--+--+--+--+--+ + 6 | | | | | | | | + +--+--+--+--+--+--+--+ + + Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR. If + (X,Y) = (0,0), the return value is the rule that runs horizontally above + the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule + between that cell and cell (0,1); and so on, up to (0,NR), which runs + horizontally below cell (0,NR-1). */ +int +table_get_rule (const struct table *table, enum table_axis axis, int x, int y) { - va_list args; - - assert (table != NULL && text != NULL); - - assert (c >= 0 ); - assert (r >= 0 ); - assert (c < table->nc); - assert (r < table->nr); - - -#if GLOBAL_DEBUGGING - if (c + table->col_ofs < 0 || r + table->row_ofs < 0 - || c + table->col_ofs >= table->nc - || r + table->row_ofs >= table->nr) - { - printf ("tab_text(): bad cell (%d+%d=%d,%d+%d=%d) in table size " - "(%d,%d)\n", - c, table->col_ofs, c + table->col_ofs, - r, table->row_ofs, r + table->row_ofs, - table->nc, table->nr); - return; - } -#endif - - va_start (args, text); - text_format (table, opt, text, args, &table->cc[c + r * table->cf]); - table->ct[c + r * table->cf] = opt; - va_end (args); + assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ)); + assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT)); + return table->class->get_rule (table, axis, x, y); } - -/* Joins cells (X1,X2)-(Y1,Y2) inclusive in TABLE, and sets them with - options OPT to have text value TEXT. */ -void -tab_joint_text (struct tab_table *table, int x1, int y1, int x2, int y2, - unsigned opt, const char *text, ...) -{ - struct tab_joined_cell *j; - - assert (table != NULL && text != NULL); - - assert (x1 + table->col_ofs >= 0); - assert (y1 + table->row_ofs >= 0); - assert (y2 >= y1); - assert (x2 >= x1); - assert (y2 + table->row_ofs < table->nr); - assert (x2 + table->col_ofs < table->nc); - -#if GLOBAL_DEBUGGING - if (x1 + table->col_ofs < 0 || x1 + table->col_ofs >= table->nc - || y1 + table->row_ofs < 0 || y1 + table->row_ofs >= table->nr - || x2 < x1 || x2 + table->col_ofs >= table->nc - || y2 < y2 || y2 + table->row_ofs >= table->nr) - { - printf ("tab_joint_text(): bad cell " - "(%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n", - x1, table->col_ofs, x1 + table->col_ofs, - y1, table->row_ofs, y1 + table->row_ofs, - x2, table->col_ofs, x2 + table->col_ofs, - y2, table->row_ofs, y2 + table->row_ofs, - table->nc, table->nr); - return; - } -#endif - - j = pool_alloc (table->container, sizeof *j); - j->hit = 0; - j->x1 = x1 + table->col_ofs; - j->y1 = y1 + table->row_ofs; - j->x2 = ++x2 + table->col_ofs; - j->y2 = ++y2 + table->row_ofs; - - { - va_list args; - - va_start (args, text); - text_format (table, opt, text, args, &j->contents); - va_end (args); - } - - opt |= TAB_JOIN; - + +struct table_unshared { - struct fixed_string *cc = &table->cc[x1 + y1 * table->cf]; - unsigned char *ct = &table->ct[x1 + y1 * table->cf]; - const int ofs = table->cf - (x2 - x1); - - int y; - - for (y = y1; y < y2; y++) - { - int x; - - for (x = x1; x < x2; x++) - { - ls_init (cc++, (char *) j, 0); - *ct++ = opt; - } - - cc += ofs; - ct += ofs; - } - } -} - -/* Sets cell (C,R) in TABLE, with options OPT, to contents STRING. */ -void -tab_raw (struct tab_table *table, int c, int r, unsigned opt, - struct fixed_string *string) -{ - assert (table != NULL && string != NULL); - -#if GLOBAL_DEBUGGING - if (c + table->col_ofs < 0 || r + table->row_ofs < 0 - || c + table->col_ofs >= table->nc - || r + table->row_ofs >= table->nr) - { - printf ("tab_float(): bad cell (%d+%d=%d,%d+%d=%d) in table size " - "(%d,%d)\n", - c, table->col_ofs, c + table->col_ofs, - r, table->row_ofs, r + table->row_ofs, - table->nc, table->nr); - return; - } -#endif + struct table table; + struct table *subtable; + }; - table->cc[c + r * table->cf] = *string; - table->ct[c + r * table->cf] = opt; -} - -/* Miscellaneous. */ +static const struct table_class table_unshared_class; -/* Sets the widths of all the columns and heights of all the rows in - table T for driver D. */ -static void -nowrap_dim (struct tab_table *t, struct outp_driver *d) -{ - t->w[0] = tab_natural_width (t, d, 0); - t->h[0] = d->font_height; -} +/* Takes ownership of TABLE and returns a table with the same contents but + which is guaranteed not to be shared (as returned by table_is_shared()). -/* Sets the widths of all the columns and heights of all the rows in - table T for driver D. */ -static void -wrap_dim (struct tab_table *t, struct outp_driver *d) -{ - t->w[0] = tab_natural_width (t, d, 0); - t->h[0] = tab_natural_height (t, d, 0); -} + If TABLE is unshared, just returns TABLE. -/* Outputs text BUF as a table with a single cell having cell options - OPTIONS, which is a combination of the TAB_* and TAT_* - constants. */ -void -tab_output_text (int options, const char *buf, ...) + The only real use for this function is to create a copy of TABLE in which + the headers can be adjusted, which is a pretty specialized use case. */ +struct table * +table_unshare (struct table *table) { - struct tab_table *t = tab_create (1, 1, 0); - - assert (buf != NULL); - if (options & TAT_PRINTF) - { - va_list args; - char *temp_buf = local_alloc (4096); - - va_start (args, buf); - nvsprintf (temp_buf, buf, args); - buf = temp_buf; - va_end (args); - } - - if (options & TAT_FIX) - { - struct outp_driver *d; - - for (d = outp_drivers (NULL); d; d = outp_drivers (d)) - { - if (!d->page_open) - d->class->open_page (d); - - if (d->class->text_set_font_by_name != NULL) - d->class->text_set_font_by_name (d, "FIXED"); - else - { - /* FIXME */ - } - } - } - - tab_text (t, 0, 0, options &~ TAT_PRINTF, buf); - tab_flags (t, SOMF_NO_TITLE | SOMF_NO_SPACING); - if (options & TAT_NOWRAP) - tab_dim (t, nowrap_dim); + if (!table_is_shared (table)) + return table; else - tab_dim (t, wrap_dim); - tab_submit (t); - - if (options & TAT_FIX) { - struct outp_driver *d; - - for (d = outp_drivers (NULL); d; d = outp_drivers (d)) - if (d->class->text_set_font_by_name != NULL) - d->class->text_set_font_by_name (d, "PROP"); - else - { - /* FIXME */ - } + struct table_unshared *tiu = xmalloc (sizeof *tiu); + table_init (&tiu->table, &table_unshared_class); + table_set_nc (&tiu->table, table_nc (table)); + table_set_nr (&tiu->table, table_nr (table)); + table_set_hl (&tiu->table, table_hl (table)); + table_set_hr (&tiu->table, table_hr (table)); + table_set_ht (&tiu->table, table_ht (table)); + table_set_hb (&tiu->table, table_hb (table)); + tiu->subtable = table; + return &tiu->table; } - - if (options & TAT_PRINTF) - local_free (buf); } -/* Set table flags to FLAGS. */ -void -tab_flags (struct tab_table *t, unsigned flags) +static struct table_unshared * +table_unshared_cast (const struct table *table) { - assert (t != NULL); - t->flags = flags; + assert (table->class == &table_unshared_class); + return UP_CAST (table, struct table_unshared, table); } -/* Easy, type-safe way to submit a tab table to som. */ -void -tab_submit (struct tab_table *t) -{ - struct som_entity s; - - assert (t != NULL); - s.class = &tab_table_class; - s.ext = t; - s.type = SOM_TABLE; - som_submit (&s); - tab_destroy (t); -} - -/* Editing. */ - -/* Set table row and column offsets for all functions that affect - cells or rules. */ -void -tab_offset (struct tab_table *t, int col, int row) -{ - int diff = 0; - - assert (t != NULL); -#if GLOBAL_DEBUGGING - if (row < -1 || row >= t->nr) - { - printf ("tab_offset(): row=%d in %d-row table\n", row, t->nr); - abort (); - } - if (col < -1 || col >= t->nc) - { - printf ("tab_offset(): col=%d in %d-column table\n", col, t->nc); - abort (); - } -#endif - - if (row != -1) - diff += (row - t->row_ofs) * t->cf, t->row_ofs = row; - if (col != -1) - diff += (col - t->col_ofs), t->col_ofs = col; - - t->cc += diff; - t->ct += diff; -} - -/* Increment the row offset by one. If the table is too small, - increase its size. */ -void -tab_next_row (struct tab_table *t) -{ - assert (t != NULL); - t->cc += t->cf; - t->ct += t->cf; - if (++t->row_ofs >= t->nr) - tab_realloc (t, -1, t->nr * 4 / 3); -} - -static struct tab_table *t; -static struct outp_driver *d; -int tab_hit; - -/* Set the current table to TABLE. */ static void -tabi_table (struct som_entity *table) +table_unshared_destroy (struct table *tiu_) { - assert (table != NULL); - assert (table->type == SOM_TABLE); - - t = table->ext; - tab_offset (t, 0, 0); - - assert (t->w == NULL && t->h == NULL); - t->w = pool_nalloc (t->container, t->nc, sizeof *t->w); - t->h = pool_nalloc (t->container, t->nr, sizeof *t->h); + struct table_unshared *tiu = table_unshared_cast (tiu_); + table_unref (tiu->subtable); + free (tiu); } -/* Set the current output device to DRIVER. */ static void -tabi_driver (struct outp_driver *driver) +table_unshared_get_cell (const struct table *tiu_, int x, int y, + struct table_cell *cell) { - int i; - - assert (driver != NULL); - d = driver; - - /* Figure out sizes of rules. */ - for (t->hr_tot = i = 0; i <= t->nr; i++) - t->hr_tot += t->hrh[i] = d->horiz_line_spacing[t->trh[i]]; - for (t->vr_tot = i = 0; i <= t->nc; i++) - t->vr_tot += t->wrv[i] = d->vert_line_spacing[t->trv[i]]; - -#if GLOBAL_DEBUGGING - for (i = 0; i < t->nr; i++) - t->h[i] = -1; - for (i = 0; i < t->nc; i++) - t->w[i] = -1; -#endif - - assert (t->dim != NULL); - t->dim (t, d); - -#if GLOBAL_DEBUGGING - { - int error = 0; - - for (i = 0; i < t->nr; i++) - { - if (t->h[i] == -1) - { - printf ("Table row %d height not initialized.\n", i); - error = 1; - } - assert (t->h[i] > 0); - } - - for (i = 0; i < t->nc; i++) - { - if (t->w[i] == -1) - { - printf ("Table column %d width not initialized.\n", i); - error = 1; - } - assert (t->w[i] > 0); - } - } -#endif - - /* Add up header sizes. */ - for (i = 0, t->wl = t->wrv[0]; i < t->l; i++) - t->wl += t->w[i] + t->wrv[i + 1]; - for (i = 0, t->ht = t->hrh[0]; i < t->t; i++) - t->ht += t->h[i] + t->hrh[i + 1]; - for (i = t->nc - t->r, t->wr = t->wrv[i]; i < t->nc; i++) - t->wr += t->w[i] + t->wrv[i + 1]; - for (i = t->nr - t->b, t->hb = t->hrh[i]; i < t->nr; i++) - t->hb += t->h[i] + t->hrh[i + 1]; - - /* Title. */ - if (!(t->flags & SOMF_NO_TITLE)) - t->ht += d->font_height; + struct table_unshared *tiu = table_unshared_cast (tiu_); + table_get_cell (tiu->subtable, x, y, cell); } -/* Return the number of columns and rows in the table into N_COLUMNS - and N_ROWS, respectively. */ -static void -tabi_count (int *n_columns, int *n_rows) +static int +table_unshared_get_rule (const struct table *tiu_, + enum table_axis axis, int x, int y) { - assert (n_columns != NULL && n_rows != NULL); - *n_columns = t->nc; - *n_rows = t->nr; + struct table_unshared *tiu = table_unshared_cast (tiu_); + return table_get_rule (tiu->subtable, axis, x, y); } -static void tabi_cumulate (int cumtype, int start, int *end, int max, int *actual); - -/* Return the horizontal and vertical size of the entire table, - including headers, for the current output device, into HORIZ and - VERT. */ -static void -tabi_area (int *horiz, int *vert) -{ - assert (horiz != NULL && vert != NULL); - +static const struct table_class table_unshared_class = { - int w, c; - - for (c = t->l + 1, w = t->wl + t->wr + t->w[t->l]; - c < t->nc - t->r; c++) - w += t->w[c] + t->wrv[c]; - *horiz = w; - } - - { - int h, r; - for (r = t->t + 1, h = t->ht + t->hb + t->h[t->t]; - r < t->nr - t->b; r++) - h += t->h[r] + t->hrh[r]; - *vert = h; - } -} - -/* Return the column style for this table into STYLE. */ -static void -tabi_columns (int *style) -{ - assert (style != NULL); - *style = t->col_style; -} - -/* Return the number of header rows/columns on the left, right, top, - and bottom sides into HL, HR, HT, and HB, respectively. */ -static void -tabi_headers (int *hl, int *hr, int *ht, int *hb) -{ - assert (hl != NULL && hr != NULL && ht != NULL && hb != NULL); - *hl = t->l; - *hr = t->r; - *ht = t->t; - *hb = t->b; -} - -/* Determines the number of rows or columns (including appropriate - headers), depending on CUMTYPE, that will fit into the space - specified. Takes rows/columns starting at index START and attempts - to fill up available space MAX. Returns in END the index of the - last row/column plus one; returns in ACTUAL the actual amount of - space the selected rows/columns (including appropriate headers) - filled. */ -static void -tabi_cumulate (int cumtype, int start, int *end, int max, int *actual) -{ - int n; - int *d; - int *r; - int total; - - assert (end != NULL && (cumtype == SOM_ROWS || cumtype == SOM_COLUMNS)); - if (cumtype == SOM_ROWS) - { - assert (start >= 0 && start < t->nr); - n = t->nr - t->b; - d = &t->h[start]; - r = &t->hrh[start + 1]; - total = t->ht + t->hb; - } else { - assert (start >= 0 && start < t->nc); - n = t->nc - t->r; - d = &t->w[start]; - r = &t->wrv[start + 1]; - total = t->wl + t->wr; - } - - total += *d++; - if (total > max) - { - if (end) - *end = start; - if (actual) - *actual = 0; - return; - } - + table_unshared_destroy, + table_unshared_get_cell, + table_unshared_get_rule, + NULL, /* paste */ + NULL, /* select */ + }; + +struct table_string { - int x; - - for (x = start + 1; x < n; x++) - { - int amt = *d++ + *r++; - - total += amt; - if (total > max) - { - total -= amt; - break; - } - } - - if (end) - *end = x; - - if (actual) - *actual = total; - } -} + struct table table; + char *string; + unsigned int options; + }; -/* Return flags set for the current table into FLAGS. */ -static void -tabi_flags (unsigned *flags) -{ - assert (flags != NULL); - *flags = t->flags; -} +static const struct table_class table_string_class; -/* Returns true if the table will fit in the given page WIDTH, - false otherwise. */ -static bool -tabi_fits_width (int width) +/* Returns a table that contains a single cell, whose contents are S with + options OPTIONS (a combination of TAB_* values). */ +struct table * +table_from_string (unsigned int options, const char *s) { - int i; - - for (i = t->l; i < t->nc - t->r; i++) - if (t->wl + t->wr + t->w[i] > width) - return false; - - return true; + struct table_string *ts = xmalloc (sizeof *ts); + table_init (&ts->table, &table_string_class); + ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1; + ts->string = xstrdup (s); + ts->options = options; + return &ts->table; } -/* Returns true if the table will fit in the given page LENGTH, - false otherwise. */ -static bool -tabi_fits_length (int length) +static struct table_string * +table_string_cast (const struct table *table) { - int i; - - for (i = t->t; i < t->nr - t->b; i++) - if (t->ht + t->hb + t->h[i] > length) - return false; - - return true; + assert (table->class == &table_string_class); + return UP_CAST (table, struct table_string, table); } -/* Sets the number of header rows/columns on the left, right, top, - and bottom sides to HL, HR, HT, and HB, respectively. */ static void -tabi_set_headers (int hl, int hr, int ht, int hb) +table_string_destroy (struct table *ts_) { - t->l = hl; - t->r = hr; - t->t = ht; - t->b = hb; + struct table_string *ts = table_string_cast (ts_); + free (ts->string); + free (ts); } -/* Render title for current table, with major index X and minor index - Y. Y may be zero, or X and Y may be zero, but X should be nonzero - if Y is nonzero. */ static void -tabi_title (int x, int y) +table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED, + struct table_cell *cell) { - char buf[1024]; - char *cp; - - if (t->flags & SOMF_NO_TITLE) - return; - - cp = spprintf (buf, "%d.%d", table_num, subtable_num); - if (x && y) - cp = spprintf (cp, "(%d:%d)", x, y); - else if (x) - cp = spprintf (cp, "(%d)", x); - if (command_name != NULL) - cp = spprintf (cp, " %s", command_name); - cp = stpcpy (cp, ". "); - if (!ls_empty_p (&t->title)) - { - memcpy (cp, ls_c_str (&t->title), ls_length (&t->title)); - cp += ls_length (&t->title); - } - *cp = 0; - - { - struct outp_text text; - - text.options = OUTP_T_JUST_LEFT | OUTP_T_HORZ | OUTP_T_VERT; - ls_init (&text.s, buf, cp - buf); - text.h = d->width; - text.v = d->font_height; - text.x = 0; - text.y = d->cp_y; - d->class->text_draw (d, &text); - } + struct table_string *ts = table_string_cast (ts_); + cell->d[TABLE_HORZ][0] = 0; + cell->d[TABLE_HORZ][1] = 1; + cell->d[TABLE_VERT][0] = 0; + cell->d[TABLE_VERT][1] = 1; + cell->contents = ts->string; + cell->options = ts->options; + cell->destructor = NULL; } -static int render_strip (int x, int y, int r, int c1, int c2, int r1, int r2); -/* Draws the table region in rectangle (X1,Y1)-(X2,Y2), where column - X2 and row Y2 are not included in the rectangle, at the current - position on the current output device. Draws headers as well. */ -static void -tabi_render (int x1, int y1, int x2, int y2) +static int +table_string_get_rule (const struct table *ts UNUSED, + enum table_axis axis UNUSED, int x UNUSED, int y UNUSED) { - int i, y; - int ranges[3][2]; - - tab_hit++; - - y = d->cp_y; - if (!(t->flags & SOMF_NO_TITLE)) - y += d->font_height; - - /* Top headers. */ - ranges[0][0] = 0; - ranges[0][1] = t->t * 2 + 1; - - /* Requested rows. */ - ranges[1][0] = y1 * 2 + 1; - ranges[1][1] = y2 * 2; - - /* Bottom headers. */ - ranges[2][0] = (t->nr - t->b) * 2; - ranges[2][1] = t->nr * 2 + 1; - - for (i = 0; i < 3; i++) - { - int r; - - for (r = ranges[i][0]; r < ranges[i][1]; r++) - { - int x = d->cp_x; - x += render_strip (x, y, r, 0, t->l * 2 + 1, y1, y2); - x += render_strip (x, y, r, x1 * 2 + 1, x2 * 2, y1, y2); - x += render_strip (x, y, r, (t->nc - t->r) * 2, - t->nc * 2 + 1, y1, y2); - y += (r & 1) ? t->h[r / 2] : t->hrh[r / 2]; - } - } + return TAL_0; } -struct som_table_class tab_table_class = +static const struct table_class table_string_class = { - tabi_table, - tabi_driver, - - tabi_count, - tabi_area, - NULL, - NULL, - tabi_columns, - NULL, - tabi_headers, - NULL, - tabi_cumulate, - tabi_flags, - tabi_fits_width, - tabi_fits_length, - - NULL, - NULL, - tabi_set_headers, - - tabi_title, - tabi_render, + table_string_destroy, + table_string_get_cell, + table_string_get_rule, + NULL, /* paste */ + NULL, /* select */ }; - -/* Render contiguous strip consisting of columns C1...C2, exclusive, - on row R, at location (X,Y). Return width of the strip thus - rendered. - - Renders joined cells, even those outside the strip, within the - rendering region (C1,R1)-(C2,R2). - - For the purposes of counting rows and columns in this function - only, horizontal rules are considered rows and vertical rules are - considered columns. - - FIXME: Doesn't use r1? Huh? */ -static int -render_strip (int x, int y, int r, int c1, int c2, int r1 UNUSED, int r2) -{ - int x_origin = x; - - /* Horizontal rules. */ - if ((r & 1) == 0) - { - int hrh = t->hrh[r / 2]; - int c; - - for (c = c1; c < c2; c++) - { - if (c & 1) - { - int style = t->rh[(c / 2) + (r / 2 * t->cf)]; - - if (style != TAL_0) - { - const struct color clr = {0, 0, 0, 0}; - struct rect rct; - - rct.x1 = x; - rct.y1 = y; - rct.x2 = x + t->w[c / 2]; - rct.y2 = y + hrh; - d->class->line_horz (d, &rct, &clr, style); - } - x += t->w[c / 2]; - } else { - const struct color clr = {0, 0, 0, 0}; - struct rect rct; - struct outp_styles s; - - rct.x1 = x; - rct.y1 = y; - rct.x2 = x + t->wrv[c / 2]; - rct.y2 = y + hrh; - - s.t = r > 0 ? t->rv[(c / 2) + (t->cf + 1) * (r / 2 - 1)] : 0; - s.b = r < 2 * t->nr ? t->rv[(c / 2) + (t->cf + 1) * (r / 2)] : 0; - s.l = c > 0 ? t->rh[(c / 2 - 1) + t->cf * (r / 2)] : 0; - s.r = c < 2 * t->nc ? t->rh[(c / 2) + t->cf * (r / 2)] : 0; - - if (s.t | s.b | s.l | s.r) - d->class->line_intersection (d, &rct, &clr, &s); - - x += t->wrv[c / 2]; - } - } - } else { - int c; - - for (c = c1; c < c2; c++) - { - if (c & 1) - { - const int index = (c / 2) + (r / 2 * t->cf); - - if (!(t->ct[index] & TAB_JOIN)) - { - struct outp_text text; - - text.options = ((t->ct[index] & OUTP_T_JUST_MASK) - | OUTP_T_HORZ | OUTP_T_VERT); - if ((t->ct[index] & TAB_EMPTY) == 0) - { - text.s = t->cc[index]; - assert (!ls_null_p (&text.s)); - text.h = t->w[c / 2]; - text.v = t->h[r / 2]; - text.x = x; - text.y = y; - d->class->text_draw (d, &text); - } - } else { - struct tab_joined_cell *j = - (struct tab_joined_cell *) ls_c_str (&t->cc[index]); - - if (j->hit != tab_hit) - { - j->hit = tab_hit; - - if (j->x1 == c / 2 && j->y1 == r / 2) - { - struct outp_text text; - - text.options = ((t->ct[index] & OUTP_T_JUST_MASK) - | OUTP_T_HORZ | OUTP_T_VERT); - text.s = j->contents; - text.x = x; - text.y = y; - - { - int c; - - for (c = j->x1, text.h = -t->wrv[j->x2]; - c < j->x2 && c < c2 / 2; c++) - text.h += t->w[c] + t->wrv[c + 1]; - } - - { - int r; - - for (r = j->y1, text.v = -t->hrh[j->y2]; - r < j->y2 && r < r2 / 2; r++) - text.v += t->h[r] + t->hrh[r + 1]; - } - d->class->text_draw (d, &text); - } - } - } - x += t->w[c / 2]; - } else { - int style = t->rv[(c / 2) + (r / 2 * (t->cf + 1))]; - - if (style != TAL_0) - { - const struct color clr = {0, 0, 0, 0}; - struct rect rct; - - rct.x1 = x; - rct.y1 = y; - rct.x2 = x + t->wrv[c / 2]; - rct.y2 = y + t->h[r / 2]; - d->class->line_vert (d, &rct, &clr, style); - } - x += t->wrv[c / 2]; - } - } - } - - return x - x_origin; -} - -/* Sets COMMAND_NAME as the name of the current command, - for embedding in output. */ -void -tab_set_command_name (const char *command_name_) -{ - free (command_name); - command_name = command_name_ ? xstrdup (command_name_) : NULL; -}