make status saner vis-a-vis arg parsing

The `status` command currently silently allows incompatible flags (i.e.,
subcommands). Too, using flags to specify subcommands misleads the user
into thinking they can specify multiple subcommands.

We recently modified the `history` command to deprecate using flags for
subcommands. This change does the same for the `status` command.

Fixes #3509
This commit is contained in:
Kurtis Rader 2016-11-04 22:32:43 -07:00
parent a275618589
commit 9e922a6e02
7 changed files with 373 additions and 154 deletions

View file

@ -14,8 +14,6 @@ history ( -h | --help )
`history` is used to search, delete, and otherwise manipulate the history of interactive commands.
Note that for backwards compatibility each subcommand can also be specified as a long option. For example, rather than `history search` you can type `history --search`. Those long options are deprecated and will be removed in a future release.
The following operations (sub-commands) are available:
- `search` returns history items matching the search string. If no search string is provided it returns all history items. This is the default operation if no other operation is specified. You only have to explicitly say `history search` if you wish to search for one of the subcommands. The `--contains` search option will be used if you don't specify a different search option. Entries are ordered newest to oldest. If stdout is attached to a tty the output will be piped through your pager by the history function. The history builtin simply writes the results to stdout.
@ -60,9 +58,10 @@ history --search --contains "foo"
history --delete --prefix "foo"
# Interactively deletes commands which start with "foo" from the history.
# You can select more than one entry by entering their IDs seperated by a space.
\endfish
\subsection history-notes Notes
If you specify both `--prefix` and `--contains` the last flag seen is used.
\endfish
Note that for backwards compatibility each subcommand can also be specified as a long option. For example, rather than `history search` you can type `history --search`. Those long options are deprecated and will be removed in a future release.

View file

@ -2,33 +2,50 @@
\subsection status-synopsis Synopsis
\fish{synopsis}
status [OPTION]
status
status is-login
status is-interactive
status is-block
status is-command-substitution
status is-no-job-control
status is-full-job-control
status is-interactive-job-control
status current-filename
status current-line-number
status print-stack-trace
status job-control CONTROL-TYPE
\endfish
\subsection status-description Description
With no arguments, `status` displays a summary of the current login and job control status of the shell.
The following options are available:
The following operations (sub-commands) are available:
- `-c` or `--is-command-substitution` returns 0 if fish is currently executing a command substitution.
- `is-command-sub` returns 0 if fish is currently executing a command substitution. Also `-c` or `--is-command-substitution`.
- `-b` or `--is-block` returns 0 if fish is currently executing a block of code.
- `is-block` returns 0 if fish is currently executing a block of code. Also `-b` or `--is-block`.
- `-i` or `--is-interactive` returns 0 if fish is interactive - that is, connected to a keyboard.
- `is-interactive` returns 0 if fish is interactive - that is, connected to a keyboard. Also `-i` or `--is-interactive`.
- `-l` or `--is-login` returns 0 if fish is a login shell - that is, if fish should perform login tasks such as setting up the PATH.
- `is-login` returns 0 if fish is a login shell - that is, if fish should perform login tasks such as setting up the PATH. Also `-l` or `--is-login`.
- `--is-full-job-control` returns 0 if full job control is enabled.
- `is-full-job-control` returns 0 if full job control is enabled. Also `--is-full-job-control` (no short flag).
- `--is-interactive-job-control` returns 0 if interactive job control is enabled.
- `is-interactive-job-control` returns 0 if interactive job control is enabled. Also, `--is-interactive-job-control` (no short flag).
- `--is-no-job-control` returns 0 if no job control is enabled.
- `is-no-job-control` returns 0 if no job control is enabled. Also `--is-no-job-control` (no short flag).
- `-f` or `--current-filename` prints the filename of the currently running script.
- `current-filename` prints the filename of the currently running script. Also `-f` or `--current-filename`.
- `-n` or `--current-line-number` prints the line number of the currently running script.
- `current-line-number` prints the line number of the currently running script. Also `-n` or `--current-line-number`.
- `-j CONTROLTYPE` or `--job-control=CONTROLTYPE` sets the job control type, which can be `none`, `full`, or `interactive`.
- `job-control CONTROL-TYPE` sets the job control type, which can be `none`, `full`, or `interactive`. Also `-j CONTROL-TYPE` or `--job-control=CONTROL-TYPE`.
- `-t` or `--print-stack-trace` prints a stack trace of all function calls on the call stack.
- `print-stack-trace` prints a stack trace of all function calls on the call stack. Also `-t` or `--print-stack-trace`.
\subsection status-notes Notes
For backwards compatibility each subcommand can also be specified as a long or short option. For example, rather than `status is-login` you can type `status --is-login`. The flag forms are deprecated and may be removed in a future release (but not before fish 3.0).
You can only specify one subcommand per invocation even if you use the flag form of the subcommand.

