SET: Improve error messages for SET CCx.
[pspp] / src / language / utilities / set.c
index 08675179ab1ac63bd305f66a33bd8d78aaecae94..a03eae4ddd94e2dc3792efee76f83af4dd84c775 100644 (file)
@@ -35,6 +35,7 @@
 #include "language/command.h"
 #include "language/lexer/format-parser.h"
 #include "language/lexer/lexer.h"
+#include "language/lexer/token.h"
 #include "libpspp/assertion.h"
 #include "libpspp/compiler.h"
 #include "libpspp/copyleft.h"
@@ -78,6 +79,13 @@ match_subcommand (struct lexer *lexer, const char *name)
     return false;
 }
 
+static int
+subcommand_start_ofs (struct lexer *lexer)
+{
+  int ofs = lex_ofs (lexer) - 1;
+  return lex_ofs_token (lexer, ofs)->type == T_EQUALS ? ofs - 1 : ofs;
+}
+
 static int
 parse_enum_valist (struct lexer *lexer, va_list args)
 {
@@ -156,16 +164,6 @@ force_parse_bool (struct lexer *lexer)
                            "OFF", false, "NO", false);
 }
 
-static bool
-force_parse_int (struct lexer *lexer, int *integerp)
-{
-  if (!lex_force_int (lexer))
-    return false;
-  *integerp = lex_integer (lexer);
-  lex_get (lexer);
-  return true;
-}
-
 static bool
 parse_output_routing (struct lexer *lexer, enum settings_output_type type)
 {
@@ -302,9 +300,12 @@ show_real_format (enum float_format float_format)
 static bool
 parse_unimplemented (struct lexer *lexer, const char *name)
 {
-  msg (SW, _("%s is not yet implemented."), name);
+  int start = subcommand_start_ofs (lexer);
   if (lex_token (lexer) != T_SLASH && lex_token (lexer) != T_ENDCMD)
     lex_get (lexer);
+  int end = lex_ofs (lexer) - 1;
+
+  lex_ofs_msg (lexer, SW, start, end, _("%s is not yet implemented."), name);
   return true;
 }
 
@@ -314,7 +315,14 @@ parse_ccx (struct lexer *lexer, enum fmt_type ccx)
   if (!lex_force_string (lexer))
     return false;
 
-  settings_set_cc (lex_tokcstr (lexer), ccx);
+  char *error = settings_set_cc (lex_tokcstr (lexer), ccx);
+  if (error)
+    {
+      lex_error (lexer, "%s", error);
+      free (error);
+      return false;
+    }
+
   lex_get (lexer);
   return true;
 }
@@ -488,7 +496,7 @@ parse_EPOCH (struct lexer *lexer)
     }
   else
     {
-      lex_error (lexer, _("expecting %s or year"), "AUTOMATIC");
+      lex_error (lexer, _("Syntax error expecting %s or year."), "AUTOMATIC");
       return false;
     }
 
@@ -516,22 +524,28 @@ show_ERRORS (const struct dataset *ds UNUSED)
 static bool
 parse_FORMAT (struct lexer *lexer)
 {
+  int start = subcommand_start_ofs (lexer);
   struct fmt_spec fmt;
 
-  lex_match (lexer, T_EQUALS);
   if (!parse_format_specifier (lexer, &fmt))
     return false;
 
-  if (!fmt_check_output (&fmt))
-    return false;
+  char *error = fmt_check_output__ (&fmt);
+  if (error)
+    {
+      lex_next_error (lexer, -1, -1, "%s", error);
+      free (error);
+      return false;
+    }
 
+  int end = lex_ofs (lexer) - 1;
   if (fmt_is_string (fmt.type))
     {
       char str[FMT_STRING_LEN_MAX + 1];
-      msg (SE, _("%s requires numeric output format as an argument.  "
-                "Specified format %s is of type string."),
-          "FORMAT",
-          fmt_to_string (&fmt, str));
+      lex_ofs_error (lexer, start, end,
+                     _("%s requires numeric output format as an argument.  "
+                       "Specified format %s is of type string."),
+                     "FORMAT", fmt_to_string (&fmt, str));
       return false;
     }
 
@@ -617,6 +631,22 @@ show_JOURNAL (const struct dataset *ds UNUSED)
           : xstrdup (enabled));
 }
 
