Remove "Written by Ben Pfaff <blp@gnu.org>" lines everywhere.
[pspp-builds.git] / src / language / command.c
index 7b903ab1a29f64a916d7f3891b960a2c3b6c4952..b46d1e83b813a7744e7b9c056fe9205b11cbd33a 100644 (file)
@@ -1,6 +1,5 @@
 /* PSPP - computes sample statistics.
    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -32,7 +31,9 @@
 #include <data/settings.h>
 #include <data/variable.h>
 #include <language/lexer/lexer.h>
+#include <language/prompt.h>
 #include <libpspp/alloc.h>
+#include <libpspp/assertion.h>
 #include <libpspp/compiler.h>
 #include <libpspp/message.h>
 #include <libpspp/message.h>
@@ -96,7 +97,8 @@ enum flags
   {
     F_ENHANCED = 0x10,        /* Allowed only in enhanced syntax mode. */
     F_TESTING = 0x20,         /* Allowed only in testing mode. */
-    F_KEEP_FINAL_TOKEN = 0x40 /* Don't skip final token in command name. */
+    F_KEEP_FINAL_TOKEN = 0x40,/* Don't skip final token in command name. */
+    F_ABBREV = 0x80           /* Not a candidate for name completion. */
   };
 
 /* A single command. */
@@ -105,7 +107,7 @@ struct command
     enum states states;         /* States in which command is allowed. */
     enum flags flags;           /* Other command requirements. */
     const char *name;          /* Command name. */
-    int (*function) (void);    /* Function to call. */
+    int (*function) (struct lexer *, struct dataset *);        /* Function to call. */
   };
 
 /* Define the command array. */
@@ -120,30 +122,32 @@ static const struct command commands[] =
 
 static const size_t command_cnt = sizeof commands / sizeof *commands;
 
-static bool verify_valid_command (const struct command *, enum cmd_state);
+static bool in_correct_state (const struct command *, enum cmd_state);
+static bool report_state_mismatch (const struct command *, enum cmd_state);
 static const struct command *find_command (const char *name);
+static void set_completion_state (enum cmd_state); 
 \f
 /* Command parser. */
 
-static const struct command *parse_command_name (void);
-static enum cmd_result do_parse_command (enum cmd_state);
+static const struct command *parse_command_name (struct lexer *lexer);
+static enum cmd_result do_parse_command (struct lexer *, struct dataset *, enum cmd_state);
 
 /* Parses an entire command, from command name to terminating
    dot.  On failure, skips to the terminating dot.
    Returns the command's success or failure result. */
 enum cmd_result
-cmd_parse (enum cmd_state state) 
+cmd_parse (struct lexer *lexer, struct dataset *ds, enum cmd_state state) 
 {
   int result;
   
   som_new_series ();
 
-  result = do_parse_command (state);
+  result = do_parse_command (lexer, ds, state);
   if (cmd_result_is_failure (result)) 
-    lex_discard_rest_of_command ();
+    lex_discard_rest_of_command (lexer);
 
   unset_cmd_algorithm ();
-  dict_clear_aux (default_dict);
+  dict_clear_aux (dataset_dict (ds));
 
   return result;
 }
@@ -151,21 +155,33 @@ cmd_parse (enum cmd_state state)
 /* Parses an entire command, from command name to terminating
    dot. */
 static enum cmd_result
-do_parse_command (enum cmd_state state)
+do_parse_command (struct lexer *lexer, struct dataset *ds, enum cmd_state state)
 {
   const struct command *command;
   enum cmd_result result;
 
-  /* Null commands can result from extra empty lines. */
-  if (token == '.')
-    return CMD_SUCCESS;
+  /* Read the command's first token. */
+  prompt_set_style (PROMPT_FIRST);
+  set_completion_state (state);
+  lex_get (lexer);
+  if (lex_token (lexer) == T_STOP)
+    return CMD_EOF;
+  else if (lex_token (lexer) == '.') 
+    {
+      /* Null commands can result from extra empty lines. */
+      return CMD_SUCCESS; 
+    }
+  prompt_set_style (PROMPT_LATER);
 
   /* Parse the command name. */
-  command = parse_command_name ();
+  command = parse_command_name (lexer);
   if (command == NULL)
     return CMD_FAILURE;
-  else if (command->function == NULL)
-    return CMD_NOT_IMPLEMENTED;
+  else if (command->function == NULL) 
+    {
+      msg (SE, _("%s is unimplemented."), command->name);
+      return CMD_NOT_IMPLEMENTED; 
+    }
   else if ((command->flags & F_TESTING) && !get_testing_mode ()) 
     {
       msg (SE, _("%s may be used only in testing mode."), command->name);
@@ -177,13 +193,16 @@ do_parse_command (enum cmd_state state)
            command->name);
        return CMD_FAILURE;
     }
