An analogous set of functions scans formatted text to yield
        values.  Scan, Scanf and Scanln read from os.Stdin; Fscan,
        Fscanf and Fscanln read from a specified io.Reader; Sscan,
-       Sscanf and Sscanln read from an argument string.  Scanln,
-       Fscanln and Sscanln stop scanning at a newline and require that
-       the items be followed by one; Scanf, Fscanf and Sscanf require
-       newlines in the input to match newlines in the format; the other
-       routines treat newlines as spaces.
+       Sscanf and Sscanln read from an argument string.
+
+       Scan, Fscan, Sscan treat newlines in the input as spaces.
+
+       Scanln, Fscanln and Sscanln stop scanning at a newline and
+       require that the items be followed by a newline or EOF.
+
+       Scanf, Fscanf and Sscanf require that (after skipping spaces)
+       newlines in the format are matched by newlines in the input
+       and vice versa.  This behavior differs from the corresponding
+       routines in C, which uniformly treat newlines as spaces.
+
+       When scanning with Scanf, Fscanf, and Sscanf, all non-empty
+       runs of space characters (except newline) are equivalent
+       to a single space in both the format and the input.  With
+       that proviso, text in the format string must match the input
+       text; scanning stops if it does not, with the return value
+       of the function indicating the number of arguments scanned.
 
        Scanf, Fscanf, and Sscanf parse the arguments according to a
        format string, analogous to that of Printf.  For example, %x
        is no syntax for scanning with a precision (no %5.2f, just
        %5f).
 
-       When scanning with a format, all non-empty runs of space
-       characters (except newline) are equivalent to a single
-       space in both the format and the input.  With that proviso,
-       text in the format string must match the input text; scanning
-       stops if it does not, with the return value of the function
-       indicating the number of arguments scanned.
-
        In all the scanning functions, a carriage return followed
        immediately by a newline is treated as a plain newline
        (\r\n means the same as \n).
 
        ReadRune() (r rune, size int, err error)
        // UnreadRune causes the next call to ReadRune to return the same rune.
        UnreadRune() error
-       // SkipSpace skips space in the input. Newlines are treated as space
-       // unless the scan operation is Scanln, Fscanln or Sscanln, in which case
-       // a newline is treated as EOF.
+       // SkipSpace skips space in the input. Newlines are treated appropriately
+       // for the operation being performed; see the package documentation
+       // for more information.
        SkipSpace()
        // Token skips space in the input if skipSpace is true, then returns the
        // run of Unicode code points c satisfying f(c).  If f is nil,
        // !unicode.IsSpace(c) is used; that is, the token will hold non-space
-       // characters.  Newlines are treated as space unless the scan operation
-       // is Scanln, Fscanln or Sscanln, in which case a newline is treated as
-       // EOF.  The returned slice points to shared data that may be overwritten
+       // characters.  Newlines are treated appropriately for the operation being
+       // performed; see the package documentation for more information.
+       // The returned slice points to shared data that may be overwritten
        // by the next call to Token, a call to a Scan function using the ScanState
        // as input, or when the calling Scan method returns.
        Token(skipSpace bool, f func(rune) bool) (token []byte, err error)
 // space-separated values into successive arguments as determined by
 // the format.  It returns the number of items successfully scanned.
 // If that is less than the number of arguments, err will report why.
+// Newlines in the input must match newlines in the format.
 func Scanf(format string, a ...interface{}) (n int, err error) {
        return Fscanf(os.Stdin, format, a...)
 }
 // Sscanf scans the argument string, storing successive space-separated
 // values into successive arguments as determined by the format.  It
 // returns the number of items successfully parsed.
+// Newlines in the input must match newlines in the format.
 func Sscanf(str string, format string, a ...interface{}) (n int, err error) {
        return Fscanf((*stringReader)(&str), format, a...)
 }
 // Fscanf scans text read from r, storing successive space-separated
 // values into successive arguments as determined by the format.  It
 // returns the number of items successfully parsed.
