mirror of
https://github.com/golang/go
synced 2024-11-25 06:57:58 -07:00
fmt: return EOF when out of input in Scan*.
Fixes #1840. R=rsc CC=golang-dev https://golang.org/cl/4548077
This commit is contained in:
parent
bc3a72fa28
commit
60dddc6db1
@ -466,6 +466,14 @@ func (s *ss) peek(ok string) bool {
|
|||||||
return strings.IndexRune(ok, rune) >= 0
|
return strings.IndexRune(ok, rune) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ss) notEOF() {
|
||||||
|
// Guarantee there is data to be read.
|
||||||
|
if rune := s.getRune(); rune == eof {
|
||||||
|
panic(os.EOF)
|
||||||
|
}
|
||||||
|
s.UnreadRune()
|
||||||
|
}
|
||||||
|
|
||||||
// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the
|
// accept checks the next rune in the input. If it's a byte (sic) in the string, it puts it in the
|
||||||
// buffer and returns true. Otherwise it return false.
|
// buffer and returns true. Otherwise it return false.
|
||||||
func (s *ss) accept(ok string) bool {
|
func (s *ss) accept(ok string) bool {
|
||||||
@ -485,11 +493,13 @@ func (s *ss) okVerb(verb int, okVerbs, typ string) bool {
|
|||||||
|
|
||||||
// scanBool returns the value of the boolean represented by the next token.
|
// scanBool returns the value of the boolean represented by the next token.
|
||||||
func (s *ss) scanBool(verb int) bool {
|
func (s *ss) scanBool(verb int) bool {
|
||||||
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
if !s.okVerb(verb, "tv", "boolean") {
|
if !s.okVerb(verb, "tv", "boolean") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Syntax-checking a boolean is annoying. We're not fastidious about case.
|
// Syntax-checking a boolean is annoying. We're not fastidious about case.
|
||||||
switch s.mustReadRune() {
|
switch s.getRune() {
|
||||||
case '0':
|
case '0':
|
||||||
return false
|
return false
|
||||||
case '1':
|
case '1':
|
||||||
@ -540,6 +550,7 @@ func (s *ss) getBase(verb int) (base int, digits string) {
|
|||||||
|
|
||||||
// scanNumber returns the numerical string with specified digits starting here.
|
// scanNumber returns the numerical string with specified digits starting here.
|
||||||
func (s *ss) scanNumber(digits string, haveDigits bool) string {
|
func (s *ss) scanNumber(digits string, haveDigits bool) string {
|
||||||
|
s.notEOF()
|
||||||
if !haveDigits && !s.accept(digits) {
|
if !haveDigits && !s.accept(digits) {
|
||||||
s.errorString("expected integer")
|
s.errorString("expected integer")
|
||||||
}
|
}
|
||||||
@ -550,7 +561,8 @@ func (s *ss) scanNumber(digits string, haveDigits bool) string {
|
|||||||
|
|
||||||
// scanRune returns the next rune value in the input.
|
// scanRune returns the next rune value in the input.
|
||||||
func (s *ss) scanRune(bitSize int) int64 {
|
func (s *ss) scanRune(bitSize int) int64 {
|
||||||
rune := int64(s.mustReadRune())
|
s.notEOF()
|
||||||
|
rune := int64(s.getRune())
|
||||||
n := uint(bitSize)
|
n := uint(bitSize)
|
||||||
x := (rune << (64 - n)) >> (64 - n)
|
x := (rune << (64 - n)) >> (64 - n)
|
||||||
if x != rune {
|
if x != rune {
|
||||||
@ -584,6 +596,7 @@ func (s *ss) scanInt(verb int, bitSize int) int64 {
|
|||||||
return s.scanRune(bitSize)
|
return s.scanRune(bitSize)
|
||||||
}
|
}
|
||||||
s.skipSpace(false)
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
base, digits := s.getBase(verb)
|
base, digits := s.getBase(verb)
|
||||||
haveDigits := false
|
haveDigits := false
|
||||||
if verb == 'U' {
|
if verb == 'U' {
|
||||||
@ -616,6 +629,7 @@ func (s *ss) scanUint(verb int, bitSize int) uint64 {
|
|||||||
return uint64(s.scanRune(bitSize))
|
return uint64(s.scanRune(bitSize))
|
||||||
}
|
}
|
||||||
s.skipSpace(false)
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
base, digits := s.getBase(verb)
|
base, digits := s.getBase(verb)
|
||||||
haveDigits := false
|
haveDigits := false
|
||||||
if verb == 'U' {
|
if verb == 'U' {
|
||||||
@ -736,6 +750,7 @@ func (s *ss) scanComplex(verb int, n int) complex128 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
s.skipSpace(false)
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
sreal, simag := s.complexTokens()
|
sreal, simag := s.complexTokens()
|
||||||
real := s.convertFloat(sreal, n/2)
|
real := s.convertFloat(sreal, n/2)
|
||||||
imag := s.convertFloat(simag, n/2)
|
imag := s.convertFloat(simag, n/2)
|
||||||
@ -749,6 +764,7 @@ func (s *ss) convertString(verb int) (str string) {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
s.skipSpace(false)
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
switch verb {
|
switch verb {
|
||||||
case 'q':
|
case 'q':
|
||||||
str = s.quotedString()
|
str = s.quotedString()
|
||||||
@ -757,16 +773,13 @@ func (s *ss) convertString(verb int) (str string) {
|
|||||||
default:
|
default:
|
||||||
str = string(s.token(true, notSpace)) // %s and %v just return the next word
|
str = string(s.token(true, notSpace)) // %s and %v just return the next word
|
||||||
}
|
}
|
||||||
// Empty strings other than with %q are not OK.
|
|
||||||
if len(str) == 0 && verb != 'q' && s.maxWid > 0 {
|
|
||||||
s.errorString("Scan: no data for string")
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// quotedString returns the double- or back-quoted string represented by the next input characters.
|
// quotedString returns the double- or back-quoted string represented by the next input characters.
|
||||||
func (s *ss) quotedString() string {
|
func (s *ss) quotedString() string {
|
||||||
quote := s.mustReadRune()
|
s.notEOF()
|
||||||
|
quote := s.getRune()
|
||||||
switch quote {
|
switch quote {
|
||||||
case '`':
|
case '`':
|
||||||
// Back-quoted: Anything goes until EOF or back quote.
|
// Back-quoted: Anything goes until EOF or back quote.
|
||||||
@ -836,6 +849,7 @@ func (s *ss) hexByte() (b byte, ok bool) {
|
|||||||
|
|
||||||
// hexString returns the space-delimited hexpair-encoded string.
|
// hexString returns the space-delimited hexpair-encoded string.
|
||||||
func (s *ss) hexString() string {
|
func (s *ss) hexString() string {
|
||||||
|
s.notEOF()
|
||||||
for {
|
for {
|
||||||
b, ok := s.hexByte()
|
b, ok := s.hexByte()
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -869,6 +883,7 @@ func (s *ss) scanOne(verb int, field interface{}) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := field.(type) {
|
switch v := field.(type) {
|
||||||
case *bool:
|
case *bool:
|
||||||
*v = s.scanBool(verb)
|
*v = s.scanBool(verb)
|
||||||
@ -903,11 +918,13 @@ func (s *ss) scanOne(verb int, field interface{}) {
|
|||||||
case *float32:
|
case *float32:
|
||||||
if s.okVerb(verb, floatVerbs, "float32") {
|
if s.okVerb(verb, floatVerbs, "float32") {
|
||||||
s.skipSpace(false)
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
*v = float32(s.convertFloat(s.floatToken(), 32))
|
*v = float32(s.convertFloat(s.floatToken(), 32))
|
||||||
}
|
}
|
||||||
case *float64:
|
case *float64:
|
||||||
if s.okVerb(verb, floatVerbs, "float64") {
|
if s.okVerb(verb, floatVerbs, "float64") {
|
||||||
s.skipSpace(false)
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
*v = s.convertFloat(s.floatToken(), 64)
|
*v = s.convertFloat(s.floatToken(), 64)
|
||||||
}
|
}
|
||||||
case *string:
|
case *string:
|
||||||
@ -945,6 +962,7 @@ func (s *ss) scanOne(verb int, field interface{}) {
|
|||||||
}
|
}
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
s.skipSpace(false)
|
s.skipSpace(false)
|
||||||
|
s.notEOF()
|
||||||
v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits()))
|
v.SetFloat(s.convertFloat(s.floatToken(), v.Type().Bits()))
|
||||||
case reflect.Complex64, reflect.Complex128:
|
case reflect.Complex64, reflect.Complex128:
|
||||||
v.SetComplex(s.scanComplex(verb, v.Type().Bits()))
|
v.SetComplex(s.scanComplex(verb, v.Type().Bits()))
|
||||||
@ -955,13 +973,13 @@ func (s *ss) scanOne(verb int, field interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorHandler turns local panics into error returns. EOFs are benign.
|
// errorHandler turns local panics into error returns.
|
||||||
func errorHandler(errp *os.Error) {
|
func errorHandler(errp *os.Error) {
|
||||||
if e := recover(); e != nil {
|
if e := recover(); e != nil {
|
||||||
if se, ok := e.(scanError); ok { // catch local error
|
if se, ok := e.(scanError); ok { // catch local error
|
||||||
if se.err != os.EOF {
|
|
||||||
*errp = se.err
|
*errp = se.err
|
||||||
}
|
} else if eof, ok := e.(os.Error); ok && eof == os.EOF { // out of input
|
||||||
|
*errp = eof
|
||||||
} else {
|
} else {
|
||||||
panic(e)
|
panic(e)
|
||||||
}
|
}
|
||||||
|
@ -660,6 +660,68 @@ func TestEOF(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that we see an EOF error if we run out of input.
|
||||||
|
// This was a buglet: we used to get "expected integer".
|
||||||
|
func TestEOFAtEndOfInput(t *testing.T) {
|
||||||
|
var i, j int
|
||||||
|
n, err := Sscanf("23", "%d %d", &i, &j)
|
||||||
|
if n != 1 || i != 23 {
|
||||||
|
t.Errorf("Sscanf expected one value of 23; got %d %d", n, i)
|
||||||
|
}
|
||||||
|
if err != os.EOF {
|
||||||
|
t.Errorf("Sscanf expected EOF; got %q", err)
|
||||||
|
}
|
||||||
|
n, err = Sscan("234", &i, &j)
|
||||||
|
if n != 1 || i != 234 {
|
||||||
|
t.Errorf("Sscan expected one value of 234; got %d %d", n, i)
|
||||||
|
}
|
||||||
|
if err != os.EOF {
|
||||||
|
t.Errorf("Sscan expected EOF; got %q", err)
|
||||||
|
}
|
||||||
|
// Trailing space is tougher.
|
||||||
|
n, err = Sscan("234 ", &i, &j)
|
||||||
|
if n != 1 || i != 234 {
|
||||||
|
t.Errorf("Sscan expected one value of 234; got %d %d", n, i)
|
||||||
|
}
|
||||||
|
if err != os.EOF {
|
||||||
|
t.Errorf("Sscan expected EOF; got %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var eofTests = []struct {
|
||||||
|
format string
|
||||||
|
v interface{}
|
||||||
|
}{
|
||||||
|
{"%s", &stringVal},
|
||||||
|
{"%q", &stringVal},
|
||||||
|
{"%x", &stringVal},
|
||||||
|
{"%v", &stringVal},
|
||||||
|
{"%v", &bytesVal},
|
||||||
|
{"%v", &intVal},
|
||||||
|
{"%v", &uintVal},
|
||||||
|
{"%v", &boolVal},
|
||||||
|
{"%v", &float32Val},
|
||||||
|
{"%v", &complex64Val},
|
||||||
|
{"%v", &renamedStringVal},
|
||||||
|
{"%v", &renamedBytesVal},
|
||||||
|
{"%v", &renamedIntVal},
|
||||||
|
{"%v", &renamedUintVal},
|
||||||
|
{"%v", &renamedBoolVal},
|
||||||
|
{"%v", &renamedFloat32Val},
|
||||||
|
{"%v", &renamedComplex64Val},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEOFAllTypes(t *testing.T) {
|
||||||
|
for i, test := range eofTests {
|
||||||
|
if _, err := Sscanf("", test.format, test.v); err != os.EOF {
|
||||||
|
t.Errorf("#%d: %s %T not eof on empty string: %s", i, test.format, test.v, err)
|
||||||
|
}
|
||||||
|
if _, err := Sscanf(" ", test.format, test.v); err != os.EOF {
|
||||||
|
t.Errorf("#%d: %s %T not eof on trailing blanks: %s", i, test.format, test.v, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify that, at least when using bufio, successive calls to Fscan do not lose runes.
|
// Verify that, at least when using bufio, successive calls to Fscan do not lose runes.
|
||||||
func TestUnreadRuneWithBufio(t *testing.T) {
|
func TestUnreadRuneWithBufio(t *testing.T) {
|
||||||
r := bufio.NewReader(strings.NewReader("123αb"))
|
r := bufio.NewReader(strings.NewReader("123αb"))
|
||||||
|
Loading…
Reference in New Issue
Block a user