// Copyright 2010 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package fmtimport ()// ScanState represents the scanner state passed to custom scanners.// Scanners may do rune-at-a-time scanning or ask the ScanState// to discover the next space-delimited token.typeScanStateinterface {// ReadRune reads the next rune (Unicode code point) from the input. // If invoked during Scanln, Fscanln, or Sscanln, ReadRune() will // return EOF after returning the first '\n' or when reading beyond // the specified width.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 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 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)// Width returns the value of the width option and whether it has been set. // The unit is Unicode code points.Width() (wid int, ok bool)// Because ReadRune is implemented by the interface, Read should never be // called by the scanning routines and a valid implementation of // ScanState may choose always to return an error from Read.Read(buf []byte) (n int, err error)}// Scanner is implemented by any value that has a Scan method, which scans// the input for the representation of a value and stores the result in the// receiver, which must be a pointer to be useful. The Scan method is called// for any argument to [Scan], [Scanf], or [Scanln] that implements it.typeScannerinterface {Scan(state ScanState, verb rune) error}// Scan scans text read from standard input, storing successive// space-separated values into successive arguments. Newlines count// as space. It returns the number of items successfully scanned.// If that is less than the number of arguments, err will report why.func ( ...any) ( int, error) {returnFscan(os.Stdin, ...)}// Scanln is similar to [Scan], but stops scanning at a newline and// after the final item there must be a newline or EOF.func ( ...any) ( int, error) {returnFscanln(os.Stdin, ...)}// 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.// The one exception: the verb %c always scans the next rune in the// input, even if it is a space (or tab etc.) or newline.func ( string, ...any) ( int, error) {returnFscanf(os.Stdin, , ...)}type stringReader stringfunc ( *stringReader) ( []byte) ( int, error) { = copy(, *) * = (*)[:]if == 0 { = io.EOF }return}// Sscan scans the argument string, storing successive space-separated// values into successive arguments. Newlines count as space. It// returns the number of items successfully scanned. If that is less// than the number of arguments, err will report why.func ( string, ...any) ( int, error) {returnFscan((*stringReader)(&), ...)}// Sscanln is similar to [Sscan], but stops scanning at a newline and// after the final item there must be a newline or EOF.func ( string, ...any) ( int, error) {returnFscanln((*stringReader)(&), ...)}// 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 ( string, string, ...any) ( int, error) {returnFscanf((*stringReader)(&), , ...)}// Fscan scans text read from r, storing successive space-separated// values into successive arguments. Newlines count as space. It// returns the number of items successfully scanned. If that is less// than the number of arguments, err will report why.func ( io.Reader, ...any) ( int, error) { , := newScanState(, true, false) , = .doScan() .free()return}// Fscanln is similar to [Fscan], but stops scanning at a newline and// after the final item there must be a newline or EOF.func ( io.Reader, ...any) ( int, error) { , := newScanState(, false, true) , = .doScan() .free()return}// 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 ( io.Reader, string, ...any) ( int, error) { , := newScanState(, false, false) , = .doScanf(, ) .free()return}// scanError represents an error generated by the scanning software.// It's used as a unique signature to identify such errors when recovering.type scanError struct { err error}const eof = -1// ss is the internal implementation of ScanState.type ss struct { rs io.RuneScanner// where to read input buf buffer// token accumulator count int// runes consumed so far. atEOF bool// already read EOFssave}// ssave holds the parts of ss that need to be// saved and restored on recursive scans.type ssave struct { validSave bool// is or was a part of an actual ss. nlIsEnd bool// whether newline terminates scan nlIsSpace bool// whether newline counts as white space argLimit int// max value of ss.count for this arg; argLimit <= limit limit int// max value of ss.count. maxWid int// width of this arg.}// The Read method is only in ScanState so that ScanState// satisfies io.Reader. It will never be called when used as// intended, so there is no need to make it actually work.func ( *ss) ( []byte) ( int, error) {return0, errors.New("ScanState's Read should not be called. Use ReadRune")}func ( *ss) () ( rune, int, error) {if .atEOF || .count >= .argLimit { = io.EOFreturn } , , = .rs.ReadRune()if == nil { .count++if .nlIsEnd && == '\n' { .atEOF = true } } elseif == io.EOF { .atEOF = true }return}func ( *ss) () ( int, bool) {if .maxWid == hugeWid {return0, false }return .maxWid, true}// The public method returns an error; this private one panics.// If getRune reaches EOF, the return value is EOF (-1).func ( *ss) () ( rune) { , , := .ReadRune()if != nil {if == io.EOF {returneof } .error() }return}// mustReadRune turns io.EOF into a panic(io.ErrUnexpectedEOF).// It is called in cases such as string scanning where an EOF is a// syntax error.func ( *ss) () ( rune) { = .getRune()if == eof { .error(io.ErrUnexpectedEOF) }return}func ( *ss) () error { .rs.UnreadRune() .atEOF = false .count--returnnil}func ( *ss) ( error) {panic(scanError{})}func ( *ss) ( string) {panic(scanError{errors.New()})}func ( *ss) ( bool, func(rune) bool) ( []byte, error) {deferfunc() {if := recover(); != nil {if , := .(scanError); { = .err } else {panic() } } }()if == nil { = notSpace } .buf = .buf[:0] = .token(, )return}// space is a copy of the unicode.White_Space ranges,// to avoid depending on package unicode.var space = [][2]uint16{ {0x0009, 0x000d}, {0x0020, 0x0020}, {0x0085, 0x0085}, {0x00a0, 0x00a0}, {0x1680, 0x1680}, {0x2000, 0x200a}, {0x2028, 0x2029}, {0x202f, 0x202f}, {0x205f, 0x205f}, {0x3000, 0x3000},}func isSpace( rune) bool {if >= 1<<16 {returnfalse } := uint16()for , := rangespace {if < [0] {returnfalse }if <= [1] {returntrue } }returnfalse}// notSpace is the default scanning function used in Token.func notSpace( rune) bool {return !isSpace()}// readRune is a structure to enable reading UTF-8 encoded code points// from an io.Reader. It is used if the Reader given to the scanner does// not already implement io.RuneScanner.type readRune struct { reader io.Reader buf [utf8.UTFMax]byte// used only inside ReadRune pending int// number of bytes in pendBuf; only >0 for bad UTF-8 pendBuf [utf8.UTFMax]byte// bytes left over peekRune rune// if >=0 next rune; when <0 is ^(previous Rune)}// readByte returns the next byte from the input, which may be// left over from a previous read if the UTF-8 was ill-formed.func ( *readRune) () ( byte, error) {if .pending > 0 { = .pendBuf[0]copy(.pendBuf[0:], .pendBuf[1:]) .pending--return } , := io.ReadFull(.reader, .pendBuf[:1])if != 1 {return0, }return .pendBuf[0], }// ReadRune returns the next UTF-8 encoded code point from the// io.Reader inside r.func ( *readRune) () ( rune, int, error) {if .peekRune >= 0 { = .peekRune .peekRune = ^.peekRune = utf8.RuneLen()return } .buf[0], = .readByte()if != nil {return }if .buf[0] < utf8.RuneSelf { // fast check for common ASCII case = rune(.buf[0]) = 1// Known to be 1.// Flip the bits of the rune so it's available to UnreadRune. .peekRune = ^return }varintfor = 1; !utf8.FullRune(.buf[:]); ++ { .buf[], = .readByte()if != nil {if == io.EOF { = nilbreak }return } } , = utf8.DecodeRune(.buf[:])if < { // an error, save the bytes for the next readcopy(.pendBuf[.pending:], .buf[:]) .pending += - }// Flip the bits of the rune so it's available to UnreadRune. .peekRune = ^return}func ( *readRune) () error {if .peekRune >= 0 {returnerrors.New("fmt: scanning called UnreadRune with no rune available") }// Reverse bit flip of previously read rune to obtain valid >=0 state. .peekRune = ^.peekRunereturnnil}var ssFree = sync.Pool{New: func() any { returnnew(ss) },}// newScanState allocates a new ss struct or grab a cached one.func newScanState( io.Reader, , bool) ( *ss, ssave) { = ssFree.Get().(*ss)if , := .(io.RuneScanner); { .rs = } else { .rs = &readRune{reader: , peekRune: -1} } .nlIsSpace = .nlIsEnd = .atEOF = false .limit = hugeWid .argLimit = hugeWid .maxWid = hugeWid .validSave = true .count = 0return}// free saves used ss structs in ssFree; avoid an allocation per invocation.func ( *ss) ( ssave) {// If it was used recursively, just restore the old state.if .validSave { .ssave = return }// Don't hold on to ss structs with large buffers.ifcap(.buf) > 1024 {return } .buf = .buf[:0] .rs = nilssFree.Put()}// SkipSpace provides Scan methods the ability to skip space and newline// characters in keeping with the current scanning mode set by format strings// and [Scan]/[Scanln].func ( *ss) () {for { := .getRune()if == eof {return }if == '\r' && .peek("\n") {continue }if == '\n' {if .nlIsSpace {continue } .errorString("unexpected newline")return }if !isSpace() { .UnreadRune()break } }}// token returns the next space-delimited string from the input. It// skips white space. For Scanln, it stops at newlines. For Scan,// newlines are treated as spaces.func ( *ss) ( bool, func(rune) bool) []byte {if { .SkipSpace() }// read until white space or newlinefor { := .getRune()if == eof {break }if !() { .UnreadRune()break } .buf.writeRune() }return .buf}var errComplex = errors.New("syntax error scanning complex number")var errBool = errors.New("syntax error scanning boolean")func indexRune( string, rune) int {for , := range {if == {return } }return -1}// consume reads the next rune in the input and reports whether it is in the ok string.// If accept is true, it puts the character into the input token.func ( *ss) ( string, bool) bool { := .getRune()if == eof {returnfalse }ifindexRune(, ) >= 0 {if { .buf.writeRune() }returntrue }if != eof && { .UnreadRune() }returnfalse}// peek reports whether the next character is in the ok string, without consuming it.func ( *ss) ( string) bool { := .getRune()if != eof { .UnreadRune() }returnindexRune(, ) >= 0}func ( *ss) () {// Guarantee there is data to be read.if := .getRune(); == eof {panic(io.EOF) } .UnreadRune()}// 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.func ( *ss) ( string) bool {return .consume(, true)}// okVerb verifies that the verb is present in the list, setting s.err appropriately if not.func ( *ss) ( rune, , string) bool {for , := range {if == {returntrue } } .errorString("bad verb '%" + string() + "' for " + )returnfalse}// scanBool returns the value of the boolean represented by the next token.func ( *ss) ( rune) bool { .SkipSpace() .notEOF()if !.okVerb(, "tv", "boolean") {returnfalse }// Syntax-checking a boolean is annoying. We're not fastidious about case.switch .getRune() {case'0':returnfalsecase'1':returntruecase't', 'T':if .accept("rR") && (!.accept("uU") || !.accept("eE")) { .error(errBool) }returntruecase'f', 'F':if .accept("aA") && (!.accept("lL") || !.accept("sS") || !.accept("eE")) { .error(errBool) }returnfalse }returnfalse}// Numerical elementsconst ( binaryDigits = "01" octalDigits = "01234567" decimalDigits = "0123456789" hexadecimalDigits = "0123456789aAbBcCdDeEfF" sign = "+-" period = "." exponent = "eEpP")// getBase returns the numeric base represented by the verb and its digit string.func ( *ss) ( rune) ( int, string) { .okVerb(, "bdoUxXv", "integer") // sets s.err = 10 = decimalDigitsswitch {case'b': = 2 = binaryDigitscase'o': = 8 = octalDigitscase'x', 'X', 'U': = 16 = hexadecimalDigits }return}// scanNumber returns the numerical string with specified digits starting here.func ( *ss) ( string, bool) string {if ! { .notEOF()if !.accept() { .errorString("expected integer") } }for .accept() { }returnstring(.buf)}// scanRune returns the next rune value in the input.func ( *ss) ( int) int64 { .notEOF() := .getRune() := uint() := (int64() << (64 - )) >> (64 - )if != int64() { .errorString("overflow on character value " + string()) }returnint64()}// scanBasePrefix reports whether the integer begins with a base prefix// and returns the base, digit string, and whether a zero was found.// It is called only if the verb is %v.func ( *ss) () ( int, string, bool) {if !.peek("0") {return0, decimalDigits + "_", false } .accept("0")// Special cases for 0, 0b, 0o, 0x.switch {case .peek("bB"): .consume("bB", true)return0, binaryDigits + "_", truecase .peek("oO"): .consume("oO", true)return0, octalDigits + "_", truecase .peek("xX"): .consume("xX", true)return0, hexadecimalDigits + "_", truedefault:return0, octalDigits + "_", true }}// scanInt returns the value of the integer represented by the next// token, checking for overflow. Any error is stored in s.err.func ( *ss) ( rune, int) int64 {if == 'c' {return .scanRune() } .SkipSpace() .notEOF() , := .getBase() := falseif == 'U' {if !.consume("U", false) || !.consume("+", false) { .errorString("bad unicode format ") } } else { .accept(sign) // If there's a sign, it will be left in the token buffer.if == 'v' { , , = .scanBasePrefix() } } := .scanNumber(, ) , := strconv.ParseInt(, , 64)if != nil { .error() } := uint() := ( << (64 - )) >> (64 - )if != { .errorString("integer overflow on token " + ) }return}// scanUint returns the value of the unsigned integer represented// by the next token, checking for overflow. Any error is stored in s.err.func ( *ss) ( rune, int) uint64 {if == 'c' {returnuint64(.scanRune()) } .SkipSpace() .notEOF() , := .getBase() := falseif == 'U' {if !.consume("U", false) || !.consume("+", false) { .errorString("bad unicode format ") } } elseif == 'v' { , , = .scanBasePrefix() } := .scanNumber(, ) , := strconv.ParseUint(, , 64)if != nil { .error() } := uint() := ( << (64 - )) >> (64 - )if != { .errorString("unsigned integer overflow on token " + ) }return}// floatToken returns the floating-point number starting here, no longer than swid// if the width is specified. It's not rigorous about syntax because it doesn't check that// we have at least some digits, but Atof will do that.func ( *ss) () string { .buf = .buf[:0]// NaN?if .accept("nN") && .accept("aA") && .accept("nN") {returnstring(.buf) }// leading sign? .accept(sign)// Inf?if .accept("iI") && .accept("nN") && .accept("fF") {returnstring(.buf) } := decimalDigits + "_" := exponentif .accept("0") && .accept("xX") { = hexadecimalDigits + "_" = "pP" }// digits?for .accept() { }// decimal point?if .accept(period) {// fraction?for .accept() { } }// exponent?if .accept() {// leading sign? .accept(sign)// digits?for .accept(decimalDigits + "_") { } }returnstring(.buf)}// complexTokens returns the real and imaginary parts of the complex number starting here.// The number might be parenthesized and has the format (N+Ni) where N is a floating-point// number and there are no spaces within.func ( *ss) () (, string) {// TODO: accept N and Ni independently? := .accept("(") = .floatToken() .buf = .buf[:0]// Must now have a sign.if !.accept("+-") { .error(errComplex) }// Sign is now in buffer := string(.buf) = .floatToken()if !.accept("i") { .error(errComplex) }if && !.accept(")") { .error(errComplex) }return , + }func hasX( string) bool {for := 0; < len(); ++ {if [] == 'x' || [] == 'X' {returntrue } }returnfalse}// convertFloat converts the string to a float64value.func ( *ss) ( string, int) float64 {// strconv.ParseFloat will handle "+0x1.fp+2", // but we have to implement our non-standard // decimal+binary exponent mix (1.2p4) ourselves.if := indexRune(, 'p'); >= 0 && !hasX() {// Atof doesn't handle power-of-2 exponents, // but they're easy to evaluate. , := strconv.ParseFloat([:], )if != nil {// Put full string into error.if , := .(*strconv.NumError); { .Num = } .error() } , := strconv.Atoi([+1:])if != nil {// Put full string into error.if , := .(*strconv.NumError); { .Num = } .error() }returnmath.Ldexp(, ) } , := strconv.ParseFloat(, )if != nil { .error() }return}// scanComplex converts the next token to a complex128 value.// The atof argument is a type-specific reader for the underlying type.// If we're reading complex64, atof will parse float32s and convert them// to float64's to avoid reproducing this code for each complex type.func ( *ss) ( rune, int) complex128 {if !.okVerb(, floatVerbs, "complex") {return0 } .SkipSpace() .notEOF() , := .complexTokens() := .convertFloat(, /2) := .convertFloat(, /2)returncomplex(, )}// convertString returns the string represented by the next input characters.// The format of the input is determined by the verb.func ( *ss) ( rune) ( string) {if !.okVerb(, "svqxX", "string") {return"" } .SkipSpace() .notEOF()switch {case'q': = .quotedString()case'x', 'X': = .hexString()default: = string(.token(true, notSpace)) // %s and %v just return the next word }return}// quotedString returns the double- or back-quoted string represented by the next input characters.func ( *ss) () string { .notEOF() := .getRune()switch {case'`':// Back-quoted: Anything goes until EOF or back quote.for { := .mustReadRune()if == {break } .buf.writeRune() }returnstring(.buf)case'"':// Double-quoted: Include the quotes and let strconv.Unquote do the backslash escapes. .buf.writeByte('"')for { := .mustReadRune() .buf.writeRune()if == '\\' {// In a legal backslash escape, no matter how long, only the character // immediately after the escape can itself be a backslash or quote. // Thus we only need to protect the first character after the backslash. .buf.writeRune(.mustReadRune()) } elseif == '"' {break } } , := strconv.Unquote(string(.buf))if != nil { .error() }returndefault: .errorString("expected quoted string") }return""}// hexDigit returns the value of the hexadecimal digit.func hexDigit( rune) (int, bool) { := int()switch {case'0', '1', '2', '3', '4', '5', '6', '7', '8', '9':return - '0', truecase'a', 'b', 'c', 'd', 'e', 'f':return10 + - 'a', truecase'A', 'B', 'C', 'D', 'E', 'F':return10 + - 'A', true }return -1, false}// hexByte returns the next hex-encoded (two-character) byte from 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 ( *ss) () ( byte, bool) { := .getRune()if == eof {return } , := hexDigit()if ! { .UnreadRune()return } , := hexDigit(.mustReadRune())if ! { .errorString("illegal hex digit")return }returnbyte(<<4 | ), true}// hexString returns the space-delimited hexpair-encoded string.func ( *ss) () string { .notEOF()for { , := .hexByte()if ! {break } .buf.writeByte() }iflen(.buf) == 0 { .errorString("no hex data for %x string")return"" }returnstring(.buf)}const ( floatVerbs = "beEfFgGv" hugeWid = 1 << 30 intBits = 32 << (^uint(0) >> 63) uintptrBits = 32 << (^uintptr(0) >> 63))// scanPercent scans a literal percent character.func ( *ss) () { .SkipSpace() .notEOF()if !.accept("%") { .errorString("missing literal %") }}// scanOne scans a single value, deriving the scanner from the type of the argument.func ( *ss) ( rune, any) { .buf = .buf[:0]varerror// If the parameter has its own Scan method, use that.if , := .(Scanner); { = .Scan(, )if != nil {if == io.EOF { = io.ErrUnexpectedEOF } .error() }return }switch v := .(type) {case *bool: * = .scanBool()case *complex64: * = complex64(.scanComplex(, 64))case *complex128: * = .scanComplex(, 128)case *int: * = int(.scanInt(, intBits))case *int8: * = int8(.scanInt(, 8))case *int16: * = int16(.scanInt(, 16))case *int32: * = int32(.scanInt(, 32))case *int64: * = .scanInt(, 64)case *uint: * = uint(.scanUint(, intBits))case *uint8: * = uint8(.scanUint(, 8))case *uint16: * = uint16(.scanUint(, 16))case *uint32: * = uint32(.scanUint(, 32))case *uint64: * = .scanUint(, 64)case *uintptr: * = uintptr(.scanUint(, uintptrBits))// Floats are tricky because you want to scan in the precision of the result, not // scan in high precision and convert, in order to preserve the correct error condition.case *float32:if .okVerb(, floatVerbs, "float32") { .SkipSpace() .notEOF() * = float32(.convertFloat(.floatToken(), 32)) }case *float64:if .okVerb(, floatVerbs, "float64") { .SkipSpace() .notEOF() * = .convertFloat(.floatToken(), 64) }case *string: * = .convertString()case *[]byte:// We scan to string and convert so we get a copy of the data. // If we scanned to bytes, the slice would point at the buffer. * = []byte(.convertString())default: := reflect.ValueOf() := if .Kind() != reflect.Pointer { .errorString("type not a pointer: " + .Type().String())return }switch := .Elem(); .Kind() {casereflect.Bool: .SetBool(.scanBool())casereflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: .SetInt(.scanInt(, .Type().Bits()))casereflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: .SetUint(.scanUint(, .Type().Bits()))casereflect.String: .SetString(.convertString())casereflect.Slice:// For now, can only handle (renamed) []byte. := .Type()if .Elem().Kind() != reflect.Uint8 { .errorString("can't scan type: " + .Type().String()) } := .convertString() .Set(reflect.MakeSlice(, len(), len()))for := 0; < len(); ++ { .Index().SetUint(uint64([])) }casereflect.Float32, reflect.Float64: .SkipSpace() .notEOF() .SetFloat(.convertFloat(.floatToken(), .Type().Bits()))casereflect.Complex64, reflect.Complex128: .SetComplex(.scanComplex(, .Type().Bits()))default: .errorString("can't scan type: " + .Type().String()) } }}// errorHandler turns local panics into error returns.func errorHandler( *error) {if := recover(); != nil {if , := .(scanError); { // catch local error * = .err } elseif , := .(error); && == io.EOF { // out of input * = } else {panic() } }}// doScan does the real work for scanning without a format string.func ( *ss) ( []any) ( int, error) {defererrorHandler(&)for , := range { .scanOne('v', ) ++ }// Check for newline (or EOF) if required (Scanln etc.).if .nlIsEnd {for { := .getRune()if == '\n' || == eof {break }if !isSpace() { .errorString("expected newline")break } } }return}// 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. 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 ( *ss) ( string) ( int) {for < len() { , := utf8.DecodeRuneInString([:])// Space processing. // In the rest of this comment "space" means spaces other than newline. // Newline in the format matches input of zero or more spaces and then newline or end-of-input. // Spaces in the format before the newline are collapsed into the newline. // Spaces in the format after the newline match zero or more spaces after the corresponding input newline. // Other spaces in the format match input of one or more spaces or end-of-input.ifisSpace() { := 0 := falseforisSpace() && < len() {if == '\n' { ++ = false } else { = true } += , = utf8.DecodeRuneInString([:]) }for := 0; < ; ++ { := .getRune()forisSpace() && != '\n' { = .getRune() }if != '\n' && != eof { .errorString("newline in format does not match input") } }if { := .getRune()if == 0 {// If the trailing space stood alone (did not follow a newline), // it must find at least one space to consume.if !isSpace() && != eof { .errorString("expected space in input to match format") }if == '\n' { .errorString("newline in input does not match format") } }forisSpace() && != '\n' { = .getRune() }if != eof { .UnreadRune() } }continue }// Verbs.if == '%' {// % at end of string is an error.if + == len() { .errorString("missing verb: % at end of format string") }// %% acts like a real percent , := utf8.DecodeRuneInString([+:]) // will not match % if string is emptyif != '%' {return } += // skip the first % }// Literals. := .mustReadRune()if != { .UnreadRune()return -1 } += }return}// doScanf does the real work when scanning with a format string.// At the moment, it handles only pointers to basic types.func ( *ss) ( string, []any) ( int, error) {defererrorHandler(&) := len() - 1// We process one item per non-trivial formatfor := 0; <= ; { := .advance([:])if > 0 { += continue }// Either we failed to advance, we have a percent character, or we ran out of input.if [] != '%' {// Can't advance format. Why not?if < 0 { .errorString("input does not match format") }// Otherwise at EOF; "too many operands" error handled belowbreak } ++ // % is one byte// do we have 20 (width)?varbool .maxWid, , = parsenum(, , )if ! { .maxWid = hugeWid } , := utf8.DecodeRuneInString([:]) += if != 'c' { .SkipSpace() }if == '%' { .scanPercent()continue// Do not consume an argument. } .argLimit = .limitif := .count + .maxWid; < .argLimit { .argLimit = }if >= len() { // out of operands .errorString("too few operands for format '%" + [-:] + "'")break } := [] .scanOne(, ) ++ .argLimit = .limit }if < len() { .errorString("too many operands") }return}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.