+static bool
+parse_LEADZERO (struct lexer *lexer)
+{
+  int leadzero = force_parse_bool (lexer);
+  if (leadzero != -1)
+    settings_set_include_leading_zero (leadzero);
+  return leadzero != -1;
+}
+
+static char *
+show_LEADZERO (const struct dataset *ds UNUSED)
+{
+  bool leadzero = settings_get_fmt_settings ()->include_leading_zero;
+  return xstrdup (leadzero ? "ON" : "OFF");
+}
+
 static bool
 parse_LENGTH (struct lexer *lexer)
 {
@@ -656,7 +686,7 @@ parse_LOCALE (struct lexer *lexer)
     set_default_encoding (s);
   else if (!set_encoding_from_locale (s))
     {
-      msg (ME, _("%s is not a recognized encoding or locale name"), s);
+      lex_error (lexer, _("%s is not a recognized encoding or locale name"), s);
       return false;
     }
 
@@ -765,14 +795,10 @@ show_MPRINT (const struct dataset *ds UNUSED)
 static bool
 parse_MXERRS (struct lexer *lexer)
 {
-  int n;
-  if (!force_parse_int (lexer, &n))
+  if (!lex_force_int_range (lexer, "MXERRS", 1, INT_MAX))
     return false;
-
-  if (n >= 1)
-    settings_set_max_messages (MSG_S_ERROR, n);
-  else
-    msg (SE, _("%s must be at least 1."), "MXERRS");
+  settings_set_max_messages (MSG_S_ERROR, lex_integer (lexer));
+  lex_get (lexer);
   return true;
 }
 
@@ -785,14 +811,10 @@ show_MXERRS (const struct dataset *ds UNUSED)
 static bool
 parse_MXLOOPS (struct lexer *lexer)
 {
-  int n;
-  if (!force_parse_int (lexer, &n))
+  if (!lex_force_int_range (lexer, "MXLOOPS", 1, INT_MAX))
     return false;
-
-  if (n >= 1)
-    settings_set_mxloops (n);
-  else
-    msg (SE, _("%s must be at least 1."), "MXLOOPS");
+  settings_set_mxloops (lex_integer (lexer));
+  lex_get (lexer);
   return true;
 }
 
@@ -805,14 +827,10 @@ show_MXLOOPS (const struct dataset *ds UNUSED)
 static bool
 parse_MXWARNS (struct lexer *lexer)
 {
-  int n;
-  if (!force_parse_int (lexer, &n))
+  if (!lex_force_int_range (lexer, "MXWARNS", 0, INT_MAX))
     return false;
-
-  if (n >= 0)
-    settings_set_max_messages (MSG_S_WARNING, n);
-  else
-    msg (SE, _("%s must not be negative."), "MXWARNS");
+  settings_set_max_messages (MSG_S_WARNING, lex_integer (lexer));
+  lex_get (lexer);
   return true;
 }
 
@@ -1145,11 +1163,24 @@ show_N (const struct dataset *ds)
 }
 
 static void
-do_show (const struct dataset *ds, const struct setting *s)
+do_show (const struct dataset *ds, const struct setting *s,
+         struct pivot_table **ptp)
 {
-  char *value = s->show (ds);
-  msg (SN, _("%s is %s."), s->name, value ? value : _("empty"));
-  free (value);
+  struct pivot_table *pt = *ptp;
+  if (!pt)
+    {
+      pt = *ptp = pivot_table_create (N_("Settings"));
+      pivot_dimension_create (pt, PIVOT_AXIS_ROW, N_("Setting"));
+    }
+
+  struct pivot_value *name = pivot_value_new_user_text (s->name, SIZE_MAX);
+  char *text = s->show (ds);
+  if (!text)
+    text = xstrdup("empty");
+  struct pivot_value *value = pivot_value_new_user_text_nocopy (text);
+
+  int row = pivot_category_create_leaf (pt->dimensions[0]->root, name);
+  pivot_table_put1 (pt, row, value);
 }
 
 static void
@@ -1218,6 +1249,7 @@ static const struct setting settings[] = {
   { "HEADER", parse_HEADER, NULL },
   { "INCLUDE", parse_INCLUDE, show_INCLUDE },
   { "JOURNAL", parse_JOURNAL, show_JOURNAL },
+  { "LEADZERO", parse_LEADZERO, show_LEADZERO },
   { "LENGTH", parse_LENGTH, show_LENGTH },
   { "LOCALE", parse_LOCALE, show_LOCALE },
   { "MDISPLAY", parse_MDISPLAY, show_MDISPLAY },
@@ -1280,39 +1312,41 @@ cmd_set (struct lexer *lexer, struct dataset *ds UNUSED)
 }
 
 static void
