SET: Improve error messages for SET CCx.
[pspp] / src / language / utilities / set.c
index 5e3489e11723a73a2466ff94f6675391b6e509dd..a03eae4ddd94e2dc3792efee76f83af4dd84c775 100644 (file)
@@ -23,9 +23,6 @@
 #include <time.h>
 #include <unistd.h>
 
-#include "gl/ftoastr.h"
-#include "gl/vasnprintf.h"
-
 #include "data/casereader.h"
 #include "data/data-in.h"
 #include "data/data-out.h"
@@ -38,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"
 #include "output/journal.h"
 #include "output/pivot-table.h"
 
+#include "gl/ftoastr.h"
 #include "gl/minmax.h"
+#include "gl/relocatable.h"
+#include "gl/vasnprintf.h"
 #include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
+#define N_(msgid) (msgid)
 
 struct setting
   {
@@ -77,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)
 {
@@ -155,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)
 {
@@ -301,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;
 }
 
@@ -313,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;
 }
@@ -487,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;
     }
 
@@ -515,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;
     }
 
@@ -616,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)
 {
@@ -655,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;
     }
 
@@ -669,6 +700,24 @@ show_LOCALE (const struct dataset *ds UNUSED)
   return xstrdup (get_default_encoding ());
 }
 
+static bool
+parse_MDISPLAY (struct lexer *lexer)
+{
+  int mdisplay = force_parse_enum (lexer,
+                                   "TEXT", SETTINGS_MDISPLAY_TEXT,
+                                   "TABLES", SETTINGS_MDISPLAY_TABLES);
+  if (mdisplay >= 0)
+    settings_set_mdisplay (mdisplay);
+  return mdisplay >= 0;
+}
+
+static char *
+show_MDISPLAY (const struct dataset *ds UNUSED)
+{
+  return xstrdup (settings_get_mdisplay () == SETTINGS_MDISPLAY_TEXT
+                  ? "TEXT" : "TABLES");
+}
+
 static bool
 parse_MESSAGES (struct lexer *lexer)
 {
@@ -746,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;
 }
 
@@ -766,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;
 }
 
@@ -786,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;
 }
 
@@ -922,12 +959,6 @@ show_SUBTITLE (const struct dataset *ds UNUSED)
   return xstrdup (output_get_subtitle ());
 }
 
-static char *
-show_SYSTEM (const struct dataset *ds UNUSED)
-{
-  return xstrdup (host_system);
-}
-
 static char *
 show_TEMPDIR (const struct dataset *ds UNUSED)
 {
@@ -1132,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
@@ -1150,6 +1194,36 @@ show_copying (const struct dataset *ds UNUSED)
 {
   fputs (copyleft, stdout);
 }
+
+static void
+add_row (struct pivot_table *table, const char *attribute,
+         const char *value)
+{
+  int row = pivot_category_create_leaf (table->dimensions[0]->root,
+                                        pivot_value_new_text (attribute));
+  if (value)
+    pivot_table_put1 (table, row, pivot_value_new_user_text (value, -1));
+}
+
+static void
+show_system (const struct dataset *ds UNUSED)
+{
+  struct pivot_table *table = pivot_table_create (N_("System Information"));
+  pivot_dimension_create (table, PIVOT_AXIS_ROW, N_("Attribute"));
+
+  add_row (table, N_("Version"), version);
+  add_row (table, N_("Host System"), host_system);
+  add_row (table, N_("Build System"), build_system);
+  add_row (table, N_("Locale Directory"), relocate (locale_dir));
+  add_row (table, N_("Compiler Version"),
+#ifdef __VERSION__
+           __VERSION__
+#else
+           "Unknown"
+#endif
+           );
+  pivot_table_submit (table);
+}
 \f
 static const struct setting settings[] = {
   { "BASETEXTDIRECTION", parse_BASETEXTDIRECTION, NULL },
@@ -1175,8 +1249,10 @@ 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 },
   { "MESSAGES", parse_MESSAGES, show_MESSAGES },
   { "MEXPAND", parse_MEXPAND, show_MEXPAND },
   { "MITERATE", parse_MITERATE, show_MITERATE },
@@ -1194,7 +1270,6 @@ static const struct setting settings[] = {
   { "SCOMPRESSION", parse_SCOMPRESSION, show_SCOMPRESSION },
   { "SEED", parse_SEED, NULL },
   { "SMALL", parse_SMALL, show_SMALL },
-  { "SYSTEM", NULL, show_SYSTEM },
   { "TEMPDIR", NULL, show_TEMPDIR },
   { "TNUMBERS", parse_TNUMBERS, show_TNUMBERS },
   { "TVARS", parse_TVARS, show_TVARS },
@@ -1237,63 +1312,65 @@ 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"))
         show_copying (ds);
+      else if (lex_match_id (lexer, "SYSTEM"))
+        show_system (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;
                 }
               }
@@ -1312,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
@@ -1321,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)
     {
@@ -1330,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)
     {
@@ -1350,13 +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;
     }
 }
-
-/*
-   Local Variables:
-   mode: c
-   End:
-*/