diff --git a/env.cpp b/env.cpp index 353085b8b..c9f986b10 100644 --- a/env.cpp +++ b/env.cpp @@ -542,8 +542,10 @@ void env_init(const struct config_paths_t *paths /* or NULL */) wcstring nshlvl_str = L"1"; if (! shlvl_str.missing()) { - long shlvl_i = wcstol(shlvl_str.c_str(), NULL, 10); - if (shlvl_i >= 0) + wchar_t *end; + long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10); + while (iswspace(*end)) ++end; /* skip trailing whitespace */ + if (shlvl_i >= 0 && *end == '\0') { nshlvl_str = to_string(shlvl_i + 1); } diff --git a/exec.cpp b/exec.cpp index 3b5506da6..7706dcc50 100644 --- a/exec.cpp +++ b/exec.cpp @@ -651,6 +651,21 @@ void exec_job(parser_t &parser, job_t *j) /* PCA This is for handling exec. Passing all_ios here matches what fish 2.0.0 and 1.x did. It's known to be wrong - for example, it means that redirections bound for subsequent commands in the pipeline will apply to exec. However, using exec in a pipeline doesn't really make sense, so I'm not trying to fix it here. */ if (!setup_child_process(j, 0, all_ios)) { + /* decrement SHLVL as we're removing ourselves from the shell "stack" */ + const env_var_t shlvl_str = env_get_string(L"SHLVL", ENV_GLOBAL | ENV_EXPORT); + wcstring nshlvl_str = L"0"; + if (!shlvl_str.missing()) + { + wchar_t *end; + long shlvl_i = wcstol(shlvl_str.c_str(), &end, 10); + while (iswspace(*end)) ++end; /* skip trailing whitespace */ + if (shlvl_i > 0 && *end == '\0') + { + nshlvl_str = to_string(shlvl_i - 1); + } + } + env_set(L"SHLVL", nshlvl_str.c_str(), ENV_GLOBAL | ENV_EXPORT); + /* launch_process _never_ returns */ diff --git a/tests/test3.in b/tests/test3.in index 56cceeb57..b83b69c04 100644 --- a/tests/test3.in +++ b/tests/test3.in @@ -236,5 +236,12 @@ echo Missing: $testu # test SHLVL # use a subshell to ensure a clean slate env SHLVL= ../fish -c 'echo SHLVL: $SHLVL; ../fish -c \'echo SHLVL: $SHLVL\'' +# exec should decrement SHLVL +env SHLVL= ../fish -c 'echo SHLVL: $SHLVL; exec ../fish -c \'echo SHLVL: $SHLVL\'' +# garbage SHLVLs should be treated as garbage +env SHLVL=3foo ../fish -c 'echo SHLVL: $SHLVL' +# whitespace is allowed though (for bash compatibility) +env SHLVL="3 " ../fish -c 'echo SHLVL: $SHLVL' +env SHLVL=" 3" ../fish -c 'echo SHLVL: $SHLVL' true diff --git a/tests/test3.out b/tests/test3.out index 50563c05b..c6a21905a 100644 --- a/tests/test3.out +++ b/tests/test3.out @@ -23,3 +23,8 @@ Missing: Missing: SHLVL: 1 SHLVL: 2 +SHLVL: 1 +SHLVL: 1 +SHLVL: 1 +SHLVL: 4 +SHLVL: 4