diff --git a/pkg/govis/govis.go b/pkg/govis/govis.go index 4200af2..cf78a08 100644 --- a/pkg/govis/govis.go +++ b/pkg/govis/govis.go @@ -32,16 +32,17 @@ type VisFlag uint // mtree only uses one set of flags, implementing them all is necessary in // order to have compatibility with BSD's vis() and unvis() commands. const ( - VisOctal VisFlag = (1 << iota) // VIS_OCTAL: Use octal \ddd format. - VisCStyle // VIS_CSTYLE: Use \[nrft0..] where appropriate. - VisSpace // VIS_SP: Also encode space. - VisTab // VIS_TAB: Also encode tab. - VisNewline // VIS_NL: Also encode newline. - VisSafe // VIS_SAFE: Encode unsafe characters. - VisNoSlash // VIS_NOSLASH: Inhibit printing '\'. - VisHTTPStyle // VIS_HTTPSTYLE: HTTP-style escape %xx. - VisGlob // VIS_GLOB: Encode glob(3) magics. - visMask VisFlag = (1 << iota) - 1 // Mask of all flags. + VisOctal VisFlag = (1 << iota) // VIS_OCTAL: Use octal \ddd format. + VisCStyle // VIS_CSTYLE: Use \[nrft0..] where appropriate. + VisSpace // VIS_SP: Also encode space. + VisTab // VIS_TAB: Also encode tab. + VisNewline // VIS_NL: Also encode newline. + VisSafe // VIS_SAFE: Encode unsafe characters. + VisNoSlash // VIS_NOSLASH: Inhibit printing '\'. + VisHTTPStyle // VIS_HTTPSTYLE: HTTP-style escape %xx. + VisGlob // VIS_GLOB: Encode glob(3) magics. + VisDoubleQuote // VIS_DQ: Encode double-quotes ("). + visMask VisFlag = (1 << iota) - 1 // Mask of all flags. VisWhite VisFlag = (VisSpace | VisTab | VisNewline) ) diff --git a/pkg/govis/unvis.go b/pkg/govis/unvis.go index 7f20ad7..b90fe72 100644 --- a/pkg/govis/unvis.go +++ b/pkg/govis/unvis.go @@ -225,9 +225,9 @@ func (p *unvisParser) escapeSequence() error { } switch ch { - case '\\': + case '\\', '"': p.Step() - return p.output.WriteByte('\\') + return p.output.WriteByte(byte(ch)) case '0', '1', '2', '3', '4', '5', '6', '7': return p.escapeDigits(8, false) diff --git a/pkg/govis/unvis_test.go b/pkg/govis/unvis_test.go index ae9945f..5915ce3 100644 --- a/pkg/govis/unvis_test.go +++ b/pkg/govis/unvis_test.go @@ -65,12 +65,13 @@ func TestUnvisCStyleEscape(t *testing.T) { expected string }{ {"", ""}, - {"\\n\\v\\t\\s", "\n\v\t "}, - {"\\\\n\\tt", "\\n\tt"}, - {"\\b", "\b"}, - {"\\r\\b\\n", "\r\b\n"}, - {"\\a\\a\\b", "\x07\x07\b"}, - {"\\f\\s\\E", "\f \x1b"}, + {`\n\v\t\s`, "\n\v\t "}, + {`\\n\tt`, "\\n\tt"}, + {`\b`, "\b"}, + {`\r\b\n`, "\r\b\n"}, + {`\a\a\b`, "\x07\x07\b"}, + {`\f\s\E`, "\f \x1b"}, + {`\"foo\"\\"bar`, `"foo"\"bar`}, // Hidden markers. They actually aren't generated by vis(3) but for // some reason, they're supported... {"test\\\ning", "testing"}, diff --git a/pkg/govis/vis.go b/pkg/govis/vis.go index e7efc6b..2965bcd 100644 --- a/pkg/govis/vis.go +++ b/pkg/govis/vis.go @@ -81,7 +81,8 @@ func vis(output *strings.Builder, ch byte, flag VisFlag) { // We must *always* encode stuff characters not in ASCII. case flag&VisGlob == VisGlob && isglob(ch): // 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. _ = output.WriteByte('\\') fallthrough diff --git a/pkg/govis/vis_test.go b/pkg/govis/vis_test.go index f7b7a32..327a9f8 100644 --- a/pkg/govis/vis_test.go +++ b/pkg/govis/vis_test.go @@ -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}, {"'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}, + // VisDoubleQuote + {`Foo="Bar's"`, `Foo="Bar's"`, 0}, + {`Foo="Bar's"`, `Foo=\"Bar's\"`, VisDoubleQuote}, + {`"“Unicode” ‘Quote’s’"`, `\"\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” ‘Quote’s’"`, `\"\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) { enc, err := Vis(test.input, test.flag)