x/crypto/ssh/terminal: replace \n with \r\n.

18e6eb769a6 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>
diff --git a/terminal.go b/terminal.go
index 741eeb1..5ea89a2 100644
--- a/terminal.go
+++ b/terminal.go
@@ -132,8 +132,11 @@
 	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 @@
 		// 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 @@
 	}
 }
 
+// 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 @@
 	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 @@
 	}
 	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/terminal_test.go b/terminal_test.go
index 6bdefb4..1d54c4f 100644
--- a/terminal_test.go
+++ b/terminal_test.go
@@ -5,6 +5,7 @@
 package terminal
 
 import (
+	"bytes"
 	"io"
 	"os"
 	"testing"
@@ -289,3 +290,17 @@
 		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)
+	}
+}