pkg/program/shell: Increment $E:SHLVL.

This fixes #834.
This commit is contained in:
Qi Xiao 2020-04-20 00:08:09 +01:00
parent 212a21d051
commit 6938c8d117
3 changed files with 95 additions and 0 deletions

View File

@ -4,6 +4,7 @@ package shell
import (
"os"
"os/signal"
"strconv"
"syscall"
"github.com/elves/elvish/pkg/cli/term"
@ -17,6 +18,7 @@ var logger = util.GetLogger("[shell] ")
func setupShell(fds [3]*os.File, p Paths, spawn bool) (*eval.Evaler, func()) {
restoreTTY := term.SetupGlobal()
ev := InitRuntime(fds[2], p, spawn)
restoreSHLVL := incSHLVL()
sigs := make(chan os.Signal)
signal.Notify(sigs)
@ -29,6 +31,7 @@ func setupShell(fds [3]*os.File, p Paths, spawn bool) (*eval.Evaler, func()) {
return ev, func() {
signal.Stop(sigs)
restoreSHLVL()
CleanupRuntime(fds[2], ev)
restoreTTY()
}
@ -41,6 +44,28 @@ func evalInTTY(ev *eval.Evaler, op eval.Op, fds [3]*os.File) error {
Ports: ports[:], Interrupt: eval.ListenInterrupts, PutInFg: true})
}
const envSHLVL = "SHLVL"
func incSHLVL() func() {
restoreSHLVL := saveEnv(envSHLVL)
i, err := strconv.Atoi(os.Getenv(envSHLVL))
if err != nil {
i = 0
}
os.Setenv(envSHLVL, strconv.Itoa(i+1))
return restoreSHLVL
}
func saveEnv(name string) func() {
v, ok := os.LookupEnv(name)
if ok {
return func() { os.Setenv(name, v) }
}
return func() { os.Unsetenv(name) }
}
// Global panic handler.
func rescue() {
r := recover()

View File

@ -0,0 +1,68 @@
package shell
import (
"os"
"testing"
)
func TestShell_SHLVL_NormalCase(t *testing.T) {
restore := saveEnv("SHLVL")
defer restore()
os.Setenv("SHLVL", "10")
testSHLVL(t, "11")
}
func TestShell_SHLVL_Unset(t *testing.T) {
restore := saveEnv("SHLVL")
defer restore()
os.Unsetenv("SHLVL")
testSHLVL(t, "1")
}
func TestShell_SHLVL_Invalid(t *testing.T) {
restore := saveEnv("SHLVL")
defer restore()
os.Setenv("SHLVL", "invalid")
testSHLVL(t, "1")
}
func TestShell_NegativeSHLVL_Increments(t *testing.T) {
// Other shells don't agree what the behavior should be:
//
// ~> E:SHLVL=-100 bash -c 'echo $SHLVL'
// 0
// ~> E:SHLVL=-100 zsh -c 'echo $SHLVL'
// -99
// ~> E:SHLVL=-100 fish -c 'echo $SHLVL'
// 1
//
// Elvish follows Zsh here.
restore := saveEnv("SHLVL")
defer restore()
os.Setenv("SHLVL", "-100")
testSHLVL(t, "-99")
}
func testSHLVL(t *testing.T, wantSHLVL string) {
t.Helper()
f := setup()
defer f.cleanup()
oldValue, oldOK := os.LookupEnv("SHLVL")
Script(f.fds(), []string{"print $E:SHLVL"}, &ScriptConfig{Cmd: true})
f.testOut(t, 1, wantSHLVL)
// Test that state of SHLVL is restored.
newValue, newOK := os.LookupEnv("SHLVL")
if newValue != oldValue {
t.Errorf("SHLVL not restored, %q -> %q", oldValue, newValue)
}
if oldOK != newOK {
t.Errorf("SHLVL existence not restored, %v -> %v", oldOK, newOK)
}
}

View File

@ -48,6 +48,8 @@ Version 0.14 has not been released yet. It is planned to be released on
- Elvish now uses `$XDG_RUNTIME_DIR` to keep runtime files if possible.
- Elvish now increments the `$SHLVL` environment variable.
# Notable bugfixes
- Elvish no longer crashes when redirecting to a high FD