1
0
Fork 0
mirror of https://github.com/vbatts/go-mtree.git synced 2025-10-03 20:21:01 +00:00

govis: support double quote escapes

This is supported by both OpenBSD and FreBSD so it seems possible that
we will run into \" sequences at some point. The handling is basically
identical to \\ sequences.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This commit is contained in:
Aleksa Sarai 2025-09-23 17:27:24 +10:00
parent 7e2695a1be
commit bd7b72e037
No known key found for this signature in database
GPG key ID: 2897FAD2B7E9446F
5 changed files with 27 additions and 19 deletions

View file

@ -32,16 +32,17 @@ type VisFlag uint
// mtree only uses one set of flags, implementing them all is necessary in // mtree only uses one set of flags, implementing them all is necessary in
// order to have compatibility with BSD's vis() and unvis() commands. // order to have compatibility with BSD's vis() and unvis() commands.
const ( const (
VisOctal VisFlag = (1 << iota) // VIS_OCTAL: Use octal \ddd format. VisOctal VisFlag = (1 << iota) // VIS_OCTAL: Use octal \ddd format.
VisCStyle // VIS_CSTYLE: Use \[nrft0..] where appropriate. VisCStyle // VIS_CSTYLE: Use \[nrft0..] where appropriate.
VisSpace // VIS_SP: Also encode space. VisSpace // VIS_SP: Also encode space.
VisTab // VIS_TAB: Also encode tab. VisTab // VIS_TAB: Also encode tab.
VisNewline // VIS_NL: Also encode newline. VisNewline // VIS_NL: Also encode newline.
VisSafe // VIS_SAFE: Encode unsafe characters. VisSafe // VIS_SAFE: Encode unsafe characters.
VisNoSlash // VIS_NOSLASH: Inhibit printing '\'. VisNoSlash // VIS_NOSLASH: Inhibit printing '\'.
VisHTTPStyle // VIS_HTTPSTYLE: HTTP-style escape %xx. VisHTTPStyle // VIS_HTTPSTYLE: HTTP-style escape %xx.
VisGlob // VIS_GLOB: Encode glob(3) magics. VisGlob // VIS_GLOB: Encode glob(3) magics.
visMask VisFlag = (1 << iota) - 1 // Mask of all flags. VisDoubleQuote // VIS_DQ: Encode double-quotes (").
visMask VisFlag = (1 << iota) - 1 // Mask of all flags.
VisWhite VisFlag = (VisSpace | VisTab | VisNewline) VisWhite VisFlag = (VisSpace | VisTab | VisNewline)
) )

View file

