fix setting of readonly vars issue with zsh rc files

This commit is contained in:
sawka
2024-01-20 13:18:14 -08:00
parent 613cd30c36
commit 713acb664d
3 changed files with 50 additions and 5 deletions
+30 -4
View File
@@ -11,6 +11,7 @@ import (
"os/exec"
"strings"
"sync"
"unicode"
"github.com/alessio/shellescape"
"github.com/wavetermdev/waveterm/waveshell/pkg/base"
@@ -105,6 +106,7 @@ var localZshMajorVersion = ""
// sentinel value for functions that should be autoloaded
const ZshFnAutoLoad = "autoload"
const ZshAutoloadFnBody = "builtin autoload -XU"
type ZshParamKey struct {
// paramtype cannot contain spaces
@@ -204,6 +206,19 @@ func makeZshTypesetStmt(varDecl *shellenv.DeclareDeclType) string {
}
}
func isZshSafeNameStr(name string) bool {
for _, ch := range name {
if ch == '_' {
continue
}
if unicode.IsLetter(ch) || unicode.IsDigit(ch) {
continue
}
return false
}
return true
}
func (z zshShellApi) MakeRcFileStr(pk *packet.RunPacketType) string {
var rcBuf bytes.Buffer
rcBuf.WriteString(z.GetBaseShellOpts() + "\n")
@@ -216,9 +231,6 @@ func (z zshShellApi) MakeRcFileStr(pk *packet.RunPacketType) string {
if ZshIgnoreVars[varDecl.Name] {
continue
}
if varDecl.IsReadOnly() {
continue
}
if ZshUniqueArrayVars[varDecl.Name] && !varDecl.IsUniqueArray() {
varDecl.AddFlag("U")
}
@@ -230,7 +242,19 @@ func (z zshShellApi) MakeRcFileStr(pk *packet.RunPacketType) string {
if stmt == "" {
continue
}
rcBuf.WriteString(makeZshTypesetStmt(varDecl))
if varDecl.IsReadOnly() {
// we can't reset read-only variables
// so we check if it is a "safe" name, and then we can write a conditional
// that only sets it if it hasn't already been set.
if !isZshSafeNameStr(varDecl.Name) {
continue
}
rcBuf.WriteString(fmt.Sprintf("if (( ! ${+%s} )); then\n", varDecl.Name))
rcBuf.WriteString(makeZshTypesetStmt(varDecl))
rcBuf.WriteString("\nfi")
} else {
rcBuf.WriteString(makeZshTypesetStmt(varDecl))
}
rcBuf.WriteString("\n")
}
if shellenv.FindVarDecl(varDecls, "ZDOTDIR") == nil {
@@ -519,6 +543,8 @@ func ParseZshFunctions(fpathArr []string, fnBytes []byte, partSeparator []byte)
source := fnSource[fnKey.ParamName]
if isSourceFileInFpath(fpathArr, source) {
fnBody[fnKey] = ZshFnAutoLoad
} else if strings.TrimSpace(fnBody[fnKey]) == ZshAutoloadFnBody {
fnBody[fnKey] = ZshFnAutoLoad
}
}
return fnBody
+18
View File
@@ -27,3 +27,21 @@ func TestParseZshDecl(t *testing.T) {
declStr = `typeset -x -g -aT FOO foo=( 1 2 3 )`
testSingleDecl(declStr)
}
func TestZshSafeDeclName(t *testing.T) {
if !isZshSafeNameStr("foo") {
t.Errorf("foo should be safe")
}
if isZshSafeNameStr("foo bar") {
t.Errorf("foo bar should not be safe")
}
if !isZshSafeNameStr("foo_bar") {
t.Errorf("foo_bar should be safe")
}
if !isZshSafeNameStr("été") {
t.Errorf("été should be be safe")
}
if isZshSafeNameStr("hello\x01z") {
t.Errorf("should not be safe")
}
}
+2 -1
View File
@@ -50,6 +50,7 @@ const ShellVarName = "SHELL"
const SigKillWaitTime = 2 * time.Second
const RtnStateFdNum = 20
const ReturnStateReadWaitTime = 2 * time.Second
const ForceDebugRcFile = false
const ClientCommandFmt = `
PATH=$PATH:~/.mshell;
@@ -821,7 +822,7 @@ func RunCommandSimple(pk *packet.RunPacketType, sender *packet.PacketSender, fro
rcFileStr += trapCmdStr
}
shellVarMap := shellenv.ShellVarMapFromState(state)
if base.HasDebugFlag(shellVarMap, base.DebugFlag_LogRcFile) {
if base.HasDebugFlag(shellVarMap, base.DebugFlag_LogRcFile) || ForceDebugRcFile {
debugRcFileName := base.GetDebugRcFileName()
err := os.WriteFile(debugRcFileName, []byte(rcFileStr), 0600)
if err != nil {