-  else if (!verify_valid_command (command, state))
-    return CMD_FAILURE;
+  else if (!in_correct_state (command, state)) 
+    {
+      report_state_mismatch (command, state);
+      return CMD_FAILURE; 
+    }
 
   /* Execute command. */
   msg_set_command_name (command->name);
   tab_set_command_name (command->name);
-  result = command->function ();
+  result = command->function (lexer, ds);
   tab_set_command_name (NULL);
   msg_set_command_name (NULL);
     
@@ -247,9 +266,9 @@ find_word (const char *string, size_t *word_len)
   return string;
 }
 
-/* Returns nonzero if strings A and B can be confused based on
+/* Returns true if strings A and B can be confused based on
    their first three letters. */
-static int
+static bool
 conflicting_3char_prefixes (const char *a, const char *b) 
 {
   size_t aw_len, bw_len;
@@ -261,7 +280,7 @@ conflicting_3char_prefixes (const char *a, const char *b)
 
   /* Words that are the same don't conflict. */
   if (aw_len == bw_len && !buf_compare_case (aw, bw, aw_len))
-    return 0;
+    return false;
   
   /* Words that are otherwise the same in the first three letters
      do conflict. */
@@ -270,9 +289,9 @@ conflicting_3char_prefixes (const char *a, const char *b)
           || (bw_len == 3 && aw_len > 3)) && !buf_compare_case (aw, bw, 3);
 }
 
-/* Returns nonzero if CMD can be confused with another command
+/* Returns true if CMD can be confused with another command
    based on the first three letters of its first word. */
-static int
+static bool
 conflicting_3char_prefix_command (const struct command *cmd) 
 {
   assert (cmd >= commands && cmd < commands + command_cnt);
@@ -405,7 +424,7 @@ find_command (const char *name)
   for (cmd = commands; cmd < commands + command_cnt; cmd++) 
     if (!strcmp (cmd->name, name))
       return cmd;
-  abort ();
+  NOT_REACHED ();
 }
 
 /* Frees the WORD_CNT words in WORDS. */
@@ -421,24 +440,24 @@ free_words (char *words[], size_t word_cnt)
 /* Flags an error that the command whose name is given by the
    WORD_CNT words in WORDS is unknown. */
 static void