+// Newlines in the input must match newlines in the format.
 func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) {
        s, old := newScanState(r, false, false)
        n, err = s.doScanf(format, a)
 
 // newScanState allocates a new ss struct or grab a cached one.
 func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) {
-       // If the reader is a *ss, then we've got a recursive
-       // call to Scan, so re-use the scan state.
-       s, ok := r.(*ss)
-       if ok {
-               old = s.ssave
-               s.limit = s.argLimit
-               s.nlIsEnd = nlIsEnd || s.nlIsEnd
-               s.nlIsSpace = nlIsSpace
-               return
-       }
-
        s = ssFree.Get().(*ss)
        if rr, ok := r.(io.RuneReader); ok {
                s.rr = rr
                s.scanOne('v', arg)
                numProcessed++
        }
-       // Check for newline if required.
-       if !s.nlIsSpace {
+       // Check for newline (or EOF) if required (Scanln etc.).
+       if s.nlIsEnd {
                for {
                        r := s.getRune()
                        if r == '\n' || r == eof {
 }
 
 // advance determines whether the next characters in the input match
-// those of the format.  It returns the number of bytes (sic) consumed
-// in the format. Newlines included, all runs of space characters in
-// either input or format behave as a single space. This routine also
-// handles the %% case.  If the return value is zero, either format
-// starts with a % (with no following %) or the input is empty.
-// If it is negative, the input did not match the string.
+// those of the format. It returns the number of bytes (sic) consumed
+// in the format. All runs of space characters in either input or
+// format behave as a single space. Newlines are special, though:
+// newlines in the format must match those in the input and vice versa.
+// This routine also handles the %% case. If the return value is zero,
+// either format starts with a % (with no following %) or the input
+// is empty. If it is negative, the input did not match the string.
 func (s *ss) advance(format string) (i int) {
        for i < len(format) {
                fmtc, w := utf8.DecodeRuneInString(format[i:])
                        i += w // skip the first %
                }
                sawSpace := false
+               wasNewline := false
+               // Skip spaces in format but absorb at most one newline.
                for isSpace(fmtc) && i < len(format) {
+                       if fmtc == '\n' {
+                               if wasNewline { // Already saw one; stop here.
+                                       break
+                               }
+                               wasNewline = true
+                       }
                        sawSpace = true
                        i += w
                        fmtc, w = utf8.DecodeRuneInString(format[i:])
                }
                if sawSpace {
-                       // There was space in the format, so there should be space (EOF)
+                       // There was space in the format, so there should be space
                        // in the input.
                        inputc := s.getRune()
-                       if inputc == eof || inputc == '\n' {
-                               // If we've reached a newline, stop now; don't read ahead.
+                       if inputc == eof {
                                return
                        }
                        if !isSpace(inputc) {
-                               // Space in format but not in input: error
+                               // Space in format but not in input.
                                s.errorString("expected space in input to match format")
                        }
-                       s.skipSpace(true)
+                       // Skip spaces but stop at newline.
+                       for inputc != '\n' && isSpace(inputc) {
+                               inputc = s.getRune()
+                       }
+                       if inputc == '\n' {
+                               if !wasNewline {
+                                       s.errorString("newline in input does not match format")
+                               }
+                               // We've reached a newline, stop now; don't read further.
+                               return
+                       }
+                       s.UnreadRune()
+                       if wasNewline {
+                               s.errorString("newline in format does not match input")
+                       }
                        continue
                }
                inputc := s.mustReadRune()
 
                t.Errorf("odd count: got count, err = %d, %v; expected 0, error", n, err)
        }
 }
+
+func TestScanNewlinesAreSpaces(t *testing.T) {
+       var a, b int
+       var tests = []struct {
+               name  string
+               text  string
+               count int
+       }{
+               {"newlines", "1\n2\n", 2},
+               {"no final newline", "1\n2", 2},
+               {"newlines with spaces ", "1  \n  2  \n", 2},
+               {"no final newline with spaces", "1  \n  2", 2},
+       }
+       for _, test := range tests {
+               n, err := Sscan(test.text, &a, &b)
+               if n != test.count {
+                       t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n)
+               }
+               if err != nil {
+                       t.Errorf("%s: unexpected error: %s", test.name, err)
+               }
+       }
+}
+
+func TestScanlnNewlinesTerminate(t *testing.T) {
+       var a, b int
+       var tests = []struct {
+               name  string
+               text  string
+               count int
+               ok    bool
+       }{
+               {"one line one item", "1\n", 1, false},
+               {"one line two items with spaces ", "   1 2    \n", 2, true},
+               {"one line two items no newline", "   1 2", 2, true},
+               {"two lines two items", "1\n2\n", 1, false},
+       }
+       for _, test := range tests {
+               n, err := Sscanln(test.text, &a, &b)
+               if n != test.count {
+                       t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n)
+               }
+               if test.ok && err != nil {
+                       t.Errorf("%s: unexpected error: %s", test.name, err)
+               }
+               if !test.ok && err == nil {
+                       t.Errorf("%s: expected error; got none", test.name)
+               }
+       }
+}
+
+func TestScanfNewlineMatchFormat(t *testing.T) {
+       var a, b int
+       var tests = []struct {
+               name   string
+               text   string
+               format string
+               count  int
+               ok     bool
+       }{
+               {"newline in both", "1\n2", "%d\n%d\n", 2, true},
+               {"newline in input", "1\n2", "%d %d", 1, false},
+               {"space-newline in input", "1 \n2", "%d %d", 1, false},
+               {"newline in format", "1 2", "%d\n%d", 1, false},
+               {"space-newline in format", "1 2", "%d \n%d", 1, false},
+               {"space-newline in both", "1 \n2", "%d \n%d", 2, true},
+               {"extra space in format", "1\n2", "%d\n %d", 2, true},
+               {"two extra spaces in format", "1\n2", "%d \n %d", 2, true},
+       }
+       for _, test := range tests {
+               n, err := Sscanf(test.text, test.format, &a, &b)
+               if n != test.count {
+                       t.Errorf("%s: expected to scan %d item(s), scanned %d", test.name, test.count, n)
+               }
+               if test.ok && err != nil {
+                       t.Errorf("%s: unexpected error: %s", test.name, err)
+               }
+               if !test.ok && err == nil {
+                       t.Errorf("%s: expected error; got none", test.name)
+               }
+       }
+}