diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index bd871b09d..1bd7c8f26 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -391,14 +391,8 @@ static void print_variables(int include_values, int esc, bool shorten_ok, int sc } } - - -/** - The set builtin. Creates, updates and erases environment variables - and environemnt variable arrays. -*/ -static int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) -{ +/// The set builtin creates, updates, and erases (removes, deletes) variables. +int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wgetopter_t w; /** Variables used for parsing the argument list */ const struct woption long_options[] = diff --git a/src/env.cpp b/src/env.cpp index b6a402acd..4a3f6c061 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -228,16 +228,14 @@ static bool var_is_locale(const wcstring &key) static void handle_locale(const wchar_t *env_var_name) { debug(2, L"handle_locale() called in response to '%ls' changing", env_var_name); const char *old_msg_locale = setlocale(LC_MESSAGES, NULL); - - for (size_t i = 0; locale_variable[i]; i++) { - const wchar_t *key = locale_variable[i]; - const env_var_t var = env_get_string(key); - if (!var.empty()) { - const std::string &name = wcs2string(key); - const std::string &value = wcs2string(var); - setenv(name.c_str(), value.c_str(), 1); - debug(3, L"locale var %s='%s'", name.c_str(), value.c_str()); - } + const env_var_t val = env_get_string(env_var_name, ENV_EXPORT); + const std::string &value = wcs2string(val); + const std::string &name = wcs2string(env_var_name); + debug(2, L"locale var %s='%s'", name.c_str(), value.c_str()); + if (val.empty()) { + unsetenv(name.c_str()); + } else { + setenv(name.c_str(), value.c_str(), 1); } char *locale = setlocale(LC_ALL, ""); @@ -273,15 +271,14 @@ static bool var_is_curses(const wcstring &key) { /// libraries. static void handle_curses(const wchar_t *env_var_name) { debug(2, L"handle_curses() called in response to '%ls' changing", env_var_name); - for (size_t i = 0; curses_variable[i]; i++) { - const wchar_t *key = curses_variable[i]; - const env_var_t var = env_get_string(key); - if (!var.empty()) { - const std::string &name = wcs2string(key); - const std::string &value = wcs2string(var); - setenv(name.c_str(), value.c_str(), 1); - debug(3, L"curses var %s='%s'", name.c_str(), value.c_str()); - } + const env_var_t val = env_get_string(env_var_name, ENV_EXPORT); + const std::string &name = wcs2string(env_var_name); + const std::string &value = wcs2string(val); + debug(2, L"curses var %s='%s'", name.c_str(), value.c_str()); + if (val.empty()) { + unsetenv(name.c_str()); + } else { + setenv(name.c_str(), value.c_str(), 1); } // TODO: Modify input_init() to allow calling it when the terminfo env vars are dynamically // changed. At the present time it can be called just once. Also, we should really only do this @@ -396,23 +393,11 @@ static bool variable_is_colon_delimited_array(const wcstring &str) return contains(str, L"PATH", L"MANPATH", L"CDPATH"); } -void env_init(const struct config_paths_t *paths /* or NULL */) -{ - /* - env_read_only variables can not be altered directly by the user - */ - - const wchar_t * const ro_keys[] = - { - L"status", - L"history", - L"version", - L"_", - L"LINES", - L"COLUMNS", - L"PWD", - //L"SHLVL", // will be inserted a bit lower down - L"FISH_VERSION", +void env_init(const struct config_paths_t *paths /* or NULL */) { + // These variables can not be altered directly by the user. + const wchar_t *const ro_keys[] = { + L"status", L"history", L"_", L"LINES", L"COLUMNS", L"PWD", L"FISH_VERSION", + // L"SHLVL" is readonly but will be inserted below after we increment it. }; for (size_t i=0; i < sizeof ro_keys / sizeof *ro_keys; i++) { @@ -432,10 +417,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) global_env = top; global = &top->env; - /* - Now the environemnt variable handling is set up, the next step - is to insert valid data - */ + // Now the environment variable handling is set up, the next step is to insert valid data. /* Import environment variables. Walk backwards so that the first one out of any duplicates wins (#2784) */ wcstring key, val; diff --git a/tests/c-locale.out b/tests/c-locale.out deleted file mode 100644 index 10a94d3e9..000000000 --- a/tests/c-locale.out +++ /dev/null @@ -1,4 +0,0 @@ -58c3bb58 -58c3bc58 -59fc59 -543f54 diff --git a/tests/c-locale.err b/tests/locale.err similarity index 100% rename from tests/c-locale.err rename to tests/locale.err diff --git a/tests/c-locale.in b/tests/locale.in similarity index 56% rename from tests/c-locale.in rename to tests/locale.in index d2f2bd5f3..d08fd7fde 100644 --- a/tests/c-locale.in +++ b/tests/locale.in @@ -1,3 +1,27 @@ +# Test behavior related to the locale. + +# Verify that our UTF-8 locale produces the expected output. +echo -n A\u00FCA | xxd --plain + +# Verify that exporting a change to the C locale produces the expected output. +# The output should include the literal byte \xFC rather than the UTF-8 sequence for \u00FC. +begin + set -lx LC_ALL C + echo -n B\u00FCB | xxd --plain +end + +# Since the previous change was localized to a block it should no +# longer be in effect and we should be back to a UTF-8 locale. +echo -n C\u00FCC | xxd --plain + +# Verify that setting a non-exported locale var doesn't affect the behavior. +# The output should include the UTF-8 sequence for \u00FC rather than that literal byte. +# Just like the previous test. +begin + set -l LC_ALL C + echo -n D\u00FCD | xxd --plain +end + # Verify that fish can pass through non-ASCII characters in the C/POSIX # locale. This is to prevent regression of # https://github.com/fish-shell/fish-shell/issues/2802. @@ -14,22 +38,24 @@ # echo output directly to the `xxd` program then via a fish instance. The # output should be "58c3bb58" for the first statement and "58c3bc58" for the # second. -echo -n X\u00fbX | \ +echo -n X\u00FBX | \ xxd --plain -echo X\u00fcX | env LC_ALL=C ../test/root/bin/fish -c 'read foo; echo -n $foo' | \ +echo X\u00FCX | env LC_ALL=C ../test/root/bin/fish -c 'read foo; echo -n $foo' | \ xxd --plain +# The next tests deliberately spawn another fish instance to test inheritence of env vars. + # This test is subtle. Despite the presence of the \u00fc unicode char (a "u" # with an umlaut) the fact the locale is C/POSIX will cause the \xfc byte to # be emitted rather than the usual UTF-8 sequence \xc3\xbc. That's because the # few single-byte unicode chars (that are not ASCII) are generally in the -# ISO-8859-1 char set which is encompased by the C locale. The output should +# ISO 8859-x char sets which are encompassed by the C locale. The output should # be "59fc59". -env LC_ALL=C ../test/root/bin/fish -c 'echo -n Y\u00fcY' | \ +env LC_ALL=C ../test/root/bin/fish -c 'echo -n Y\u00FCY' | \ xxd --plain # The user can specify a wide unicode character (one requiring more than a # single byte). In the C/POSIX locales we substitute a question-mark for the # unencodable wide char. The output should be "543f54". -env LC_ALL=C ../test/root/bin/fish -c 'echo -n T\u01fdT' | \ +env LC_ALL=C ../test/root/bin/fish -c 'echo -n T\u01FDT' | \ xxd --plain diff --git a/tests/locale.out b/tests/locale.out new file mode 100644 index 000000000..b7c97c156 --- /dev/null +++ b/tests/locale.out @@ -0,0 +1,8 @@ +41c3bc41 +42fc42 +43c3bc43 +44c3bc44 +58c3bb58 +58c3bc58 +59fc59 +543f54 diff --git a/tests/c-locale.status b/tests/locale.status similarity index 100% rename from tests/c-locale.status rename to tests/locale.status