fixed bash-for-windows
This commit is contained in:
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user