diff options
Diffstat (limited to 'libgo/go/fmt/scan.go')
-rw-r--r-- | libgo/go/fmt/scan.go | 103 |
1 files changed, 62 insertions, 41 deletions
diff --git a/libgo/go/fmt/scan.go b/libgo/go/fmt/scan.go index d7befeae43e..5b9b516353b 100644 --- a/libgo/go/fmt/scan.go +++ b/libgo/go/fmt/scan.go @@ -34,16 +34,16 @@ type ScanState interface { 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) @@ -81,6 +81,8 @@ func Scanln(a ...interface{}) (n int, err error) { // Scanf scans text read from standard input, storing successive // 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...) } @@ -113,6 +115,7 @@ func Sscanln(str string, a ...interface{}) (n int, err error) { // 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...) } @@ -140,6 +143,7 @@ func Fscanln(r io.Reader, a ...interface{}) (n int, err error) { // 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) @@ -387,17 +391,6 @@ var ssFree = sync.Pool{ // 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 @@ -875,34 +868,39 @@ func (s *ss) quotedString() string { return "" } -// hexDigit returns the value of the hexadecimal digit -func (s *ss) hexDigit(d rune) int { +// hexDigit returns the value of the hexadecimal digit. +func hexDigit(d rune) (int, bool) { digit := int(d) switch digit { case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - return digit - '0' + return digit - '0', true case 'a', 'b', 'c', 'd', 'e', 'f': - return 10 + digit - 'a' + return 10 + digit - 'a', true case 'A', 'B', 'C', 'D', 'E', 'F': - return 10 + digit - 'A' + return 10 + digit - 'A', true } - s.errorString("illegal hex digit") - return 0 + return -1, false } // hexByte returns the next hex-encoded (two-character) byte from the input. -// There must be either two hexadecimal digits or a space character in the input. +// It returns ok==false if the next bytes in the input do not encode a hex byte. +// If the first byte is hex and the second is not, processing stops. func (s *ss) hexByte() (b byte, ok bool) { rune1 := s.getRune() if rune1 == eof { return } - if isSpace(rune1) { + value1, ok := hexDigit(rune1) + if !ok { s.UnreadRune() return } - rune2 := s.mustReadRune() - return byte(s.hexDigit(rune1)<<4 | s.hexDigit(rune2)), true + value2, ok := hexDigit(s.mustReadRune()) + if !ok { + s.errorString("illegal hex digit") + return + } + return byte(value1<<4 | value2), true } // hexString returns the space-delimited hexpair-encoded string. @@ -1050,8 +1048,8 @@ func (s *ss) doScan(a []interface{}) (numProcessed int, err error) { 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 { @@ -1067,12 +1065,13 @@ func (s *ss) doScan(a []interface{}) (numProcessed int, err error) { } // 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:]) @@ -1085,24 +1084,45 @@ func (s *ss) advance(format string) (i int) { 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() @@ -1144,6 +1164,7 @@ func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err erro if !widPresent { s.maxWid = hugeWid } + s.SkipSpace() s.argLimit = s.limit if f := s.count + s.maxWid; f < s.argLimit { s.argLimit = f |