-static bool
-ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
- struct ctables *ct, struct ctables_table *t)
-{
- if (!lex_match_id (lexer, "VARIABLES"))
- return false;
- lex_match (lexer, T_EQUALS);
-
- struct variable **vars;
- size_t n_vars;
- if (!parse_variables (lexer, dict, &vars, &n_vars, PV_NO_SCRATCH))
- return false;
-
- const struct fmt_spec *common_format = var_get_print_format (vars[0]);
- for (size_t i = 1; i < n_vars; i++)
- {
- const struct fmt_spec *f = var_get_print_format (vars[i]);
- if (f->type != common_format->type)
- {
- common_format = NULL;
- break;
- }
- }
- bool parse_strings
- = (common_format
- && (fmt_get_category (common_format->type)
- & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)));
-
- struct ctables_categories *c = xmalloc (sizeof *c);
- *c = (struct ctables_categories) { .n_refs = n_vars, .show_empty = true };
- for (size_t i = 0; i < n_vars; i++)
- {
- struct ctables_categories **cp
- = &t->categories[var_get_dict_index (vars[i])];
- ctables_categories_unref (*cp);
- *cp = c;
- }
-
- size_t allocated_cats = 0;
- int cats_start_ofs = -1;
- int cats_end_ofs = -1;
- if (lex_match (lexer, T_LBRACK))
- {
- cats_start_ofs = lex_ofs (lexer);
- do
- {
- if (c->n_cats >= allocated_cats)
- c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
-
- int start_ofs = lex_ofs (lexer);
- struct ctables_category *cat = &c->cats[c->n_cats];
- if (!ctables_table_parse_explicit_category (lexer, dict, ct, cat))
- goto error;
- cat->location = lex_ofs_location (lexer, start_ofs, lex_ofs (lexer) - 1);
- c->n_cats++;
-
- lex_match (lexer, T_COMMA);
- }
- while (!lex_match (lexer, T_RBRACK));
- cats_end_ofs = lex_ofs (lexer) - 1;
- }
-
- struct ctables_category cat = {
- .type = CCT_VALUE,
- .include_missing = false,
- .sort_ascending = true,
- };
- bool show_totals = false;
- char *total_label = NULL;
- bool totals_before = false;
- while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
- {
- if (!c->n_cats && lex_match_id (lexer, "ORDER"))
- {
- lex_match (lexer, T_EQUALS);
- if (lex_match_id (lexer, "A"))
- cat.sort_ascending = true;
- else if (lex_match_id (lexer, "D"))
- cat.sort_ascending = false;
- else
- {
- lex_error_expecting (lexer, "A", "D");
- goto error;
- }
- }
- else if (!c->n_cats && lex_match_id (lexer, "KEY"))
- {
- lex_match (lexer, T_EQUALS);
- if (lex_match_id (lexer, "VALUE"))
- cat.type = CCT_VALUE;
- else if (lex_match_id (lexer, "LABEL"))
- cat.type = CCT_LABEL;
- else
- {
- cat.type = CCT_FUNCTION;
- if (!parse_ctables_summary_function (lexer, &cat.sort_function,
- &cat.weighted, &cat.area))
- goto error;
-
- if (lex_match (lexer, T_LPAREN))
- {
- cat.sort_var = parse_variable (lexer, dict);
- if (!cat.sort_var)
- goto error;
-
- if (cat.sort_function == CTSF_PTILE)
- {
- lex_match (lexer, T_COMMA);
- if (!lex_force_num_range_closed (lexer, "PTILE", 0, 100))
- goto error;
- cat.percentile = lex_number (lexer);
- lex_get (lexer);
- }
-
- if (!lex_force_match (lexer, T_RPAREN))
- goto error;
- }
- else if (ctables_function_availability (cat.sort_function)
- == CTFA_SCALE)
- {
- bool UNUSED b = lex_force_match (lexer, T_LPAREN);
- goto error;
- }
- }
- }
- else if (!c->n_cats && lex_match_id (lexer, "MISSING"))
- {
- lex_match (lexer, T_EQUALS);
- if (lex_match_id (lexer, "INCLUDE"))
- cat.include_missing = true;
- else if (lex_match_id (lexer, "EXCLUDE"))
- cat.include_missing = false;
- else
- {
- lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
- goto error;
- }
- }
- else if (lex_match_id (lexer, "TOTAL"))
- {
- lex_match (lexer, T_EQUALS);
- if (!parse_bool (lexer, &show_totals))
- goto error;
- }
- else if (lex_match_id (lexer, "LABEL"))
- {
- lex_match (lexer, T_EQUALS);
- if (!lex_force_string (lexer))
- goto error;
- free (total_label);
- total_label = ss_xstrdup (lex_tokss (lexer));
- lex_get (lexer);
- }
- else if (lex_match_id (lexer, "POSITION"))
- {
- lex_match (lexer, T_EQUALS);
- if (lex_match_id (lexer, "BEFORE"))
- totals_before = true;
- else if (lex_match_id (lexer, "AFTER"))
- totals_before = false;
- else
- {
- lex_error_expecting (lexer, "BEFORE", "AFTER");
- goto error;
- }
- }
- else if (lex_match_id (lexer, "EMPTY"))
- {
- lex_match (lexer, T_EQUALS);
- if (lex_match_id (lexer, "INCLUDE"))
- c->show_empty = true;
- else if (lex_match_id (lexer, "EXCLUDE"))
- c->show_empty = false;
- else
- {
- lex_error_expecting (lexer, "INCLUDE", "EXCLUDE");
- goto error;
- }
- }
- else
- {
- if (!c->n_cats)
- lex_error_expecting (lexer, "ORDER", "KEY", "MISSING",
- "TOTAL", "LABEL", "POSITION", "EMPTY");
- else
- lex_error_expecting (lexer, "TOTAL", "LABEL", "POSITION", "EMPTY");
- goto error;
- }
- }
-
- if (!c->n_cats)
- {
- if (c->n_cats >= allocated_cats)
- c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
- c->cats[c->n_cats++] = cat;
- }
-
- if (show_totals)
- {
- if (c->n_cats >= allocated_cats)
- c->cats = x2nrealloc (c->cats, &allocated_cats, sizeof *c->cats);
-
- struct ctables_category *totals;
- if (totals_before)
- {
- insert_element (c->cats, c->n_cats, sizeof *c->cats, 0);
- totals = &c->cats[0];
- }
- else
- totals = &c->cats[c->n_cats];
- c->n_cats++;
-
- *totals = (struct ctables_category) {
- .type = CCT_TOTAL,
- .total_label = total_label ? total_label : xstrdup (_("Total")),
- };
- }