项目文件夹

文件
wehub-resource-sync 36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:06 +08:00

121 行
3.0 KiB
Go

package output
import (
"strings"
"unicode"
"unicode/utf8"
)
const hexDigits = "0123456789abcdef"
// SanitizeTerminal makes a string safe to print to an interactive terminal
// by just replacing control characters with visible escape sequences (e.g. "\x1b")
// Examples:
// - "hi\x1b[31mred" -> "hi\x1b[31mred" (ESC becomes visible)
// - "nul:\x00" -> "nul:\x00"
// - "bad:\xff" -> "bad:\xff" (invalid UTF-8 byte)
// - "a\tb\nc" -> "a\tb\nc" (tabs/newlines are untouched)
func SanitizeTerminal(s string) string {
idx := 0
// fast path: scan until we find a control rune / invalid UTF-8 byte
for idx < len(s) {
r, size := utf8.DecodeRuneInString(s[idx:])
if r == utf8.RuneError && size == 1 {
break
}
if r == '\n' || r == '\t' {
idx += size
continue
}
if unicode.IsControl(r) {
break
}
idx += size
}
if idx == len(s) {
return s
}
var b strings.Builder
b.Grow(len(s) + 8)
b.WriteString(s[:idx])
// slow path: walk the remainder and rewrite any control bytes/runes
for idx < len(s) {
r, size := utf8.DecodeRuneInString(s[idx:])
if r == utf8.RuneError && size == 1 {
// preserve invalid bytes without letting them act as controls just in case
appendEscapedByte(&b, s[idx])
idx++
continue
}
if r == '\n' || r == '\t' {
b.WriteRune(r)
idx += size
continue
}
if unicode.IsControl(r) {
appendEscapedRune(&b, r)
idx += size
continue
}
b.WriteString(s[idx : idx+size])
idx += size
}
return b.String()
}
// SanitizeTerminalLine is SanitizeTerminal for values rendered as a single line
// or table cell (process names, command lines, env entries, paths).
// SanitizeTerminal intentionally preserves \n and \t for multi-line layout; in a
// single-line field those let an embedded value forge extra output lines or
// shift columns, so they are escaped here too.
func SanitizeTerminalLine(s string) string {
if strings.ContainsAny(s, "\n\t") {
s = strings.NewReplacer("\n", `\n`, "\t", `\t`).Replace(s)
}
return SanitizeTerminal(s)
}
func appendEscapedByte(b *strings.Builder, bt byte) {
b.WriteString(`\x`)
b.WriteByte(hexDigits[bt>>4])
b.WriteByte(hexDigits[bt&0x0f])
}
// while this looks extremely bad, it just outputs this:
// - r = 0x1b -> "\x1b"
// - r = 0x2028 -> "\u2028"
// - r = 0x1f600 -> "\U0001f600"
func appendEscapedRune(b *strings.Builder, r rune) {
// 0xFF: "\xHH" (simple byte escape)
if r <= 0xFF {
appendEscapedByte(b, byte(r))
return
}
// <= 0xFFFF: "\uHHHH" (BMP escape)
if r <= 0xFFFF {
b.WriteString(`\u`)
b.WriteByte(hexDigits[(r>>12)&0x0f])
b.WriteByte(hexDigits[(r>>8)&0x0f])
b.WriteByte(hexDigits[(r>>4)&0x0f])
b.WriteByte(hexDigits[r&0x0f])
return
}
// otherwise: "\UHHHHHHHH" (full 32-bit escape)
b.WriteString(`\U`)
b.WriteByte(hexDigits[(r>>28)&0x0f])
b.WriteByte(hexDigits[(r>>24)&0x0f])
b.WriteByte(hexDigits[(r>>20)&0x0f])
b.WriteByte(hexDigits[(r>>16)&0x0f])
b.WriteByte(hexDigits[(r>>12)&0x0f])
b.WriteByte(hexDigits[(r>>8)&0x0f])
b.WriteByte(hexDigits[(r>>4)&0x0f])
b.WriteByte(hexDigits[r&0x0f])
}