#include "data/dataset.h"
#include "data/dictionary.h"
+#include "data/mrset.h"
#include "language/command.h"
#include "language/lexer/format-parser.h"
#include "language/lexer/lexer.h"
#include "language/lexer/variable-parser.h"
+#include "libpspp/assertion.h"
#include "libpspp/hmap.h"
#include "libpspp/message.h"
#include "output/pivot-table.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
+#define N_(msgid) (msgid)
enum ctables_vlabel
{
format. Otherwise, this string is displayed. */
char *missing;
+ /* Indexed by variable dictionary index. */
enum ctables_vlabel *vlabels;
bool mrsets_count_duplicates; /* MRSETS. */
static void ctables_axis_destroy (struct ctables_axis *);
+enum ctables_format
+ {
+ CTF_COUNT,
+ CTF_PERCENT,
+ CTF_GENERAL
+ };
+
#define SUMMARIES \
/* All variables. */ \
- S(CTSF_COUNT, "COUNT") \
- S(CTSF_ECOUNT, "ECOUNT") \
- S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT") \
- S(CTSF_COLPCT_COUNT, "COLPCT.COUNT") \
- S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT") \
- S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT") \
- S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT") \
- S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT") \
- S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT") \
- S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN") \
- S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN") \
- S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN") \
- S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN") \
- S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN") \
- S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN") \
- S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN") \
- S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN") \
- S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN") \
- S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN") \
- S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN") \
- S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN") \
- S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN") \
- S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN") \
+ S(CTSF_COUNT, "COUNT", N_("Count"), CTF_COUNT) \
+ S(CTSF_ECOUNT, "ECOUNT", N_("Adjusted Count"), CTF_COUNT) \
+ S(CTSF_ROWPCT_COUNT, "ROWPCT.COUNT", N_("Row %"), CTF_PERCENT) \
+ S(CTSF_COLPCT_COUNT, "COLPCT.COUNT", N_("Column %"), CTF_PERCENT) \
+ S(CTSF_TABLEPCT_COUNT, "TABLEPCT.COUNT", N_("Table %"), CTF_PERCENT) \
+ S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT.COUNT", N_("Subtable %"), CTF_PERCENT) \
+ S(CTSF_LAYERPCT_COUNT, "LAYERPCT.COUNT", N_("Layer %"), CTF_PERCENT) \
+ S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT.COUNT", N_("Layer Row %"), CTF_PERCENT) \
+ S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT.COUNT", N_("Layer Column %"), CTF_PERCENT) \
+ S(CTSF_ROWPCT_VALIDN, "ROWPCT.VALIDN", N_("Row Valid N %"), CTF_PERCENT) \
+ S(CTSF_COLPCT_VALIDN, "COLPCT.VALIDN", N_("Column Valid N %"), CTF_PERCENT) \
+ S(CTSF_TABLEPCT_VALIDN, "TABLEPCT.VALIDN", N_("Table Valid N %"), CTF_PERCENT) \
+ S(CTSF_SUBTABLEPCT_VALIDN, "SUBTABLEPCT.VALIDN", N_("Subtable Valid N %"), CTF_PERCENT) \
+ S(CTSF_LAYERPCT_VALIDN, "LAYERPCT.VALIDN", N_("Layer Valid N %"), CTF_PERCENT) \
+ S(CTSF_LAYERROWPCT_VALIDN, "LAYERROWPCT.VALIDN", N_("Layer Row Valid N %"), CTF_PERCENT) \
+ S(CTSF_LAYERCOLPCT_VALIDN, "LAYERCOLPCT.VALIDN", N_("Layer Column Valid N %"), CTF_PERCENT) \
+ S(CTSF_ROWPCT_TOTALN, "ROWPCT.TOTALN", N_("Row Total N %"), CTF_PERCENT) \
+ S(CTSF_COLPCT_TOTALN, "COLPCT.TOTALN", N_("Column Total N %"), CTF_PERCENT) \
+ S(CTSF_TABLEPCT_TOTALN, "TABLEPCT.TOTALN", N_("Table Total N %"), CTF_PERCENT) \
+ S(CTSF_SUBTABLEPCT_TOTALN, "SUBTABLEPCT.TOTALN", N_("Subtable Total N %"), CTF_PERCENT) \
+ S(CTSF_LAYERPCT_TOTALN, "LAYERPCT.TOTALN", N_("Layer Total N %"), CTF_PERCENT) \
+ S(CTSF_LAYERROWPCT_TOTALN, "LAYERROWPCT.TOTALN", N_("Layer Row Total N %"), CTF_PERCENT) \
+ S(CTSF_LAYERCOLPCT_TOTALN, "LAYERCOLPCT.TOTALN", N_("Layer Column Total N %"), CTF_PERCENT) \
\
/* Scale variables, totals, and subtotals. */ \
- S(CTSF_MAXIMUM, "!MAXIMUM") \
- S(CTSF_MEAN, "!MEAN") \
- S(CTSF_MEDIAN, "!MEDIAN") \
- S(CTSF_MINIMUM, "!MINIMUM") \
- S(CTSF_MISSING, "!MISSING") \
- S(CTSF_MODE, "!MODE") \
- S(CTSF_PTILE, "!PTILE") \
- S(CTSF_RANGE, "!RANGE") \
- S(CTSF_SEMAN, "!SEMAN") \
- S(CTSF_STDDEV, "!STDDEV") \
- S(CTSF_SUM, "!SUM") \
- S(CSTF_TOTALN, "!TOTALN") \
- S(CTSF_ETOTALN, "!ETOTALN") \
- S(CTSF_VALIDN, "!VALIDN") \
- S(CTSF_EVALIDN, "!EVALIDN") \
- S(CTSF_VARIANCE, "!VARIANCE") \
- S(CTSF_ROWPCT_SUM, "ROWPCT.SUM") \
- S(CTSF_COLPCT_SUM, "COLPCT.SUM") \
- S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM") \
- S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM") \
- S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM") \
- S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM") \
- S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM") \
+ S(CTSF_MAXIMUM, "MAXIMUM", N_("Maximum"), CTF_GENERAL) \
+ S(CTSF_MEAN, "MEAN", N_("Mean"), CTF_GENERAL) \
+ S(CTSF_MEDIAN, "MEDIAN", N_("Median"), CTF_GENERAL) \
+ S(CTSF_MINIMUM, "MINIMUM", N_("Minimum"), CTF_GENERAL) \
+ S(CTSF_MISSING, "MISSING", N_("Missing"), CTF_GENERAL) \
+ S(CTSF_MODE, "MODE", N_("Mode"), CTF_GENERAL) \
+ S(CTSF_PTILE, "PTILE", N_("Percentile"), CTF_GENERAL) \
+ S(CTSF_RANGE, "RANGE", N_("Range"), CTF_GENERAL) \
+ S(CTSF_SEMEAN, "SEMEAN", N_("Std Error of Mean"), CTF_GENERAL) \
+ S(CTSF_STDDEV, "STDDEV", N_("Std Deviation"), CTF_GENERAL) \
+ S(CTSF_SUM, "SUM", N_("Sum"), CTF_GENERAL) \
+ S(CSTF_TOTALN, "TOTALN", N_("Total N"), CTF_COUNT) \
+ S(CTSF_ETOTALN, "ETOTALN", N_("Adjusted Total N"), CTF_COUNT) \
+ S(CTSF_VALIDN, "VALIDN", N_("Valid N"), CTF_COUNT) \
+ S(CTSF_EVALIDN, "EVALIDN", N_("Adjusted Valid N"), CTF_COUNT) \
+ S(CTSF_VARIANCE, "VARIANCE", N_("Variance"), CTF_GENERAL) \
+ S(CTSF_ROWPCT_SUM, "ROWPCT.SUM", N_("Row Sum %"), CTF_PERCENT) \
+ S(CTSF_COLPCT_SUM, "COLPCT.SUM", N_("Column Sum %"), CTF_PERCENT) \
+ S(CTSF_TABLEPCT_SUM, "TABLEPCT.SUM", N_("Table Sum %"), CTF_PERCENT) \
+ S(CTSF_SUBTABLEPCT_SUM, "SUBTABLEPCT.SUM", N_("Subtable Sum %"), CTF_PERCENT) \
+ S(CTSF_LAYERPCT_SUM, "LAYERPCT.SUM", N_("Layer Sum %"), CTF_PERCENT) \
+ S(CTSF_LAYERROWPCT_SUM, "LAYERROWPCT.SUM", N_("Layer Row Sum %"), CTF_PERCENT) \
+ S(CTSF_LAYERCOLPCT_SUM, "LAYERCOLPCT.SUM", N_("Layer Column Sum %"), CTF_PERCENT) \
\
/* Multiple response sets. */ \
- S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES") \
- S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES") \
- S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES") \
- S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES") \
- S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES") \
- S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES") \
- S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES") \
- S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT") \
- S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT") \
- S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT") \
- S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT") \
- S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT") \
- S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT") \
- S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT") \
- S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES") \
- S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES") \
- S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES") \
- S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES") \
- S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES") \
- S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES") \
- S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.COUNT.RESPONSES")
+ S(CTSF_RESPONSES, "RESPONSES", N_("Responses"), CTF_COUNT) \
+ S(CTSF_ROWPCT_RESPONSES, "ROWPCT.RESPONSES", N_("Row Responses %"), CTF_PERCENT) \
+ S(CTSF_COLPCT_RESPONSES, "COLPCT.RESPONSES", N_("Column Responses %"), CTF_PERCENT) \
+ S(CTSF_TABLEPCT_RESPONSES, "TABLEPCT.RESPONSES", N_("Table Responses %"), CTF_PERCENT) \
+ S(CTSF_SUBTABLEPCT_RESPONSES, "SUBTABLEPCT.RESPONSES", N_("Subtable Responses %"), CTF_PERCENT) \
+ S(CTSF_LAYERPCT_RESPONSES, "LAYERPCT.RESPONSES", N_("Layer Responses %"), CTF_PERCENT) \
+ S(CTSF_LAYERROWPCT_RESPONSES, "LAYERROWPCT.RESPONSES", N_("Layer Row Responses %"), CTF_PERCENT) \
+ S(CTSF_LAYERCOLPCT_RESPONSES, "LAYERCOLPCT.RESPONSES", N_("Layer Column Responses %"), CTF_PERCENT) \
+ S(CTSF_ROWPCT_RESPONSES_COUNT, "ROWPCT.RESPONSES.COUNT", N_("Row Responses % (Base: Count)"), CTF_PERCENT) \
+ S(CTSF_COLPCT_RESPONSES_COUNT, "COLPCT.RESPONSES.COUNT", N_("Column Responses % (Base: Count)"), CTF_PERCENT) \
+ S(CTSF_TABLEPCT_RESPONSES_COUNT, "TABLEPCT.RESPONSES.COUNT", N_("Table Responses % (Base: Count)"), CTF_PERCENT) \
+ S(CTSF_SUBTABLEPCT_RESPONSES_COUNT, "SUBTABLEPCT.RESPONSES.COUNT", N_("Subtable Responses % (Base: Count)"), CTF_PERCENT) \
+ S(CTSF_LAYERPCT_RESPONSES_COUNT, "LAYERPCT.RESPONSES.COUNT", N_("Layer Responses % (Base: Count)"), CTF_PERCENT) \
+ S(CTSF_LAYERROWPCT_RESPONSES_COUNT, "LAYERROWPCT.RESPONSES.COUNT", N_("Layer Row Responses % (Base: Count)"), CTF_PERCENT) \
+ S(CTSF_LAYERCOLPCT_RESPONSES_COUNT, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Responses % (Base: Count)"), CTF_PERCENT) \
+ S(CTSF_ROWPCT_COUNT_RESPONSES, "ROWPCT.COUNT.RESPONSES", N_("Row Count % (Base: Responses)"), CTF_PERCENT) \
+ S(CTSF_COLPCT_COUNT_RESPONSES, "COLPCT.COUNT.RESPONSES", N_("Column Count % (Base: Responses)"), CTF_PERCENT) \
+ S(CTSF_TABLEPCT_COUNT_RESPONSES, "TABLEPCT.COUNT.RESPONSES", N_("Table Count % (Base: Responses)"), CTF_PERCENT) \
+ S(CTSF_SUBTABLEPCT_COUNT_RESPONSES, "SUBTABLEPCT.COUNT.RESPONSES", N_("Subtable Count % (Base: Responses)"), CTF_PERCENT) \
+ S(CTSF_LAYERPCT_COUNT_RESPONSES, "LAYERPCT.COUNT.RESPONSES", N_("Layer Count % (Base: Responses)"), CTF_PERCENT) \
+ S(CTSF_LAYERROWPCT_COUNT_RESPONSES, "LAYERROWPCT.COUNT.RESPONSES", N_("Layer Row Count % (Base: Responses)"), CTF_PERCENT) \
+ S(CTSF_LAYERCOLPCT_COUNT_RESPONSES, "LAYERCOLPCT.RESPONSES.COUNT", N_("Layer Column Count % (Base: Responses)"), CTF_PERCENT)
enum ctables_summary_function
{
-#define S(ENUM, NAME) ENUM,
+#define S(ENUM, NAME, LABEL, FORMAT) ENUM,
SUMMARIES
#undef S
};
enum {
-#define S(ENUM, NAME) +1
+#define S(ENUM, NAME, LABEL, FORMAT) +1
N_CTSF_FUNCTIONS = SUMMARIES
#undef S
};
struct ctables_summary
{
enum ctables_summary_function function;
+ double percentile; /* CTSF_PTILE only. */
char *label;
struct fmt_spec format; /* XXX extra CTABLES formats */
};
struct substring name;
};
static struct pair names[] = {
-#define S(ENUM, NAME) { ENUM, SS_LITERAL_INITIALIZER (NAME) },
+#define S(ENUM, NAME, LABEL, FORMAT) { ENUM, SS_LITERAL_INITIALIZER (NAME) },
SUMMARIES
/* The .COUNT suffix may be omitted. */
- S(CTSF_ROWPCT_COUNT, "ROWPCT")
- S(CTSF_COLPCT_COUNT, "COLPCT")
- S(CTSF_TABLEPCT_COUNT, "TABLEPCT")
- S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT")
- S(CTSF_LAYERPCT_COUNT, "LAYERPCT")
- S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT")
- S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT")
+ S(CTSF_ROWPCT_COUNT, "ROWPCT", _, _)
+ S(CTSF_COLPCT_COUNT, "COLPCT", _, _)
+ S(CTSF_TABLEPCT_COUNT, "TABLEPCT", _, _)
+ S(CTSF_SUBTABLEPCT_COUNT, "SUBTABLEPCT", _, _)
+ S(CTSF_LAYERPCT_COUNT, "LAYERPCT", _, _)
+ S(CTSF_LAYERROWPCT_COUNT, "LAYERROWPCT", _, _)
+ S(CTSF_LAYERCOLPCT_COUNT, "LAYERCOLPCT", _, _)
#undef S
};
};
static struct ctables_summary *
-add_summary (struct ctables_axis *axis, size_t *allocated_summaries)
+add_summary (struct ctables_axis *axis, enum ctables_summary_function function,
+ double percentile, size_t *allocated_summaries)
{
if (axis->n_summaries >= *allocated_summaries)
axis->summaries = x2nrealloc (axis->summaries, allocated_summaries,
sizeof *axis->summaries);
+ static const char *default_labels[] = {
+#define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = LABEL,
+ SUMMARIES
+#undef S
+ };
+ char *label = (function == CTSF_PTILE
+ ? xasprintf (_("Percentile %.2f"), percentile)
+ : xstrdup (gettext (default_labels[function])));
+
+ static const enum ctables_format default_formats[] = {
+#define S(ENUM, NAME, LABEL, FORMAT) [ENUM] = FORMAT,
+ SUMMARIES
+#undef S
+ };
+ struct fmt_spec format;
+ switch (default_formats[function])
+ {
+ case CTF_COUNT:
+ format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
+ break;
+
+ case CTF_PERCENT:
+ format = (struct fmt_spec) { .type = FMT_PCT, .w = 40, .d = 1 };
+ break;
+
+ case CTF_GENERAL:
+ format = *(axis->op == CTAO_VAR
+ ? var_get_print_format (axis->var)
+ : var_get_print_format (axis->mrset->vars[0]));
+ break;
+
+ default:
+ NOT_REACHED ();
+ }
+
struct ctables_summary *s = &axis->summaries[axis->n_summaries++];
- *s = (struct ctables_summary) { .function = CTSF_COUNT };
+ *s = (struct ctables_summary) {
+ .function = function,
+ .percentile = percentile,
+ .label = label,
+ .format = format,
+ };
return s;
}
{
do
{
- struct ctables_summary *s = add_summary (axis, &allocated_summaries);
- if (!parse_ctables_summary_function (ctx->lexer, &s->function))
+ enum ctables_summary_function function;
+ if (!parse_ctables_summary_function (ctx->lexer, &function))
goto error;
+
+ double percentile = 0;
+ if (function == CTSF_PTILE)
+ {
+ if (!lex_force_num_range_closed (ctx->lexer, "PTILE", 0, 100))
+ goto error;
+ percentile = lex_number (ctx->lexer);
+ lex_get (ctx->lexer);
+ }
+
+ struct ctables_summary *s = add_summary (axis, function, percentile,
+ &allocated_summaries);
if (lex_is_string (ctx->lexer))
{
+ free (s->label);
s->label = ss_xstrdup (lex_tokss (ctx->lexer));
lex_get (ctx->lexer);
}
while (!lex_match (ctx->lexer, T_RBRACK));
}
else
- {
- struct ctables_summary *s = add_summary (axis, &allocated_summaries);
- s->function = axis->scale ? CTSF_MEAN : CTSF_COUNT;
- s->label = xstrdup (axis->scale ? _("Mean") : _("Count"));
- s->format = (struct fmt_spec) { .type = FMT_F, .w = 40 };
- }
+ add_summary (axis, axis->scale ? CTSF_MEAN : CTSF_COUNT, 0,
+ &allocated_summaries);
return axis;
error:
if (!lhs)
return NULL;
- while (lex_match (ctx->lexer, T_PLUS))
+ while (lex_match (ctx->lexer, T_GT))
{
struct ctables_axis *rhs = ctables_axis_parse_primary (ctx);
if (!rhs)
return t->axes[a] != NULL;
}
+static void
+ctables_chisq_destroy (struct ctables_chisq *chisq)
+{
+ free (chisq);
+}
+
+static void
+ctables_pairwise_destroy (struct ctables_pairwise *pairwise)
+{
+ free (pairwise);
+}
+
+static void
+ctables_table_uninit (struct ctables_table *t)
+{
+ if (!t)
+ return;
+
+ ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
+ ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
+ ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
+ free (t->caption);
+ free (t->corner);
+ free (t->title);
+ ctables_chisq_destroy (t->chisq);
+ ctables_pairwise_destroy (t->pairwise);
+}
+
+static void
+ctables_destroy (struct ctables *ct)
+{
+ if (!ct)
+ return;
+
+ pivot_table_look_unref (ct->look);
+ free (ct->zero);
+ free (ct->missing);
+ free (ct->vlabels);
+ for (size_t i = 0; i < ct->n_tables; i++)
+ ctables_table_uninit (&ct->tables[i]);
+ free (ct->tables);
+ free (ct);
+}
+
int
cmd_ctables (struct lexer *lexer, struct dataset *ds)
{
size_t n_vars = dict_get_n_vars (dataset_dict (ds));
enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
- for (size_t i = 0; n_vars; i++)
+ for (size_t i = 0; i < n_vars; i++)
vlabels[i] = CTVL_DEFAULT;
struct ctables *ct = xmalloc (sizeof *ct);
goto error;
}
}
+ if (lex_token (lexer) == T_ENDCMD)
+ break;
if (!lex_force_match (lexer, T_SLASH))
- goto error;
+ break;
/* XXX Validate axes. */
while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
}
}
while (lex_token (lexer) != T_ENDCMD);
-
+ ctables_destroy (ct);
return CMD_SUCCESS;
error:
- /* XXX free */
+ ctables_destroy (ct);
return CMD_FAILURE;
}