X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcommand.c;h=70d5b02e747416e9f08554d4c57868196aef3812;hb=9105b67fe006fe41c044e3659325594a52d0c899;hp=ed043d2197363fc0ef81cebae2f119f480573ad1;hpb=b74d09af5e07f954c18e7cdb8aca3af47fa10208;p=pspp diff --git a/src/language/command.c b/src/language/command.c index ed043d2197..70d5b02e74 100644 --- a/src/language/command.c +++ b/src/language/command.c @@ -1,6 +1,5 @@ /* PSPP - computes sample statistics. Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . 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 #include #include +#include #include +#include #include #include #include @@ -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,52 +122,78 @@ 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. */ -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_in_state (struct lexer *lexer, struct dataset *ds, + enum cmd_state state) { int result; - + som_new_series (); - result = do_parse_command (state); - if (cmd_result_is_failure (result)) - lex_discard_rest_of_command (); + result = do_parse_command (lexer, ds, state); + if (cmd_result_is_failure (result)) + lex_discard_rest_of_command (lexer); unset_cmd_algorithm (); - dict_clear_aux (default_dict); + dict_clear_aux (dataset_dict (ds)); return result; } +enum cmd_result +cmd_parse (struct lexer *lexer, struct dataset *ds) +{ + const struct dictionary *dict = dataset_dict (ds); + return cmd_parse_in_state (lexer, ds, + proc_has_source (ds) && + dict_get_var_cnt (dict) > 0 ? + CMD_STATE_DATA : CMD_STATE_INITIAL); +} + + /* 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 +205,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 +278,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 +292,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 +301,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 +436,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,10 +452,10 @@ 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; @@ -448,29 +479,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] = ds_xstrdup (&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 +516,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 +527,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 +554,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 +565,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 +611,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 +625,76 @@ 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; - - 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 */ /* 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,25 +702,25 @@ 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_cstr (&tokstr)) == -1) + if (remove (ds_cstr (lex_tokstr (lexer))) == -1) { msg (SW, _("Error removing `%s': %s."), - ds_cstr (&tokstr), strerror (errno)); + ds_cstr (lex_tokstr (lexer)), strerror (errno)); return CMD_FAILURE; } return CMD_SUCCESS; } -#ifdef unix -/* Spawn a shell process. */ -static int +#if HAVE_FORK && HAVE_EXECL +/* Spawn an interactive shell process. */ +static bool shell (void) { int pid; @@ -739,71 +761,48 @@ shell (void) case -1: msg (SE, _("Couldn't fork: %s."), strerror (errno)); - return 0; + return false; default: assert (pid > 0); while (wait (NULL) != pid) ; - return 1; + return true; } } -#endif /* unix */ - -/* Parses the HOST command argument and executes the specified - command. Returns a suitable command return code. */ -static int -run_command (void) +#else /* !(HAVE_FORK && HAVE_EXECL) */ +/* Don't know how to spawn an interactive shell. */ +static bool +shell (void) { - const char *cmd; - int string; - - /* Handle either a string argument or a full-line argument. */ - { - int c = lex_look_ahead (); + msg (SE, _("Interactive shell not supported on this platform.")); + return false; +} +#endif - if (c == '\'' || c == '"') - { - lex_get (); - if (!lex_force_string ()) - return CMD_FAILURE; - cmd = ds_cstr (&tokstr); - string = 1; - } - else - { - cmd = lex_rest_of_line (NULL); - lex_discard_line (); - string = 0; - } - } +/* Executes the specified COMMAND in a subshell. Returns true if + successful, false otherwise. */ +static bool +run_command (const char *command) +{ + if (system (NULL) == 0) + { + msg (SE, _("Command shell not supported on this platform.")); + return false; + } /* Execute the command. */ - if (system (cmd) == -1) + if (system (command) == -1) msg (SE, _("Error executing command: %s."), strerror (errno)); - /* Finish parsing. */ - if (string) - { - lex_get (); - - if (token != '.') - { - lex_error (_("expecting end of command")); - return CMD_FAILURE; - } - } - else - token = '.'; - - return CMD_SUCCESS; + return true; } /* Parses, performs the HOST command. */ int -cmd_host (void) +cmd_host (struct lexer *lexer, struct dataset *ds UNUSED) { - int code; + int look_ahead; if (get_safer_mode ()) { @@ -811,44 +810,45 @@ cmd_host (void) return CMD_FAILURE; } -#ifdef unix - /* Figure out whether to invoke an interactive shell or to execute a - single shell command. */ - if (lex_look_ahead () == '.') + look_ahead = lex_look_ahead (lexer); + if (look_ahead == '.') { - lex_get (); - code = shell () ? CMD_FAILURE : CMD_SUCCESS; + lex_get (lexer); + return shell () ? CMD_SUCCESS : CMD_FAILURE; } - else - code = run_command (); -#else /* !unix */ - /* Make sure that the system has a command interpreter, then run a - command. */ - if (system (NULL) != 0) - code = run_command (); - else + else if (look_ahead == '\'' || look_ahead == '"') { - msg (SE, _("No operating system support for this command.")); - code = CMD_FAILURE; - } -#endif /* !unix */ + bool ok; + + lex_get (lexer); + if (!lex_force_string (lexer)) + NOT_REACHED (); + ok = run_command (ds_cstr (lex_tokstr (lexer))); - return code; + lex_get (lexer); + return ok ? lex_end_of_command (lexer) : CMD_FAILURE; + } + else + { + bool ok = run_command (lex_rest_of_line (lexer, NULL)); + lex_discard_line (lexer); + return ok ? CMD_SUCCESS : CMD_FAILURE; + } } /* 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; }