-unknown_command_error (char *const words[], size_t word_cnt) 
+unknown_command_error (struct lexer *lexer, char *const words[], size_t word_cnt) 
 {
   if (word_cnt == 0) 
-    lex_error (_("expecting command name"));
+    lex_error (lexer, _("expecting command name"));
   else 
     {
       struct string s;
       size_t i;
 
-      ds_init (&s);
+      ds_init_empty (&s);
       for (i = 0; i < word_cnt; i++) 
         {
           if (i != 0)
-            ds_putc (&s, ' ');
-          ds_puts (&s, words[i]);
+            ds_put_char (&s, ' ');
+          ds_put_cstr (&s, words[i]);
         }
 
-      msg (SE, _("Unknown command %s."), ds_c_str (&s));
+      msg (SE, _("Unknown command %s."), ds_cstr (&s));
 
       ds_destroy (&s);
     }
@@ -448,29 +467,30 @@ unknown_command_error (char *const words[], size_t word_cnt)
    struct command if successful.
    If not successful, return a null pointer. */
 static const struct command *
-parse_command_name (void)
+parse_command_name (struct lexer *lexer)
 {
   char *words[16];
   int word_cnt;
   int complete_word_cnt;
   int dash_possible;
 
-  if (token == T_EXP || token == '*' || token == '[') 
+  if (lex_token (lexer) == T_EXP || 
+                 lex_token (lexer) == '*' || lex_token (lexer) == '[') 
     return find_command ("COMMENT");
 
   dash_possible = 0;
   word_cnt = complete_word_cnt = 0;
-  while (token == T_ID || (dash_possible && token == '-')) 
+  while (lex_token (lexer) == T_ID || (dash_possible && lex_token (lexer) == '-')) 
     {
       int cmd_match_cnt;
       
       assert (word_cnt < sizeof words / sizeof *words);
-      if (token == T_ID) 
+      if (lex_token (lexer) == T_ID) 
         {
-          words[word_cnt] = xstrdup (ds_c_str (&tokstr));
+          words[word_cnt] = ds_xstrdup (lex_tokstr (lexer));
           str_uppercase (words[word_cnt]); 
         }
-      else if (token == '-')
+      else if (lex_token (lexer) == '-')
         words[word_cnt] = xstrdup ("-");
       word_cnt++;
 
@@ -484,7 +504,7 @@ parse_command_name (void)
           if (command != NULL) 
             {
               if (!(command->flags & F_KEEP_FINAL_TOKEN))
-                lex_get ();
+                lex_get (lexer);
               free_words (words, word_cnt);
               return command;
             }
@@ -495,7 +515,7 @@ parse_command_name (void)
           if (get_complete_match (words, word_cnt) != NULL)
             complete_word_cnt = word_cnt;
         }
-      lex_get ();
+      lex_get (lexer);
     }
 
   /* If we saw a complete command name earlier, drop back to
@@ -522,9 +542,9 @@ parse_command_name (void)
         {
           word_cnt--;
           if (strcmp (words[word_cnt], "-")) 
-            lex_put_back_id (words[word_cnt]);
+            lex_put_back_id (lexer, words[word_cnt]);
           else
-            lex_put_back ('-');
+            lex_put_back (lexer, '-');
           free (words[word_cnt]);
         }
 
@@ -533,25 +553,29 @@ parse_command_name (void)
     }
 
   /* We didn't get a valid command name. */
-  unknown_command_error (words, word_cnt);
+  unknown_command_error (lexer, words, word_cnt);
   free_words (words, word_cnt);
   return NULL;
 }
 
 /* Returns true if COMMAND is allowed in STATE,
-   false otherwise.
-   If COMMAND is not allowed, emits an appropriate error
-   message. */
+   false otherwise. */
 static bool
-verify_valid_command (const struct command *command, enum cmd_state state)
+in_correct_state (const struct command *command, enum cmd_state state) 
 {
-  if ((state == CMD_STATE_INITIAL && command->states & S_INITIAL)
-      || (state == CMD_STATE_DATA && command->states & S_DATA)
-      || (state == CMD_STATE_INPUT_PROGRAM
-          && command->states & S_INPUT_PROGRAM)
-      || (state == CMD_STATE_FILE_TYPE && command->states & S_FILE_TYPE))
-    return true;
+  return ((state == CMD_STATE_INITIAL && command->states & S_INITIAL)
+          || (state == CMD_STATE_DATA && command->states & S_DATA)
+          || (state == CMD_STATE_INPUT_PROGRAM
+              && command->states & S_INPUT_PROGRAM)
+          || (state == CMD_STATE_FILE_TYPE && command->states & S_FILE_TYPE));
+}
 
