work on memory leaks
[pspp] / src / language / stats / ctables.c
index b2bf14b39992e4f24a02aa55dd29d4af1002218a..4b4d58d8a9add8c1e79a41b909c28449ad8b67ba 100644 (file)
@@ -445,6 +445,8 @@ struct ctables_stack
     size_t n;
   };
 
+static void ctables_stack_uninit (struct ctables_stack *);
+
 struct ctables_value
   {
     struct hmap_node node;
@@ -470,6 +472,8 @@ struct ctables_section
     struct hmap domains[N_CTDTS]; /* Contains "struct ctables_domain"s. */
   };
 
+static void ctables_section_uninit (struct ctables_section *);
+
 struct ctables_table
   {
     struct ctables *ctables;
@@ -795,7 +799,7 @@ enum ctables_function_availability
   {
     CTFA_ALL,                /* Any variables. */
     CTFA_SCALE,              /* Only scale variables, totals, and subtotals. */
-    CTFA_MRSETS,             /* Only multiple-response sets */
+    //CTFA_MRSETS,             /* Only multiple-response sets */
   };
 
 struct ctables_summary_spec
@@ -1165,16 +1169,17 @@ add_summary_spec (struct ctables_axis *axis,
       const char *var_name = var_get_name (axis->var);
       switch (ctables_function_availability (function))
         {
+#if 0
         case CTFA_MRSETS:
           msg_at (SE, loc, _("Summary function %s applies only to multiple "
                              "response sets."), function_name);
           msg_at (SN, axis->loc, _("'%s' is not a multiple response set."),
                   var_name);
           return false;
+#endif
 
         case CTFA_SCALE:
-#if 0
-          if (!axis->scale)
+          if (!axis->scale && sv != CSV_TOTAL)
             {
               msg_at (SE, loc,
                       _("Summary function %s applies only to scale variables."),
@@ -1183,7 +1188,6 @@ add_summary_spec (struct ctables_axis *axis,
                       var_name);
               return false;
             }
-#endif
           break;
 
         case CTFA_ALL:
@@ -1245,7 +1249,6 @@ ctables_axis_parse_primary (struct ctables_axis_parse_ctx *ctx)
   struct ctables_axis *axis = xmalloc (sizeof *axis);
   *axis = (struct ctables_axis) { .op = CTAO_VAR, .var = var };
 
-  /* XXX should figure out default measures by reading data */
   axis->scale = (lex_match_phrase (ctx->lexer, "[S]") ? true
                  : lex_match_phrase (ctx->lexer, "[C]") ? false
                  : var_get_measure (var) == MEASURE_SCALE);
@@ -1293,15 +1296,17 @@ parse_ctables_format_specifier (struct lexer *lexer, struct fmt_spec *format,
               && fmt_check_type_compat (format, VAL_NUMERIC));
     }
 
+  lex_get (lexer);
   if (format->w < 2)
     {
-      msg (SE, _("Output format %s requires width 2 or greater."), type);
+      lex_next_error (lexer, -1, -1,
+                      _("Output format %s requires width 2 or greater."), type);
       return false;
     }
   else if (format->d > format->w - 1)
     {
-      msg (SE, _("Output format %s requires width greater than decimals."),
-           type);
+      lex_next_error (lexer, -1, -1, _("Output format %s requires width "
+                                       "greater than decimals."), type);
       return false;
     }
   else
@@ -1535,13 +1540,20 @@ ctables_table_destroy (struct ctables_table *t)
   if (!t)
     return;
 
+  for (size_t i = 0; i < t->n_sections; i++)
+    ctables_section_uninit (&t->sections[i]);
+  free (t->sections);
+
   for (size_t i = 0; i < t->n_categories; i++)
     ctables_categories_unref (t->categories[i]);
   free (t->categories);
 
-  ctables_axis_destroy (t->axes[PIVOT_AXIS_COLUMN]);
-  ctables_axis_destroy (t->axes[PIVOT_AXIS_ROW]);
-  ctables_axis_destroy (t->axes[PIVOT_AXIS_LAYER]);
+  for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
+    {
+      ctables_axis_destroy (t->axes[a]);
+      ctables_stack_uninit (&t->stacks[a]);
+    }
+
   free (t->caption);
   free (t->corner);
   free (t->title);
@@ -1556,6 +1568,7 @@ ctables_destroy (struct ctables *ct)
   if (!ct)
     return;
 
+  fmt_settings_uninit (&ct->ctables_formats);
   pivot_table_look_unref (ct->look);
   free (ct->zero);
   free (ct->missing);
@@ -1877,6 +1890,7 @@ ctables_recursive_check_postcompute (struct dictionary *dict,
     case CTPO_CAT_NUMBER:
     case CTPO_CAT_STRING:
     case CTPO_CAT_NRANGE:
+    case CTPO_CAT_SRANGE:
     case CTPO_CAT_MISSING:
     case CTPO_CAT_OTHERNM:
     case CTPO_CAT_SUBTOTAL:
@@ -1897,11 +1911,13 @@ ctables_recursive_check_postcompute (struct dictionary *dict,
                             ngettext ("These categories include %zu instance "
                                       "of SUBTOTAL or HSUBTOTAL, so references "
                                       "from computed categories must refer to "
-                                      "subtotals by position.",
+                                      "subtotals by position, "
+                                      "e.g. SUBTOTAL[1].",
                                       "These categories include %zu instances "
                                       "of SUBTOTAL or HSUBTOTAL, so references "
                                       "from computed categories must refer to "
-                                      "subtotals by position.",
+                                      "subtotals by position, "
+                                      "e.g. SUBTOTAL[1].",
                                       n_subtotals),
                             n_subtotals);
                     msg_at (SN, e->location,
@@ -1915,9 +1931,17 @@ ctables_recursive_check_postcompute (struct dictionary *dict,
                       "in the category list."),
                     pc_cat->pc->name);
             msg_at (SN, e->location, _("This is the missing category."));
-            msg_at (SN, cats_location,
-                    _("To fix the problem, add the missing category to the "
-                      "list of categories here."));
+            if (e->op == CTPO_CAT_SUBTOTAL)
+              msg_at (SN, cats_location,
+                      _("To fix the problem, add subtotals to the "
+                        "list of categories here."));
+            else if (e->op == CTPO_CAT_TOTAL)
+              msg (SN, _("To fix the problem, add TOTAL=YES to the variable's "
+                         "CATEGORIES specification."));
+            else
+              msg_at (SN, cats_location,
+                      _("To fix the problem, add the missing category to the "
+                        "list of categories here."));
             return false;
           }
         if (pc_cat->pc->hide_source_cats)
@@ -1939,10 +1963,9 @@ ctables_recursive_check_postcompute (struct dictionary *dict,
               dict, e->subs[i], pc_cat, cats, cats_location))
           return false;
       return true;
-
-    default:
-      NOT_REACHED ();
     }
+
+  NOT_REACHED ();
 }
 
 static bool
