| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748 |
- // Copyright 2020 The Libc Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- //go:build !(linux && (amd64 || arm64 || loong64 || ppc64le || s390x || riscv64 || 386 || arm))
- package libc // import "modernc.org/libc"
- import (
- "io"
- "strconv"
- "strings"
- "unsafe"
- )
- // The format string consists of a sequence of directives which describe how to
- // process the sequence of input characters. If processing of a directive
- // fails, no further input is read, and scanf() returns. A "failure" can
- // be either of the following: input failure, meaning that input characters
- // were unavailable, or matching failure, meaning that the input was
- // inappropriate.
- func scanf(r io.ByteScanner, format, args uintptr) (nvalues int32) {
- // var src []byte //TODO-
- var ok bool
- out:
- for {
- c := *(*byte)(unsafe.Pointer(format))
- // src = append(src, c) //TODO-
- switch c {
- case '%':
- var n int
- var match bool
- format, n, match = scanfConversion(r, format, &args)
- if !match {
- break out
- }
- nvalues += int32(n)
- ok = true
- case 0:
- break out
- case ' ', '\t', '\n', '\r', '\v', '\f':
- format = skipWhiteSpace(format)
- ok = true
- next:
- for {
- c, err := r.ReadByte()
- if err != nil {
- break out
- }
- switch c {
- case ' ', '\t', '\n', '\r', '\v', '\f':
- // nop
- default:
- r.UnreadByte()
- break next
- }
- }
- default:
- c2, err := r.ReadByte()
- if err != nil {
- break out
- }
- if c2 != c {
- r.UnreadByte()
- break out
- }
- format++
- ok = true
- }
- }
- if ok {
- return nvalues
- }
- return -1 // stdio.EOF but not defined for windows
- }
- func scanfConversion(r io.ByteScanner, format uintptr, args *uintptr) (_ uintptr, nvalues int, match bool) {
- format++ // '%'
- // Each conversion specification in format begins with either the character '%'
- // or the character sequence "%n$" (see below for the distinction) followed by:
- mod := 0
- width := -1
- discard := false
- flags:
- for {
- switch c := *(*byte)(unsafe.Pointer(format)); c {
- case '*':
- // An optional '*' assignment-suppression character: scanf() reads input as
- // directed by the conversion specification, but discards the input. No
- // corresponding pointer argument is re‐ quired, and this specification is not
- // included in the count of successful assignments returned by scanf().
- format++
- discard = true
- case '\'':
- // For decimal conversions, an optional quote character ('). This specifies
- // that the input number may include thousands' separators as defined by the
- // LC_NUMERIC category of the current locale. (See setlocale(3).) The quote
- // character may precede or follow the '*' assignment-suppression character.
- format++
- panic(todo(""))
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- // An optional decimal integer which specifies the maximum field width.
- // Reading of characters stops either when this maximum is reached or when a
- // nonmatching character is found, whichever happens first. Most conversions
- // discard initial white space characters (the exceptions are noted below), and
- // these discarded characters don't count toward the maximum field width.
- // String input conversions store a terminating null byte ('\0') to mark the
- // end of the input; the maximum field width does not include this terminator.
- width = 0
- num:
- for {
- var digit int
- switch c := *(*byte)(unsafe.Pointer(format)); {
- default:
- break num
- case c >= '0' && c <= '9':
- format++
- digit = int(c) - '0'
- }
- width0 := width
- width = 10*width + digit
- if width < width0 {
- panic(todo(""))
- }
- }
- case 'h', 'j', 'l', 'L', 'q', 't', 'z':
- format, mod = parseLengthModifier(format)
- default:
- break flags
- }
- }
- // A conversion specifier that specifies the type of input conversion to be
- // performed.
- switch c := *(*byte)(unsafe.Pointer(format)); c {
- case '%':
- // Matches a literal '%'. That is, %% in the format string matches a single
- // input '%' character. No conversion is done (but initial white space
- // characters are discarded), and assign‐ ment does not occur.
- format++
- skipReaderWhiteSpace(r)
- c, err := r.ReadByte()
- if err != nil {
- return format, -1, false
- }
- if c == '%' {
- return format, 1, true
- }
- r.UnreadByte()
- return format, 0, false
- case 'd':
- // Matches an optionally signed decimal integer; the next pointer must be a
- // pointer to int.
- format++
- skipReaderWhiteSpace(r)
- var digit, n uint64
- allowSign := true
- neg := false
- dec:
- for ; width != 0; width-- {
- c, err := r.ReadByte()
- if err != nil {
- if match {
- break dec
- }
- return 0, 0, false
- }
- if allowSign {
- switch c {
- case '-':
- allowSign = false
- neg = true
- continue
- case '+':
- allowSign = false
- continue
- }
- }
- switch {
- case c >= '0' && c <= '9':
- digit = uint64(c) - '0'
- default:
- r.UnreadByte()
- break dec
- }
- match = true
- n0 := n
- n = n*10 + digit
- if n < n0 {
- panic(todo(""))
- }
- }
- if !match {
- break
- }
- if !discard {
- arg := VaUintptr(args)
- v := int64(n)
- if neg {
- v = -v
- }
- switch mod {
- case modNone:
- *(*int32)(unsafe.Pointer(arg)) = int32(v)
- case modH:
- *(*int16)(unsafe.Pointer(arg)) = int16(v)
- case modHH:
- *(*int8)(unsafe.Pointer(arg)) = int8(v)
- case modL:
- *(*long)(unsafe.Pointer(arg)) = long(v)
- case modLL:
- *(*int64)(unsafe.Pointer(arg)) = int64(v)
- default:
- panic(todo("", mod))
- }
- }
- nvalues = 1
- case 'D':
- // Equivalent to ld; this exists only for backward compatibility. (Note:
- // thus only in libc4. In libc5 and glibc the %D is silently ignored, causing
- // old programs to fail mysteriously.)
- format++
- panic(todo(""))
- case 'i':
- // Matches an optionally signed integer; the next pointer must be a pointer to
- // int. The integer is read in base 16 if it begins with 0x or 0X, in base 8
- // if it begins with 0, and in base 10 otherwise. Only characters that
- // correspond to the base are used.
- format++
- panic(todo(""))
- case 'o':
- // Matches an unsigned octal integer; the next pointer must be a pointer to
- // unsigned int.
- format++
- panic(todo(""))
- case 'u':
- // Matches an unsigned decimal integer; the next pointer must be a pointer to
- // unsigned int.
- format++
- panic(todo(""))
- case 'x', 'X':
- // Matches an unsigned hexadecimal integer; the next pointer must be a pointer
- // to unsigned int.
- format++
- skipReaderWhiteSpace(r)
- var digit, n uint64
- allowPrefix := true
- var b []byte
- hex:
- for ; width != 0; width-- {
- c, err := r.ReadByte()
- if err != nil {
- if match || err == io.EOF {
- break hex
- }
- panic(todo("", err))
- }
- if allowPrefix {
- if len(b) == 1 && b[0] == '0' && (c == 'x' || c == 'X') {
- allowPrefix = false
- match = false
- b = nil
- continue
- }
- b = append(b, c)
- }
- switch {
- case c >= '0' && c <= '9':
- digit = uint64(c) - '0'
- case c >= 'a' && c <= 'f':
- digit = uint64(c) - 'a' + 10
- case c >= 'A' && c <= 'F':
- digit = uint64(c) - 'A' + 10
- default:
- r.UnreadByte()
- break hex
- }
- match = true
- n0 := n
- n = n<<4 + digit
- if n < n0 {
- panic(todo(""))
- }
- }
- if !match {
- break
- }
- if !discard {
- arg := VaUintptr(args)
- switch mod {
- case modNone:
- *(*uint32)(unsafe.Pointer(arg)) = uint32(n)
- case modH:
- *(*uint16)(unsafe.Pointer(arg)) = uint16(n)
- case modHH:
- *(*byte)(unsafe.Pointer(arg)) = byte(n)
- case modL:
- *(*ulong)(unsafe.Pointer(arg)) = ulong(n)
- default:
- panic(todo(""))
- }
- }
- nvalues = 1
- case 'f', 'e', 'g', 'E', 'a':
- // Matches an optionally signed floating-point number; the next pointer must be
- // a pointer to float.
- format++
- skipReaderWhiteSpace(r)
- seq := fpLiteral(r)
- if len(seq) == 0 {
- return 0, 0, false
- }
- var neg bool
- switch seq[0] {
- case '+':
- seq = seq[1:]
- case '-':
- neg = true
- seq = seq[1:]
- }
- n, err := strconv.ParseFloat(string(seq), 64)
- if err != nil {
- panic(todo("", err))
- }
- if !discard {
- arg := VaUintptr(args)
- if neg {
- n = -n
- }
- switch mod {
- case modNone:
- *(*float32)(unsafe.Pointer(arg)) = float32(n)
- case modL:
- *(*float64)(unsafe.Pointer(arg)) = n
- default:
- panic(todo("", mod, neg, n))
- }
- }
- return format, 1, true
- case 's':
- // Matches a sequence of non-white-space characters; the next pointer must be
- // a pointer to the initial element of a character array that is long enough to
- // hold the input sequence and the terminating null byte ('\0'), which is added
- // automatically. The input string stops at white space or at the maximum
- // field width, whichever occurs first.
- var c byte
- var err error
- var arg uintptr
- if !discard {
- arg = VaUintptr(args)
- }
- scans:
- for ; width != 0; width-- {
- if c, err = r.ReadByte(); err != nil {
- if err != io.EOF {
- nvalues = -1
- }
- break scans
- }
- switch c {
- case ' ', '\t', '\n', '\r', '\v', '\f':
- break scans
- }
- nvalues = 1
- match = true
- if !discard {
- *(*byte)(unsafe.Pointer(arg)) = c
- arg++
- }
- }
- if match {
- switch {
- case width == 0:
- r.UnreadByte()
- fallthrough
- default:
- if !discard {
- *(*byte)(unsafe.Pointer(arg)) = 0
- }
- }
- }
- case 'c':
- // Matches a sequence of characters whose length is specified by the maximum
- // field width (default 1); the next pointer must be a pointer to char, and
- // there must be enough room for all the characters (no terminating null byte
- // is added). The usual skip of leading white space is suppressed. To skip
- // white space first, use an explicit space in the format.
- format++
- panic(todo(""))
- case '[':
- // Matches a nonempty sequence of characters from the specified set of
- // accepted characters; the next pointer must be a pointer to char, and there
- // must be enough room for all the char‐ acters in the string, plus a
- // terminating null byte. The usual skip of leading white space is suppressed.
- // The string is to be made up of characters in (or not in) a particular set;
- // the set is defined by the characters between the open bracket [ character
- // and a close bracket ] character. The set excludes those characters if the
- // first character after the open bracket is a circumflex (^). To include a
- // close bracket in the set, make it the first character after the open bracket
- // or the circumflex; any other position will end the set. The hyphen
- // character - is also special; when placed between two other characters, it
- // adds all intervening characters to the set. To include a hyphen, make it
- // the last character before the final close bracket. For instance, [^]0-9-]
- // means the set "everything except close bracket, zero through nine, and
- // hyphen". The string ends with the appearance of a character not in the
- // (or, with a circumflex, in) set or when the field width runs out.
- format++
- var re0 []byte
- bracket:
- for i := 0; ; i++ {
- c := *(*byte)(unsafe.Pointer(format))
- format++
- if c == ']' && i != 0 {
- break bracket
- }
- re0 = append(re0, c)
- }
- set := map[byte]struct{}{}
- re := string(re0)
- neg := strings.HasPrefix(re, "^")
- if neg {
- re = re[1:]
- }
- for len(re) != 0 {
- switch {
- case len(re) >= 3 && re[1] == '-':
- for c := re[0]; c <= re[2]; c++ {
- set[c] = struct{}{}
- }
- re = re[3:]
- default:
- set[c] = struct{}{}
- re = re[1:]
- }
- }
- var arg uintptr
- if !discard {
- arg = VaUintptr(args)
- }
- for ; width != 0; width-- {
- c, err := r.ReadByte()
- if err != nil {
- if err == io.EOF {
- return format, nvalues, match
- }
- return format, -1, match
- }
- if _, ok := set[c]; ok == !neg {
- match = true
- nvalues = 1
- if !discard {
- *(*byte)(unsafe.Pointer(arg)) = c
- arg++
- }
- }
- }
- if match {
- switch {
- case width == 0:
- r.UnreadByte()
- fallthrough
- default:
- if !discard {
- *(*byte)(unsafe.Pointer(arg)) = 0
- }
- }
- }
- case 'p':
- // Matches a pointer value (as printed by %p in printf(3); the next pointer
- // must be a pointer to a pointer to void.
- format++
- skipReaderWhiteSpace(r)
- c, err := r.ReadByte()
- if err != nil {
- panic(todo("", err))
- }
- if c == '0' {
- if c, err = r.ReadByte(); err != nil {
- panic(todo("", err))
- }
- if c != 'x' && c != 'X' {
- r.UnreadByte()
- }
- }
- var digit, n uint64
- ptr:
- for ; width != 0; width-- {
- c, err := r.ReadByte()
- if err != nil {
- if match {
- break ptr
- }
- panic(todo(""))
- }
- switch {
- case c >= '0' && c <= '9':
- digit = uint64(c) - '0'
- case c >= 'a' && c <= 'f':
- digit = uint64(c) - 'a' + 10
- case c >= 'A' && c <= 'F':
- digit = uint64(c) - 'A' + 10
- default:
- r.UnreadByte()
- break ptr
- }
- match = true
- n0 := n
- n = n<<4 + digit
- if n < n0 {
- panic(todo(""))
- }
- }
- if !match {
- break
- }
- if !discard {
- arg := VaUintptr(args)
- *(*uintptr)(unsafe.Pointer(arg)) = uintptr(n)
- }
- nvalues = 1
- case 'n':
- // Nothing is expected; instead, the number of characters consumed thus far
- // from the input is stored through the next pointer, which must be a pointer
- // to int. This is not a conversion and does not increase the count returned
- // by the function. The assignment can be suppressed with the *
- // assignment-suppression character, but the effect on the return value is
- // undefined. Therefore %*n conversions should not be used.
- format++
- panic(todo(""))
- default:
- panic(todo("%#U", c))
- }
- return format, nvalues, match
- }
- func skipReaderWhiteSpace(r io.ByteScanner) error {
- for {
- c, err := r.ReadByte()
- if err != nil {
- return err
- }
- switch c {
- case ' ', '\t', '\n', '\r', '\v', '\f':
- // ok
- default:
- r.UnreadByte()
- return nil
- }
- }
- }
- func skipWhiteSpace(s uintptr) uintptr {
- for {
- switch c := *(*byte)(unsafe.Pointer(s)); c {
- case ' ', '\t', '\n', '\r', '\v', '\f':
- s++
- default:
- return s
- }
- }
- }
- // [-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?
- func fpLiteral(rd io.ByteScanner) (seq []byte) {
- const endOfText = 0x110000
- var pos, width, length int
- defer func() {
- if len(seq) > length {
- rd.UnreadByte()
- seq = seq[:len(seq)-1]
- }
- }()
- var r rune
- step := func(pos int) (rune, int) {
- b, err := rd.ReadByte()
- if err != nil {
- return endOfText, 0
- }
- seq = append(seq, b)
- return rune(b), 1
- }
- move := func() {
- pos += width
- if r != endOfText {
- r, width = step(pos + width)
- }
- }
- accept := func(x rune) bool {
- if r == x {
- move()
- return true
- }
- return false
- }
- accept2 := func(x rune) bool {
- if r <= x {
- move()
- return true
- }
- return false
- }
- r = endOfText
- width = 0
- r, width = step(pos)
- if accept('.') {
- goto l7
- }
- if accept('+') {
- goto l30
- }
- if accept('-') {
- goto l30
- }
- if r < '0' {
- goto l4out
- }
- if accept2('9') {
- goto l35
- }
- l4out:
- return seq
- l7:
- if r < '0' {
- goto l7out
- }
- if accept2('9') {
- goto l10
- }
- l7out:
- return seq
- l10:
- length = pos
- if accept('E') {
- goto l18
- }
- if accept('e') {
- goto l18
- }
- if r < '0' {
- goto l15out
- }
- if accept2('9') {
- goto l10
- }
- l15out:
- return seq
- l18:
- if accept('+') {
- goto l23
- }
- if accept('-') {
- goto l23
- }
- if r < '0' {
- goto l20out
- }
- if accept2('9') {
- goto l26
- }
- l20out:
- return seq
- l23:
- if r < '0' {
- goto l23out
- }
- if accept2('9') {
- goto l26
- }
- l23out:
- return seq
- l26:
- length = pos
- if r < '0' {
- goto l27out
- }
- if accept2('9') {
- goto l26
- }
- l27out:
- return seq
- l30:
- if accept('.') {
- goto l7
- }
- if r < '0' {
- goto l32out
- }
- if accept2('9') {
- goto l35
- }
- l32out:
- return seq
- l35:
- length = pos
- if accept('.') {
- goto l7
- }
- if accept('E') {
- goto l18
- }
- if accept('e') {
- goto l18
- }
- if r < '0' {
- goto l42out
- }
- if accept2('9') {
- goto l35
- }
- l42out:
- return seq
- }
|