View file

@ -1,14 +1,25 @@
# Note that when a completion file is sourced a new block scope is created so `set -l` works.
set -l __fish_status_all_commands is-login is-interactive is-block is-command-substitution is-no-job-control is-interactive-job-control is-full-job-control current-filename current-line-number print-stack-trace job-control
# These are the recognized flags.
complete -c status -s h -l help --description "Display help and exit"
complete -c status -l is-command-substitution --description "Test if a command substitution is currently evaluated"
complete -c status -l is-block --description "Test if a code block is currently evaluated"
complete -c status -l is-interactive --description "Test if this is an interactive shell"
complete -c status -l is-login --description "Test if this is a login shell"
complete -c status -l is-full-job-control --description "Test if all new jobs are put under job control"
complete -c status -l is-interactive-job-control --description "Test if only interactive new jobs are put under job control"
complete -c status -l is-no-job-control --description "Test if new jobs are never put under job control"
complete -c status -s j -l job-control -xa "full interactive none" --description "Set which jobs are out under job control"
complete -c status -s t -l print-stack-trace --description "Print a list of all function calls leading up to running the current command"
complete -c status -s f -l current-filename --description "Print the filename of the currently running script"
complete -c status -s n -l current-line-number --description "Print the line number of the currently running script"
complete -c status -s t -l print-stack-trace --description "Prints a trace of all function calls on the stack"
# The "is-something" subcommands.
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-login -d "Test if this is a login shell"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-interactive -d "Test if this is an interactive shell"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-command-substitution -d "Test if a command substitution is currently evaluated"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-block -d "Test if a code block is currently evaluated"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-no-job-control -d "Test if new jobs are never put under job control"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-interactive-job-control -d "Test if only interactive new jobs are put under job control"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a is-full-job-control -d "Test if all new jobs are put under job control"
# The subcommands that are not "is-something" which don't change the fish state.
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-filename -d "Print the filename of the currently running script"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a current-line-number -d "Print the line number of the currently running script"
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a print-stack-trace -d "Print a list of all function calls leading up to running the current command"
# The job-control command changes fish state.
complete -f -c status -n "not __fish_seen_subcommand_from $__fish_status_all_commands" -a job-control -d "Set which jobs are under job control"
complete -f -c status -n "__fish_seen_subcommand_from job-control" -a full -d "Set all jobs under job control"
complete -f -c status -n "__fish_seen_subcommand_from job-control" -a interactive -d "Set only interactive jobs under job control"
complete -f -c status -n "__fish_seen_subcommand_from job-control" -a none -d "Set no jobs under job control"

View file

