From e60a2bf333a1e95d256505dbfe665177cc92eb14 Mon Sep 17 00:00:00 2001 From: c-expert-zigbee Date: Mon, 5 Dec 2016 09:23:45 -0800 Subject: [PATCH] x/crypto/ssh/terminal: replace \n with \r\n. 911fafb28f4 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 --- ssh/terminal/terminal.go | 42 ++++++++++++++++++++++++++++++----- ssh/terminal/terminal_test.go | 15 +++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/ssh/terminal/terminal.go b/ssh/terminal/terminal.go index 741eeb13..5ea89a24 100644 --- a/ssh/terminal/terminal.go +++ b/ssh/terminal/terminal.go @@ -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. @@ -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') } } @@ -593,6 +596,35 @@ 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() @@ -600,7 +632,7 @@ func (t *Terminal) Write(buf []byte) (n int, err error) { 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 @@ -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 } diff --git a/ssh/terminal/terminal_test.go b/ssh/terminal/terminal_test.go index 6bdefb4e..1d54c4f6 100644 --- a/ssh/terminal/terminal_test.go +++ b/ssh/terminal/terminal_test.go @@ -5,6 +5,7 @@ package terminal import ( + "bytes" "io" "os" "testing" @@ -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) + } +}