From f0c61427f3324b610404f1146b53639380e43dbd Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Mon, 5 Feb 2024 01:00:01 +0000 Subject: [PATCH] Fix escape characters for content with newline Given an input like so: ```markdown 'foo' 'bar' ``` We should be escaping both opening `'`. Before this change only the first was escaped because we only look at the first character of a `[]byte` and did not account for newlines. Signed-off-by: Brian Goff --- md2man/roff.go | 23 +++++++++++++++++++++++ md2man/roff_test.go | 32 ++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/md2man/roff.go b/md2man/roff.go index 4b19188..66cc9b0 100644 --- a/md2man/roff.go +++ b/md2man/roff.go @@ -1,6 +1,7 @@ package md2man import ( + "bufio" "bytes" "fmt" "io" @@ -322,6 +323,28 @@ func out(w io.Writer, output string) { } func escapeSpecialChars(w io.Writer, text []byte) { + scanner := bufio.NewScanner(bytes.NewReader(text)) + + // count the number of lines in the text + // we need to know this to avoid adding a newline after the last line + n := bytes.Count(text, []byte{'\n'}) + idx := 0 + + for scanner.Scan() { + dt := scanner.Bytes() + if idx < n { + idx++ + dt = append(dt, '\n') + } + escapeSpecialCharsLine(w, dt) + } + + if err := scanner.Err(); err != nil { + panic(err) + } +} + +func escapeSpecialCharsLine(w io.Writer, text []byte) { for i := 0; i < len(text); i++ { // escape initial apostrophe or period if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') { diff --git a/md2man/roff_test.go b/md2man/roff_test.go index af03326..dee4862 100644 --- a/md2man/roff_test.go +++ b/md2man/roff_test.go @@ -360,6 +360,8 @@ func TestEscapeCharacters(t *testing.T) { tests := []string{ "Test-one_two&three\\four~five", ".nh\n\n.PP\nTest-one_two&three\\\\four~five\n", + "'foo'\n'bar'", + ".nh\n\n.PP\n\\&'foo'\n\\&'bar'\n", } doTestsInline(t, tests) } @@ -438,23 +440,25 @@ func doTestsParam(t *testing.T, tests []string, params TestParams) { execRecoverableTestSuite(t, tests, params, func(candidate *string) { for i := 0; i+1 < len(tests); i += 2 { input := tests[i] - *candidate = input - expected := tests[i+1] - actual := runMarkdown(*candidate, params) - if actual != expected { - t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]", - *candidate, expected, actual) - } + t.Run(input, func(t *testing.T) { + *candidate = input + expected := tests[i+1] + actual := runMarkdown(*candidate, params) + if actual != expected { + t.Errorf("\nInput [%#v]\nExpected[%#v]\nActual [%#v]", + *candidate, expected, actual) + } - // now test every substring to stress test bounds checking - if !testing.Short() { - for start := 0; start < len(input); start++ { - for end := start + 1; end <= len(input); end++ { - *candidate = input[start:end] - runMarkdown(*candidate, params) + // now test every substring to stress test bounds checking + if !testing.Short() { + for start := 0; start < len(input); start++ { + for end := start + 1; end <= len(input); end++ { + *candidate = input[start:end] + runMarkdown(*candidate, params) + } } } - } + }) } }) }