@ -2158,104 +2158,218 @@ static int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv)
return exit_res;
}
enum status_cmd_t {
STATUS_NOOP,
STATUS_IS_LOGIN,
STATUS_IS_INTERACTIVE,
STATUS_IS_BLOCK,
STATUS_IS_COMMAND_SUB,
STATUS_IS_FULL_JOB_CTRL,
STATUS_IS_INTERACTIVE_JOB_CTRL,
STATUS_IS_NO_JOB_CTRL,
STATUS_CURRENT_FILENAME,
STATUS_CURRENT_LINE_NUMBER,
STATUS_SET_JOB_CONTROL,
STATUS_PRINT_STACK_TRACE
};
static status_cmd_t status_string_to_cmd(const wchar_t *status_command) {
if (wcscmp(status_command, L"is-login") == 0) {
return STATUS_IS_LOGIN;
} else if (wcscmp(status_command, L"is-interactive") == 0) {
return STATUS_IS_INTERACTIVE;
} else if (wcscmp(status_command, L"is-block") == 0) {
return STATUS_IS_BLOCK;
} else if (wcscmp(status_command, L"is-command-sub") == 0) {
return STATUS_IS_COMMAND_SUB;
} else if (wcscmp(status_command, L"is-full-job-control") == 0) {
return STATUS_IS_FULL_JOB_CTRL;
} else if (wcscmp(status_command, L"is-interactive-job-control") == 0) {
return STATUS_IS_INTERACTIVE_JOB_CTRL;
} else if (wcscmp(status_command, L"is-no-job-control") == 0) {
return STATUS_IS_NO_JOB_CTRL;
} else if (wcscmp(status_command, L"current-filename") == 0) {
return STATUS_CURRENT_FILENAME;
} else if (wcscmp(status_command, L"current-line-number") == 0) {
return STATUS_CURRENT_LINE_NUMBER;
} else if (wcscmp(status_command, L"job-control") == 0) {
return STATUS_SET_JOB_CONTROL;
} else if (wcscmp(status_command, L"print-stack-trace") == 0) {
return STATUS_PRINT_STACK_TRACE;
}
return STATUS_NOOP;
}
static const wcstring status_cmd_to_string(status_cmd_t status_cmd) {
switch (status_cmd) {
case STATUS_NOOP:
return L"no-op";
case STATUS_IS_LOGIN:
return L"is-login";
case STATUS_IS_INTERACTIVE:
return L"is-interactive";
case STATUS_IS_BLOCK:
return L"is-block";
case STATUS_IS_COMMAND_SUB:
return L"is-command-sub";
case STATUS_IS_FULL_JOB_CTRL:
return L"is-full-job-control";
case STATUS_IS_INTERACTIVE_JOB_CTRL:
return L"is-interactive-job-control";
case STATUS_IS_NO_JOB_CTRL:
return L"is-no-job-control";
case STATUS_CURRENT_FILENAME:
return L"current-filename";
case STATUS_CURRENT_LINE_NUMBER:
return L"current-line-number";
case STATUS_SET_JOB_CONTROL:
return L"job-control";
case STATUS_PRINT_STACK_TRACE:
return L"print-stack-trace";
}
}
/// Remember the status subcommand and disallow selecting more than one status subcommand.
static bool set_status_cmd(wchar_t *const cmd, status_cmd_t *status_cmd, status_cmd_t sub_cmd,
io_streams_t &streams) {
if (*status_cmd != STATUS_NOOP) {
wchar_t err_text[1024];
swprintf(err_text, sizeof(err_text) / sizeof(wchar_t),
_(L"you cannot do both '%ls' and '%ls' in the same invocation"),
status_cmd_to_string(*status_cmd).c_str(), status_cmd_to_string(sub_cmd).c_str());
streams.err.append_format(BUILTIN_ERR_COMBO2, cmd, err_text);
return false;
}
*status_cmd = sub_cmd;
return true;
}
#define CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd) \
if (args.size() != 0) { \
streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, \
status_cmd_to_string(status_cmd).c_str(), 0, args.size()); \
status = STATUS_BUILTIN_ERROR; \
break; \
}
int job_control_str_to_mode(const wchar_t *mode, wchar_t *cmd, io_streams_t &streams) {
if (wcscmp(mode, L"full") == 0) {
return JOB_CONTROL_ALL;
} else if (wcscmp(mode, L"interactive") == 0) {
return JOB_CONTROL_INTERACTIVE;
} else if (wcscmp(mode, L"none") == 0) {
return JOB_CONTROL_NONE;
}
streams.err.append_format(L"%ls: Invalid job control mode '%ls'\n", cmd, mode);
return -1;
}
/// The status builtin. Gives various status information on fish.
static int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
wgetopter_t w;
enum {
NORMAL,
IS_SUBST,
IS_BLOCK,
IS_INTERACTIVE,
IS_LOGIN,
IS_FULL_JOB_CONTROL,
IS_INTERACTIVE_JOB_CONTROL,
IS_NO_JOB_CONTROL,
STACK_TRACE,
DONE,
CURRENT_FILENAME,
CURRENT_LINE_NUMBER
};
int mode = NORMAL;
wchar_t *cmd = argv[0];
int argc = builtin_count_args(argv);
int res = STATUS_BUILTIN_OK;
status_cmd_t status_cmd = STATUS_NOOP;
int status = STATUS_BUILTIN_OK;
int new_job_control_mode = -1;
const struct woption long_options[] = {
{L"help", no_argument, 0, 'h'},
{L"is-command-substitution", no_argument, 0, 'c'},
{L"is-block", no_argument, 0, 'b'},
{L"is-interactive", no_argument, 0, 'i'},
{L"is-login", no_argument, 0, 'l'},
{L"is-full-job-control", no_argument, &mode, IS_FULL_JOB_CONTROL},
{L"is-interactive-job-control", no_argument, &mode, IS_INTERACTIVE_JOB_CONTROL},
{L"is-no-job-control", no_argument, &mode, IS_NO_JOB_CONTROL},
{L"current-filename", no_argument, 0, 'f'},
{L"current-line-number", no_argument, 0, 'n'},
{L"job-control", required_argument, 0, 'j'},
{L"print-stack-trace", no_argument, 0, 't'},
{0, 0, 0, 0}};
while (1) {
int opt_index = 0;
int opt = w.wgetopt_long(argc, argv, L":cbilfnhj:t", long_options, &opt_index);
if (opt == -1) break;
/// Note: Do not add new flags that represent subcommands. We're encouraging people to switch to
/// the non-flag subcommand form. While these flags are deprecated they must be supported at
/// least until fish 3.0 and possibly longer to avoid breaking everyones config.fish and other
/// scripts.
const wchar_t *short_options = L":cbilfnhj:t";
const struct woption long_options[] = {{L"help", no_argument, 0, 'h'},
{L"is-command-substitution", no_argument, 0, 'c'},
{L"is-block", no_argument, 0, 'b'},
{L"is-interactive", no_argument, 0, 'i'},
{L"is-login", no_argument, 0, 'l'},
{L"is-full-job-control", no_argument, 0, 1},
{L"is-interactive-job-control", no_argument, 0, 2},
{L"is-no-job-control", no_argument, 0, 3},
{L"current-filename", no_argument, 0, 'f'},
{L"current-line-number", no_argument, 0, 'n'},
{L"job-control", required_argument, 0, 'j'},
{L"print-stack-trace", no_argument, 0, 't'},
{0, 0, 0, 0}};
int opt;
wgetopter_t w;
while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) {
switch (opt) {
case 0: {
if (long_options[opt_index].flag != 0) break;
streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0],
long_options[opt_index].name);
builtin_print_help(parser, streams, argv[0], streams.err);
return STATUS_BUILTIN_ERROR;
case 1: {
if (!set_status_cmd(cmd, &status_cmd, STATUS_IS_FULL_JOB_CTRL, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 2: {
if (!set_status_cmd(cmd, &status_cmd, STATUS_IS_INTERACTIVE_JOB_CTRL, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 3: {
if (!set_status_cmd(cmd, &status_cmd, STATUS_IS_NO_JOB_CTRL, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'c': {
mode = IS_SUBST;
if (!set_status_cmd(cmd, &status_cmd, STATUS_IS_COMMAND_SUB, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'b': {
mode = IS_BLOCK;
if (!set_status_cmd(cmd, &status_cmd, STATUS_IS_BLOCK, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'i': {
mode = IS_INTERACTIVE;
if (!set_status_cmd(cmd, &status_cmd, STATUS_IS_INTERACTIVE, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'l': {
mode = IS_LOGIN;
if (!set_status_cmd(cmd, &status_cmd, STATUS_IS_LOGIN, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'f': {
mode = CURRENT_FILENAME;
if (!set_status_cmd(cmd, &status_cmd, STATUS_CURRENT_FILENAME, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'n': {
mode = CURRENT_LINE_NUMBER;
if (!set_status_cmd(cmd, &status_cmd, STATUS_CURRENT_LINE_NUMBER, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'j': {
if (!set_status_cmd(cmd, &status_cmd, STATUS_SET_JOB_CONTROL, streams)) {
return STATUS_BUILTIN_ERROR;
}
new_job_control_mode = job_control_str_to_mode(w.woptarg, cmd, streams);
if (new_job_control_mode == -1) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 't': {
if (!set_status_cmd(cmd, &status_cmd, STATUS_PRINT_STACK_TRACE, streams)) {
return STATUS_BUILTIN_ERROR;
}
break;
}
case 'h': {
builtin_print_help(parser, streams, argv[0], streams.out);
return STATUS_BUILTIN_OK;
}
case 'j': {
if (wcscmp(w.woptarg, L"full") == 0)
job_control_mode = JOB_CONTROL_ALL;
else if (wcscmp(w.woptarg, L"interactive") == 0)
job_control_mode = JOB_CONTROL_INTERACTIVE;
else if (wcscmp(w.woptarg, L"none") == 0)
job_control_mode = JOB_CONTROL_NONE;
else {
streams.err.append_format(L"%ls: Invalid job control mode '%ls'\n", L"status",
w.woptarg);
res = STATUS_BUILTIN_ERROR;
}
mode = DONE;
break;
}
case 't': {
mode = STACK_TRACE;
break;
}
case ':': {
builtin_missing_argument(parser, streams, argv[0], argv[w.woptind - 1]);
return STATUS_BUILTIN_ERROR;
@ -2265,57 +2379,30 @@ static int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **arg
return STATUS_BUILTIN_ERROR;
}
default: {
DIE("unexpected opt");
DIE("unexpected retval from wgetopt_long");
break;
}
}
}
if (res == STATUS_BUILTIN_ERROR) {
return res;
// If a status command hasn't already been specified via a flag check the first word.
// Note that this can be simplified after we eliminate allowing subcommands as flags.
if (w.woptind < argc) {
status_cmd_t subcmd = status_string_to_cmd(argv[w.woptind]);
if (subcmd != STATUS_NOOP) {
if (!set_status_cmd(cmd, &status_cmd, subcmd, streams)) {
return STATUS_BUILTIN_ERROR;
}
w.woptind++;
}
}
switch (mode) {
case DONE: {
return STATUS_BUILTIN_OK;
}
case CURRENT_FILENAME: {
const wchar_t *fn = parser.current_filename();
// Every argument that we haven't consumed already is an argument for a subcommand.
const wcstring_list_t args(argv + w.woptind, argv + argc);
if (!fn) fn = _(L"Standard input");
streams.out.append_format(L"%ls\n", fn);
return STATUS_BUILTIN_OK;
}
case CURRENT_LINE_NUMBER: {
streams.out.append_format(L"%d\n", parser.get_lineno());
return STATUS_BUILTIN_OK;
}
case IS_INTERACTIVE: {
return !is_interactive_session;
}
case IS_SUBST: {
return !is_subshell;
}
case IS_BLOCK: {
return !is_block;
}
case IS_LOGIN: {
return !is_login;
}
case IS_FULL_JOB_CONTROL: {
return job_control_mode != JOB_CONTROL_ALL;
}
case IS_INTERACTIVE_JOB_CONTROL: {
return job_control_mode != JOB_CONTROL_INTERACTIVE;
}
case IS_NO_JOB_CONTROL: {
return job_control_mode != JOB_CONTROL_NONE;
}
case STACK_TRACE: {
streams.out.append(parser.stack_trace());
return STATUS_BUILTIN_OK;
}
case NORMAL: {
switch (status_cmd) {
case STATUS_NOOP: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
if (is_login) {
streams.out.append_format(_(L"This is a login shell\n"));
} else {
@ -2328,12 +2415,84 @@ static int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **arg
? _(L"Only on interactive jobs")
: (job_control_mode == JOB_CONTROL_NONE ? _(L"Never") : _(L"Always")));
streams.out.append(parser.stack_trace());
return STATUS_BUILTIN_OK;
break;
}
case STATUS_SET_JOB_CONTROL: {
if (new_job_control_mode != -1) {
// Flag form was used.
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
} else {
if (args.size() != 1) {
streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd,
status_cmd_to_string(status_cmd).c_str(), 1,
args.size());
status = STATUS_BUILTIN_ERROR;
break;
}
new_job_control_mode = job_control_str_to_mode(args[0].c_str(), cmd, streams);
if (new_job_control_mode == -1) {
return STATUS_BUILTIN_ERROR;
}
}
job_control_mode = new_job_control_mode;
break;
}
case STATUS_CURRENT_FILENAME: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
const wchar_t *fn = parser.current_filename();
if (!fn) fn = _(L"Standard input");
streams.out.append_format(L"%ls\n", fn);
break;
}
case STATUS_CURRENT_LINE_NUMBER: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
streams.out.append_format(L"%d\n", parser.get_lineno());
break;
}
case STATUS_IS_INTERACTIVE: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
status = !is_interactive_session;
break;
}
case STATUS_IS_COMMAND_SUB: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
status = !is_subshell;
break;
}
case STATUS_IS_BLOCK: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
status = !is_block;
break;
}
case STATUS_IS_LOGIN: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
status = !is_login;
break;
}
case STATUS_IS_FULL_JOB_CTRL: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
status = job_control_mode != JOB_CONTROL_ALL;
break;
}
case STATUS_IS_INTERACTIVE_JOB_CTRL: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
status = job_control_mode != JOB_CONTROL_INTERACTIVE;
break;
}
case STATUS_IS_NO_JOB_CTRL: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
status = job_control_mode != JOB_CONTROL_NONE;
break;
}
case STATUS_PRINT_STACK_TRACE: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(status_cmd)
streams.out.append(parser.stack_trace());
break;
}
default: { break; }
}
DIE("status subcommand not handled");
return status;
}
/// The exit builtin. Calls reader_exit to exit and returns the value specified.
@ -2905,8 +3064,10 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
bool case_sensitive = false;
bool null_terminate = false;
// TODO: Remove the long options that correspond to subcommands (e.g., '--delete') on or after
// 2017-10 (which will be a full year after these flags have been deprecated).
/// Note: Do not add new flags that represent subcommands. We're encouraging people to switch to
/// the non-flag subcommand form. While many of these flags are deprecated they must be
/// supported at least until fish 3.0 and possibly longer to avoid breaking everyones
/// config.fish and other scripts.
const wchar_t *short_options = L":Cmn:epchtz";
const struct woption long_options[] = {{L"prefix", no_argument, NULL, 'p'},
{L"contains", no_argument, NULL, 'c'},
@ -3035,9 +3196,12 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
// If a history command hasn't already been specified via a flag check the first word.
// Note that this can be simplified after we eliminate allowing subcommands as flags.
// See the TODO above regarding the `long_options` array.
if (hist_cmd == HIST_NOOP && w.woptind < argc) {
hist_cmd = hist_string_to_cmd(argv[w.woptind]);
if (hist_cmd != HIST_NOOP) {
if (w.woptind < argc) {
hist_cmd_t subcmd = hist_string_to_cmd(argv[w.woptind]);
if (subcmd != HIST_NOOP) {
if (!set_hist_cmd(cmd, &hist_cmd, subcmd, streams)) {
return STATUS_BUILTIN_ERROR;
}
w.woptind++;
}
}

View file

@ -1,2 +1,8 @@
<W> fish: An error occurred while redirecting file '/'
open: Is a directory
status: Invalid combination of options,
you cannot do both 'is-interactive' and 'is-login' in the same invocation
status: Invalid combination of options,
you cannot do both 'is-block' and 'is-interactive' in the same invocation
status: Invalid job control mode 'full1'
status: Invalid job control mode '1none'

View file

@ -1,17 +1,41 @@
# vim: set filetype=fish:
status -b
or echo 'top level'
and echo '"status -b" unexpectedly returned true at top level'
begin
status -b
or echo '"status -b" unexpectedly returned false inside a begin block'
end
and echo 'block'
# Issue #1728
# Bad file redirection on a block causes `status --is-block` to return 0 forever.
begin; end >/ # / is a directory, it can't be opened for writing
status -b
and echo 'unexpected block'
and echo '"status -b" unexpectedly returned true after bad redirect on a begin block'
true
status -l
and echo '"status -l" unexpectedly returned true for a non-login shell'
status -i
and echo '"status -i" unexpectedly returned true for a non-interactive shell'
status is-login
and echo '"status is-login" unexpectedly returned true for a non-login shell'
status is-interactive
and echo '"status is-interactive" unexpectedly returned true for a non-interactive shell'
# We should get an error message about an invalid combination of flags.
status --is-interactive --is-login
# We should get an error message about an unexpected arg for `status
# is-block`.
status -b is-interactive
# Try to set the job control to an invalid mode.
status job-control full1
status --job-control=1none
# Now set it to a valid mode.
status job-control none

View file

@ -1,2 +0,0 @@
top level
block