// SPDX-License-Identifier: GPL-3.0-or-later package main import ( "fmt" "strings" "unicode/utf8" ) // display makes s safe to print to a terminal (spec ยง15.1). File names, // paths, reasons and tool messages come from outside krino: a control // character in one could move the cursor, recolour or erase what krino // prints, and a bidirectional control could reorder it, so a name could // hide a step or fake one. Every C0 control, DEL, C1 control, Unicode // bidirectional embedding, override or isolate, and every byte that is not // valid UTF-8 is shown as an escape instead - \x1b for a byte, \u202e for // a rune. Everything else, backslash included, is printed as it is. Text // that is plain printable ASCII is returned without copying. func display(s string) string { plain := true for i := 0; i < len(s); i++ { if c := s[i]; c < 0x20 || c >= 0x7f { plain = false break } } if plain { return s } var b strings.Builder for i := 0; i < len(s); { r, size := utf8.DecodeRuneInString(s[i:]) switch { case r == utf8.RuneError && size == 1: fmt.Fprintf(&b, `\x%02x`, s[i]) case r < 0x20 || r == 0x7f: fmt.Fprintf(&b, `\x%02x`, r) case controlRune(r): fmt.Fprintf(&b, `\u%04x`, r) default: b.WriteString(s[i : i+size]) } i += size } return b.String() } // controlRune reports whether r is a C1 control or a Unicode bidirectional // embedding, override or isolate: printable-looking code points a terminal // acts on. func controlRune(r rune) bool { return (r >= 0x80 && r <= 0x9f) || (r >= 0x202a && r <= 0x202e) || (r >= 0x2066 && r <= 0x2069) }