set: Allow erasing multiple variables at once

See #7377.
This commit is contained in:
Fabian Homborg 2020-10-04 12:22:14 +02:00
parent aecd639fee
commit 00ab51bedc
2 changed files with 66 additions and 44 deletions

View file

@ -641,54 +641,58 @@ static int builtin_set_show(const wchar_t *cmd, const set_cmd_opts_t &opts, int
/// Erase a variable.
static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, wchar_t **argv,
parser_t &parser, io_streams_t &streams) {
if (argc != 1) {
streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, L"--erase", 1, argc);
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_CMD_ERROR;
}
int ret = STATUS_CMD_OK;
for (int i = 0; i < argc; i++) {
int scope = compute_scope(opts); // calculate the variable scope based on the provided options
wchar_t *dest = argv[i];
int scope = compute_scope(opts); // calculate the variable scope based on the provided options
wchar_t *dest = argv[0];
std::vector<long> indexes;
int idx_count = parse_index(indexes, dest, scope, streams, parser.vars());
if (idx_count == -1) {
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_CMD_ERROR;
}
int retval;
if (!valid_var_name(dest)) {
streams.err.append_format(BUILTIN_ERR_VARNAME, cmd, dest);
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
}
std::vector<event_t> evts;
if (idx_count == 0) { // unset the var
retval = parser.vars().remove(dest, scope, &evts);
// When a non-existent-variable is unset, return ENV_NOT_FOUND as $status
// but do not emit any errors at the console as a compromise between user
// friendliness and correctness.
if (retval != ENV_NOT_FOUND) {
handle_env_return(retval, cmd, dest, streams);
std::vector<long> indexes;
int idx_count = parse_index(indexes, dest, scope, streams, parser.vars());
if (idx_count == -1) {
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_CMD_ERROR;
}
} else { // remove just the specified indexes of the var
const auto dest_var = parser.vars().get(dest, scope);
if (!dest_var) return STATUS_CMD_ERROR;
wcstring_list_t result;
dest_var->to_list(result);
erase_values(result, indexes);
retval = env_set_reporting_errors(cmd, dest, scope, result, streams, parser.vars(), &evts);
}
// Fire any events.
for (const auto &evt : evts) {
event_fire(parser, evt);
}
int retval;
if (!valid_var_name(dest)) {
streams.err.append_format(BUILTIN_ERR_VARNAME, cmd, dest);
builtin_print_error_trailer(parser, streams.err, cmd);
return STATUS_INVALID_ARGS;
}
if (retval != STATUS_CMD_OK) return retval;
return check_global_scope_exists(cmd, opts, dest, streams, parser);
std::vector<event_t> evts;
if (idx_count == 0) { // unset the var
retval = parser.vars().remove(dest, scope, &evts);
// When a non-existent-variable is unset, return ENV_NOT_FOUND as $status
// but do not emit any errors at the console as a compromise between user
// friendliness and correctness.
if (retval != ENV_NOT_FOUND) {
handle_env_return(retval, cmd, dest, streams);
}
} else { // remove just the specified indexes of the var
const auto dest_var = parser.vars().get(dest, scope);
if (!dest_var) return STATUS_CMD_ERROR;
wcstring_list_t result;
dest_var->to_list(result);
erase_values(result, indexes);
retval = env_set_reporting_errors(cmd, dest, scope, result, streams, parser.vars(), &evts);
}
// Fire any events.
for (const auto &evt : evts) {
event_fire(parser, evt);
}
// Set $status to the last error value.
// This is cheesy, but I don't expect this to be checked often.
if (retval != STATUS_CMD_OK) {
ret = retval;
} else {
retval = check_global_scope_exists(cmd, opts, dest, streams, parser);
if (retval != STATUS_CMD_OK) ret = retval;
}
}
return ret;
}
/// This handles the common case of setting the entire var to a set of values.

View file

@ -664,4 +664,22 @@ set -S newvariable
#CHECK: $newvariable: set in global scope, unexported, a path variable with 1 elements
#CHECK: $newvariable[1]: |foo|
set foo foo
set bar bar
set -e baz
set -e foo baz bar
echo $status
#CHECK: 4
set -S foo baz bar
set foo 1 2 3
set bar 1 2 3
set -e foo[1] bar[2]
echo $foo
#CHECK: 2 3
echo $bar
#CHECK: 1 3
true