@@ -2001,9 +2024,11 @@ ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
     }
 
   size_t allocated_cats = 0;
+  int cats_start_ofs = -1;
+  int cats_end_ofs = -1;
   if (lex_match (lexer, T_LBRACK))
     {
-      int cats_start_ofs = lex_ofs (lexer);
+      cats_start_ofs = lex_ofs (lexer);
       do
         {
           if (c->n_cats >= allocated_cats)
@@ -2019,94 +2044,7 @@ ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
           lex_match (lexer, T_COMMA);
         }
       while (!lex_match (lexer, T_RBRACK));
-
-      struct msg_location *cats_location
-        = lex_ofs_location (lexer, cats_start_ofs, lex_ofs (lexer) - 1);
-      for (size_t i = 0; i < c->n_cats; i++)
-        {
-          struct ctables_category *cat = &c->cats[i];
-          switch (cat->type)
-            {
-            case CCT_POSTCOMPUTE:
-              cat->parse_format = parse_strings ? common_format->type : FMT_F;
-              if (!ctables_recursive_check_postcompute (dict, cat->pc->expr,
-                                                        cat, c, cats_location))
-                return false;
-              break;
-
-            case CCT_NUMBER:
-            case CCT_NRANGE:
-              for (size_t j = 0; j < n_vars; j++)
-                if (var_is_alpha (vars[j]))
-                  {
-                    msg_at (SE, cat->location,
-                            _("This category specification may be applied "
-                              "only to numeric variables, but this "
-                              "subcommand tries to apply it to string "
-                              "variable %s."),
-                            var_get_name (vars[j]));
-                    return false;
-                  }
-              break;
-
-            case CCT_STRING:
-              if (parse_strings)
-                {
-                  double n;
-                  if (!parse_category_string (cat->location, cat->string, dict,
-                                              common_format->type, &n))
-                    return false;
-
-                  ss_dealloc (&cat->string);
-
-                  cat->type = CCT_NUMBER;
-                  cat->number = n;
-                }
-              else if (!all_strings (vars, n_vars, cat))
-                return false;
-              break;
-
-            case CCT_SRANGE:
-              if (parse_strings)
-                {
-                  double n[2];
-
-                  if (!cat->srange[0].string)
-                    n[0] = -DBL_MAX;
-                  else if (!parse_category_string (cat->location,
-                                                   cat->srange[0], dict,
-                                                   common_format->type, &n[0]))
-                    return false;
-
-                  if (!cat->srange[1].string)
-                    n[1] = DBL_MAX;
-                  else if (!parse_category_string (cat->location,
-                                                   cat->srange[1], dict,
-                                                   common_format->type, &n[1]))
-                    return false;
-
-                  ss_dealloc (&cat->srange[0]);
-                  ss_dealloc (&cat->srange[1]);
-
-                  cat->type = CCT_NRANGE;
-                  cat->nrange[0] = n[0];
-                  cat->nrange[1] = n[1];
-                }
-              else if (!all_strings (vars, n_vars, cat))
-                return false;
-              break;
-
-            case CCT_MISSING:
-            case CCT_OTHERNM:
-            case CCT_SUBTOTAL:
-            case CCT_TOTAL:
-            case CCT_VALUE:
-            case CCT_LABEL:
-            case CCT_FUNCTION:
-            case CCT_EXCLUDED_MISSING:
-              break;
-            }
-        }
+      cats_end_ofs = lex_ofs (lexer) - 1;
     }
 
   struct ctables_category cat = {
@@ -2297,6 +2235,97 @@ ctables_table_parse_categories (struct lexer *lexer, struct dictionary *dict,
         }
     }
 
