2021-08-29 17:41:41 +03:00
|
|
|
|
// 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 (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"unsafe"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
modNone = iota
|
|
|
|
|
modHH
|
|
|
|
|
modH
|
|
|
|
|
modL
|
|
|
|
|
modLL
|
|
|
|
|
modQ
|
|
|
|
|
modCapitalL
|
|
|
|
|
modJ
|
|
|
|
|
modZ
|
|
|
|
|
modCapitalZ
|
|
|
|
|
modT
|
|
|
|
|
mod32
|
|
|
|
|
mod64
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Format of the format string
|
|
|
|
|
//
|
|
|
|
|
// The format string is a character string, beginning and ending in its initial
|
|
|
|
|
// shift state, if any. The format string is composed of zero or more
|
|
|
|
|
// directives: ordinary characters (not %), which are copied unchanged to
|
|
|
|
|
// the output stream; and conversion specifications, each of which results in
|
|
|
|
|
// fetching zero or more subsequent arguments.
|
|
|
|
|
func printf(format, args uintptr) []byte {
|
2021-11-27 17:26:58 +03:00
|
|
|
|
format0 := format
|
|
|
|
|
args0 := args
|
2021-08-29 17:41:41 +03:00
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
|
for {
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case '%':
|
|
|
|
|
format = printfConversion(buf, format, &args)
|
|
|
|
|
case 0:
|
2021-09-08 23:12:23 +03:00
|
|
|
|
if dmesgs {
|
2021-11-27 17:26:58 +03:00
|
|
|
|
dmesg("%v: %q, %#x -> %q", origin(1), GoString(format0), args0, buf.Bytes())
|
2021-09-08 23:12:23 +03:00
|
|
|
|
}
|
2021-08-29 17:41:41 +03:00
|
|
|
|
return buf.Bytes()
|
|
|
|
|
default:
|
|
|
|
|
format++
|
|
|
|
|
buf.WriteByte(c)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Each conversion specification is introduced by the character %, and ends
|
|
|
|
|
// with a conversion specifier. In between there may be (in this order) zero
|
|
|
|
|
// or more flags, an optional minimum field width, an optional precision and
|
|
|
|
|
// an optional length modifier.
|
|
|
|
|
func printfConversion(buf *bytes.Buffer, format uintptr, args *uintptr) uintptr {
|
|
|
|
|
format++ // '%'
|
|
|
|
|
spec := "%"
|
|
|
|
|
|
|
|
|
|
// Flags characters
|
|
|
|
|
//
|
|
|
|
|
// The character % is followed by zero or more of the following flags:
|
|
|
|
|
flags:
|
|
|
|
|
for {
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case '#':
|
|
|
|
|
// The value should be converted to an "alternate form". For o conversions,
|
|
|
|
|
// the first character of the output string is made zero (by prefixing a 0 if
|
|
|
|
|
// it was not zero already). For x and X conversions, a nonzero result has
|
|
|
|
|
// the string "0x" (or "0X" for X conversions) prepended to it. For a, A, e,
|
|
|
|
|
// E, f, F, g, and G conversions, the result will always contain a decimal
|
|
|
|
|
// point, even if no digits follow it (normally, a decimal point appears in the
|
|
|
|
|
// results of those conversions only if a digit follows). For g and G
|
|
|
|
|
// conversions, trailing zeros are not removed from the result as they would
|
|
|
|
|
// otherwise be. For other conversions, the result is undefined.
|
|
|
|
|
format++
|
|
|
|
|
spec += "#"
|
|
|
|
|
case '0':
|
|
|
|
|
// The value should be zero padded. For d, i, o, u, x, X, a, A, e, E, f, F,
|
|
|
|
|
// g, and G conversions, the converted value is padded on the left with zeros
|
|
|
|
|
// rather than blanks. If the 0 and - flags both appear, the 0 flag is
|
|
|
|
|
// ignored. If a precision is given with a numeric conversion (d, i, o, u, x,
|
|
|
|
|
// and X), the 0 flag is ignored. For other conversions, the behav‐ ior is
|
|
|
|
|
// undefined.
|
|
|
|
|
format++
|
|
|
|
|
spec += "0"
|
|
|
|
|
case '-':
|
|
|
|
|
// The converted value is to be left adjusted on the field boundary. (The
|
|
|
|
|
// default is right justification.) The converted value is padded on the right
|
|
|
|
|
// with blanks, rather than on the left with blanks or zeros. A - overrides a
|
|
|
|
|
// 0 if both are given.
|
|
|
|
|
format++
|
|
|
|
|
spec += "-"
|
|
|
|
|
case ' ':
|
|
|
|
|
// A blank should be left before a positive number (or empty string) produced
|
|
|
|
|
// by a signed conversion.
|
|
|
|
|
format++
|
|
|
|
|
spec += " "
|
|
|
|
|
case '+':
|
|
|
|
|
// A sign (+ or -) should always be placed before a number produced by a signed
|
|
|
|
|
// conversion. By default, a sign is used only for negative numbers. A +
|
|
|
|
|
// overrides a space if both are used.
|
|
|
|
|
format++
|
|
|
|
|
spec += "+"
|
|
|
|
|
default:
|
|
|
|
|
break flags
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
format, width, hasWidth := parseFieldWidth(format)
|
|
|
|
|
if hasWidth {
|
|
|
|
|
spec += strconv.Itoa(width)
|
|
|
|
|
}
|
|
|
|
|
format, prec, hasPrecision := parsePrecision(format, args)
|
|
|
|
|
format, mod := parseLengthModifier(format)
|
|
|
|
|
|
|
|
|
|
var str string
|
|
|
|
|
|
|
|
|
|
more:
|
|
|
|
|
// Conversion specifiers
|
|
|
|
|
//
|
|
|
|
|
// A character that specifies the type of conversion to be applied. The
|
|
|
|
|
// conversion specifiers and their meanings are:
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case 'd', 'i':
|
|
|
|
|
// The int argument is converted to signed decimal notation. The precision,
|
|
|
|
|
// if any, gives the minimum number of digits that must appear; if the
|
|
|
|
|
// converted value requires fewer digits, it is padded on the left with zeros.
|
|
|
|
|
// The default precision is 1. When 0 is printed with an explicit precision 0,
|
|
|
|
|
// the output is empty.
|
|
|
|
|
format++
|
|
|
|
|
var arg int64
|
|
|
|
|
switch mod {
|
|
|
|
|
case modNone, modL, modLL, mod64:
|
|
|
|
|
arg = VaInt64(args)
|
|
|
|
|
case modH:
|
|
|
|
|
arg = int64(int16(VaInt32(args)))
|
|
|
|
|
case modHH:
|
|
|
|
|
arg = int64(int8(VaInt32(args)))
|
|
|
|
|
case mod32:
|
|
|
|
|
arg = int64(VaInt32(args))
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("", mod))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if arg == 0 && hasPrecision && prec == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if hasPrecision {
|
|
|
|
|
panic(todo("", prec))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f := spec + "d"
|
|
|
|
|
str = fmt.Sprintf(f, arg)
|
|
|
|
|
case 'u':
|
|
|
|
|
// The unsigned int argument is converted to unsigned decimal notation. The
|
|
|
|
|
// precision, if any, gives the minimum number of digits that must appear; if
|
|
|
|
|
// the converted value requires fewer digits, it is padded on the left with
|
|
|
|
|
// zeros. The default precision is 1. When 0 is printed with an explicit
|
|
|
|
|
// precision 0, the output is empty.
|
|
|
|
|
format++
|
|
|
|
|
var arg uint64
|
|
|
|
|
switch mod {
|
|
|
|
|
case modNone, modL, modLL, mod64:
|
|
|
|
|
arg = VaUint64(args)
|
|
|
|
|
case modH:
|
|
|
|
|
arg = uint64(uint16(VaInt32(args)))
|
|
|
|
|
case modHH:
|
|
|
|
|
arg = uint64(uint8(VaInt32(args)))
|
|
|
|
|
case mod32:
|
|
|
|
|
arg = uint64(VaInt32(args))
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("", mod))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if arg == 0 && hasPrecision && prec == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if hasPrecision {
|
|
|
|
|
panic(todo("", prec))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f := spec + "d"
|
|
|
|
|
str = fmt.Sprintf(f, arg)
|
|
|
|
|
case 'o':
|
|
|
|
|
// The unsigned int argument is converted to unsigned octal notation. The
|
|
|
|
|
// precision, if any, gives the minimum number of digits that must appear; if
|
|
|
|
|
// the converted value requires fewer digits, it is padded on the left with
|
|
|
|
|
// zeros. The default precision is 1. When 0 is printed with an explicit
|
|
|
|
|
// precision 0, the output is empty.
|
|
|
|
|
format++
|
|
|
|
|
var arg uint64
|
|
|
|
|
switch mod {
|
|
|
|
|
case modNone, modL, modLL, mod64:
|
|
|
|
|
arg = VaUint64(args)
|
|
|
|
|
case modH:
|
|
|
|
|
arg = uint64(uint16(VaInt32(args)))
|
|
|
|
|
case modHH:
|
|
|
|
|
arg = uint64(uint8(VaInt32(args)))
|
|
|
|
|
case mod32:
|
|
|
|
|
arg = uint64(VaInt32(args))
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("", mod))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if arg == 0 && hasPrecision && prec == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if hasPrecision {
|
|
|
|
|
panic(todo("", prec))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f := spec + "o"
|
|
|
|
|
str = fmt.Sprintf(f, arg)
|
|
|
|
|
case 'I':
|
|
|
|
|
if !isWindows {
|
|
|
|
|
panic(todo("%#U", c))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
format++
|
|
|
|
|
switch c = *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case 'x', 'X':
|
|
|
|
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-wsprintfa
|
|
|
|
|
//
|
|
|
|
|
// Ix, IX
|
|
|
|
|
//
|
|
|
|
|
// 64-bit unsigned hexadecimal integer in lowercase or uppercase on 64-bit
|
|
|
|
|
// platforms, 32-bit unsigned hexadecimal integer in lowercase or uppercase on
|
|
|
|
|
// 32-bit platforms.
|
|
|
|
|
if unsafe.Sizeof(int(0)) == 4 {
|
|
|
|
|
mod = mod32
|
|
|
|
|
}
|
|
|
|
|
case '3':
|
|
|
|
|
// https://en.wikipedia.org/wiki/Printf_format_string#Length_field
|
|
|
|
|
//
|
|
|
|
|
// I32 For integer types, causes printf to expect a 32-bit (double word) integer argument.
|
|
|
|
|
format++
|
|
|
|
|
switch c = *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case '2':
|
|
|
|
|
format++
|
|
|
|
|
mod = mod32
|
|
|
|
|
goto more
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("%#U", c))
|
|
|
|
|
}
|
|
|
|
|
case '6':
|
|
|
|
|
// https://en.wikipedia.org/wiki/Printf_format_string#Length_field
|
|
|
|
|
//
|
|
|
|
|
// I64 For integer types, causes printf to expect a 64-bit (quad word) integer argument.
|
|
|
|
|
format++
|
|
|
|
|
switch c = *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case '4':
|
|
|
|
|
format++
|
|
|
|
|
mod = mod64
|
|
|
|
|
goto more
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("%#U", c))
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("%#U", c))
|
|
|
|
|
}
|
|
|
|
|
fallthrough
|
|
|
|
|
case 'X':
|
|
|
|
|
fallthrough
|
|
|
|
|
case 'x':
|
|
|
|
|
// The unsigned int argument is converted to unsigned hexadecimal notation.
|
|
|
|
|
// The letters abcdef are used for x conversions; the letters ABCDEF are used
|
|
|
|
|
// for X conversions. The precision, if any, gives the minimum number of
|
|
|
|
|
// digits that must appear; if the converted value requires fewer digits, it is
|
|
|
|
|
// padded on the left with zeros. The default precision is 1. When 0 is
|
|
|
|
|
// printed with an explicit precision 0, the output is empty.
|
|
|
|
|
format++
|
|
|
|
|
var arg uint64
|
|
|
|
|
switch mod {
|
|
|
|
|
case modNone, modL, modLL, mod64:
|
|
|
|
|
arg = VaUint64(args)
|
|
|
|
|
case modH:
|
|
|
|
|
arg = uint64(uint16(VaInt32(args)))
|
|
|
|
|
case modHH:
|
|
|
|
|
arg = uint64(uint8(VaInt32(args)))
|
|
|
|
|
case mod32:
|
|
|
|
|
arg = uint64(VaInt32(args))
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("", mod))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if arg == 0 && hasPrecision && prec == 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.Contains(spec, "#") && arg == 0 {
|
|
|
|
|
spec = strings.ReplaceAll(spec, "#", "")
|
|
|
|
|
}
|
|
|
|
|
var f string
|
|
|
|
|
switch {
|
|
|
|
|
case hasPrecision:
|
|
|
|
|
f = fmt.Sprintf("%s.%d%c", spec, prec, c)
|
|
|
|
|
default:
|
|
|
|
|
f = spec + string(c)
|
|
|
|
|
}
|
|
|
|
|
str = fmt.Sprintf(f, arg)
|
|
|
|
|
case 'e', 'E':
|
|
|
|
|
// The double argument is rounded and converted in the style [-]d.ddde±dd where
|
|
|
|
|
// there is one digit before the decimal-point character and the number of
|
|
|
|
|
// digits after it is equal to the precision; if the precision is missing, it
|
|
|
|
|
// is taken as 6; if the precision is zero, no decimal-point character appears.
|
|
|
|
|
// An E conversion uses the letter E (rather than e) to intro‐ duce the
|
|
|
|
|
// exponent. The exponent always contains at least two digits; if the value is
|
|
|
|
|
// zero, the exponent is 00.
|
|
|
|
|
format++
|
|
|
|
|
arg := VaFloat64(args)
|
|
|
|
|
if !hasPrecision {
|
|
|
|
|
prec = 6
|
|
|
|
|
}
|
|
|
|
|
f := fmt.Sprintf("%s.%d%c", spec, prec, c)
|
|
|
|
|
str = fmt.Sprintf(f, arg)
|
|
|
|
|
case 'f', 'F':
|
|
|
|
|
// The double argument is rounded and converted to decimal notation in the
|
|
|
|
|
// style [-]ddd.ddd, where the number of digits after the decimal-point
|
|
|
|
|
// character is equal to the precision specification. If the precision
|
|
|
|
|
// is missing, it is taken as 6; if the precision is explicitly zero, no
|
|
|
|
|
// decimal-point character appears. If a decimal point appears, at least one
|
|
|
|
|
// digit appears before it.
|
|
|
|
|
format++
|
|
|
|
|
arg := VaFloat64(args)
|
|
|
|
|
if !hasPrecision {
|
|
|
|
|
prec = 6
|
|
|
|
|
}
|
|
|
|
|
f := fmt.Sprintf("%s.%d%c", spec, prec, c)
|
|
|
|
|
str = fmt.Sprintf(f, arg)
|
|
|
|
|
case 'G':
|
|
|
|
|
fallthrough
|
|
|
|
|
case 'g':
|
|
|
|
|
// The double argument is converted in style f or e (or F or E for G
|
|
|
|
|
// conversions). The precision specifies the number of significant digits. If
|
|
|
|
|
// the precision is missing, 6 digits are given; if the precision is zero, it
|
|
|
|
|
// is treated as 1. Style e is used if the exponent from its conversion is
|
|
|
|
|
// less than -4 or greater than or equal to the precision. Trailing zeros are
|
|
|
|
|
// removed from the fractional part of the result; a decimal point appears only
|
|
|
|
|
// if it is followed by at least one digit.
|
|
|
|
|
format++
|
|
|
|
|
arg := VaFloat64(args)
|
|
|
|
|
if !hasPrecision {
|
|
|
|
|
prec = 6
|
|
|
|
|
}
|
|
|
|
|
if prec == 0 {
|
|
|
|
|
prec = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f := fmt.Sprintf("%s.%d%c", spec, prec, c)
|
|
|
|
|
str = fmt.Sprintf(f, arg)
|
|
|
|
|
case 's':
|
|
|
|
|
// If no l modifier is present: the const char * argument is expected to be a
|
|
|
|
|
// pointer to an array of character type (pointer to a string). Characters
|
|
|
|
|
// from the array are written up to (but not including) a terminating null byte
|
|
|
|
|
// ('\0'); if a precision is specified, no more than the number specified are
|
|
|
|
|
// written. If a precision is given, no null byte need be present; if
|
|
|
|
|
// the precision is not specified, or is greater than the size of the array,
|
|
|
|
|
// the array must contain a terminating null byte.
|
|
|
|
|
//
|
|
|
|
|
// If an l modifier is present: the const wchar_t * argument is expected
|
|
|
|
|
// to be a pointer to an array of wide characters. Wide characters from the
|
|
|
|
|
// array are converted to multibyte characters (each by a call to the
|
|
|
|
|
// wcrtomb(3) function, with a conversion state starting in the initial state
|
|
|
|
|
// before the first wide character), up to and including a terminating null
|
|
|
|
|
// wide character. The resulting multibyte characters are written up to
|
|
|
|
|
// (but not including) the terminating null byte. If a precision is specified,
|
|
|
|
|
// no more bytes than the number specified are written, but no partial
|
|
|
|
|
// multibyte characters are written. Note that the precision determines the
|
|
|
|
|
// number of bytes written, not the number of wide characters or screen
|
|
|
|
|
// positions. The array must contain a terminating null wide character,
|
|
|
|
|
// unless a precision is given and it is so small that the number of bytes
|
|
|
|
|
// written exceeds it before the end of the array is reached.
|
|
|
|
|
format++
|
|
|
|
|
arg := VaUintptr(args)
|
|
|
|
|
switch mod {
|
|
|
|
|
case modNone:
|
|
|
|
|
var f string
|
|
|
|
|
switch {
|
|
|
|
|
case hasPrecision:
|
|
|
|
|
f = fmt.Sprintf("%s.%ds", spec, prec)
|
2021-11-27 17:26:58 +03:00
|
|
|
|
str = fmt.Sprintf(f, GoString(arg))
|
2021-08-29 17:41:41 +03:00
|
|
|
|
default:
|
|
|
|
|
f = spec + "s"
|
|
|
|
|
str = fmt.Sprintf(f, GoString(arg))
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
}
|
|
|
|
|
case 'p':
|
|
|
|
|
// The void * pointer argument is printed in hexadecimal (as if by %#x or
|
|
|
|
|
// %#lx).
|
|
|
|
|
format++
|
|
|
|
|
arg := VaUintptr(args)
|
|
|
|
|
buf.WriteString("0x")
|
|
|
|
|
buf.WriteString(strconv.FormatInt(int64(arg), 16))
|
|
|
|
|
case 'c':
|
|
|
|
|
// If no l modifier is present, the int argument is converted to an unsigned
|
|
|
|
|
// char, and the resulting character is written. If an l modifier is present,
|
|
|
|
|
// the wint_t (wide character) ar‐ gument is converted to a multibyte sequence
|
|
|
|
|
// by a call to the wcrtomb(3) function, with a conversion state starting in
|
|
|
|
|
// the initial state, and the resulting multibyte string is writ‐ ten.
|
|
|
|
|
format++
|
|
|
|
|
switch mod {
|
|
|
|
|
case modNone:
|
|
|
|
|
arg := VaInt32(args)
|
|
|
|
|
buf.WriteByte(byte(arg))
|
|
|
|
|
default:
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
}
|
|
|
|
|
case '%':
|
|
|
|
|
// A '%' is written. No argument is converted. The complete conversion
|
|
|
|
|
// specification is '%%'.
|
|
|
|
|
format++
|
|
|
|
|
buf.WriteByte('%')
|
|
|
|
|
default:
|
|
|
|
|
panic(todo("%#U", c))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf.WriteString(str)
|
|
|
|
|
return format
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Field width
|
|
|
|
|
//
|
|
|
|
|
// An optional decimal digit string (with nonzero first digit) specifying a
|
|
|
|
|
// minimum field width. If the converted value has fewer characters than the
|
|
|
|
|
// field width, it will be padded with spa‐ ces on the left (or right, if the
|
|
|
|
|
// left-adjustment flag has been given). Instead of a decimal digit string one
|
|
|
|
|
// may write "*" or "*m$" (for some decimal integer m) to specify that the
|
|
|
|
|
// field width is given in the next argument, or in the m-th argument,
|
|
|
|
|
// respectively, which must be of type int. A negative field width is taken as
|
|
|
|
|
// a '-' flag followed by a positive field width. In no case does a
|
|
|
|
|
// nonexistent or small field width cause truncation of a field; if the result
|
|
|
|
|
// of a conversion is wider than the field width, the field is expanded to
|
|
|
|
|
// contain the conversion result.
|
|
|
|
|
func parseFieldWidth(format uintptr) (_ uintptr, n int, ok bool) {
|
|
|
|
|
first := true
|
|
|
|
|
for {
|
|
|
|
|
var digit int
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); {
|
|
|
|
|
case first && c == '0':
|
|
|
|
|
return format, n, ok
|
|
|
|
|
case first && c == '*':
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
case c >= '0' && c <= '9':
|
|
|
|
|
format++
|
|
|
|
|
ok = true
|
|
|
|
|
first = false
|
|
|
|
|
digit = int(c) - '0'
|
|
|
|
|
default:
|
|
|
|
|
return format, n, ok
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n0 := n
|
|
|
|
|
n = 10*n + digit
|
|
|
|
|
if n < n0 {
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Precision
|
|
|
|
|
//
|
|
|
|
|
// An optional precision, in the form of a period ('.') followed by an
|
|
|
|
|
// optional decimal digit string. Instead of a decimal digit string one may
|
|
|
|
|
// write "*" or "*m$" (for some decimal integer m) to specify that the
|
|
|
|
|
// precision is given in the next argument, or in the m-th argument,
|
|
|
|
|
// respectively, which must be of type int. If the precision is given as just
|
|
|
|
|
// '.', the precision is taken to be zero. A negative precision is taken
|
|
|
|
|
// as if the precision were omitted. This gives the minimum number of digits
|
|
|
|
|
// to appear for d, i, o, u, x, and X conversions, the number of digits to
|
|
|
|
|
// appear after the radix character for a, A, e, E, f, and F conversions, the
|
|
|
|
|
// maximum number of significant digits for g and G conversions, or the maximum
|
|
|
|
|
// number of characters to be printed from a string for s and S conversions.
|
|
|
|
|
func parsePrecision(format uintptr, args *uintptr) (_ uintptr, n int, ok bool) {
|
|
|
|
|
for {
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case '.':
|
|
|
|
|
format++
|
|
|
|
|
first := true
|
|
|
|
|
for {
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); {
|
|
|
|
|
case first && c == '*':
|
|
|
|
|
format++
|
|
|
|
|
n = int(VaInt32(args))
|
|
|
|
|
return format, n, true
|
|
|
|
|
case c >= '0' && c <= '9':
|
|
|
|
|
format++
|
|
|
|
|
first = false
|
|
|
|
|
n0 := n
|
|
|
|
|
n = 10*n + (int(c) - '0')
|
|
|
|
|
if n < n0 {
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return format, n, true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return format, 0, false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Length modifier
|
|
|
|
|
//
|
|
|
|
|
// Here, "integer conversion" stands for d, i, o, u, x, or X conversion.
|
|
|
|
|
//
|
|
|
|
|
// hh A following integer conversion corresponds to a signed char or
|
|
|
|
|
// unsigned char argument, or a following n conversion corresponds to a pointer
|
|
|
|
|
// to a signed char argument.
|
|
|
|
|
//
|
|
|
|
|
// h A following integer conversion corresponds to a short int or unsigned
|
|
|
|
|
// short int argument, or a following n conversion corresponds to a pointer to
|
|
|
|
|
// a short int argument.
|
|
|
|
|
//
|
|
|
|
|
// l (ell) A following integer conversion corresponds to a long int or
|
|
|
|
|
// unsigned long int argument, or a following n conversion corresponds to a
|
|
|
|
|
// pointer to a long int argument, or a fol‐ lowing c conversion corresponds to
|
|
|
|
|
// a wint_t argument, or a following s conversion corresponds to a pointer to
|
|
|
|
|
// wchar_t argument.
|
|
|
|
|
//
|
|
|
|
|
// ll (ell-ell). A following integer conversion corresponds to a long long
|
|
|
|
|
// int or unsigned long long int argument, or a following n conversion
|
|
|
|
|
// corresponds to a pointer to a long long int argument.
|
|
|
|
|
//
|
|
|
|
|
// q A synonym for ll. This is a nonstandard extension, derived from BSD;
|
|
|
|
|
// avoid its use in new code.
|
|
|
|
|
//
|
|
|
|
|
// L A following a, A, e, E, f, F, g, or G conversion corresponds to a
|
|
|
|
|
// long double argument. (C99 allows %LF, but SUSv2 does not.)
|
|
|
|
|
//
|
|
|
|
|
// j A following integer conversion corresponds to an intmax_t or
|
|
|
|
|
// uintmax_t argument, or a following n conversion corresponds to a pointer to
|
|
|
|
|
// an intmax_t argument.
|
|
|
|
|
//
|
|
|
|
|
// z A following integer conversion corresponds to a size_t or ssize_t
|
|
|
|
|
// argument, or a following n conversion corresponds to a pointer to a size_t
|
|
|
|
|
// argument.
|
|
|
|
|
//
|
|
|
|
|
// Z A nonstandard synonym for z that predates the appearance of z. Do
|
|
|
|
|
// not use in new code.
|
|
|
|
|
//
|
|
|
|
|
// t A following integer conversion corresponds to a ptrdiff_t argument,
|
|
|
|
|
// or a following n conversion corresponds to a pointer to a ptrdiff_t
|
|
|
|
|
// argument.
|
|
|
|
|
|
|
|
|
|
func parseLengthModifier(format uintptr) (_ uintptr, n int) {
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case 'h':
|
|
|
|
|
format++
|
|
|
|
|
n = modH
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case 'h':
|
|
|
|
|
format++
|
|
|
|
|
n = modHH
|
|
|
|
|
}
|
|
|
|
|
return format, n
|
|
|
|
|
case 'l':
|
|
|
|
|
format++
|
|
|
|
|
n = modL
|
|
|
|
|
switch c := *(*byte)(unsafe.Pointer(format)); c {
|
|
|
|
|
case 'l':
|
|
|
|
|
format++
|
|
|
|
|
n = modLL
|
|
|
|
|
}
|
|
|
|
|
return format, n
|
|
|
|
|
case 'q':
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
case 'L':
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
case 'j':
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
case 'z':
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
case 'Z':
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
case 't':
|
|
|
|
|
panic(todo(""))
|
|
|
|
|
default:
|
|
|
|
|
return format, 0
|
|
|
|
|
}
|
|
|
|
|
}
|