@ -225,9 +225,9 @@ func (p *unvisParser) escapeSequence() error {
} }
switch ch { switch ch {
case '\\': case '\\', '"':
p.Step() p.Step()
return p.output.WriteByte('\\') return p.output.WriteByte(byte(ch))
case '0', '1', '2', '3', '4', '5', '6', '7': case '0', '1', '2', '3', '4', '5', '6', '7':
return p.escapeDigits(8, false) return p.escapeDigits(8, false)

View file

@ -65,12 +65,13 @@ func TestUnvisCStyleEscape(t *testing.T) {
expected string expected string
}{ }{
{"", ""}, {"", ""},
{"\\n\\v\\t\\s", "\n\v\t "}, {`\n\v\t\s`, "\n\v\t "},
{"\\\\n\\tt", "\\n\tt"}, {`\\n\tt`, "\\n\tt"},
{"\\b", "\b"}, {`\b`, "\b"},
{"\\r\\b\\n", "\r\b\n"}, {`\r\b\n`, "\r\b\n"},
{"\\a\\a\\b", "\x07\x07\b"}, {`\a\a\b`, "\x07\x07\b"},
{"\\f\\s\\E", "\f \x1b"}, {`\f\s\E`, "\f \x1b"},
{`\"foo\"\\"bar`, `"foo"\"bar`},
// Hidden markers. They actually aren't generated by vis(3) but for // Hidden markers. They actually aren't generated by vis(3) but for
// some reason, they're supported... // some reason, they're supported...
{"test\\\ning", "testing"}, {"test\\\ning", "testing"},

View file

@ -81,7 +81,8 @@ func vis(output *strings.Builder, ch byte, flag VisFlag) {
// We must *always* encode stuff characters not in ASCII. // We must *always* encode stuff characters not in ASCII.
case flag&VisGlob == VisGlob && isglob(ch): case flag&VisGlob == VisGlob && isglob(ch):
// Glob characters are graphical but can be forced to be encoded. // Glob characters are graphical but can be forced to be encoded.
case flag&VisNoSlash == 0 && ch == '\\': case flag&VisNoSlash == 0 && ch == '\\',
flag&VisDoubleQuote == VisDoubleQuote && ch == '"':
// Prefix \ if applicable. // Prefix \ if applicable.
_ = output.WriteByte('\\') _ = output.WriteByte('\\')
fallthrough fallthrough

View file

@ -104,6 +104,11 @@ func TestVisFlags(t *testing.T) {
{"62_\u00c6\u00c62\u00ae\u00b7m\u00db\u00c3r^\u00bfp\u00c6u'q\u00fbc2\u00f0u\u00b8\u00dd\u00e8v\u00ff\u00b0\u00dc\u00c2\u00f53\u00db-k\u00f2sd4\\p\u00da\u00a6\u00d3\u00eea<\u00e6s{\u00a0p\u00f0\u00ffj\u00e0\u00e8\u00b8\u00b8\u00bc\u00fcb", `62_\303\206\303\2062\302\256\302\267m\303\233\303\203r^\302\277p\303\206u'q\303\273c2\303\260u\302\270\303\235\303\250v\303\277\302\260\303\234\303\202\303\2653\303\233-k\303\262sd4\\p\303\232\302\246\303\223\303\256a<\303\246s{\302\240p\303\260\303\277j\303\240\303\250\302\270\302\270\302\274\303\274b`, VisGlob | VisOctal}, {"62_\u00c6\u00c62\u00ae\u00b7m\u00db\u00c3r^\u00bfp\u00c6u'q\u00fbc2\u00f0u\u00b8\u00dd\u00e8v\u00ff\u00b0\u00dc\u00c2\u00f53\u00db-k\u00f2sd4\\p\u00da\u00a6\u00d3\u00eea<\u00e6s{\u00a0p\u00f0\u00ffj\u00e0\u00e8\u00b8\u00b8\u00bc\u00fcb", `62_\303\206\303\2062\302\256\302\267m\303\233\303\203r^\302\277p\303\206u'q\303\273c2\303\260u\302\270\303\235\303\250v\303\277\302\260\303\234\303\202\303\2653\303\233-k\303\262sd4\\p\303\232\302\246\303\223\303\256a<\303\246s{\302\240p\303\260\303\277j\303\240\303\250\302\270\302\270\302\274\303\274b`, VisGlob | VisOctal},
{"'3Ze\u050e|\u02del\u069du-Rpct4+Z5b={@_{b", `'3Ze\M-T\M^N|\M-K\M^^l\M-Z\M^]u-Rpct4+Z5b={@_{b`, VisGlob}, {"'3Ze\u050e|\u02del\u069du-Rpct4+Z5b={@_{b", `'3Ze\M-T\M^N|\M-K\M^^l\M-Z\M^]u-Rpct4+Z5b={@_{b`, VisGlob},
{"'3Ze\u050e|\u02del\u069du-Rpct4+Z5b={@_{b", `'3Ze\324\216|\313\236l\332\235u-Rpct4+Z5b={@_{b`, VisGlob | VisOctal}, {"'3Ze\u050e|\u02del\u069du-Rpct4+Z5b={@_{b", `'3Ze\324\216|\313\236l\332\235u-Rpct4+Z5b={@_{b`, VisGlob | VisOctal},
// VisDoubleQuote
{`Foo="Bar's"`, `Foo="Bar's"`, 0},
{`Foo="Bar's"`, `Foo=\"Bar's\"`, VisDoubleQuote},
{`"“Unicode” Quotes"`, `\"\M-b\M^@\M^\Unicode\M-b\M^@\M^] \M-b\M^@\M^XQuote\M-b\M^@\M^Ys\M-b\M^@\M^Y\"`, VisDoubleQuote},
{`"“Unicode” Quotes"`, `\"\342\200\234Unicode\342\200\235 \342\200\230Quote\342\200\231s\342\200\231\"`, VisDoubleQuote | VisOctal},
} { } {
t.Run(fmt.Sprintf("Test%.2d", idx), func(t *testing.T) { t.Run(fmt.Sprintf("Test%.2d", idx), func(t *testing.T) {
enc, err := Vis(test.input, test.flag) enc, err := Vis(test.input, test.flag)