gotosocial/vendor/modernc.org/libc/scanf.go

444 lines
11 KiB
Go
Raw Normal View History

// 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.
package libc // import "modernc.org/libc"
import (
"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 *strings.Reader, 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 *strings.Reader, 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
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++
panic(todo(""))
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++
panic(todo(""))
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
}
panic(todo("", err))
}
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
}
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(n)
default:
panic(todo(""))
}
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 {
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
}
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++
panic(todo(""))
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.
format++
panic(todo(""))
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++
panic(todo(""))
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(""))
}
if c != '0' {
r.UnreadByte()
panic(todo(""))
}
if c, err = r.ReadByte(); err != nil {
panic(todo(""))
}
if c != 'x' && c != 'X' {
r.UnreadByte()
panic(todo(""))
}
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
}
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 *strings.Reader) 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
}
}
}