+
+/* 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)
+ {
+ switch (command->states)
+ {
+ /* One allowed state. */
+ case S_INITIAL:
+ msg (SE, _("%s is allowed only before the active file has "
+ "been defined."), command->name);
+ break;
+ case S_DATA:
+ msg (SE, _("%s is allowed only after the active file has "
+ "been defined."), command->name);
+ break;
+ case S_INPUT_PROGRAM:
+ msg (SE, _("%s is allowed only inside INPUT PROGRAM."),
+ command->name);
+ break;
+ case S_FILE_TYPE:
+ msg (SE, _("%s is allowed only inside FILE TYPE."), command->name);
+ break;
+
+ /* Two allowed states. */
+ case S_INITIAL | S_DATA:
+ NOT_REACHED ();
+ case S_INITIAL | S_INPUT_PROGRAM:
+ msg (SE, _("%s is allowed only before the active file has "
+ "been defined or inside INPUT PROGRAM."), command->name);
+ break;
+ case S_INITIAL | S_FILE_TYPE:
+ msg (SE, _("%s is allowed only before the active file has "
+ "been defined or inside FILE TYPE."), command->name);
+ break;
+ case S_DATA | S_INPUT_PROGRAM:
+ msg (SE, _("%s is allowed only after the active file has "
+ "been defined or inside INPUT PROGRAM."), command->name);
+ break;
+ case S_DATA | S_FILE_TYPE:
+ msg (SE, _("%s is allowed only after the active file has "
+ "been defined or inside FILE TYPE."), command->name);
+ break;
+ case S_INPUT_PROGRAM | S_FILE_TYPE:
+ msg (SE, _("%s is allowed only inside INPUT PROGRAM "
+ "or inside FILE TYPE."), command->name);
+ break;
+
+ /* Three allowed states. */
+ case S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE:
+ msg (SE, _("%s is allowed only after the active file has "
+ "been defined, inside INPUT PROGRAM, or inside "
+ "FILE TYPE."), command->name);
+ break;
+ case S_INITIAL | S_INPUT_PROGRAM | S_FILE_TYPE:
+ msg (SE, _("%s is allowed only before the active file has "
+ "been defined, inside INPUT PROGRAM, or inside "
+ "FILE TYPE."), command->name);
+ break;
+ case S_INITIAL | S_DATA | S_FILE_TYPE:
+ NOT_REACHED ();
+ case S_INITIAL | S_DATA | S_INPUT_PROGRAM:
+ NOT_REACHED ();
+
+ /* Four allowed states. */
+ case S_INITIAL | S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE:
+ NOT_REACHED ();
+
+ default:
+ NOT_REACHED ();
+ }
+ }
+ 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) || settings_get_testing_mode ())
+ && (!((*cmd)->flags & F_ENHANCED) || settings_get_syntax () == ENHANCED)
+ && !((*cmd)->flags & F_ABBREV)
+ && ((*cmd)->function != NULL)
+ && in_correct_state (*cmd, completion_state))
+ return (*cmd)++->name;
+
+ return NULL;
+}