about summary refs log tree commit diff
path: root/vendor/modernc.org/libc/scanf.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/modernc.org/libc/scanf.go')
-rw-r--r--vendor/modernc.org/libc/scanf.go748
1 files changed, 748 insertions, 0 deletions
diff --git a/vendor/modernc.org/libc/scanf.go b/vendor/modernc.org/libc/scanf.go
new file mode 100644
index 0000000..ebc3ada
--- /dev/null
+++ b/vendor/modernc.org/libc/scanf.go
@@ -0,0 +1,748 @@
+// 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))
+
+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
+}