+  if (cats_start_ofs != -1)
+    {
+      struct msg_location *cats_location
+        = lex_ofs_location (lexer, cats_start_ofs, cats_end_ofs);
+      for (size_t i = 0; i < c->n_cats; i++)
+        {
+          struct ctables_category *cat = &c->cats[i];
+          switch (cat->type)
+            {
+            case CCT_POSTCOMPUTE:
+              cat->parse_format = parse_strings ? common_format->type : FMT_F;
+              if (!ctables_recursive_check_postcompute (dict, cat->pc->expr,
+                                                        cat, c, cats_location))
+                return false;
+              break;
+
+            case CCT_NUMBER:
+            case CCT_NRANGE:
+              for (size_t j = 0; j < n_vars; j++)
+                if (var_is_alpha (vars[j]))
+                  {
+                    msg_at (SE, cat->location,
+                            _("This category specification may be applied "
+                              "only to numeric variables, but this "
+                              "subcommand tries to apply it to string "
+                              "variable %s."),
+                            var_get_name (vars[j]));
+                    return false;
+                  }
+              break;
+
+            case CCT_STRING:
+              if (parse_strings)
+                {
+                  double n;
+                  if (!parse_category_string (cat->location, cat->string, dict,
+                                              common_format->type, &n))
+                    return false;
+
+                  ss_dealloc (&cat->string);
+
+                  cat->type = CCT_NUMBER;
+                  cat->number = n;
+                }
+              else if (!all_strings (vars, n_vars, cat))
+                return false;
+              break;
+
+            case CCT_SRANGE:
+              if (parse_strings)
+                {
+                  double n[2];
+
+                  if (!cat->srange[0].string)
+                    n[0] = -DBL_MAX;
+                  else if (!parse_category_string (cat->location,
+                                                   cat->srange[0], dict,
+                                                   common_format->type, &n[0]))
+                    return false;
+
+                  if (!cat->srange[1].string)
+                    n[1] = DBL_MAX;
+                  else if (!parse_category_string (cat->location,
+                                                   cat->srange[1], dict,
+                                                   common_format->type, &n[1]))
+                    return false;
+
+                  ss_dealloc (&cat->srange[0]);
+                  ss_dealloc (&cat->srange[1]);
+
+                  cat->type = CCT_NRANGE;
+                  cat->nrange[0] = n[0];
+                  cat->nrange[1] = n[1];
+                }
+              else if (!all_strings (vars, n_vars, cat))
+                return false;
+              break;
+
+            case CCT_MISSING:
+            case CCT_OTHERNM:
+            case CCT_SUBTOTAL:
+            case CCT_TOTAL:
+            case CCT_VALUE:
+            case CCT_LABEL:
+            case CCT_FUNCTION:
+            case CCT_EXCLUDED_MISSING:
+              break;
+            }
+        }
+    }
+
   return true;
 }
 