+/* Emits an appropriate error message for trying to invoke
+   COMMAND in STATE. */
+static bool
+report_state_mismatch (const struct command *command, enum cmd_state state)
+{
+  assert (!in_correct_state (command, state));
   if (state == CMD_STATE_INITIAL || state == CMD_STATE_DATA)
     {
       const char *allowed[3];
@@ -575,7 +599,7 @@ verify_valid_command (const struct command *command, enum cmd_state state)
       else if (allowed_cnt == 3)
         s = xasprintf (_("%s, %s, or %s"), allowed[0], allowed[1], allowed[2]);
       else
-        abort ();
+        NOT_REACHED ();
 
       msg (SE, _("%s is allowed only %s."), command->name, s);
 
@@ -589,90 +613,76 @@ verify_valid_command (const struct command *command, enum cmd_state state)
   return false;
 }
 \f
-/* Readline command name completion. */
+/* Command name completion. */
 
-#if HAVE_READLINE
-static char *command_generator (const char *text, int state);
-
-/* Returns a set of completions for TEXT.
-   This is of the proper form for assigning to
-   rl_attempted_completion_function. */
-char **
-pspp_attempted_completion_function (const char *text,
-                                    int start, int end UNUSED)
+static enum cmd_state completion_state = CMD_STATE_INITIAL;
+
+static void
+set_completion_state (enum cmd_state state) 
 {
-  if (start == 0) 
-    {
-      /* Complete command name at start of line. */
-      return rl_completion_matches (text, command_generator); 
-    }
-  else 
-    {
-      /* Otherwise don't do any completion. */
-      rl_attempted_completion_over = 1;
-      return NULL; 
-    }
+  completion_state = state;
 }
 
-/* If STATE is 0, returns the first command name matching TEXT.
-   Otherwise, returns the next command name matching TEXT.
-   Returns a null pointer when no matches are left. */
-static char *
-command_generator (const char *text, int state) 
+/* Returns the next possible completion of a command name that
+   begins with PREFIX, in the current command state, or a null
+   pointer if no completions remain.
+   Before calling the first time, set *CMD to a null pointer. */
+const char *
+cmd_complete (const char *prefix, const struct command **cmd)
 {
-  static const struct command *cmd;
-  
-  if (!state)
-    cmd = commands;
-
-  for (; cmd < commands + command_cnt; cmd++) 
-    if (!memcasecmp (cmd->name, text, strlen (text))
-        && (!(cmd->flags & F_TESTING) || get_testing_mode ())
-        && (!(cmd->flags & F_ENHANCED) || get_syntax () == ENHANCED))
-      return xstrdup (cmd++->name);
+  if (*cmd == NULL)
+    *cmd = commands;
+
+  for (; *cmd < commands + command_cnt; (*cmd)++) 
+    if (!memcasecmp ((*cmd)->name, prefix, strlen (prefix))
+        && (!((*cmd)->flags & F_TESTING) || get_testing_mode ())
+        && (!((*cmd)->flags & F_ENHANCED) || get_syntax () == ENHANCED)
+        && !((*cmd)->flags & F_ABBREV)
+        && ((*cmd)->function != NULL)
+        && in_correct_state (*cmd, completion_state))
+      return (*cmd)++->name;
 
   return NULL;
 }
-#endif /* HAVE_READLINE */
 \f
 /* Simple commands. */
 
 /* Parse and execute FINISH command. */
 int
-cmd_finish (void)
+cmd_finish (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
 {
   return CMD_FINISH;
 }
 
 /* Parses the N command. */
 int
-cmd_n_of_cases (void)
+cmd_n_of_cases (struct lexer *lexer, struct dataset *ds)
 {
   /* Value for N. */
   int x;
 
-  if (!lex_force_int ())
+  if (!lex_force_int (lexer))
     return CMD_FAILURE;
-  x = lex_integer ();
-  lex_get ();
-  if (!lex_match_id ("ESTIMATED"))
-    dict_set_case_limit (default_dict, x);
+  x = lex_integer (lexer);
+  lex_get (lexer);
+  if (!lex_match_id (lexer, "ESTIMATED"))
+    dict_set_case_limit (dataset_dict (ds), x);
 
-  return lex_end_of_command ();
+  return lex_end_of_command (lexer);
 }
 
 /* Parses, performs the EXECUTE procedure. */
 int
-cmd_execute (void)
+cmd_execute (struct lexer *lexer, struct dataset *ds)
 {
-  if (!procedure (NULL, NULL))
+  if (!procedure (ds, NULL, NULL))
     return CMD_CASCADING_FAILURE;
-  return lex_end_of_command ();
+  return lex_end_of_command (lexer);
 }
 
 /* Parses, performs the ERASE command. */
 int
-cmd_erase (void)
+cmd_erase (struct lexer *lexer, struct dataset *ds UNUSED)
 {
   if (get_safer_mode ()) 
     { 
@@ -680,16 +690,16 @@ cmd_erase (void)
       return CMD_FAILURE; 
     } 
   
-  if (!lex_force_match_id ("FILE"))
+  if (!lex_force_match_id (lexer, "FILE"))
     return CMD_FAILURE;
-  lex_match ('=');
-  if (!lex_force_string ())
+  lex_match (lexer, '=');
+  if (!lex_force_string (lexer))
     return CMD_FAILURE;
 
-  if (remove (ds_c_str (&tokstr)) == -1)
+  if (remove (ds_cstr (lex_tokstr (lexer))) == -1)
     {
       msg (SW, _("Error removing `%s': %s."),
-          ds_c_str (&tokstr), strerror (errno));
+          ds_cstr (lex_tokstr (lexer)), strerror (errno));
       return CMD_FAILURE;
     }
 
@@ -753,27 +763,27 @@ shell (void)
 /* Parses the HOST command argument and executes the specified
    command.  Returns a suitable command return code. */
 static int
-run_command (void)
+run_command (struct lexer *lexer)
 {
   const char *cmd;
   int string;
 
   /* Handle either a string argument or a full-line argument. */
   {
-    int c = lex_look_ahead ();
+    int c = lex_look_ahead (lexer);
 
     if (c == '\'' || c == '"')
       {
-       lex_get ();
-       if (!lex_force_string ())
+       lex_get (lexer);
+       if (!lex_force_string (lexer))
          return CMD_FAILURE;
-       cmd = ds_c_str (&tokstr);
+       cmd = ds_cstr (lex_tokstr (lexer));
        string = 1;
       }
     else
       {
-       cmd = lex_rest_of_line (NULL);
-        lex_discard_line ();
+       cmd = lex_rest_of_line (lexer, NULL);
+        lex_discard_line (lexer);
        string = 0;
       }
   }
@@ -785,23 +795,21 @@ run_command (void)
   /* Finish parsing. */
   if (string)
     {
-      lex_get ();
+      lex_get (lexer);
 
-      if (token != '.')
+      if (lex_token (lexer) != '.')
        {
-         lex_error (_("expecting end of command"));
+         lex_error (lexer, _("expecting end of command"));
          return CMD_FAILURE;
        }
     }
-  else
-    token = '.';
 
   return CMD_SUCCESS;
 }
 
 /* Parses, performs the HOST command. */
 int
-cmd_host (void)
+cmd_host (struct lexer *lexer, struct dataset *ds UNUSED)
 {
   int code;
 
@@ -814,18 +822,18 @@ cmd_host (void)
 #ifdef unix
   /* Figure out whether to invoke an interactive shell or to execute a
      single shell command. */
-  if (lex_look_ahead () == '.')
+  if (lex_look_ahead (lexer) == '.')
     {
-      lex_get ();
+      lex_get (lexer);
       code = shell () ? CMD_FAILURE : CMD_SUCCESS;
     }
   else
-    code = run_command ();
+    code = run_command (lexer);
 #else /* !unix */
   /* Make sure that the system has a command interpreter, then run a
      command. */
   if (system (NULL) != 0)
-    code = run_command ();
+    code = run_command (lexer);
   else
     {
       msg (SE, _("No operating system support for this command."));
@@ -838,17 +846,17 @@ cmd_host (void)
 
 /* Parses, performs the NEW FILE command. */
 int
-cmd_new_file (void)
+cmd_new_file (struct lexer *lexer, struct dataset *ds)
 {
-  discard_variables ();
+  discard_variables (ds);
 
-  return lex_end_of_command ();
+  return lex_end_of_command (lexer);
 }
 
 /* Parses a comment. */
 int
-cmd_comment (void)
+cmd_comment (struct lexer *lexer, struct dataset *ds UNUSED)
 {
-  lex_skip_comment ();
+  lex_skip_comment (lexer);
   return CMD_SUCCESS;
 }