Skip to content

Commit

Permalink
x/crypto/ssh/terminal: replace \n with \r\n.
Browse files Browse the repository at this point in the history
18e6eb7 made MakeRaw match C's behaviour. This included clearing the
OPOST flag, which means that one now needs to write \r\n for a newline,
otherwise the cursor doesn't move back to the beginning and the terminal
prints a staircase.

(Dear god, we're still emulating line printers.)

This change causes the terminal package to do the required
transformation.

Fixes golang/go#17364.

Change-Id: Ida15d3cf701a21eaa59161ab61b3ed4dee2ded46
Reviewed-on: https://go-review.googlesource.com/33902
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
  • Loading branch information
agl committed Dec 7, 2016
1 parent d7ffdd5 commit 3ea8c23
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
42 changes: 37 additions & 5 deletions terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,11 @@ const (
keyPasteEnd
)

var pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
var pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
var (
crlf = []byte{'\r', '\n'}
pasteStart = []byte{keyEscape, '[', '2', '0', '0', '~'}
pasteEnd = []byte{keyEscape, '[', '2', '0', '1', '~'}
)

// bytesToKey tries to parse a key sequence from b. If successful, it returns
// the key and the remainder of the input. Otherwise it returns utf8.RuneError.
Expand Down Expand Up @@ -333,7 +336,7 @@ func (t *Terminal) advanceCursor(places int) {
// So, if we are stopping at the end of a line, we
// need to write a newline so that our cursor can be
// advanced to the next line.
t.outBuf = append(t.outBuf, '\n')
t.outBuf = append(t.outBuf, '\r', '\n')
}
}

Expand Down Expand Up @@ -593,14 +596,43 @@ func (t *Terminal) writeLine(line []rune) {
}
}

// writeWithCRLF writes buf to w but replaces all occurances of \n with \r\n.
func writeWithCRLF(w io.Writer, buf []byte) (n int, err error) {
for len(buf) > 0 {
i := bytes.IndexByte(buf, '\n')
todo := len(buf)
if i >= 0 {
todo = i
}

var nn int
nn, err = w.Write(buf[:todo])
n += nn
if err != nil {
return n, err
}
buf = buf[todo:]

if i >= 0 {
if _, err = w.Write(crlf); err != nil {
return n, err
}
n += 1
buf = buf[1:]
}
}

return n, nil
}

func (t *Terminal) Write(buf []byte) (n int, err error) {
t.lock.Lock()
defer t.lock.Unlock()

if t.cursorX == 0 && t.cursorY == 0 {
// This is the easy case: there's nothing on the screen that we
// have to move out of the way.
return t.c.Write(buf)
return writeWithCRLF(t.c, buf)
}

// We have a prompt and possibly user input on the screen. We
Expand All @@ -620,7 +652,7 @@ func (t *Terminal) Write(buf []byte) (n int, err error) {
}
t.outBuf = t.outBuf[:0]

if n, err = t.c.Write(buf); err != nil {
if n, err = writeWithCRLF(t.c, buf); err != nil {
return
}

Expand Down
15 changes: 15 additions & 0 deletions terminal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package terminal

import (
"bytes"
"io"
"os"
"testing"
Expand Down Expand Up @@ -289,3 +290,17 @@ func TestMakeRawState(t *testing.T) {
t.Errorf("states do not match; was %v, expected %v", raw, st)
}
}

func TestOutputNewlines(t *testing.T) {
// \n should be changed to \r\n in terminal output.
buf := new(bytes.Buffer)
term := NewTerminal(buf, ">")

term.Write([]byte("1\n2\n"))
output := string(buf.Bytes())
const expected = "1\r\n2\r\n"

if output != expected {
t.Errorf("incorrect output: was %q, expected %q", output, expected)
}
}

0 comments on commit 3ea8c23

Please sign in to comment.