@@ -4244,7 +4273,6 @@ ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
       }
 
     case CTPO_CAT_NUMBER:
-    case CTPO_CAT_STRING:
     case CTPO_CAT_MISSING:
     case CTPO_CAT_OTHERNM:
     case CTPO_CAT_SUBTOTAL:
@@ -4258,6 +4286,25 @@ ctables_pcexpr_evaluate (const struct ctables_pcexpr_evaluate_ctx *ctx,
         return ctables_pcexpr_evaluate_category (ctx, &cv);
       }
 
+    case CTPO_CAT_STRING:
+      {
+        int width = var_get_width (ctx->section->nests[ctx->pc_a]->vars[ctx->pc_a_idx]);
+        char *s = NULL;
+        if (width > e->string.length)
+          {
+            s = xmalloc (width);
+            buf_copy_rpad (s, width, e->string.string, e->string.length, ' ');
+          }
+        struct ctables_cell_value cv = {
+          .category = ctables_find_category_for_postcompute (ctx->section->table->ctables->dict, ctx->cats, ctx->parse_format, e),
+          .value = { .s = CHAR_CAST (uint8_t *, s ? s : e->string.string) },
+        };
+        assert (cv.category != NULL);
+        double retval = ctables_pcexpr_evaluate_category (ctx, &cv);
+        free (s);
+        return retval;
+      }
+
     case CTPO_ADD:
       return ctables_pcexpr_evaluate_nonterminal (ctx, e, 2, ctpo_add);
 
@@ -4617,6 +4664,8 @@ ctables_table_output (struct ctables *ct, struct ctables_table *t)
             }
           free (sorted);
           free (groups);
+          free (levels);
+          free (sections);
         }
     }
 
@@ -4719,7 +4768,12 @@ ctables_check_label_position (struct ctables_table *t, enum pivot_axis_type a)
     return true;
 
   const struct ctables_nest *n0 = &stack->nests[0];
-  assert (n0->n > 0);
+  if (n0->n == 0)
+    {
+      assert (stack->n == 1);
+      return true;
+    }
+
   const struct variable *v0 = n0->vars[n0->n - 1];
   struct ctables_categories *c0 = t->categories[var_get_dict_index (v0)];
   t->clabels_example = v0;
@@ -4896,6 +4950,10 @@ ctables_prepare_table (struct ctables_table *t)
         struct ctables_nest *nest = xmalloc (sizeof *nest);
         *nest = (struct ctables_nest) { .n = 0 };
         t->stacks[a] = (struct ctables_stack) { .nests = nest, .n = 1 };
+
+        /* There's no point in moving labels away from an axis that has no
+           labels, so avoid dealing with the special cases around that. */
+        t->label_axis[a] = a;
       }
 
   struct ctables_stack *stack = &t->stacks[t->summary_axis];
@@ -5291,6 +5349,23 @@ ctables_section_clear (struct ctables_section *s)
     }
 }
 
