diff --git a/doc_src/set.txt b/doc_src/set.txt index 859a0d899..d19181bbf 100644 --- a/doc_src/set.txt +++ b/doc_src/set.txt @@ -8,6 +8,7 @@ set [OPTIONS] VARIABLE_NAME[INDICES]... VALUES... set ( -q | --query ) [SCOPE_OPTIONS] VARIABLE_NAMES... set ( -e | --erase ) [SCOPE_OPTIONS] VARIABLE_NAME set ( -e | --erase ) [SCOPE_OPTIONS] VARIABLE_NAME[INDICES]... +set ( -S | --show ) [SCOPE_OPTIONS] [VARIABLE_NAME]... \endfish \subsection set-description Description @@ -39,12 +40,14 @@ The following options are available: - `-n` or `--names` List only the names of all defined variables, not their value. The names are guaranteed to be sorted. +- `-S` or `--show` Shows information about the given variables. If no variable names are given then all variables are shown in sorted order. No other flags can be used with this option. The information shown includes whether or not it is set in each of the local, global, and universal scopes. If it is set in one of those scopes whether or not it is exported is reported. The individual elements are also shown along with the length of each element. + - `-L` or `--long` do not abbreviate long values when printing set variables If a variable is set to more than one value, the variable will be an array with the specified elements. If a variable is set to zero elements, it will become an array with zero elements. -If the variable name is one or more array elements, such as `PATH[1 3 7]`, only those array elements specified will be changed. When array indices are specified to `set`, multiple arguments may be used to specify additional indexes, e.g. `set PATH[1] PATH[4] /bin /sbin`. If you specify a negative index when expanding or assigning to an array variable, the index will be calculated from the end of the array. For example, the index -1 means the last index of an array. +If the variable name is one or more array elements, such as `PATH[1 3 7]`, only those array elements specified will be changed. If you specify a negative index when expanding or assigning to an array variable, the index will be calculated from the end of the array. For example, the index -1 means the last index of an array. The scoping rules when creating or updating a variable are: diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 8e2766706..843218f2c 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -303,6 +303,91 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc } } +static void show_scope(const wchar_t *var_name, int scope, io_streams_t &streams) { + const wchar_t *scope_name; + switch (scope) { + case ENV_LOCAL: { + scope_name = L"local"; + break; + } + case ENV_GLOBAL: { + scope_name = L"global"; + break; + } + case ENV_UNIVERSAL: { + scope_name = L"universal"; + break; + } + default: { + DIE("invalid scope"); + break; + } + } + + if (env_exist(var_name, scope)) { + const env_var_t evar = env_get_string(var_name, scope | ENV_EXPORT | ENV_USER); + const wchar_t *exportv = evar.missing() ? _(L"unexported") : _(L"exported"); + + const env_var_t var = env_get_string(var_name, scope | ENV_USER); + wcstring_list_t result; + if (!var.empty()) tokenize_variable_array(var, result); + + streams.out.append_format(_(L"$%ls: set in %ls scope, %ls, with %d elements\n"), var_name, + scope_name, exportv, result.size()); + for (size_t i = 0; i < result.size(); i++) { + if (result.size() > 100) { + if (i == 50) streams.out.append(L"...\n"); + if (i >= 50 && i < result.size() - 50) continue; + } + const wcstring value = result[i]; + const wcstring escaped_val = + escape_string(value.c_str(), ESCAPE_NO_QUOTED, STRING_STYLE_SCRIPT); + streams.out.append_format(_(L"$%ls[%d]: length=%d value=|%ls|\n"), var_name, i, + value.size(), escaped_val.c_str()); + } + } else { + streams.out.append_format(_(L"$%ls: not set in %ls scope\n"), var_name, scope_name); + } +} + +/// Show mode. Show information about the named variable(s). +static int builtin_set_show(const wchar_t *cmd, int argc, wchar_t **argv, + parser_t &parser, io_streams_t &streams) { + if (argc == 0) { // show all vars + wcstring_list_t names = env_get_names(ENV_USER); + sort(names.begin(), names.end()); + for (auto it : names) { + show_scope(it.c_str(), ENV_LOCAL, streams); + show_scope(it.c_str(), ENV_GLOBAL, streams); + show_scope(it.c_str(), ENV_UNIVERSAL, streams); + streams.out.push_back(L'\n'); + } + } else { + for (int i = 0; i < argc; i++) { + wchar_t *arg = argv[i]; + + if (!valid_var_name(arg)) { + streams.err.append_format(_(L"$%ls: invalid var name\n"), arg); + continue; + } + + if (wcschr(arg, L'[')) { + streams.err.append_format( + _(L"%ls: `set --show` does not allow slices with the var names\n"), cmd); + builtin_print_help(parser, streams, cmd, streams.err); + return STATUS_CMD_ERROR; + } + + show_scope(arg, ENV_LOCAL, streams); + show_scope(arg, ENV_GLOBAL, streams); + show_scope(arg, ENV_UNIVERSAL, streams); + streams.out.push_back(L'\n'); + } + } + + return STATUS_CMD_OK; +} + /// The set builtin creates, updates, and erases (removes, deletes) variables. int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; @@ -312,6 +397,7 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int local = 0, global = 0, exportv = 0; int erase = 0, list = 0, unexport = 0; int universal = 0, query = 0; + bool show = false; bool shorten_ok = true; bool preserve_failure_exit_status = true; const int incoming_exit_status = proc_get_last_status(); @@ -325,7 +411,7 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // Variables used for parsing the argument list. This command is atypical in using the "+" // (REQUIRE_ORDER) option for flag parsing. This is not typical of most fish commands. It means // we stop scanning for flags when the first non-flag argument is seen. - static const wchar_t *short_options = L"+:LUeghlnqux"; + static const wchar_t *short_options = L"+:LSUeghlnqux"; static const struct woption long_options[] = {{L"export", no_argument, NULL, 'x'}, {L"global", no_argument, NULL, 'g'}, {L"local", no_argument, NULL, 'l'}, @@ -335,6 +421,7 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { {L"universal", no_argument, NULL, 'U'}, {L"long", no_argument, NULL, 'L'}, {L"query", no_argument, NULL, 'q'}, + {L"show", no_argument, NULL, 'S'}, {L"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}}; @@ -382,6 +469,11 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { preserve_failure_exit_status = false; break; } + case 'S': { + show = true; + preserve_failure_exit_status = false; + break; + } case 'h': { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; @@ -401,6 +493,12 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } } + if (show) { + argc -= w.woptind; + argv += w.woptind; + return builtin_set_show(cmd, argc, argv, parser, streams); + } + // Ok, all arguments have been parsed, let's validate them. If we are checking the existance of // a variable (-q) we can not also specify scope. if (query && (erase || list)) {