summaryrefslogtreecommitdiffstats
path: root/vendor/modernc.org/libc/printf.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/modernc.org/libc/printf.go')
-rw-r--r--vendor/modernc.org/libc/printf.go592
1 files changed, 592 insertions, 0 deletions
diff --git a/vendor/modernc.org/libc/printf.go b/vendor/modernc.org/libc/printf.go
new file mode 100644
index 00000000..ce580daf
--- /dev/null
+++ b/vendor/modernc.org/libc/printf.go
@@ -0,0 +1,592 @@
+// 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 {
+ format0 := format
+ args0 := args
+ buf := bytes.NewBuffer(nil)
+ for {
+ switch c := *(*byte)(unsafe.Pointer(format)); c {
+ case '%':
+ format = printfConversion(buf, format, &args)
+ case 0:
+ if dmesgs {
+ dmesg("%v: %q, %#x -> %q", origin(1), GoString(format0), args0, buf.Bytes())
+ }
+ 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)
+ str = fmt.Sprintf(f, GoString(arg))
+ 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
+ }
+}