diff options
author | Rob Pike <r@golang.org> | 2010-06-24 15:24:25 -0700 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2010-06-24 15:24:25 -0700 |
commit | 8a8fa410a043d8d95a79881cc7777930da113f9f (patch) | |
tree | 0c3b269fbccf0375ab1de922e9f0d845cb283e53 /src | |
parent | c3a14037297827d36b77a49cf0cbd0849295e79a (diff) | |
download | golang-8a8fa410a043d8d95a79881cc7777930da113f9f.tar.gz |
fmt.Scan: fix handling of EOFs.
Fixes issue 876.
R=rsc
CC=golang-dev
http://codereview.appspot.com/1675048
Diffstat (limited to 'src')
-rw-r--r-- | src/pkg/fmt/scan.go | 13 | ||||
-rw-r--r-- | src/pkg/fmt/scan_test.go | 42 |
2 files changed, 53 insertions, 2 deletions
diff --git a/src/pkg/fmt/scan.go b/src/pkg/fmt/scan.go index 9f3b40117..ded1f7719 100644 --- a/src/pkg/fmt/scan.go +++ b/src/pkg/fmt/scan.go @@ -125,12 +125,15 @@ type scanError struct { err os.Error } +const EOF = -1 + // ss is the internal implementation of ScanState. type ss struct { rr readRuner // where to read input buf bytes.Buffer // token accumulator nlIsSpace bool // whether newline counts as white space peekRune int // one-rune lookahead + atEOF bool // already read EOF maxWid int // max width of field, in runes widPresent bool // width was specified wid int // width consumed so far; used in accept() @@ -150,11 +153,12 @@ func (s *ss) Width() (wid int, ok bool) { return s.maxWid, s.widPresent } -const EOF = -1 - // The public method returns an error; this private one panics. // If getRune reaches EOF, the return value is EOF (-1). func (s *ss) getRune() (rune int) { + if s.atEOF { + return EOF + } if s.peekRune >= 0 { rune = s.peekRune s.peekRune = -1 @@ -163,6 +167,7 @@ func (s *ss) getRune() (rune int) { rune, _, err := s.rr.ReadRune() if err != nil { if err == os.EOF { + s.atEOF = true return EOF } s.error(err) @@ -174,6 +179,9 @@ func (s *ss) getRune() (rune int) { // It is called in cases such as string scanning where an EOF is a // syntax error. func (s *ss) mustGetRune() (rune int) { + if s.atEOF { + s.error(io.ErrUnexpectedEOF) + } if s.peekRune >= 0 { rune = s.peekRune s.peekRune = -1 @@ -291,6 +299,7 @@ func newScanState(r io.Reader, nlIsSpace bool) *ss { } s.nlIsSpace = nlIsSpace s.peekRune = -1 + s.atEOF = false s.maxWid = 0 s.widPresent = false return s diff --git a/src/pkg/fmt/scan_test.go b/src/pkg/fmt/scan_test.go index f195c0317..1e0319836 100644 --- a/src/pkg/fmt/scan_test.go +++ b/src/pkg/fmt/scan_test.go @@ -493,3 +493,45 @@ func TestScanlnWithMiddleNewline(t *testing.T) { t.Errorf("expected newline error scanning string with extra newline, got: %s", err) } } + +// Special Reader that counts reads at end of file. +type eofCounter struct { + reader *strings.Reader + eofCount int +} + +func (ec *eofCounter) Read(b []byte) (n int, err os.Error) { + n, err = ec.reader.Read(b) + if n == 0 { + ec.eofCount++ + } + return +} + +// Verify that when we scan, we see at most EOF once per call to a Scan function, +// and then only when it's really an EOF +func TestEOF(t *testing.T) { + ec := &eofCounter{strings.NewReader("123\n"), 0} + var a int + n, err := Fscanln(ec, &a) + if err != nil { + t.Error("unexpected error", err) + } + if n != 1 { + t.Error("expected to scan one item, got", n) + } + if ec.eofCount != 0 { + t.Error("expected zero EOFs", ec.eofCount) + ec.eofCount = 0 // reset for next test + } + n, err = Fscanln(ec, &a) + if err == nil { + t.Error("expected error scanning empty string") + } + if n != 0 { + t.Error("expected to scan zero items, got", n) + } + if ec.eofCount != 1 { + t.Error("expected one EOF, got", ec.eofCount) + } +} |