source: code/trunk/vendor/modernc.org/libc/printf.go@ 823

Last change on this file since 823 was 822, checked in by yakumo.izuru, 22 months ago

Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 19.1 KB
Line 
1// Copyright 2020 The Libc Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package libc // import "modernc.org/libc"
6
7import (
8 "bytes"
9 "fmt"
10 "runtime"
11 "strconv"
12 "strings"
13 "unsafe"
14)
15
16const (
17 modNone = iota
18 modHH
19 modH
20 modL
21 modLL
22 modLD
23 modQ
24 modCapitalL
25 modJ
26 modZ
27 modCapitalZ
28 modT
29 mod32
30 mod64
31)
32
33// Format of the format string
34//
35// The format string is a character string, beginning and ending in its initial
36// shift state, if any. The format string is composed of zero or more
37// directives: ordinary characters (not %), which are copied unchanged to
38// the output stream; and conversion specifications, each of which results in
39// fetching zero or more subsequent arguments.
40func printf(format, args uintptr) []byte {
41 format0 := format
42 args0 := args
43 buf := bytes.NewBuffer(nil)
44 for {
45 switch c := *(*byte)(unsafe.Pointer(format)); c {
46 case '%':
47 format = printfConversion(buf, format, &args)
48 case 0:
49 if dmesgs {
50 dmesg("%v: %q, %#x -> %q", origin(1), GoString(format0), args0, buf.Bytes())
51 }
52 return buf.Bytes()
53 default:
54 format++
55 buf.WriteByte(c)
56 }
57 }
58}
59
60// Each conversion specification is introduced by the character %, and ends
61// with a conversion specifier. In between there may be (in this order) zero
62// or more flags, an optional minimum field width, an optional precision and
63// an optional length modifier.
64func printfConversion(buf *bytes.Buffer, format uintptr, args *uintptr) uintptr {
65 format++ // '%'
66 spec := "%"
67
68 // Flags characters
69 //
70 // The character % is followed by zero or more of the following flags:
71flags:
72 for {
73 switch c := *(*byte)(unsafe.Pointer(format)); c {
74 case '#':
75 // The value should be converted to an "alternate form". For o conversions,
76 // the first character of the output string is made zero (by prefixing a 0 if
77 // it was not zero already). For x and X conversions, a nonzero result has
78 // the string "0x" (or "0X" for X conversions) prepended to it. For a, A, e,
79 // E, f, F, g, and G conversions, the result will always contain a decimal
80 // point, even if no digits follow it (normally, a decimal point appears in the
81 // results of those conversions only if a digit follows). For g and G
82 // conversions, trailing zeros are not removed from the result as they would
83 // otherwise be. For other conversions, the result is undefined.
84 format++
85 spec += "#"
86 case '0':
87 // The value should be zero padded. For d, i, o, u, x, X, a, A, e, E, f, F,
88 // g, and G conversions, the converted value is padded on the left with zeros
89 // rather than blanks. If the 0 and - flags both appear, the 0 flag is
90 // ignored. If a precision is given with a numeric conversion (d, i, o, u, x,
91 // and X), the 0 flag is ignored. For other conversions, the behav‐ ior is
92 // undefined.
93 format++
94 spec += "0"
95 case '-':
96 // The converted value is to be left adjusted on the field boundary. (The
97 // default is right justification.) The converted value is padded on the right
98 // with blanks, rather than on the left with blanks or zeros. A - overrides a
99 // 0 if both are given.
100 format++
101 spec += "-"
102 case ' ':
103 // A blank should be left before a positive number (or empty string) produced
104 // by a signed conversion.
105 format++
106 spec += " "
107 case '+':
108 // A sign (+ or -) should always be placed before a number produced by a signed
109 // conversion. By default, a sign is used only for negative numbers. A +
110 // overrides a space if both are used.
111 format++
112 spec += "+"
113 default:
114 break flags
115 }
116 }
117 format, width, hasWidth := parseFieldWidth(format)
118 if hasWidth {
119 spec += strconv.Itoa(width)
120 }
121 format, prec, hasPrecision := parsePrecision(format, args)
122 format, mod := parseLengthModifier(format)
123
124 var str string
125
126more:
127 // Conversion specifiers
128 //
129 // A character that specifies the type of conversion to be applied. The
130 // conversion specifiers and their meanings are:
131 switch c := *(*byte)(unsafe.Pointer(format)); c {
132 case 'd', 'i':
133 // The int argument is converted to signed decimal notation. The precision,
134 // if any, gives the minimum number of digits that must appear; if the
135 // converted value requires fewer digits, it is padded on the left with zeros.
136 // The default precision is 1. When 0 is printed with an explicit precision 0,
137 // the output is empty.
138 format++
139 var arg int64
140 if isWindows && mod == modL {
141 mod = modNone
142 }
143 switch mod {
144 case modL, modLL, mod64:
145 arg = VaInt64(args)
146 case modH:
147 arg = int64(int16(VaInt32(args)))
148 case modHH:
149 arg = int64(int8(VaInt32(args)))
150 case mod32, modNone:
151 arg = int64(VaInt32(args))
152 default:
153 panic(todo("", mod))
154 }
155
156 if arg == 0 && hasPrecision && prec == 0 {
157 break
158 }
159
160 if hasPrecision {
161 panic(todo("", prec))
162 }
163
164 f := spec + "d"
165 str = fmt.Sprintf(f, arg)
166 case 'u':
167 // The unsigned int argument is converted to unsigned decimal notation. The
168 // precision, if any, gives the minimum number of digits that must appear; if
169 // the converted value requires fewer digits, it is padded on the left with
170 // zeros. The default precision is 1. When 0 is printed with an explicit
171 // precision 0, the output is empty.
172 format++
173 var arg uint64
174 if isWindows && mod == modL {
175 mod = modNone
176 }
177 switch mod {
178 case modNone:
179 arg = uint64(VaUint32(args))
180 case modL, modLL, mod64:
181 arg = VaUint64(args)
182 case modH:
183 arg = uint64(uint16(VaInt32(args)))
184 case modHH:
185 arg = uint64(uint8(VaInt32(args)))
186 case mod32:
187 arg = uint64(VaInt32(args))
188 default:
189 panic(todo("", mod))
190 }
191
192 if arg == 0 && hasPrecision && prec == 0 {
193 break
194 }
195
196 if hasPrecision {
197 panic(todo("", prec))
198 }
199
200 f := spec + "d"
201 str = fmt.Sprintf(f, arg)
202 case 'o':
203 // The unsigned int argument is converted to unsigned octal notation. The
204 // precision, if any, gives the minimum number of digits that must appear; if
205 // the converted value requires fewer digits, it is padded on the left with
206 // zeros. The default precision is 1. When 0 is printed with an explicit
207 // precision 0, the output is empty.
208 format++
209 var arg uint64
210 if isWindows && mod == modL {
211 mod = modNone
212 }
213 switch mod {
214 case modNone:
215 arg = uint64(VaUint32(args))
216 case modL, modLL, mod64:
217 arg = VaUint64(args)
218 case modH:
219 arg = uint64(uint16(VaInt32(args)))
220 case modHH:
221 arg = uint64(uint8(VaInt32(args)))
222 case mod32:
223 arg = uint64(VaInt32(args))
224 default:
225 panic(todo("", mod))
226 }
227
228 if arg == 0 && hasPrecision && prec == 0 {
229 break
230 }
231
232 if hasPrecision {
233 panic(todo("", prec))
234 }
235
236 f := spec + "o"
237 str = fmt.Sprintf(f, arg)
238 case 'I':
239 if !isWindows {
240 panic(todo("%#U", c))
241 }
242
243 format++
244 switch c = *(*byte)(unsafe.Pointer(format)); c {
245 case 'x', 'X':
246 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-wsprintfa
247 //
248 // Ix, IX
249 //
250 // 64-bit unsigned hexadecimal integer in lowercase or uppercase on 64-bit
251 // platforms, 32-bit unsigned hexadecimal integer in lowercase or uppercase on
252 // 32-bit platforms.
253 if unsafe.Sizeof(int(0)) == 4 {
254 mod = mod32
255 }
256 case '3':
257 // https://en.wikipedia.org/wiki/Printf_format_string#Length_field
258 //
259 // I32 For integer types, causes printf to expect a 32-bit (double word) integer argument.
260 format++
261 switch c = *(*byte)(unsafe.Pointer(format)); c {
262 case '2':
263 format++
264 mod = mod32
265 goto more
266 default:
267 panic(todo("%#U", c))
268 }
269 case '6':
270 // https://en.wikipedia.org/wiki/Printf_format_string#Length_field
271 //
272 // I64 For integer types, causes printf to expect a 64-bit (quad word) integer argument.
273 format++
274 switch c = *(*byte)(unsafe.Pointer(format)); c {
275 case '4':
276 format++
277 mod = mod64
278 goto more
279 default:
280 panic(todo("%#U", c))
281 }
282 default:
283 panic(todo("%#U", c))
284 }
285 fallthrough
286 case 'X':
287 fallthrough
288 case 'x':
289 // The unsigned int argument is converted to unsigned hexadecimal notation.
290 // The letters abcdef are used for x conversions; the letters ABCDEF are used
291 // for X conversions. The precision, if any, gives the minimum number of
292 // digits that must appear; if the converted value requires fewer digits, it is
293 // padded on the left with zeros. The default precision is 1. When 0 is
294 // printed with an explicit precision 0, the output is empty.
295 format++
296 var arg uint64
297 if isWindows && mod == modL {
298 mod = modNone
299 }
300 switch mod {
301 case modNone:
302 arg = uint64(VaUint32(args))
303 case modL, modLL, mod64:
304 arg = VaUint64(args)
305 case modH:
306 arg = uint64(uint16(VaInt32(args)))
307 case modHH:
308 arg = uint64(uint8(VaInt32(args)))
309 case mod32:
310 arg = uint64(VaInt32(args))
311 default:
312 panic(todo("", mod))
313 }
314
315 if arg == 0 && hasPrecision && prec == 0 {
316 break
317 }
318
319 if strings.Contains(spec, "#") && arg == 0 {
320 spec = strings.ReplaceAll(spec, "#", "")
321 }
322 var f string
323 switch {
324 case hasPrecision:
325 f = fmt.Sprintf("%s.%d%c", spec, prec, c)
326 default:
327 f = spec + string(c)
328 }
329 str = fmt.Sprintf(f, arg)
330 case 'e', 'E':
331 // The double argument is rounded and converted in the style [-]d.ddde±dd where
332 // there is one digit before the decimal-point character and the number of
333 // digits after it is equal to the precision; if the precision is missing, it
334 // is taken as 6; if the precision is zero, no decimal-point character appears.
335 // An E conversion uses the letter E (rather than e) to intro‐ duce the
336 // exponent. The exponent always contains at least two digits; if the value is
337 // zero, the exponent is 00.
338 format++
339 arg := VaFloat64(args)
340 if !hasPrecision {
341 prec = 6
342 }
343 f := fmt.Sprintf("%s.%d%c", spec, prec, c)
344 str = fmt.Sprintf(f, arg)
345 case 'f', 'F':
346 // The double argument is rounded and converted to decimal notation in the
347 // style [-]ddd.ddd, where the number of digits after the decimal-point
348 // character is equal to the precision specification. If the precision
349 // is missing, it is taken as 6; if the precision is explicitly zero, no
350 // decimal-point character appears. If a decimal point appears, at least one
351 // digit appears before it.
352 format++
353 arg := VaFloat64(args)
354 if !hasPrecision {
355 prec = 6
356 }
357 f := fmt.Sprintf("%s.%d%c", spec, prec, c)
358 str = fixNanInf(fmt.Sprintf(f, arg))
359 case 'G':
360 fallthrough
361 case 'g':
362 // The double argument is converted in style f or e (or F or E for G
363 // conversions). The precision specifies the number of significant digits. If
364 // the precision is missing, 6 digits are given; if the precision is zero, it
365 // is treated as 1. Style e is used if the exponent from its conversion is
366 // less than -4 or greater than or equal to the precision. Trailing zeros are
367 // removed from the fractional part of the result; a decimal point appears only
368 // if it is followed by at least one digit.
369 format++
370 arg := VaFloat64(args)
371 if !hasPrecision {
372 prec = 6
373 }
374 if prec == 0 {
375 prec = 1
376 }
377
378 f := fmt.Sprintf("%s.%d%c", spec, prec, c)
379 str = fixNanInf(fmt.Sprintf(f, arg))
380 case 's':
381 // If no l modifier is present: the const char * argument is expected to be a
382 // pointer to an array of character type (pointer to a string). Characters
383 // from the array are written up to (but not including) a terminating null byte
384 // ('\0'); if a precision is specified, no more than the number specified are
385 // written. If a precision is given, no null byte need be present; if
386 // the precision is not specified, or is greater than the size of the array,
387 // the array must contain a terminating null byte.
388 //
389 // If an l modifier is present: the const wchar_t * argument is expected
390 // to be a pointer to an array of wide characters. Wide characters from the
391 // array are converted to multibyte characters (each by a call to the
392 // wcrtomb(3) function, with a conversion state starting in the initial state
393 // before the first wide character), up to and including a terminating null
394 // wide character. The resulting multibyte characters are written up to
395 // (but not including) the terminating null byte. If a precision is specified,
396 // no more bytes than the number specified are written, but no partial
397 // multibyte characters are written. Note that the precision determines the
398 // number of bytes written, not the number of wide characters or screen
399 // positions. The array must contain a terminating null wide character,
400 // unless a precision is given and it is so small that the number of bytes
401 // written exceeds it before the end of the array is reached.
402 format++
403 arg := VaUintptr(args)
404 switch mod {
405 case modNone:
406 var f string
407 switch {
408 case hasPrecision:
409 f = fmt.Sprintf("%s.%ds", spec, prec)
410 str = fmt.Sprintf(f, GoString(arg))
411 default:
412 f = spec + "s"
413 str = fmt.Sprintf(f, GoString(arg))
414 }
415 default:
416 panic(todo(""))
417 }
418 case 'p':
419 // The void * pointer argument is printed in hexadecimal (as if by %#x or
420 // %#lx).
421 format++
422 switch runtime.GOOS {
423 case "windows":
424 switch runtime.GOARCH {
425 case "386", "arm":
426 fmt.Fprintf(buf, "%08X", VaUintptr(args))
427 default:
428 fmt.Fprintf(buf, "%016X", VaUintptr(args))
429 }
430 default:
431 fmt.Fprintf(buf, "%#0x", VaUintptr(args))
432 }
433 case 'c':
434 // If no l modifier is present, the int argument is converted to an unsigned
435 // char, and the resulting character is written. If an l modifier is present,
436 // the wint_t (wide character) ar‐ gument is converted to a multibyte sequence
437 // by a call to the wcrtomb(3) function, with a conversion state starting in
438 // the initial state, and the resulting multibyte string is writ‐ ten.
439 format++
440 switch mod {
441 case modNone:
442 arg := VaInt32(args)
443 buf.WriteByte(byte(arg))
444 default:
445 panic(todo(""))
446 }
447 case '%':
448 // A '%' is written. No argument is converted. The complete conversion
449 // specification is '%%'.
450 format++
451 buf.WriteByte('%')
452 default:
453 panic(todo("%#U", c))
454 }
455
456 buf.WriteString(str)
457 return format
458}
459
460// Field width
461//
462// An optional decimal digit string (with nonzero first digit) specifying a
463// minimum field width. If the converted value has fewer characters than the
464// field width, it will be padded with spa‐ ces on the left (or right, if the
465// left-adjustment flag has been given). Instead of a decimal digit string one
466// may write "*" or "*m$" (for some decimal integer m) to specify that the
467// field width is given in the next argument, or in the m-th argument,
468// respectively, which must be of type int. A negative field width is taken as
469// a '-' flag followed by a positive field width. In no case does a
470// nonexistent or small field width cause truncation of a field; if the result
471// of a conversion is wider than the field width, the field is expanded to
472// contain the conversion result.
473func parseFieldWidth(format uintptr) (_ uintptr, n int, ok bool) {
474 first := true
475 for {
476 var digit int
477 switch c := *(*byte)(unsafe.Pointer(format)); {
478 case first && c == '0':
479 return format, n, ok
480 case first && c == '*':
481 panic(todo(""))
482 case c >= '0' && c <= '9':
483 format++
484 ok = true
485 first = false
486 digit = int(c) - '0'
487 default:
488 return format, n, ok
489 }
490
491 n0 := n
492 n = 10*n + digit
493 if n < n0 {
494 panic(todo(""))
495 }
496 }
497}
498
499// Precision
500//
501// An optional precision, in the form of a period ('.') followed by an
502// optional decimal digit string. Instead of a decimal digit string one may
503// write "*" or "*m$" (for some decimal integer m) to specify that the
504// precision is given in the next argument, or in the m-th argument,
505// respectively, which must be of type int. If the precision is given as just
506// '.', the precision is taken to be zero. A negative precision is taken
507// as if the precision were omitted. This gives the minimum number of digits
508// to appear for d, i, o, u, x, and X conversions, the number of digits to
509// appear after the radix character for a, A, e, E, f, and F conversions, the
510// maximum number of significant digits for g and G conversions, or the maximum
511// number of characters to be printed from a string for s and S conversions.
512func parsePrecision(format uintptr, args *uintptr) (_ uintptr, n int, ok bool) {
513 for {
514 switch c := *(*byte)(unsafe.Pointer(format)); c {
515 case '.':
516 format++
517 first := true
518 for {
519 switch c := *(*byte)(unsafe.Pointer(format)); {
520 case first && c == '*':
521 format++
522 n = int(VaInt32(args))
523 return format, n, true
524 case c >= '0' && c <= '9':
525 format++
526 first = false
527 n0 := n
528 n = 10*n + (int(c) - '0')
529 if n < n0 {
530 panic(todo(""))
531 }
532 default:
533 return format, n, true
534 }
535 }
536 default:
537 return format, 0, false
538 }
539 }
540}
541
542// Length modifier
543//
544// Here, "integer conversion" stands for d, i, o, u, x, or X conversion.
545//
546// hh A following integer conversion corresponds to a signed char or
547// unsigned char argument, or a following n conversion corresponds to a pointer
548// to a signed char argument.
549//
550// h A following integer conversion corresponds to a short int or unsigned
551// short int argument, or a following n conversion corresponds to a pointer to
552// a short int argument.
553//
554// l (ell) A following integer conversion corresponds to a long int or
555// unsigned long int argument, or a following n conversion corresponds to a
556// pointer to a long int argument, or a fol‐ lowing c conversion corresponds to
557// a wint_t argument, or a following s conversion corresponds to a pointer to
558// wchar_t argument.
559//
560// ll (ell-ell). A following integer conversion corresponds to a long long
561// int or unsigned long long int argument, or a following n conversion
562// corresponds to a pointer to a long long int argument.
563//
564// q A synonym for ll. This is a nonstandard extension, derived from BSD;
565// avoid its use in new code.
566//
567// L A following a, A, e, E, f, F, g, or G conversion corresponds to a
568// long double argument. (C99 allows %LF, but SUSv2 does not.)
569//
570// j A following integer conversion corresponds to an intmax_t or
571// uintmax_t argument, or a following n conversion corresponds to a pointer to
572// an intmax_t argument.
573//
574// z A following integer conversion corresponds to a size_t or ssize_t
575// argument, or a following n conversion corresponds to a pointer to a size_t
576// argument.
577//
578// Z A nonstandard synonym for z that predates the appearance of z. Do
579// not use in new code.
580//
581// t A following integer conversion corresponds to a ptrdiff_t argument,
582// or a following n conversion corresponds to a pointer to a ptrdiff_t
583// argument.
584
585func parseLengthModifier(format uintptr) (_ uintptr, n int) {
586 switch c := *(*byte)(unsafe.Pointer(format)); c {
587 case 'h':
588 format++
589 n = modH
590 switch c := *(*byte)(unsafe.Pointer(format)); c {
591 case 'h':
592 format++
593 n = modHH
594 }
595 return format, n
596 case 'l':
597 format++
598 n = modL
599 switch c := *(*byte)(unsafe.Pointer(format)); c {
600 case 'l':
601 format++
602 n = modLL
603 }
604 return format, n
605 case 'q':
606 panic(todo(""))
607 case 'L':
608 format++
609 n = modLD
610 return format, n
611 case 'j':
612 panic(todo(""))
613 case 'z':
614 panic(todo(""))
615 case 'Z':
616 panic(todo(""))
617 case 't':
618 panic(todo(""))
619 default:
620 return format, 0
621 }
622}
623
624func fixNanInf(s string) string {
625 switch s {
626 case "NaN":
627 return "nan"
628 case "+Inf", "-Inf":
629 return "inf"
630 default:
631 return s
632 }
633}
Note: See TracBrowser for help on using the repository browser.