-show_all (const struct dataset *ds)
+show_all (const struct dataset *ds, struct pivot_table **ptp)
 {
   for (size_t i = 0; i < sizeof settings / sizeof *settings; i++)
     if (settings[i].show)
-      do_show (ds, &settings[i]);
+      do_show (ds, &settings[i], ptp);
 }
 
 static void
-show_all_cc (const struct dataset *ds)
+show_all_cc (const struct dataset *ds, struct pivot_table **ptp)
 {
   for (size_t i = 0; i < sizeof settings / sizeof *settings; i++)
     {
       const struct setting *s = &settings[i];
       if (s->show && !strncmp (s->name, "CC", 2))
-        do_show (ds, s);
+        do_show (ds, s, ptp);
     }
 }
 
 int
 cmd_show (struct lexer *lexer, struct dataset *ds)
 {
+  struct pivot_table *pt = NULL;
   if (lex_token (lexer) == T_ENDCMD)
     {
-      show_all (ds);
+      show_all (ds, &pt);
+      pivot_table_submit (pt);
       return CMD_SUCCESS;
     }
 
   do
     {
       if (lex_match (lexer, T_ALL))
-        show_all (ds);
+        show_all (ds, &pt);
       else if (lex_match_id (lexer, "CC"))
-        show_all_cc (ds);
+        show_all_cc (ds, &pt);
       else if (lex_match_id (lexer, "WARRANTY"))
         show_warranty (ds);
       else if (lex_match_id (lexer, "COPYING") || lex_match_id (lexer, "LICENSE"))
@@ -1322,23 +1356,21 @@ cmd_show (struct lexer *lexer, struct dataset *ds)
       else if (lex_match_id (lexer, "TITLE"))
         {
           struct setting s = { .name = "TITLE", .show = show_TITLE };
-          do_show (ds, &s);
+          do_show (ds, &s, &pt);
         }
       else if (lex_match_id (lexer, "SUBTITLE"))
         {
           struct setting s = { .name = "SUBTITLE", .show = show_SUBTITLE };
-          do_show (ds, &s);
+          do_show (ds, &s, &pt);
         }
       else if (lex_token (lexer) == T_ID)
         {
-          int i;
-
-          for (i = 0; i < sizeof settings / sizeof *settings; i++)
+          for (size_t i = 0; i < sizeof settings / sizeof *settings; i++)
             {
               const struct setting *s = &settings[i];
               if (s->show && lex_match_id (lexer, s->name))
                 {
-                  do_show (ds, s);
+                  do_show (ds, s, &pt);
                   goto found;
                 }
               }
@@ -1357,6 +1389,9 @@ cmd_show (struct lexer *lexer, struct dataset *ds)
     }
   while (lex_token (lexer) != T_ENDCMD);
 
+  if (pt)
+    pivot_table_submit (pt);
+
   return CMD_SUCCESS;
 }
 \f
@@ -1366,7 +1401,7 @@ static struct settings *saved_settings[MAX_SAVED_SETTINGS];
 static int n_saved_settings;
 
 int
-cmd_preserve (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
+cmd_preserve (struct lexer *lexer, struct dataset *ds UNUSED)
 {
   if (n_saved_settings < MAX_SAVED_SETTINGS)
     {
@@ -1375,16 +1410,17 @@ cmd_preserve (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
     }
   else
     {
-      msg (SE, _("Too many %s commands without a %s: at most "
-                 "%d levels of saved settings are allowed."),
-          "PRESERVE", "RESTORE",
-           MAX_SAVED_SETTINGS);
+      lex_next_error (lexer, -1, -1,
+                      _("Too many %s commands without a %s: at most "
+                        "%d levels of saved settings are allowed."),
+                      "PRESERVE", "RESTORE",
+                      MAX_SAVED_SETTINGS);
       return CMD_CASCADING_FAILURE;
     }
 }
 
 int
-cmd_restore (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
+cmd_restore (struct lexer *lexer, struct dataset *ds UNUSED)
 {
   if (n_saved_settings > 0)
     {
@@ -1395,7 +1431,8 @@ cmd_restore (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
     }
   else
     {
-      msg (SE, _("%s without matching %s."), "RESTORE", "PRESERVE");
+      lex_next_error (lexer, -1, -1,
+                      _("%s without matching %s."), "RESTORE", "PRESERVE");
       return CMD_FAILURE;
     }
 }