From dde2d330987216d801d9e50c589d9406e48fd5cf Mon Sep 17 00:00:00 2001 From: Fabian Boehm Date: Tue, 21 Jun 2022 18:08:09 +0200 Subject: [PATCH] `set --show`: Show the originally inherited value, if any This adds a line to `set --show`s output like ``` $PATH: originally inherited as |/home/alfa/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/var/lib/flatpak/exports/bin| ``` to help with debugging. Note that this means keeping an additional copy of the original environment around. At most this would be one ARG_MAX's worth, which is about 2M. --- src/builtins/set.cpp | 15 ++++++++++++++- src/env.cpp | 9 +++++++++ src/env.h | 4 ++++ tests/checks/set.fish | 8 ++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/builtins/set.cpp b/src/builtins/set.cpp index 1b48cdd72..7ebef09d9 100644 --- a/src/builtins/set.cpp +++ b/src/builtins/set.cpp @@ -556,14 +556,22 @@ static int builtin_set_show(const wchar_t *cmd, const set_cmd_opts_t &opts, int const wchar_t **argv, parser_t &parser, io_streams_t &streams) { UNUSED(opts); const auto &vars = parser.vars(); + auto inheriteds = env_get_inherited(); if (argc == 0) { // show all vars - wcstring_list_t names = parser.vars().get_names(ENV_USER); + wcstring_list_t names = vars.get_names(ENV_USER); sort(names.begin(), names.end()); for (const auto &name : names) { if (name == L"history") continue; show_scope(name.c_str(), ENV_LOCAL, streams, vars); show_scope(name.c_str(), ENV_GLOBAL, streams, vars); show_scope(name.c_str(), ENV_UNIVERSAL, streams, vars); + + // Show the originally imported value as a debugging aid. + auto inherited = inheriteds.find(name); + if (inherited != inheriteds.end()) { + const wcstring escaped_val = escape_string(inherited->second, ESCAPE_NO_QUOTED, STRING_STYLE_SCRIPT); + streams.out.append_format(_(L"$%ls: originally inherited as |%ls|\n"), name.c_str(), escaped_val.c_str()); + } } } else { for (int i = 0; i < argc; i++) { @@ -585,6 +593,11 @@ static int builtin_set_show(const wchar_t *cmd, const set_cmd_opts_t &opts, int show_scope(arg, ENV_LOCAL, streams, vars); show_scope(arg, ENV_GLOBAL, streams, vars); show_scope(arg, ENV_UNIVERSAL, streams, vars); + auto inherited = inheriteds.find(arg); + if (inherited != inheriteds.end()) { + const wcstring escaped_val = escape_string(inherited->second, ESCAPE_NO_QUOTED, STRING_STYLE_SCRIPT); + streams.out.append_format(_(L"$%ls: originally inherited as |%ls|\n"), arg, escaped_val.c_str()); + } } } diff --git a/src/env.cpp b/src/env.cpp index 6c4275b76..1a73abb7d 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -284,6 +285,12 @@ static void setup_path() { } } +static std::map inheriteds; + +const std::map &env_get_inherited() { + return inheriteds; +} + void env_init(const struct config_paths_t *paths, bool do_uvars, bool default_paths) { env_stack_t &vars = env_stack_t::principal(); // Import environment variables. Walk backwards so that the first one out of any duplicates wins @@ -300,9 +307,11 @@ void env_init(const struct config_paths_t *paths, bool do_uvars, bool default_pa if (!electric_var_t::for_name(key_and_val)) { vars.set_empty(key_and_val, ENV_EXPORT | ENV_GLOBAL); } + inheriteds[key] = L""; } else { key.assign(key_and_val, 0, eql); val.assign(key_and_val, eql + 1, wcstring::npos); + inheriteds[key] = val; if (!electric_var_t::for_name(key)) { // fish_user_paths should not be exported; attempting to re-import it from // a value we previously (due to user error) exported will cause impossibly diff --git a/src/env.h b/src/env.h index 07bf89fb0..d5c106f23 100644 --- a/src/env.h +++ b/src/env.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -311,4 +312,7 @@ wcstring env_get_runtime_path(); void setenv_lock(const char *name, const char *value, int overwrite); void unsetenv_lock(const char *name); +/// Returns the originally inherited variables and their values. +/// This is a simple key->value map and not e.g. cut into paths. +const std::map &env_get_inherited(); #endif diff --git a/tests/checks/set.fish b/tests/checks/set.fish index 4cfdb90fb..65d40f0cc 100644 --- a/tests/checks/set.fish +++ b/tests/checks/set.fish @@ -719,6 +719,7 @@ set -S PWD #CHECK: $PWD: set in global scope, exported, with 1 elements #CHECK: Variable is read-only #CHECK: $PWD[1]: |{{.*}}| +#CHECK: $PWD: originally inherited as |{{.*}}| set -ql history echo $status @@ -913,3 +914,10 @@ set -S status # CHECK: Variable is read-only # CHECK: $status[1]: |2| +# See that we show inherited variables correctly: +foo=bar $FISH -c 'set foo 1 2 3; set --show foo' +# CHECK: $foo: set in global scope, exported, with 3 elements +# CHECK: $foo[1]: |1| +# CHECK: $foo[2]: |2| +# CHECK: $foo[3]: |3| +# CHECK: $foo: originally inherited as |bar|