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.
This commit is contained in:
Fabian Boehm 2022-06-21 18:08:09 +02:00
parent bfeebca75a
commit dde2d33098
4 changed files with 35 additions and 1 deletions

View file

@ -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());
}
}
}

View file

@ -13,6 +13,7 @@
#include <algorithm>
#include <iterator>
#include <map>
#include <mutex>
#include <set>
#include <utility>
@ -284,6 +285,12 @@ static void setup_path() {
}
}
static std::map<wcstring, wcstring> inheriteds;
const std::map<wcstring, wcstring> &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

View file

@ -5,6 +5,7 @@
#include <stddef.h>
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
@ -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<wcstring, wcstring> &env_get_inherited();
#endif

View file

@ -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|