+static void
+ctables_section_uninit (struct ctables_section *s)
+{
+  ctables_section_clear (s);
+
+  for (enum pivot_axis_type a = 0; a < PIVOT_N_AXES; a++)
+    {
+      for (size_t i = 0; i < s->nests[a]->n; i++)
+        hmap_destroy (&s->occurrences[a][i]);
+      free (s->occurrences[a]);
+    }
+
+  hmap_destroy (&s->cells);
+  for (size_t i = 0; i < N_CTDTS; i++)
+    hmap_destroy (&s->domains[i]);
+}
+
 static void
 ctables_table_clear (struct ctables_table *t)
 {
@@ -5530,6 +5605,15 @@ ctpo_cat_nrange (double low, double high)
   };
 }
 
+static struct ctables_pcexpr
+ctpo_cat_srange (struct substring low, struct substring high)
+{
+  return (struct ctables_pcexpr) {
+    .op = CTPO_CAT_SRANGE,
+    .srange = { low, high },
+  };
+}
+
 static struct ctables_pcexpr *
 ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
 {
@@ -5566,10 +5650,22 @@ ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
     {
       if (lex_match_id (lexer, "LO"))
         {
-          if (!lex_force_match_id (lexer, "THRU") || lex_force_num (lexer))
+          if (!lex_force_match_id (lexer, "THRU"))
             return false;
-          e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
-          lex_get (lexer);
+
+          if (lex_is_string (lexer))
+            {
+              struct substring low = { .string = NULL };
+              struct substring high = parse_substring (lexer, dict);
+              e = ctpo_cat_srange (low, high);
+            }
+          else
+            {
+              if (!lex_force_num (lexer))
+                return false;
+              e = ctpo_cat_nrange (-DBL_MAX, lex_number (lexer));
+              lex_get (lexer);
+            }
         }
       else if (lex_is_number (lexer))
         {
@@ -5593,12 +5689,28 @@ ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
         }
       else if (lex_is_string (lexer))
         {
-          struct substring s = recode_substring_pool (
-            dict_get_encoding (dict), "UTF-8", lex_tokss (lexer), NULL);
-          ss_rtrim (&s, ss_cstr (" "));
+          struct substring s = parse_substring (lexer, dict);
 
-          e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
-          lex_get (lexer);
+          if (lex_match_id (lexer, "THRU"))
+            {
+              struct substring high;
+
+              if (lex_match_id (lexer, "HI"))
+                high = (struct substring) { .string = NULL };
+              else
+                {
+                  if (!lex_force_string (lexer))
+                    {
+                      ss_dealloc (&s);
+                      return false;
+                    }
+                  high = parse_substring (lexer, dict);
+                }
+
+              e = ctpo_cat_srange (s, high);
+            }
+          else
+            e = (struct ctables_pcexpr) { .op = CTPO_CAT_STRING, .string = s };
         }
       else
         {
@@ -5610,6 +5722,11 @@ ctable_pcexpr_parse_primary (struct lexer *lexer, struct dictionary *dict)
         {
           if (e.op == CTPO_CAT_STRING)
             ss_dealloc (&e.string);
+          else if (e.op == CTPO_CAT_SRANGE)
+            {
+              ss_dealloc (&e.srange[0]);
+              ss_dealloc (&e.srange[1]);
+            }
           return NULL;
         }
     }
@@ -5747,7 +5864,12 @@ ctables_parse_pcompute (struct lexer *lexer, struct dictionary *dict,
 {
   int pcompute_start = lex_ofs (lexer) - 1;
 
-  if (!lex_force_match (lexer, T_AND) || !lex_force_id (lexer))
+  if (!lex_match (lexer, T_AND))
+    {
+      lex_error_expecting (lexer, "&");
+      return false;
+    }
+  if (!lex_force_id (lexer))
     return false;
 
   char *name = ss_xstrdup (lex_tokss (lexer));
@@ -6021,14 +6143,15 @@ put_title_text (struct string *out, struct substring in, time_t now,
 int
 cmd_ctables (struct lexer *lexer, struct dataset *ds)
 {
-  struct casereader *input;
-  if (measure_guesser_is_needed (ds))
+  struct casereader *input = NULL;
+
+  struct measure_guesser *mg = measure_guesser_create (ds);
+  if (mg)
     {
       input = proc_open (ds);
-      measure_guesser_run (ds, input);
+      measure_guesser_run (mg, input);
+      measure_guesser_destroy (mg);
     }
-  else
-    input = NULL;
 
   size_t n_vars = dict_get_n_vars (dataset_dict (ds));
   enum ctables_vlabel *vlabels = xnmalloc (n_vars, sizeof *vlabels);
@@ -6245,7 +6368,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
           if (!ct->e_weight)
             goto error;
         }
-      else if (lex_match_id (lexer, " HIDESMALLCOUNTS"))
+      else if (lex_match_id (lexer, "HIDESMALLCOUNTS"))
         {
           if (lex_match_id (lexer, "COUNT"))
             {
@@ -6404,7 +6527,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
           break;
         }
       if (!lex_force_match (lexer, T_SLASH))
-        break;
+        goto error;
 
       while (!lex_match_id (lexer, "TABLE") && lex_token (lexer) != T_ENDCMD)
         {
@@ -6442,46 +6565,43 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
             }
           else if (lex_match_id (lexer, "CLABELS"))
             {
-              while (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
+              if (lex_match_id (lexer, "AUTO"))
                 {
-                  if (lex_match_id (lexer, "AUTO"))
-                    {
-                      t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
-                      t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
-                    }
-                  else if (lex_match_id (lexer, "ROWLABELS"))
-                    {
-                      lex_match (lexer, T_EQUALS);
-                      if (lex_match_id (lexer, "OPPOSITE"))
-                        t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
-                      else if (lex_match_id (lexer, "LAYER"))
-                        t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
-                      else
-                        {
-                          lex_error_expecting (lexer, "OPPOSITE", "LAYER");
-                          goto error;
-                        }
-                    }
-                  else if (lex_match_id (lexer, "COLLABELS"))
+                  t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_ROW;
+                  t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_COLUMN;
+                }
+              else if (lex_match_id (lexer, "ROWLABELS"))
+                {
+                  lex_match (lexer, T_EQUALS);
+                  if (lex_match_id (lexer, "OPPOSITE"))
+                    t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_COLUMN;
+                  else if (lex_match_id (lexer, "LAYER"))
+                    t->label_axis[PIVOT_AXIS_ROW] = PIVOT_AXIS_LAYER;
+                  else
                     {
-                      lex_match (lexer, T_EQUALS);
-                      if (lex_match_id (lexer, "OPPOSITE"))
-                        t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
-                      else if (lex_match_id (lexer, "LAYER"))
-                        t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
-                      else
-                        {
-                          lex_error_expecting (lexer, "OPPOSITE", "LAYER");
-                          goto error;
-                        }
+                      lex_error_expecting (lexer, "OPPOSITE", "LAYER");
+                      goto error;
                     }
+                }
+              else if (lex_match_id (lexer, "COLLABELS"))
+                {
+                  lex_match (lexer, T_EQUALS);
+                  if (lex_match_id (lexer, "OPPOSITE"))
+                    t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_ROW;
+                  else if (lex_match_id (lexer, "LAYER"))
+                    t->label_axis[PIVOT_AXIS_COLUMN] = PIVOT_AXIS_LAYER;
                   else
                     {
-                      lex_error_expecting (lexer, "AUTO", "ROWLABELS",
-                                           "COLLABELS");
+                      lex_error_expecting (lexer, "OPPOSITE", "LAYER");
                       goto error;
                     }
                 }
+              else
+                {
+                  lex_error_expecting (lexer, "AUTO", "ROWLABELS",
+                                       "COLLABELS");
+                  goto error;
+                }
             }
           else if (lex_match_id (lexer, "CRITERIA"))
             {
@@ -6565,7 +6685,7 @@ cmd_ctables (struct lexer *lexer, struct dataset *ds)
                   else if (lex_match_id (lexer, "INCLUDEMRSETS"))
                     {
                       lex_match (lexer, T_EQUALS);
-                      if (parse_bool (lexer, &t->chisq->include_mrsets))
+                      if (!parse_bool (lexer, &t->chisq->include_mrsets))
                         goto error;
                     }
                   else if (lex_match_id (lexer, "CATEGORIES"))