+
+/* Returns true if COMMAND is allowed in STATE,
+ false otherwise.
+ If COMMAND is not allowed, emits an appropriate error
+ message. */
+static bool
+verify_valid_command (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;
+
+ if (state == CMD_STATE_INITIAL || state == CMD_STATE_DATA)
+ {
+ const char *allowed[3];
+ int allowed_cnt;
+ char *s;
+
+ allowed_cnt = 0;
+ if (command->states & S_INITIAL)
+ allowed[allowed_cnt++] = _("before the active file has been defined");
+ else if (command->states & S_DATA)
+ allowed[allowed_cnt++] = _("after the active file has been defined");
+ if (command->states & S_INPUT_PROGRAM)
+ allowed[allowed_cnt++] = _("inside INPUT PROGRAM");
+ if (command->states & S_FILE_TYPE)
+ allowed[allowed_cnt++] = _("inside FILE TYPE");
+
+ if (allowed_cnt == 1)
+ s = xstrdup (allowed[0]);
+ else if (allowed_cnt == 2)
+ s = xasprintf (_("%s or %s"), allowed[0], allowed[1]);
+ else if (allowed_cnt == 3)
+ s = xasprintf (_("%s, %s, or %s"), allowed[0], allowed[1], allowed[2]);
+ else
+ abort ();
+
+ msg (SE, _("%s is allowed only %s."), command->name, s);
+
+ free (s);
+ }
+ else if (state == CMD_STATE_INPUT_PROGRAM)
+ msg (SE, _("%s is not allowed inside INPUT PROGRAM."), command->name);
+ else if (state == CMD_STATE_FILE_TYPE)
+ msg (SE, _("%s is not allowed inside FILE TYPE."), command->name);
+
+ return false;
+}
+\f
+/* Readline 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)
+{
+ 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;
+ }
+}
+
+/* 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)
+{
+ 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);
+
+ return NULL;
+}
+#endif /* HAVE_READLINE */