lexer: Check that 'read' function in lex_source returns valid value.
[pspp] / src / language / lexer / lexer.c
index 686aafd0d4c99fecc640828f2567a99fb414868c..1caadac1de9854433d499122686d1ffa052d9704 100644 (file)
@@ -268,20 +268,116 @@ lex_next_error (struct lexer *lexer, int n0, int n1, const char *format, ...)
   va_end (args);
 }
 
-/* Reports an error to the effect that subcommand SBC may only be
-   specified once. */
+/* Prints a syntax error message saying that OPTION0 or one of the other
+   strings following it, up to the first NULL, is expected. */
+void
+lex_error_expecting (struct lexer *lexer, const char *option0, ...)
+{
+  enum { MAX_OPTIONS = 8 };
+  const char *options[MAX_OPTIONS + 1];
+  va_list args;
+  int n;
+
+  va_start (args, option0);
+  options[0] = option0;
+  n = 0;
+  while (n + 1 < MAX_OPTIONS && options[n] != NULL)
+    options[++n] = va_arg (args, const char *);
+  va_end (args);
+
+  switch (n)
+    {
+    case 0:
+      lex_error (lexer, NULL);
+      break;
+
+    case 1:
+      lex_error (lexer, _("expecting %s"), options[0]);
+      break;
+
+    case 2:
+      lex_error (lexer, _("expecting %s or %s"), options[0], options[1]);
+      break;
+
+    case 3:
+      lex_error (lexer, _("expecting %s, %s, or %s"), options[0], options[1],
+                 options[2]);
+      break;
+
+    case 4:
+      lex_error (lexer, _("expecting %s, %s, %s, or %s"),
+                 options[0], options[1], options[2], options[3]);
+      break;
+
+    case 5:
+      lex_error (lexer, _("expecting %s, %s, %s, %s, or %s"),
+                 options[0], options[1], options[2], options[3], options[4]);
+      break;
+
+    case 6:
+      lex_error (lexer, _("expecting %s, %s, %s, %s, %s, or %s"),
+                 options[0], options[1], options[2], options[3], options[4],
+                 options[5]);
+      break;
+
+    case 7:
+      lex_error (lexer, _("expecting %s, %s, %s, %s, %s, %s, or %s"),
+                 options[0], options[1], options[2], options[3], options[4],
+                 options[5], options[6]);
+      break;
+
+    case 8:
+      lex_error (lexer, _("expecting %s, %s, %s, %s, %s, %s, %s, or %s"),
+                 options[0], options[1], options[2], options[3], options[4],
+                 options[5], options[6], options[7]);
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+}
+
+/* Reports an error to the effect that subcommand SBC may only be specified
+   once.
+
+   This function does not take a lexer as an argument or use lex_error(),
+   because the result would ordinarily just be redundant: "Syntax error at
+   SUBCOMMAND: Subcommand SUBCOMMAND may only be specified once.", which does
+   not help the user find the error. */
 void
 lex_sbc_only_once (const char *sbc)
 {
   msg (SE, _("Subcommand %s may only be specified once."), sbc);
 }
 
-/* Reports an error to the effect that subcommand SBC is
-   missing. */
+/* Reports an error to the effect that subcommand SBC is missing.
+
+   This function does not take a lexer as an argument or use lex_error(),
+   because a missing subcommand can normally be detected only after the whole
+   command has been parsed, and so lex_error() would always report "Syntax
+   error at end of command", which does not help the user find the error. */
 void
-lex_sbc_missing (struct lexer *lexer, const char *sbc)
+lex_sbc_missing (const char *sbc)
 {
-  lex_error (lexer, _("missing required subcommand %s"), sbc);
+  msg (SE, _("Required subcommand %s was not specified."), sbc);
+}
+
+/* Reports an error to the effect that specification SPEC may only be specified
+   once within subcommand SBC. */
+void
+lex_spec_only_once (struct lexer *lexer, const char *sbc, const char *spec)
+{
+  lex_error (lexer, _("%s may only be specified once within subcommand %s"),
+             spec, sbc);
+}
+
+/* Reports an error to the effect that specification SPEC is missing within
+   subcommand SBC. */
+void
+lex_spec_missing (struct lexer *lexer, const char *sbc, const char *spec)
+{
+  lex_error (lexer, _("Required %s specification missing from %s subcommand"),
+             sbc, spec);
 }
 
 /* Prints a syntax error message containing the current token and
@@ -491,7 +587,7 @@ lex_force_match_id (struct lexer *lexer, const char *identifier)
     return true;
   else
     {
-      lex_error (lexer, _("expecting `%s'"), identifier);
+      lex_error_expecting (lexer, identifier, NULL_SENTINEL);
       return false;
     }
 }
@@ -508,7 +604,9 @@ lex_force_match (struct lexer *lexer, enum token_type type)
     }
   else
     {
-      lex_error (lexer, _("expecting `%s'"), token_type_to_string (type));
+      char *s = xasprintf ("`%s'", token_type_to_string (type));
+      lex_error_expecting (lexer, s, NULL_SENTINEL);
+      free (s);
       return false;
     }
 }
@@ -527,6 +625,21 @@ lex_force_string (struct lexer *lexer)
     }
 }
 
+/* If the current token is a string or an identifier, does nothing and returns
+   true.  Otherwise, reports an error and returns false.
+
+   This is meant for use in syntactic situations where we want to encourage the
+   user to supply a quoted string, but for compatibility we also accept
+   identifiers.  (One example of such a situation is file names.)  Therefore,
+   the error message issued when the current token is wrong only says that a
+   string is expected and doesn't mention that an identifier would also be
+   accepted. */
+bool
+lex_force_string_or_id (struct lexer *lexer)
+{
+  return lex_is_integer (lexer) || lex_force_string (lexer);
+}
+
 /* If the current token is an integer, does nothing and returns true.
    Otherwise, reports an error and returns false. */
 bool
@@ -1096,14 +1209,18 @@ lex_source_read__ (struct lex_source *src)
   do
     {
       size_t head_ofs;
+      size_t space;
       size_t n;
 
       lex_source_expand__ (src);
 
       head_ofs = src->head - src->tail;
+      space = src->allocated - head_ofs;
       n = src->reader->class->read (src->reader, &src->buffer[head_ofs],
-                                    src->allocated - head_ofs,
+                                    space,
                                     segmenter_get_prompt (&src->segmenter));
+      assert (n <= space);
+
       if (n == 0)
         {
           /* End of input.
@@ -1452,8 +1569,8 @@ static void
 lex_source_destroy (struct lex_source *src)
 {
   char *file_name = src->reader->file_name;
-  if (src->reader->class->close != NULL)
-    src->reader->class->close (src->reader);
+  if (src->reader->class->destroy != NULL)
+    src->reader->class->destroy (src->reader);
   free (file_name);
   free (src->buffer);
   while (!deque_is_empty (&src->deque))