From 5729150f2c4a00caf688e7e3b514b10b1987e211 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 28 Jun 2006 05:49:03 +0000 Subject: [PATCH] Fix regression in command name completion reported by John Darrington. Now completion is again state-dependent and occurs only on the first line of a command. --- src/language/ChangeLog | 27 ++++++++ src/language/command.c | 108 +++++++++++++++++--------------- src/language/command.h | 7 +-- src/language/data-io/ChangeLog | 9 +++ src/language/data-io/inpt-pgm.c | 4 +- src/language/line-buffer.c | 16 ++--- src/language/line-buffer.h | 20 +++--- src/ui/terminal/ChangeLog | 19 ++++++ src/ui/terminal/main.c | 23 +------ src/ui/terminal/read-line.c | 61 ++++++++++++++++-- src/ui/terminal/read-line.h | 3 +- 11 files changed, 193 insertions(+), 104 deletions(-) diff --git a/src/language/ChangeLog b/src/language/ChangeLog index a3d3b20b..962a80a1 100644 --- a/src/language/ChangeLog +++ b/src/language/ChangeLog @@ -1,3 +1,30 @@ +Tue Jun 27 22:36:38 2006 Ben Pfaff + + Fix regression in command name completion reported by John + Darrington. Now completion is again state-dependent and occurs + only on the first line of a command. + + * command.c (do_parse_command): Move reading the first token of + the command here, from execute_command and cmd_input_program. + Call set_completion_state and getl_set_prompt_style here. + (do_parse_command) Use in_correct_state instead of + verify_valid_command. + (verify_valid_command) Break into two new functions, + in_correct_state and report_state_mismatch. + (set_completion_state) New function. + (cmd_complete) New function. + [HAVE_READLINE] (pspp_attempted_completion_function) Removed. + [HAVE_READLINE] (command_generator) Removed. + + * line-buffer.c: (struct getl_source) Change `interactive' member + signature to take enum getl_prompt_style instead of const char *. + (create_interactive_source) Ditto, for parameter type. + (getl_append_interactive) Ditto. + (read_line_from_source) Pass get_prompt_style() to interactive + function instead of get_prompt(). + (get_prompt) Removed. + (get_prompt_style) New function. + Sat May 6 13:25:25 2006 Ben Pfaff Continue reforming procedure execution. In this phase, remove diff --git a/src/language/command.c b/src/language/command.c index ed043d21..c55ff1b8 100644 --- a/src/language/command.c +++ b/src/language/command.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -120,8 +121,10 @@ 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); /* Command parser. */ @@ -156,9 +159,18 @@ do_parse_command (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. */ + getl_set_prompt_style (GETL_PROMPT_FIRST); + set_completion_state (state); + lex_get (); + if (token == T_STOP) + return CMD_EOF; + else if (token == '.') + { + /* Null commands can result from extra empty lines. */ + return CMD_SUCCESS; + } + getl_set_prompt_style (GETL_PROMPT_LATER); /* Parse the command name. */ command = parse_command_name (); @@ -177,8 +189,11 @@ 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); @@ -539,19 +554,23 @@ parse_command_name (void) } /* 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]; @@ -589,51 +608,36 @@ verify_valid_command (const struct command *command, enum cmd_state state) return false; } -/* 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; + if (*cmd == NULL) + *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); + 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)->function != NULL) + && in_correct_state (*cmd, completion_state)) + return (*cmd)++->name; return NULL; } -#endif /* HAVE_READLINE */ /* Simple commands. */ diff --git a/src/language/command.h b/src/language/command.h index ec51f658..3cf5b997 100644 --- a/src/language/command.h +++ b/src/language/command.h @@ -54,12 +54,11 @@ enum cmd_state CMD_STATE_FILE_TYPE /* Inside FILE TYPE. */ }; -#if HAVE_READLINE -char **pspp_attempted_completion_function (const char *, int start, int end); -#endif - enum cmd_result cmd_parse (enum cmd_state); +struct command; +const char *cmd_complete (const char *, const struct command **); + /* Prototype all the command functions. */ #define DEF_CMD(STATES, FLAGS, NAME, FUNCTION) int FUNCTION (void); #define UNIMPL_CMD(NAME, DESCRIPTION) diff --git a/src/language/data-io/ChangeLog b/src/language/data-io/ChangeLog index 31e809e5..37047d67 100644 --- a/src/language/data-io/ChangeLog +++ b/src/language/data-io/ChangeLog @@ -1,3 +1,12 @@ +Tue Jun 27 22:44:28 2006 Ben Pfaff + + Fix regression in command name completion reported by John + Darrington. Now completion is again state-dependent and occurs + only on the first line of a command. + + * inpt-pgm.c: (cmd_input_program) Reading of first token in + command moved into cmd_parse. + Fri Jun 9 13:56:00 2006 Ben Pfaff Reform string library. diff --git a/src/language/data-io/inpt-pgm.c b/src/language/data-io/inpt-pgm.c index 51c36981..94fa4044 100644 --- a/src/language/data-io/inpt-pgm.c +++ b/src/language/data-io/inpt-pgm.c @@ -119,9 +119,7 @@ cmd_input_program (void) inside_input_program = true; for (;;) { - enum cmd_result result; - lex_get (); - result = cmd_parse (CMD_STATE_INPUT_PROGRAM); + enum cmd_result result = cmd_parse (CMD_STATE_INPUT_PROGRAM); if (result == CMD_END_INPUT_PROGRAM) break; else if (result == CMD_END_CASE) diff --git a/src/language/line-buffer.c b/src/language/line-buffer.c index 1b48f538..96ef8559 100644 --- a/src/language/line-buffer.c +++ b/src/language/line-buffer.c @@ -85,7 +85,7 @@ struct getl_source function; /* INTERACTIVE. */ - bool (*interactive) (struct string *line, const char *prompt); + bool (*interactive) (struct string *line, enum getl_prompt_style); } u; @@ -103,7 +103,7 @@ static void close_source (void); static void init_prompts (void); static void uninit_prompts (void); -static const char *get_prompt (void); +static enum getl_prompt_style get_prompt_style (void); /* Initialize getl. */ void @@ -214,7 +214,7 @@ create_function_source (bool (*read) (struct string *line, /* Creates an interactive source with the given FUNCTION. */ static struct getl_source * create_interactive_source (bool (*function) (struct string *line, - const char *prompt)) + enum getl_prompt_style)) { struct getl_source *s = xmalloc (sizeof *s); s->fn = NULL; @@ -297,7 +297,7 @@ getl_include_function (bool (*read) (struct string *line, obtained or false at end of file. */ void getl_append_interactive (bool (*function) (struct string *line, - const char *prompt)) + enum getl_prompt_style)) { append_source (create_interactive_source (function)); } @@ -489,7 +489,7 @@ read_line_from_source (struct string *line, struct getl_source *s) case FUNCTION: return s->u.function.read (line, &s->fn, &s->ln, s->u.function.aux); case INTERACTIVE: - return s->u.interactive (line, get_prompt ()); + return s->u.interactive (line, get_prompt_style ()); } abort (); @@ -589,8 +589,8 @@ getl_set_prompt_style (enum getl_prompt_style style) } /* Returns the current prompt. */ -static const char * -get_prompt (void) +static enum getl_prompt_style +get_prompt_style (void) { - return prompts[current_style]; + return current_style; } diff --git a/src/language/line-buffer.h b/src/language/line-buffer.h index 59dab0a5..b47a2ce3 100644 --- a/src/language/line-buffer.h +++ b/src/language/line-buffer.h @@ -23,6 +23,14 @@ #include #include +enum getl_prompt_style + { + GETL_PROMPT_FIRST, /* First line of command. */ + GETL_PROMPT_LATER, /* Second or later line of command. */ + GETL_PROMPT_DATA, /* Between BEGIN DATA and END DATA. */ + GETL_PROMPT_CNT + }; + /* Current line. This line may be modified by modules other than getl.c, and by lexer.c in particular. (Ugh.) */ extern struct string getl_buf; @@ -43,23 +51,13 @@ void getl_include_function (bool (*read) (struct string *line, void (*close) (void *aux), void *aux); void getl_append_interactive (bool (*function) (struct string *line, - const char *prompt)); + enum getl_prompt_style)); void getl_abort_noninteractive (void); bool getl_is_interactive (void); bool getl_read_line (bool *interactive); void getl_location (const char **, int *); - -/* Prompting. */ - -enum getl_prompt_style - { - GETL_PROMPT_FIRST, /* First line of command. */ - GETL_PROMPT_LATER, /* Second or later line of command. */ - GETL_PROMPT_DATA, /* Between BEGIN DATA and END DATA. */ - GETL_PROMPT_CNT - }; const char *getl_get_prompt (enum getl_prompt_style); void getl_set_prompt (enum getl_prompt_style, const char *); diff --git a/src/ui/terminal/ChangeLog b/src/ui/terminal/ChangeLog index 00cef0f7..039ac644 100644 --- a/src/ui/terminal/ChangeLog +++ b/src/ui/terminal/ChangeLog @@ -1,3 +1,22 @@ +Tue Jun 27 22:44:56 2006 Ben Pfaff + + Fix regression in command name completion reported by John + Darrington. Now completion is again state-dependent and occurs + only on the first line of a command. + + * main.c (main): Reading of first token in command moved into + cmd_parse. + (execute_command) Removed. + + * read-line.c: [HAVE_READLINE] (readln_initialize) Postpone + setting rl_attempted_completion_function until readln_read. + [HAVE_READLINE] (readln_read) Change param from const char * to + enum getl_prompt_style. Set rl_attempted_completion_function + based on style. + [HAVE_READLINE] (complete_command_name) New function. + [HAVE_READLINE] (dont_complete) New function. + [HAVE_READLINE] (command_generator) New function. + Tue Jun 27 08:23:07 2006 Ben Pfaff * automake.mk (src_ui_terminal_pspp_LDADD): Add $(LIBICONV). diff --git a/src/ui/terminal/main.c b/src/ui/terminal/main.c index e19dd0c7..2ad53d36 100644 --- a/src/ui/terminal/main.c +++ b/src/ui/terminal/main.c @@ -62,7 +62,6 @@ static void i18n_init (void); static void fpu_init (void); -static int execute_command (void); static void terminate (bool success) NO_RETURN; /* If a segfault happens, issue a message to that effect and halt */ @@ -102,7 +101,8 @@ main (int argc, char **argv) for (;;) { - int result = execute_command (); + int result = cmd_parse (proc_has_source () + ? CMD_STATE_DATA : CMD_STATE_INITIAL); if (result == CMD_EOF || result == CMD_FINISH) break; if (result == CMD_CASCADING_FAILURE && !getl_is_interactive ()) @@ -118,25 +118,6 @@ main (int argc, char **argv) terminate (!any_errors ()); } - -/* Parses a command and returns the result. */ -static int -execute_command (void) -{ - /* Read the command's first token. - The first token is part of the first line of the command. */ - getl_set_prompt_style (GETL_PROMPT_FIRST); - lex_get (); - if (token == T_STOP) - return CMD_EOF; - - /* Parse the command. - Any lines read after the first token must be continuation - lines. */ - getl_set_prompt_style (GETL_PROMPT_LATER); - return cmd_parse (proc_has_source () - ? CMD_STATE_DATA : CMD_STATE_INITIAL); -} static void i18n_init (void) diff --git a/src/ui/terminal/read-line.c b/src/ui/terminal/read-line.c index a87adfcf..08f93623 100644 --- a/src/ui/terminal/read-line.c +++ b/src/ui/terminal/read-line.c @@ -37,15 +37,19 @@ #include #include +#include "xalloc.h" + #include "gettext.h" #define _(msgid) gettext (msgid) - #if HAVE_READLINE #include #include static char *history_file; + +static char **complete_command_name (const char *, int, int); +static char **dont_complete (const char *, int, int); #endif /* HAVE_READLINE */ static bool initialised = false; @@ -58,7 +62,6 @@ readln_initialize (void) #if HAVE_READLINE rl_basic_word_break_characters = "\n"; - rl_attempted_completion_function = pspp_attempted_completion_function; #ifdef unix if (history_file == NULL) { @@ -113,19 +116,23 @@ welcome (void) Returns true if successful, false at end of file. Suitable for passing to getl_append_interactive(). */ bool -readln_read (struct string *line, const char *prompt) +readln_read (struct string *line, enum getl_prompt_style style) { + const char *prompt = getl_get_prompt (style); #if HAVE_READLINE char *string; #endif - assert(initialised); + assert (initialised); reset_msg_count (); welcome (); #if HAVE_READLINE + rl_attempted_completion_function = (style == GETL_PROMPT_FIRST + ? complete_command_name + : dont_complete); string = readline (prompt); if (string == NULL) return false; @@ -149,3 +156,49 @@ readln_read (struct string *line, const char *prompt) return false; #endif } + +#ifdef HAVE_READLINE +static char *command_generator (const char *text, int state); + +/* Returns a set of command name completions for TEXT. + This is of the proper form for assigning to + rl_attempted_completion_function. */ +static char ** +complete_command_name (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; + } +} + +/* Do not do any completion for TEXT. */ +static char ** +dont_complete (const char *text UNUSED, int start UNUSED, int end UNUSED) +{ + 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; + const char *name; + + if (state == 0) + cmd = NULL; + name = cmd_complete (text, &cmd); + return name ? xstrdup (name) : NULL; +} +#endif /* HAVE_READLINE */ diff --git a/src/ui/terminal/read-line.h b/src/ui/terminal/read-line.h index cadd2cce..9d490fc9 100644 --- a/src/ui/terminal/read-line.h +++ b/src/ui/terminal/read-line.h @@ -21,10 +21,11 @@ #define READLN_H #include +#include void readln_initialize (void); void readln_uninitialize (void); -bool readln_read (struct string *line, const char *prompt); +bool readln_read (struct string *line, enum getl_prompt_style); #endif /* READLN_H */ -- 2.30.2