+
+/* Returns true if COMMAND is allowed in STATE,
+ false otherwise. */
+static bool
+in_correct_state (const struct command *command, enum cmd_state state)
+{
+ 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];
+ 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
+ NOT_REACHED ();
+
+ 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
+/* Command name completion. */
+
+static enum cmd_state completion_state = CMD_STATE_INITIAL;
+
+static void
+set_completion_state (enum cmd_state state)
+{
+ completion_state = 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)
+{
+ 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;
+}