waveterm/pkg/util/shellutil/shellquote.go
Sylvie Crowe ff5f26709c
WSL Updates for New Architecture (#1756)
This adapts most of the WSL code to follow the new architecture that ssh
uses.

---------

Co-authored-by: sawka <mike@commandline.dev>
2025-01-16 15:54:58 -08:00

131 lines
2.4 KiB
Go

// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package shellutil
import "regexp"
var (
safePattern = regexp.MustCompile(`^[a-zA-Z0-9_/.-]+$`)
envVarNamePattern = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)
)
func IsValidEnvVarName(name string) bool {
return envVarNamePattern.MatchString(name)
}
func HardQuote(s string) string {
if s == "" {
return "\"\""
}
if safePattern.MatchString(s) {
return s
}
buf := make([]byte, 0, len(s)+5)
buf = append(buf, '"')
for i := 0; i < len(s); i++ {
switch s[i] {
case '"', '\\', '$', '`':
buf = append(buf, '\\', s[i])
case '\n':
buf = append(buf, '\\', '\n')
default:
buf = append(buf, s[i])
}
}
buf = append(buf, '"')
return string(buf)
}
// does not encode newlines or backticks
func HardQuoteFish(s string) string {
if s == "" {
return "\"\""
}
if safePattern.MatchString(s) {
return s
}
buf := make([]byte, 0, len(s)+5)
buf = append(buf, '"')
for i := 0; i < len(s); i++ {
switch s[i] {
case '"', '\\', '$':
buf = append(buf, '\\', s[i])
default:
buf = append(buf, s[i])
}
}
buf = append(buf, '"')
return string(buf)
}
func HardQuotePowerShell(s string) string {
if s == "" {
return "\"\""
}
buf := make([]byte, 0, len(s)+5)
buf = append(buf, '"')
for i := 0; i < len(s); i++ {
c := s[i]
// In PowerShell, backtick (`) is the escape character
switch c {
case '"', '`', '$':
buf = append(buf, '`')
case '\n':
buf = append(buf, '`', 'n') // PowerShell uses `n for newline
}
buf = append(buf, c)
}
buf = append(buf, '"')
return string(buf)
}
func SoftQuote(s string) string {
if s == "" {
return "\"\""
}
// Handle special case of ~ paths
if len(s) > 0 && s[0] == '~' {
// If it's just ~ or ~/something with no special chars, leave it as is
if len(s) == 1 || (len(s) > 1 && s[1] == '/' && safePattern.MatchString(s[2:])) {
return s
}
// Otherwise quote everything after the ~ (including the /)
if len(s) > 1 && s[1] == '/' {
return "~" + SoftQuote(s[1:])
}
}
if safePattern.MatchString(s) {
return s
}
buf := make([]byte, 0, len(s)+5)
buf = append(buf, '"')
for i := 0; i < len(s); i++ {
c := s[i]
// In soft quote, we don't escape $ to allow expansion
if c == '"' || c == '\\' || c == '`' {
buf = append(buf, '\\')
}
buf = append(buf, c)
}
buf = append(buf, '"')
return string(buf)
}