fixed bash-for-windows

This commit is contained in:
Cametendo
2026-05-31 21:49:13 +02:00
parent 7b3a101946
commit 9037606447
8 changed files with 437 additions and 123 deletions

View File

@@ -18,6 +18,8 @@ var aliases = make(map[string]string)
func (s *Shell) initBuiltins() {
s.builtins = map[string]func([]string) error{
"{": s.builtinTrue,
"}": s.builtinTrue,
// Shell builtins
"cd": s.builtinCd,
"pwd": s.builtinPwd,
@@ -442,84 +444,111 @@ func (s *Shell) builtinPrintf(args []string) error {
fmtArgs := args[1:]
var result strings.Builder
// POSIX: if more arguments than format specifiers, re-apply the format
// until all args are consumed. Track consumed count per pass to detect
// a format with no specifiers (avoid infinite loop).
argIdx := 0
i := 0
for i < len(format) {
if format[i] == '%' && i+1 < len(format) {
i++
// Optional width/precision
specStart := i
for i < len(format) && (format[i] == '-' || format[i] == '0' || (format[i] >= '1' && format[i] <= '9') || format[i] == '.') {
for {
consumed := 0
i := 0
for i < len(format) {
if format[i] == '%' && i+1 < len(format) {
i++
// Optional width/precision flags
specStart := i
for i < len(format) && (format[i] == '-' || format[i] == '0' || (format[i] >= '1' && format[i] <= '9') || format[i] == '.') {
i++
}
spec := format[specStart:i]
if i >= len(format) {
break
}
arg := ""
if argIdx < len(fmtArgs) {
arg = fmtArgs[argIdx]
argIdx++
consumed++
}
switch format[i] {
case 's':
if spec != "" {
result.WriteString(fmt.Sprintf("%-"+spec+"s", arg)) //nolint
} else {
result.WriteString(arg)
}
case 'd':
n := toInt(arg)
if spec != "" {
result.WriteString(fmt.Sprintf("%"+spec+"d", n))
} else {
result.WriteString(strconv.Itoa(n))
}
case 'f':
f, _ := strconv.ParseFloat(arg, 64)
if spec != "" {
result.WriteString(fmt.Sprintf("%"+spec+"f", f))
} else {
result.WriteString(fmt.Sprintf("%f", f))
}
case 'x':
n := toInt(arg)
result.WriteString(fmt.Sprintf("%x", n))
case 'o':
n := toInt(arg)
result.WriteString(fmt.Sprintf("%o", n))
case '%':
result.WriteByte('%')
if consumed > 0 {
consumed-- // %% consumes no arg
}
default:
result.WriteByte('%')
result.WriteByte(format[i])
if consumed > 0 {
consumed--
}
}
i++
} else if format[i] == '\\' && i+1 < len(format) {
i++
switch format[i] {
case 'n':
result.WriteByte('\n')
case 't':
result.WriteByte('\t')
case 'r':
result.WriteByte('\r')
case '\\':
result.WriteByte('\\')
case 'a':
result.WriteByte('\a')
case 'b':
result.WriteByte('\b')
case 'e', 'E':
result.WriteByte(0x1b) // ESC
case '0', '1', '2', '3', '4', '5', '6', '7':
// Octal escape \NNN (up to 3 octal digits)
oct := int(format[i] - '0')
for k := 0; k < 2 && i+1 < len(format) && format[i+1] >= '0' && format[i+1] <= '7'; k++ {
i++
oct = oct*8 + int(format[i]-'0')
}
result.WriteByte(byte(oct))
default:
result.WriteByte('\\')
result.WriteByte(format[i])
}
i++
} else {
result.WriteByte(format[i])
i++
}
spec := format[specStart:i]
if i >= len(format) {
break
}
arg := ""
if argIdx < len(fmtArgs) {
arg = fmtArgs[argIdx]
argIdx++
}
switch format[i] {
case 's':
if spec != "" {
result.WriteString(fmt.Sprintf("%-"+spec+"s", arg)) //nolint
} else {
result.WriteString(arg)
}
case 'd':
n := toInt(arg)
if spec != "" {
result.WriteString(fmt.Sprintf("%"+spec+"d", n))
} else {
result.WriteString(strconv.Itoa(n))
}
case 'f':
f, _ := strconv.ParseFloat(arg, 64)
if spec != "" {
result.WriteString(fmt.Sprintf("%"+spec+"f", f))
} else {
result.WriteString(fmt.Sprintf("%f", f))
}
case 'x':
n := toInt(arg)
result.WriteString(fmt.Sprintf("%x", n))
case 'o':
n := toInt(arg)
result.WriteString(fmt.Sprintf("%o", n))
case '%':
result.WriteByte('%')
argIdx-- // no arg consumed
default:
result.WriteByte('%')
result.WriteByte(format[i])
argIdx--
}
i++
} else if format[i] == '\\' && i+1 < len(format) {
i++
switch format[i] {
case 'n':
result.WriteByte('\n')
case 't':
result.WriteByte('\t')
case 'r':
result.WriteByte('\r')
case '\\':
result.WriteByte('\\')
case 'a':
result.WriteByte('\a')
case 'b':
result.WriteByte('\b')
default:
result.WriteByte('\\')
result.WriteByte(format[i])
}
i++
} else {
result.WriteByte(format[i])
i++
}
// Stop looping if all args consumed, no args given, or format has
// no specifiers (consumed==0 means infinite loop risk).
if argIdx >= len(fmtArgs) || consumed == 0 {
break
}
}
fmt.Fprint(s.Stdout, result.String())