Add '--init-command', '-C' to the command line switches.

In order to allow the execution of commands before dropping to an
interactive prompt, a new switch, '-C' or '--init-command' has been
added to those switches that we accept.

The documentation has been updated correspondingly.

The original code only supported a single command list to be executed,
and this command list terminates the shell when it completes. To allow
the new command list to preceed the original one, both have been
wrapped in a new container class 'command_line_switches_t'. This is
then passed around in place of the list of strings we used previously.

I had considered moving the interactive, login and other command line
switch states into this container, but doing so would change far more
of the code, moving the structure to be available globally, and I
wasn't confident of the impact. However, this might be a useful thing
to do in the future.

A new function, run_command_list, was lifted from the prior execution
code, and re-used for both the initial command and the regular command
execution.
This commit is contained in:
Charles Ferguson 2017-06-29 15:21:00 +01:00 committed by Kurtis Rader
parent 3b5fdc3fb0
commit 053d940d0a
2 changed files with 49 additions and 16 deletions

View file

@ -13,6 +13,8 @@ The following options are available:
- `-c` or `--command=COMMANDS` evaluate the specified commands instead of reading from the commandline
- `-C` or `--init-command=COMMANDS` evaluate the specified commands after reading the configuration, before running the command specified by `-c` or reading interactive input
- `-d` or `--debug-level=DEBUG_LEVEL` specify the verbosity level of fish. A higher number means higher verbosity. The default level is 1.
- `-i` or `--interactive` specify that fish is to run in interactive mode

View file

@ -1,3 +1,4 @@
//
// The main loop of the fish program.
/*
Copyright (C) 2005-2008 Axel Liljencrantz
@ -57,6 +58,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#define PATH_MAX 4096
#endif
// container to hold the options specified within the command line
class fish_cmd_opts_t {
public:
// Commands to be executed in place of interactive shell.
std::vector<std::string> batch_cmds;
// Commands to execute after the shell's config has been read.
std::vector<std::string> postconfig_cmds;
};
/// If we are doing profiling, the filename to output to.
static const char *s_profiling_output_filename = NULL;
@ -214,10 +225,23 @@ static int read_init(const struct config_paths_t &paths) {
return 1;
}
int run_command_list(std::vector<std::string> *cmds, const io_chain_t &io) {
int res = 1;
parser_t &parser = parser_t::principal_parser();
for (size_t i = 0; i < cmds->size(); i++) {
const wcstring cmd_wcs = str2wcstring(cmds->at(i));
res = parser.eval(cmd_wcs, io, TOP);
}
return res;
}
/// Parse the argument list, return the index of the first non-flag arguments.
static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds) {
static const char *short_opts = "+hilnvc:p:d:D:";
static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) {
static const char *short_opts = "+hilnvc:C:p:d:D:";
static const struct option long_opts[] = {{"command", required_argument, NULL, 'c'},
{"init-command", required_argument, NULL, 'C'},
{"debug-level", required_argument, NULL, 'd'},
{"debug-stack-frames", required_argument, NULL, 'D'},
{"interactive", no_argument, NULL, 'i'},
@ -232,7 +256,11 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
while ((opt = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
switch (opt) {
case 'c': {
cmds->push_back(optarg);
opts->batch_cmds.push_back(optarg);
break;
}
case 'C': {
opts->postconfig_cmds.push_back(optarg);
break;
}
case 'd': {
@ -251,7 +279,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
break;
}
case 'h': {
cmds->push_back("__fish_print_help fish");
opts->batch_cmds.push_back("__fish_print_help fish");
break;
}
case 'i': {
@ -305,7 +333,7 @@ static int fish_parse_opt(int argc, char **argv, std::vector<std::string> *cmds)
// We are an interactive session if we have not been given an explicit
// command or file to execute and stdin is a tty. Note that the -i or
// --interactive options also force interactive mode.
if (cmds->size() == 0 && optind == argc && isatty(STDIN_FILENO)) {
if (opts->batch_cmds.size() == 0 && optind == argc && isatty(STDIN_FILENO)) {
is_interactive_session = 1;
}
@ -331,8 +359,8 @@ int main(int argc, char **argv) {
argv = (char **)dummy_argv; //!OCLINT(parameter reassignment)
argc = 1; //!OCLINT(parameter reassignment)
}
std::vector<std::string> cmds;
my_optind = fish_parse_opt(argc, argv, &cmds);
fish_cmd_opts_t opts;
my_optind = fish_parse_opt(argc, argv, &opts);
// No-exec is prohibited when in interactive mode.
if (is_interactive_session && no_exec) {
@ -370,19 +398,22 @@ int main(int argc, char **argv) {
// Stomp the exit status of any initialization commands (issue #635).
proc_set_last_status(STATUS_CMD_OK);
// Run post-config commands specified as arguments, if any.
if (!opts.postconfig_cmds.empty()) {
res = run_command_list(&opts.postconfig_cmds, empty_ios);
}
if (!opts.batch_cmds.empty()) {
// Run the commands specified as arguments, if any.
if (!cmds.empty()) {
// Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds.
if (is_login) {
fish_xdm_login_hack_hack_hack_hack(&cmds, argc - my_optind, argv + my_optind);
}
for (size_t i = 0; i < cmds.size(); i++) {
const wcstring cmd_wcs = str2wcstring(cmds.at(i));
res = parser.eval(cmd_wcs, empty_ios, TOP);
// Do something nasty to support OpenSUSE assuming we're bash. This may modify cmds.
fish_xdm_login_hack_hack_hack_hack(&opts.batch_cmds, argc - my_optind,
argv + my_optind);
}
res = run_command_list(&opts.batch_cmds, empty_ios);
reader_exit(0, 0);
} else if (my_optind == argc) {
// Interactive mode
// Implicitly interactive mode.
res = reader_read(STDIN_FILENO, empty_ios);
} else {
char *file = *(argv + (my_optind++));