source: mainline/common/printf/printf_core.c

Last change on this file was 163e34c, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 months ago

Actually convert the printf outputs everywhere

  • Property mode set to 100644
File size: 40.2 KB
Line 
1/*
2 * Copyright (c) 2001-2004 Jakub Jermar
3 * Copyright (c) 2006 Josef Cejka
4 * Copyright (c) 2009 Martin Decky
5 * Copyright (c) 2025 Jiří Zárevúcky
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * - The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/** @addtogroup libc
33 * @{
34 */
35/**
36 * @file
37 * @brief Printing functions.
38 */
39
40#include <_bits/uchar.h>
41#include <_bits/wint_t.h>
42#include <assert.h>
43#include <ctype.h>
44#include <errno.h>
45#include <limits.h>
46#include <macros.h>
47#include <printf_core.h>
48#include <stddef.h>
49#include <stdint.h>
50#include <stdlib.h>
51#include <str.h>
52
53/* Disable float support in kernel, because we usually disable floating operations there. */
54#if __STDC_HOSTED__
55#define HAS_FLOAT
56#endif
57
58#ifdef HAS_FLOAT
59#include <double_to_str.h>
60#include <ieee_double.h>
61#endif
62
63/** show prefixes 0x or 0 */
64#define __PRINTF_FLAG_PREFIX 0x00000001
65
66/** show the decimal point even if no fractional digits present */
67#define __PRINTF_FLAG_DECIMALPT 0x00000001
68
69/** signed / unsigned number */
70#define __PRINTF_FLAG_SIGNED 0x00000002
71
72/** print leading zeroes */
73#define __PRINTF_FLAG_ZEROPADDED 0x00000004
74
75/** align to left */
76#define __PRINTF_FLAG_LEFTALIGNED 0x00000010
77
78/** always show + sign */
79#define __PRINTF_FLAG_SHOWPLUS 0x00000020
80
81/** print space instead of plus */
82#define __PRINTF_FLAG_SPACESIGN 0x00000040
83
84/** show big characters */
85#define __PRINTF_FLAG_BIGCHARS 0x00000080
86
87/** number has - sign */
88#define __PRINTF_FLAG_NEGATIVE 0x00000100
89
90/** don't print trailing zeros in the fractional part */
91#define __PRINTF_FLAG_NOFRACZEROS 0x00000200
92
93/**
94 * Buffer big enough for 64-bit number printed in base 2.
95 */
96#define PRINT_NUMBER_BUFFER_SIZE 64
97
98/** Get signed or unsigned integer argument */
99#define PRINTF_GET_INT_ARGUMENT(type, ap, flags) \
100 ({ \
101 unsigned type res; \
102 \
103 if ((flags) & __PRINTF_FLAG_SIGNED) { \
104 signed type arg = va_arg((ap), signed type); \
105 \
106 if (arg < 0) { \
107 res = -arg; \
108 (flags) |= __PRINTF_FLAG_NEGATIVE; \
109 } else \
110 res = arg; \
111 } else \
112 res = va_arg((ap), unsigned type); \
113 \
114 res; \
115 })
116
117/** Enumeration of possible arguments types.
118 */
119typedef enum {
120 PrintfQualifierByte = 0,
121 PrintfQualifierShort,
122 PrintfQualifierInt,
123 PrintfQualifierLong,
124 PrintfQualifierLongLong,
125 PrintfQualifierPointer,
126} qualifier_t;
127
128static const char _digits_small[] = "0123456789abcdef";
129static const char _digits_big[] = "0123456789ABCDEF";
130
131static const char _nullstr[] = "(NULL)";
132static const char _replacement[] = u8"�";
133static const char _spaces[] = " ";
134static const char _zeros[] = "000000000000000000000000000000000000000000000000";
135
136static void _set_errno(errno_t rc)
137{
138 #ifdef errno
139 errno = rc;
140 #endif
141}
142
143static size_t _utf8_bytes(char32_t c)
144{
145 if (c < 0x80)
146 return 1;
147
148 if (c < 0x800)
149 return 2;
150
151 if (c < 0xD800)
152 return 3;
153
154 /* Surrogate code points, invalid in UTF-32. */
155 if (c < 0xE000)
156 return sizeof(_replacement) - 1;
157
158 if (c < 0x10000)
159 return 3;
160
161 if (c < 0x110000)
162 return 4;
163
164 /* Invalid character. */
165 return sizeof(_replacement) - 1;
166}
167
168/** Counts characters and utf8 bytes in a wide string up to a byte limit.
169 * @param max_bytes Byte length limit for string's utf8 conversion.
170 * @param[out] len The number of wide characters
171 * @return Number of utf8 bytes that the first *len characters in the string
172 * will convert to. Will always be less than max_bytes.
173 */
174static size_t _utf8_wstr_bytes_len(char32_t *s, size_t max_bytes, size_t *len)
175{
176 size_t bytes = 0;
177 size_t i;
178
179 for (i = 0; bytes < max_bytes && s[i]; i++) {
180 size_t next = _utf8_bytes(s[i]);
181 if (max_bytes - bytes < next)
182 break;
183
184 bytes += next;
185 }
186
187 *len = i;
188 return bytes;
189}
190
191#define TRY(expr) ({ errno_t rc = (expr); if (rc != EOK) return rc; })
192
193static inline void _saturating_add(size_t *a, size_t b)
194{
195 size_t s = *a + b;
196 /* Only works because size_t is unsigned. */
197 *a = (s < b) ? SIZE_MAX : s;
198}
199
200static errno_t _write_bytes(const char *buf, size_t n, printf_spec_t *ps,
201 size_t *written_bytes)
202{
203 errno_t rc = ps->write(buf, n, ps->data);
204 if (rc != EOK)
205 return rc;
206
207 _saturating_add(written_bytes, n);
208 return EOK;
209}
210
211/** Write one UTF-32 character. */
212static errno_t _write_uchar(char32_t ch, printf_spec_t *ps,
213 size_t *written_bytes)
214{
215 char utf8[4];
216 size_t offset = 0;
217
218 if (chr_encode(ch, utf8, &offset, sizeof(utf8)) == EOK)
219 return _write_bytes(utf8, offset, ps, written_bytes);
220
221 /* Invalid character. */
222 return _write_bytes(_replacement, sizeof(_replacement) - 1, ps, written_bytes);
223}
224
225/** Write n UTF-32 characters. */
226static errno_t _write_chars(const char32_t *buf, size_t n, printf_spec_t *ps,
227 size_t *written_bytes)
228{
229 for (size_t i = 0; i < n; i++)
230 TRY(_write_uchar(buf[i], ps, written_bytes));
231
232 return EOK;
233}
234
235static errno_t _write_char(char c, printf_spec_t *ps, size_t *written_bytes)
236{
237 return _write_bytes(&c, 1, ps, written_bytes);
238}
239
240static errno_t _write_spaces(size_t n, printf_spec_t *ps, size_t *written_bytes)
241{
242 size_t max_spaces = sizeof(_spaces) - 1;
243
244 while (n > max_spaces) {
245 TRY(_write_bytes(_spaces, max_spaces, ps, written_bytes));
246 n -= max_spaces;
247 }
248
249 return _write_bytes(_spaces, n, ps, written_bytes);
250}
251
252static errno_t _write_zeros(size_t n, printf_spec_t *ps, size_t *written_bytes)
253{
254 size_t max_zeros = sizeof(_zeros) - 1;
255
256 while (n > max_zeros) {
257 TRY(_write_bytes(_zeros, max_zeros, ps, written_bytes));
258 n -= max_zeros;
259 }
260
261 return _write_bytes(_zeros, n, ps, written_bytes);
262}
263
264/** Print one formatted ASCII character.
265 *
266 * @param ch Character to print.
267 * @param width Width modifier.
268 * @param flags Flags that change the way the character is printed.
269 */
270static errno_t _format_char(const char c, size_t width, uint32_t flags,
271 printf_spec_t *ps, size_t *written_bytes)
272{
273 size_t bytes = 1;
274
275 if (width <= bytes)
276 return _write_char(c, ps, written_bytes);
277
278 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
279 TRY(_write_char(c, ps, written_bytes));
280 TRY(_write_spaces(width - bytes, ps, written_bytes));
281 } else {
282 TRY(_write_spaces(width - bytes, ps, written_bytes));
283 TRY(_write_char(c, ps, written_bytes));
284 }
285
286 return EOK;
287}
288
289/** Print one formatted wide character.
290 *
291 * @param ch Character to print.
292 * @param width Width modifier.
293 * @param flags Flags that change the way the character is printed.
294 */
295static errno_t _format_uchar(const char32_t ch, size_t width, uint32_t flags,
296 printf_spec_t *ps, size_t *written_bytes)
297{
298 /*
299 * All widths in printf() are specified in bytes. It might seem nonsensical
300 * with unicode text, but that's the way the function is defined. The width
301 * is barely useful if you want column alignment in terminal, but keep in
302 * mind that counting code points is only marginally better for that.
303 * Characters can span more than one unicode code point, even in languages
304 * based on latin alphabet, and a single unicode code point can occupy two
305 * spaces in east asian scripts.
306 *
307 * What the width can actually be useful for is padding, when you need the
308 * output to fill an exact number of bytes in a file. That use would break
309 * if we did our own thing here.
310 */
311
312 size_t bytes = _utf8_bytes(ch);
313
314 if (width <= bytes)
315 return _write_uchar(ch, ps, written_bytes);
316
317 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
318 TRY(_write_uchar(ch, ps, written_bytes));
319 TRY(_write_spaces(width - bytes, ps, written_bytes));
320 } else {
321 TRY(_write_spaces(width - bytes, ps, written_bytes));
322 TRY(_write_uchar(ch, ps, written_bytes));
323 }
324
325 return EOK;
326}
327
328/** Print string.
329 *
330 * @param str String to be printed.
331 * @param width Width modifier.
332 * @param precision Precision modifier.
333 * @param flags Flags that modify the way the string is printed.
334 */
335static errno_t _format_cstr(const char *str, size_t width, int precision,
336 uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
337{
338 if (str == NULL)
339 str = _nullstr;
340
341 /* Negative precision == unspecified. */
342 size_t max_bytes = (precision < 0) ? SIZE_MAX : (size_t) precision;
343 size_t bytes = str_nsize(str, max_bytes);
344
345 if (width <= bytes)
346 return _write_bytes(str, bytes, ps, written_bytes);
347
348 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
349 TRY(_write_bytes(str, bytes, ps, written_bytes));
350 TRY(_write_spaces(width - bytes, ps, written_bytes));
351 } else {
352 TRY(_write_spaces(width - bytes, ps, written_bytes));
353 TRY(_write_bytes(str, bytes, ps, written_bytes));
354 }
355
356 return EOK;
357}
358
359/** Print wide string.
360 *
361 * @param str Wide string to be printed.
362 * @param width Width modifier.
363 * @param precision Precision modifier.
364 * @param flags Flags that modify the way the string is printed.
365 */
366static errno_t _format_wstr(char32_t *str, size_t width, int precision,
367 uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
368{
369 if (!str)
370 return _format_cstr(_nullstr, width, precision, flags, ps, written_bytes);
371
372 /* Width and precision are always byte-based. See _format_uchar() */
373 /* Negative precision == unspecified. */
374 size_t max_bytes = (precision < 0) ? SIZE_MAX : (size_t) precision;
375
376 size_t len;
377 size_t bytes = _utf8_wstr_bytes_len(str, max_bytes, &len);
378
379 if (width <= bytes)
380 return _write_chars(str, len, ps, written_bytes);
381
382 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
383 TRY(_write_chars(str, len, ps, written_bytes));
384 TRY(_write_spaces(width - bytes, ps, written_bytes));
385 } else {
386 TRY(_write_spaces(width - bytes, ps, written_bytes));
387 TRY(_write_chars(str, len, ps, written_bytes));
388 }
389
390 return EOK;
391}
392
393static char _sign(uint32_t flags)
394{
395 if (!(flags & __PRINTF_FLAG_SIGNED))
396 return 0;
397
398 if (flags & __PRINTF_FLAG_NEGATIVE)
399 return '-';
400
401 if (flags & __PRINTF_FLAG_SHOWPLUS)
402 return '+';
403
404 if (flags & __PRINTF_FLAG_SPACESIGN)
405 return ' ';
406
407 return 0;
408}
409
410/** Print a number in a given base.
411 *
412 * Print significant digits of a number in given base.
413 *
414 * @param num Number to print.
415 * @param width Width modifier.
416 * @param precision Precision modifier.
417 * @param base Base to print the number in (must be between 2 and 16).
418 * @param flags Flags that modify the way the number is printed.
419 */
420static errno_t _format_number(uint64_t num, size_t width, int precision, int base,
421 uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
422{
423 assert(base >= 2 && base <= 16);
424
425 /* Default precision for numeric output is 1. */
426 size_t min_digits = (precision < 0) ? 1 : precision;
427
428 bool bigchars = flags & __PRINTF_FLAG_BIGCHARS;
429 bool prefix = flags & __PRINTF_FLAG_PREFIX;
430 bool left_aligned = flags & __PRINTF_FLAG_LEFTALIGNED;
431 bool zero_padded = flags & __PRINTF_FLAG_ZEROPADDED;
432
433 const char *digits = bigchars ? _digits_big : _digits_small;
434
435 char buffer[PRINT_NUMBER_BUFFER_SIZE];
436 char *end = &buffer[PRINT_NUMBER_BUFFER_SIZE];
437
438 /* Write number to the buffer. */
439 int offset = 0;
440 while (num > 0) {
441 end[--offset] = digits[num % base];
442 num /= base;
443 }
444
445 char *number = &end[offset];
446 size_t number_len = end - number;
447 char sign = _sign(flags);
448
449 if (left_aligned) {
450 /* Space padded right-aligned. */
451 size_t real_size = max(number_len, min_digits);
452
453 if (sign) {
454 TRY(_write_char(sign, ps, written_bytes));
455 real_size++;
456 }
457
458 if (prefix && base == 2 && number_len > 0) {
459 TRY(_write_bytes(bigchars ? "0B" : "0b", 2, ps, written_bytes));
460 real_size += 2;
461 }
462
463 if (prefix && base == 16 && number_len > 0) {
464 TRY(_write_bytes(bigchars ? "0X" : "0x", 2, ps, written_bytes));
465 real_size += 2;
466 }
467
468 if (min_digits > number_len) {
469 TRY(_write_zeros(min_digits - number_len, ps, written_bytes));
470 } else if (prefix && base == 8) {
471 TRY(_write_zeros(1, ps, written_bytes));
472 real_size++;
473 }
474
475 TRY(_write_bytes(number, number_len, ps, written_bytes));
476
477 if (width > real_size)
478 TRY(_write_spaces(width - real_size, ps, written_bytes));
479
480 return EOK;
481 }
482
483 /* Zero padded number (ignored when left aligned or if precision is specified). */
484 if (precision < 0 && zero_padded) {
485 size_t real_size = number_len;
486
487 if (sign) {
488 TRY(_write_char(sign, ps, written_bytes));
489 real_size++;
490 }
491
492 if (prefix && base == 2 && number_len > 0) {
493 TRY(_write_bytes(bigchars ? "0B" : "0b", 2, ps, written_bytes));
494 real_size += 2;
495 }
496
497 if (prefix && base == 16 && number_len > 0) {
498 TRY(_write_bytes(bigchars ? "0X" : "0x", 2, ps, written_bytes));
499 real_size += 2;
500 }
501
502 if (width > real_size)
503 TRY(_write_zeros(width - real_size, ps, written_bytes));
504 else if (number_len == 0 || (prefix && base == 8))
505 TRY(_write_char('0', ps, written_bytes));
506
507 return _write_bytes(number, number_len, ps, written_bytes);
508 }
509
510 /* Space padded right-aligned. */
511 size_t real_size = max(number_len, min_digits);
512 if (sign)
513 real_size++;
514
515 if (prefix && (base == 2 || base == 16) && number_len > 0)
516 real_size += 2;
517
518 if (prefix && base == 8 && number_len >= min_digits)
519 real_size += 1;
520
521 if (width > real_size)
522 TRY(_write_spaces(width - real_size, ps, written_bytes));
523
524 if (sign)
525 TRY(_write_char(sign, ps, written_bytes));
526
527 if (prefix && base == 2 && number_len > 0)
528 TRY(_write_bytes(bigchars ? "0B" : "0b", 2, ps, written_bytes));
529
530 if (prefix && base == 16 && number_len > 0)
531 TRY(_write_bytes(bigchars ? "0X" : "0x", 2, ps, written_bytes));
532
533 if (min_digits > number_len)
534 TRY(_write_zeros(min_digits - number_len, ps, written_bytes));
535 else if (prefix && base == 8)
536 TRY(_write_char('0', ps, written_bytes));
537
538 return _write_bytes(number, number_len, ps, written_bytes);
539}
540
541#ifdef HAS_FLOAT
542
543/** Unformatted double number string representation. */
544typedef struct {
545 /** Buffer with len digits, no sign or leading zeros. */
546 char *str;
547 /** Number of digits in str. */
548 int len;
549 /** Decimal exponent, ie number = str * 10^dec_exp */
550 int dec_exp;
551 /** True if negative. */
552 bool neg;
553} double_str_t;
554
555/** Returns the sign character or 0 if no sign should be printed. */
556static char _get_sign_char(bool negative, uint32_t flags)
557{
558 if (negative) {
559 return '-';
560 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
561 return '+';
562 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
563 return ' ';
564 } else {
565 return 0;
566 }
567}
568
569/** Prints a special double (ie NaN, infinity) padded to width characters. */
570static errno_t _format_special(ieee_double_t val, int width, uint32_t flags,
571 printf_spec_t *ps, size_t *written_bytes)
572{
573 assert(val.is_special);
574
575 char sign = _get_sign_char(val.is_negative, flags);
576
577 const int str_len = 3;
578 const char *str;
579
580 if (flags & __PRINTF_FLAG_BIGCHARS) {
581 str = val.is_infinity ? "INF" : "NAN";
582 } else {
583 str = val.is_infinity ? "inf" : "nan";
584 }
585
586 int padding_len = max(0, width - ((sign ? 1 : 0) + str_len));
587
588 /* Leading padding. */
589 if (!(flags & __PRINTF_FLAG_LEFTALIGNED))
590 TRY(_write_spaces(padding_len, ps, written_bytes));
591
592 if (sign)
593 TRY(_write_char(sign, ps, written_bytes));
594
595 TRY(_write_bytes(str, str_len, ps, written_bytes));
596
597 /* Trailing padding. */
598 if (flags & __PRINTF_FLAG_LEFTALIGNED)
599 TRY(_write_spaces(padding_len, ps, written_bytes));
600
601 return EOK;
602}
603
604/** Trims trailing zeros but leaves a single "0" intact. */
605static void _fp_trim_trailing_zeros(char *buf, int *len, int *dec_exp)
606{
607 /* Cut the zero off by adjusting the exponent. */
608 while (2 <= *len && '0' == buf[*len - 1]) {
609 --*len;
610 ++*dec_exp;
611 }
612}
613
614/** Textually round up the last digit thereby eliminating it. */
615static void _fp_round_up(char *buf, int *len, int *dec_exp)
616{
617 assert(1 <= *len);
618
619 char *last_digit = &buf[*len - 1];
620
621 int carry = ('5' <= *last_digit);
622
623 /* Cut the digit off by adjusting the exponent. */
624 --*len;
625 ++*dec_exp;
626 --last_digit;
627
628 if (carry) {
629 /* Skip all the digits to cut off/round to zero. */
630 while (buf <= last_digit && '9' == *last_digit) {
631 --last_digit;
632 }
633
634 /* last_digit points to the last digit to round but not '9' */
635 if (buf <= last_digit) {
636 *last_digit += 1;
637 int new_len = last_digit - buf + 1;
638 *dec_exp += *len - new_len;
639 *len = new_len;
640 } else {
641 /* All len digits rounded to 0. */
642 buf[0] = '1';
643 *dec_exp += *len;
644 *len = 1;
645 }
646 } else {
647 /* The only digit was rounded to 0. */
648 if (last_digit < buf) {
649 buf[0] = '0';
650 *dec_exp = 0;
651 *len = 1;
652 }
653 }
654}
655
656/** Format and print the double string repressentation according
657 * to the %f specifier.
658 */
659static errno_t _format_double_str_fixed(double_str_t *val_str, int precision, int width,
660 uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
661{
662 int len = val_str->len;
663 char *buf = val_str->str;
664 int dec_exp = val_str->dec_exp;
665
666 assert(0 < len);
667 assert(0 <= precision);
668 assert(0 <= dec_exp || -dec_exp <= precision);
669
670 /* Number of integral digits to print (at least leading zero). */
671 int int_len = max(1, len + dec_exp);
672
673 char sign = _get_sign_char(val_str->neg, flags);
674
675 /* Fractional portion lengths. */
676 int last_frac_signif_pos = max(0, -dec_exp);
677 int leading_frac_zeros = max(0, last_frac_signif_pos - len);
678 int signif_frac_figs = min(last_frac_signif_pos, len);
679 int trailing_frac_zeros = precision - last_frac_signif_pos;
680 char *buf_frac = buf + len - signif_frac_figs;
681
682 if (flags & __PRINTF_FLAG_NOFRACZEROS)
683 trailing_frac_zeros = 0;
684
685 int frac_len = leading_frac_zeros + signif_frac_figs + trailing_frac_zeros;
686
687 bool has_decimal_pt = (0 < frac_len) || (flags & __PRINTF_FLAG_DECIMALPT);
688
689 /* Number of non-padding chars to print. */
690 int num_len = (sign ? 1 : 0) + int_len + (has_decimal_pt ? 1 : 0) + frac_len;
691
692 int padding_len = max(0, width - num_len);
693
694 /* Leading padding and sign. */
695
696 if (!(flags & (__PRINTF_FLAG_LEFTALIGNED | __PRINTF_FLAG_ZEROPADDED)))
697 TRY(_write_spaces(padding_len, ps, written_bytes));
698
699 if (sign)
700 TRY(_write_char(sign, ps, written_bytes));
701
702 if (flags & __PRINTF_FLAG_ZEROPADDED)
703 TRY(_write_zeros(padding_len, ps, written_bytes));
704
705 /* Print the intergral part of the buffer. */
706
707 int buf_int_len = min(len, len + dec_exp);
708
709 if (0 < buf_int_len) {
710 TRY(_write_bytes(buf, buf_int_len, ps, written_bytes));
711
712 /* Print trailing zeros of the integral part of the number. */
713 TRY(_write_zeros(int_len - buf_int_len, ps, written_bytes));
714 } else {
715 /* Single leading integer 0. */
716 TRY(_write_char('0', ps, written_bytes));
717 }
718
719 /* Print the decimal point and the fractional part. */
720 if (has_decimal_pt) {
721 TRY(_write_char('.', ps, written_bytes));
722
723 /* Print leading zeros of the fractional part of the number. */
724 TRY(_write_zeros(leading_frac_zeros, ps, written_bytes));
725
726 /* Print significant digits of the fractional part of the number. */
727 if (0 < signif_frac_figs)
728 TRY(_write_bytes(buf_frac, signif_frac_figs, ps, written_bytes));
729
730 /* Print trailing zeros of the fractional part of the number. */
731 TRY(_write_zeros(trailing_frac_zeros, ps, written_bytes));
732 }
733
734 /* Trailing padding. */
735 if (flags & __PRINTF_FLAG_LEFTALIGNED)
736 TRY(_write_spaces(padding_len, ps, written_bytes));
737
738 return EOK;
739}
740
741/** Convert, format and print a double according to the %f specifier.
742 *
743 * @param g Double to print.
744 * @param precision Number of fractional digits to print. If 0 no
745 * decimal point will be printed unless the flag
746 * __PRINTF_FLAG_DECIMALPT is specified.
747 * @param width Minimum number of characters to display. Pads
748 * with '0' or ' ' depending on the set flags;
749 * @param flags Printf flags.
750 * @param ps Printing functions.
751 */
752static errno_t _format_double_fixed(double g, int precision, int width,
753 uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
754{
755 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
756 flags &= ~__PRINTF_FLAG_ZEROPADDED;
757 }
758
759 if (flags & __PRINTF_FLAG_DECIMALPT) {
760 flags &= ~__PRINTF_FLAG_NOFRACZEROS;
761 }
762
763 ieee_double_t val = extract_ieee_double(g);
764
765 if (val.is_special) {
766 return _format_special(val, width, flags, ps, written_bytes);
767 }
768
769 char buf[MAX_DOUBLE_STR_BUF_SIZE];
770 const size_t buf_size = MAX_DOUBLE_STR_BUF_SIZE;
771 double_str_t val_str;
772
773 val_str.str = buf;
774 val_str.neg = val.is_negative;
775
776 if (0 <= precision) {
777 /*
778 * Request one more digit so we can round the result. The last
779 * digit it returns may have an error of at most +/- 1.
780 */
781 val_str.len = double_to_fixed_str(val, -1, precision + 1, buf, buf_size,
782 &val_str.dec_exp);
783
784 /*
785 * Round using the last digit to produce precision fractional digits.
786 * If less than precision+1 fractional digits were output the last
787 * digit is definitely inaccurate so also round to get rid of it.
788 */
789 _fp_round_up(buf, &val_str.len, &val_str.dec_exp);
790
791 /* Rounding could have introduced trailing zeros. */
792 if (flags & __PRINTF_FLAG_NOFRACZEROS) {
793 _fp_trim_trailing_zeros(buf, &val_str.len, &val_str.dec_exp);
794 }
795 } else {
796 /* Let the implementation figure out the proper precision. */
797 val_str.len = double_to_short_str(val, buf, buf_size, &val_str.dec_exp);
798
799 /* Precision needed for the last significant digit. */
800 precision = max(0, -val_str.dec_exp);
801 }
802
803 return _format_double_str_fixed(&val_str, precision, width, flags, ps, written_bytes);
804}
805
806/** Prints the decimal exponent part of a %e specifier formatted number. */
807static errno_t _format_exponent(int exp_val, uint32_t flags, printf_spec_t *ps,
808 size_t *written_bytes)
809{
810 char exp_ch = (flags & __PRINTF_FLAG_BIGCHARS) ? 'E' : 'e';
811 TRY(_write_char(exp_ch, ps, written_bytes));
812
813 char exp_sign = (exp_val < 0) ? '-' : '+';
814 TRY(_write_char(exp_sign, ps, written_bytes));
815
816 /* Print the exponent. */
817 exp_val = abs(exp_val);
818
819 char exp_str[4] = { 0 };
820
821 exp_str[0] = '0' + exp_val / 100;
822 exp_str[1] = '0' + (exp_val % 100) / 10;
823 exp_str[2] = '0' + (exp_val % 10);
824
825 int exp_len = (exp_str[0] == '0') ? 2 : 3;
826 const char *exp_str_start = &exp_str[3] - exp_len;
827
828 return _write_bytes(exp_str_start, exp_len, ps, written_bytes);
829}
830
831/** Format and print the double string repressentation according
832 * to the %e specifier.
833 */
834static errno_t _format_double_str_scient(double_str_t *val_str, int precision,
835 int width, uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
836{
837 int len = val_str->len;
838 int dec_exp = val_str->dec_exp;
839 char *buf = val_str->str;
840
841 assert(0 < len);
842
843 char sign = _get_sign_char(val_str->neg, flags);
844 bool has_decimal_pt = (0 < precision) || (flags & __PRINTF_FLAG_DECIMALPT);
845 int dec_pt_len = has_decimal_pt ? 1 : 0;
846
847 /* Fractional part lengths. */
848 int signif_frac_figs = len - 1;
849 int trailing_frac_zeros = precision - signif_frac_figs;
850
851 if (flags & __PRINTF_FLAG_NOFRACZEROS) {
852 trailing_frac_zeros = 0;
853 }
854
855 int frac_len = signif_frac_figs + trailing_frac_zeros;
856
857 int exp_val = dec_exp + len - 1;
858 /* Account for exponent sign and 'e'; minimum 2 digits. */
859 int exp_len = 2 + (abs(exp_val) >= 100 ? 3 : 2);
860
861 /* Number of non-padding chars to print. */
862 int num_len = (sign ? 1 : 0) + 1 + dec_pt_len + frac_len + exp_len;
863
864 int padding_len = max(0, width - num_len);
865
866 if (!(flags & (__PRINTF_FLAG_LEFTALIGNED | __PRINTF_FLAG_ZEROPADDED)))
867 TRY(_write_spaces(padding_len, ps, written_bytes));
868
869 if (sign)
870 TRY(_write_char(sign, ps, written_bytes));
871
872 if (flags & __PRINTF_FLAG_ZEROPADDED)
873 TRY(_write_zeros(padding_len, ps, written_bytes));
874
875 /* Single leading integer. */
876 TRY(_write_char(buf[0], ps, written_bytes));
877
878 /* Print the decimal point and the fractional part. */
879 if (has_decimal_pt) {
880 TRY(_write_char('.', ps, written_bytes));
881
882 /* Print significant digits of the fractional part of the number. */
883 if (0 < signif_frac_figs)
884 TRY(_write_bytes(buf + 1, signif_frac_figs, ps, written_bytes));
885
886 /* Print trailing zeros of the fractional part of the number. */
887 TRY(_write_zeros(trailing_frac_zeros, ps, written_bytes));
888 }
889
890 /* Print the exponent. */
891 TRY(_format_exponent(exp_val, flags, ps, written_bytes));
892
893 if (flags & __PRINTF_FLAG_LEFTALIGNED)
894 TRY(_write_spaces(padding_len, ps, written_bytes));
895
896 return EOK;
897}
898
899/** Convert, format and print a double according to the %e specifier.
900 *
901 * Note that if g is large, the output may be huge (3e100 prints
902 * with at least 100 digits).
903 *
904 * %e style: [-]d.dddde+dd
905 * left-justified: [-]d.dddde+dd[space_pad]
906 * right-justified: [space_pad][-][zero_pad]d.dddde+dd
907 *
908 * @param g Double to print.
909 * @param precision Number of fractional digits to print, ie
910 * precision + 1 significant digits to display. If 0 no
911 * decimal point will be printed unless the flag
912 * __PRINTF_FLAG_DECIMALPT is specified. If negative
913 * the shortest accurate number will be printed.
914 * @param width Minimum number of characters to display. Pads
915 * with '0' or ' ' depending on the set flags;
916 * @param flags Printf flags.
917 * @param ps Printing functions.
918 */
919static errno_t _format_double_scientific(double g, int precision, int width,
920 uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
921{
922 if (flags & __PRINTF_FLAG_LEFTALIGNED)
923 flags &= ~__PRINTF_FLAG_ZEROPADDED;
924
925 ieee_double_t val = extract_ieee_double(g);
926
927 if (val.is_special)
928 return _format_special(val, width, flags, ps, written_bytes);
929
930 char buf[MAX_DOUBLE_STR_BUF_SIZE];
931 const size_t buf_size = MAX_DOUBLE_STR_BUF_SIZE;
932 double_str_t val_str;
933
934 val_str.str = buf;
935 val_str.neg = val.is_negative;
936
937 if (0 <= precision) {
938 /*
939 * Request one more digit (in addition to the leading integer)
940 * so we can round the result. The last digit it returns may
941 * have an error of at most +/- 1.
942 */
943 val_str.len = double_to_fixed_str(val, precision + 2, -1, buf, buf_size,
944 &val_str.dec_exp);
945
946 /*
947 * Round the extra digit to produce precision+1 significant digits.
948 * If less than precision+2 significant digits were returned the last
949 * digit is definitely inaccurate so also round to get rid of it.
950 */
951 _fp_round_up(buf, &val_str.len, &val_str.dec_exp);
952
953 /* Rounding could have introduced trailing zeros. */
954 if (flags & __PRINTF_FLAG_NOFRACZEROS) {
955 _fp_trim_trailing_zeros(buf, &val_str.len, &val_str.dec_exp);
956 }
957 } else {
958 /* Let the implementation figure out the proper precision. */
959 val_str.len = double_to_short_str(val, buf, buf_size, &val_str.dec_exp);
960
961 /* Use all produced digits. */
962 precision = val_str.len - 1;
963 }
964
965 return _format_double_str_scient(&val_str, precision, width, flags, ps, written_bytes);
966}
967
968/** Convert, format and print a double according to the %g specifier.
969 *
970 * %g style chooses between %f and %e.
971 *
972 * @param g Double to print.
973 * @param precision Number of significant digits to display; excluding
974 * any leading zeros from this count. If negative
975 * the shortest accurate number will be printed.
976 * @param width Minimum number of characters to display. Pads
977 * with '0' or ' ' depending on the set flags;
978 * @param flags Printf flags.
979 * @param ps Printing functions.
980 *
981 * @return The number of characters printed; negative on failure.
982 */
983static errno_t _format_double_generic(double g, int precision, int width,
984 uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
985{
986 ieee_double_t val = extract_ieee_double(g);
987
988 if (val.is_special)
989 return _format_special(val, width, flags, ps, written_bytes);
990
991 char buf[MAX_DOUBLE_STR_BUF_SIZE];
992 const size_t buf_size = MAX_DOUBLE_STR_BUF_SIZE;
993 int dec_exp;
994 int len;
995
996 /* Honor the user requested number of significant digits. */
997 if (0 <= precision) {
998 /*
999 * Do a quick and dirty conversion of a single digit to determine
1000 * the decimal exponent.
1001 */
1002 len = double_to_fixed_str(val, 1, -1, buf, buf_size, &dec_exp);
1003 assert(0 < len);
1004
1005 precision = max(1, precision);
1006
1007 if (-4 <= dec_exp && dec_exp < precision) {
1008 precision = precision - (dec_exp + 1);
1009 return _format_double_fixed(g, precision, width,
1010 flags | __PRINTF_FLAG_NOFRACZEROS, ps, written_bytes);
1011 } else {
1012 --precision;
1013 return _format_double_scientific(g, precision, width,
1014 flags | __PRINTF_FLAG_NOFRACZEROS, ps, written_bytes);
1015 }
1016 } else {
1017 /* Convert to get the decimal exponent and digit count. */
1018 len = double_to_short_str(val, buf, buf_size, &dec_exp);
1019 assert(0 < len);
1020
1021 if (flags & __PRINTF_FLAG_LEFTALIGNED) {
1022 flags &= ~__PRINTF_FLAG_ZEROPADDED;
1023 }
1024
1025 double_str_t val_str;
1026 val_str.str = buf;
1027 val_str.len = len;
1028 val_str.neg = val.is_negative;
1029 val_str.dec_exp = dec_exp;
1030
1031 int first_digit_pos = len + dec_exp;
1032 int last_digit_pos = dec_exp;
1033
1034 /* The whole number (15 digits max) fits between dec places 15 .. -6 */
1035 if (len <= 15 && -6 <= last_digit_pos && first_digit_pos <= 15) {
1036 /* Precision needed for the last significant digit. */
1037 precision = max(0, -val_str.dec_exp);
1038 return _format_double_str_fixed(&val_str, precision, width, flags, ps, written_bytes);
1039 } else {
1040 /* Use all produced digits. */
1041 precision = val_str.len - 1;
1042 return _format_double_str_scient(&val_str, precision, width, flags, ps, written_bytes);
1043 }
1044 }
1045}
1046
1047/** Convert, format and print a double according to the specifier.
1048 *
1049 * Depending on the specifier it prints the double using the styles
1050 * %g, %f or %e by means of print_double_generic(), print_double_fixed(),
1051 * print_double_scientific().
1052 *
1053 * @param g Double to print.
1054 * @param spec Specifier of the style to print in; one of: 'g','G','f','F',
1055 * 'e','E'.
1056 * @param precision Number of fractional digits to display. If negative
1057 * the shortest accurate number will be printed for style %g;
1058 * negative precision defaults to 6 for styles %f, %e.
1059 * @param width Minimum number of characters to display. Pads
1060 * with '0' or ' ' depending on the set flags;
1061 * @param flags Printf flags.
1062 * @param ps Printing functions.
1063 */
1064static errno_t _format_double(double g, char spec, int precision, int width,
1065 uint32_t flags, printf_spec_t *ps, size_t *written_chars)
1066{
1067 switch (spec) {
1068 case 'F':
1069 flags |= __PRINTF_FLAG_BIGCHARS;
1070 /* Fallthrough */
1071 case 'f':
1072 precision = (precision < 0) ? 6 : precision;
1073 return _format_double_fixed(g, precision, width, flags, ps, written_chars);
1074
1075 case 'E':
1076 flags |= __PRINTF_FLAG_BIGCHARS;
1077 /* Fallthrough */
1078 case 'e':
1079 precision = (precision < 0) ? 6 : precision;
1080 return _format_double_scientific(g, precision, width, flags, ps, written_chars);
1081
1082 case 'G':
1083 flags |= __PRINTF_FLAG_BIGCHARS;
1084 /* Fallthrough */
1085 case 'g':
1086 return _format_double_generic(g, precision, width, flags, ps, written_chars);
1087
1088 default:
1089 assert(false);
1090 return -1;
1091 }
1092}
1093
1094#endif
1095
1096static const char *_strchrnul(const char *s, int c)
1097{
1098 while (*s != c && *s != 0)
1099 s++;
1100 return s;
1101}
1102
1103/** Read a sequence of digits from the format string as a number.
1104 * If the number has too many digits to fit in int, returns INT_MAX.
1105 */
1106static int _read_num(const char *fmt, size_t *i)
1107{
1108 const char *s;
1109 unsigned n = 0;
1110
1111 for (s = &fmt[*i]; isdigit(*s); s++) {
1112 unsigned digit = (*s - '0');
1113
1114 /* Check for overflow */
1115 if (n > INT_MAX / 10 || n * 10 > INT_MAX - digit) {
1116 n = INT_MAX;
1117 while (isdigit(*s))
1118 s++;
1119 break;
1120 }
1121
1122 n = n * 10 + digit;
1123 }
1124
1125 *i = s - fmt;
1126 return n;
1127}
1128
1129static uint32_t _parse_flags(const char *fmt, size_t *i)
1130{
1131 uint32_t flags = 0;
1132
1133 while (true) {
1134 switch (fmt[(*i)++]) {
1135 case '#':
1136 flags |= __PRINTF_FLAG_PREFIX;
1137 flags |= __PRINTF_FLAG_DECIMALPT;
1138 continue;
1139 case '-':
1140 flags |= __PRINTF_FLAG_LEFTALIGNED;
1141 continue;
1142 case '+':
1143 flags |= __PRINTF_FLAG_SHOWPLUS;
1144 continue;
1145 case ' ':
1146 flags |= __PRINTF_FLAG_SPACESIGN;
1147 continue;
1148 case '0':
1149 flags |= __PRINTF_FLAG_ZEROPADDED;
1150 continue;
1151 }
1152
1153 --*i;
1154 break;
1155 }
1156
1157 return flags;
1158}
1159
1160static bool _eat_char(const char *s, size_t *idx, int c)
1161{
1162 if (s[*idx] != c)
1163 return false;
1164
1165 (*idx)++;
1166 return true;
1167}
1168
1169static qualifier_t _read_qualifier(const char *s, size_t *idx)
1170{
1171 switch (s[(*idx)++]) {
1172 case 't': /* ptrdiff_t */
1173 case 'z': /* size_t */
1174 if (sizeof(ptrdiff_t) == sizeof(int))
1175 return PrintfQualifierInt;
1176 else
1177 return PrintfQualifierLong;
1178
1179 case 'h':
1180 if (_eat_char(s, idx, 'h'))
1181 return PrintfQualifierByte;
1182 else
1183 return PrintfQualifierShort;
1184
1185 case 'l':
1186 if (_eat_char(s, idx, 'l'))
1187 return PrintfQualifierLongLong;
1188 else
1189 return PrintfQualifierLong;
1190
1191 case 'j':
1192 return PrintfQualifierLongLong;
1193
1194 default:
1195 --*idx;
1196
1197 /* Unspecified */
1198 return PrintfQualifierInt;
1199 }
1200}
1201
1202/** Print formatted string.
1203 *
1204 * Print string formatted according to the fmt parameter and variadic arguments.
1205 * Each formatting directive must have the following form:
1206 *
1207 * \% [ FLAGS ] [ WIDTH ] [ .PRECISION ] [ TYPE ] CONVERSION
1208 *
1209 * FLAGS:@n
1210 * - "#" Force to print prefix. For \%o conversion, the prefix is 0, for
1211 * \%x and \%X prefixes are 0x and 0X and for conversion \%b the
1212 * prefix is 0b.
1213 *
1214 * - "-" Align to left.
1215 *
1216 * - "+" Print positive sign just as negative.
1217 *
1218 * - " " If the printed number is positive and "+" flag is not set,
1219 * print space in place of sign.
1220 *
1221 * - "0" Print 0 as padding instead of spaces. Zeroes are placed between
1222 * sign and the rest of the number. This flag is ignored if "-"
1223 * flag is specified.
1224 *
1225 * WIDTH:@n
1226 * - Specify the minimal width of a printed argument. If it is bigger,
1227 * width is ignored. If width is specified with a "*" character instead of
1228 * number, width is taken from parameter list. And integer parameter is
1229 * expected before parameter for processed conversion specification. If
1230 * this value is negative its absolute value is taken and the "-" flag is
1231 * set.
1232 *
1233 * PRECISION:@n
1234 * - Value precision. For numbers it specifies minimum valid numbers.
1235 * Smaller numbers are printed with leading zeroes. Bigger numbers are not
1236 * affected. Strings with more than precision characters are cut off. Just
1237 * as with width, an "*" can be used used instead of a number. An integer
1238 * value is then expected in parameters. When both width and precision are
1239 * specified using "*", the first parameter is used for width and the
1240 * second one for precision.
1241 *
1242 * TYPE:@n
1243 * - "hh" Signed or unsigned char.@n
1244 * - "h" Signed or unsigned short.@n
1245 * - "" Signed or unsigned int (default value).@n
1246 * - "l" Signed or unsigned long int.@n
1247 * If conversion is "c", the character is wint_t (wide character).@n
1248 * If conversion is "s", the string is char32_t * (wide string).@n
1249 * - "ll" Signed or unsigned long long int.@n
1250 * - "z" Signed or unsigned ssize_t or site_t.@n
1251 *
1252 * CONVERSION:@n
1253 * - % Print percentile character itself.
1254 *
1255 * - c Print single character. The character is expected to be plain
1256 * ASCII (e.g. only values 0 .. 127 are valid).@n
1257 * If type is "l", then the character is expected to be wide character
1258 * (e.g. values 0 .. 0x10ffff are valid).
1259 *
1260 * - s Print zero terminated string. If a NULL value is passed as
1261 * value, "(NULL)" is printed instead.@n
1262 * If type is "l", then the string is expected to be wide string.
1263 *
1264 * - P, p Print value of a pointer. Void * value is expected and it is
1265 * printed in hexadecimal notation with prefix (as with
1266 * \%#0.8X / \%#0.8x for 32-bit or \%#0.16lX / \%#0.16lx for 64-bit
1267 * long pointers).
1268 *
1269 * - b Print value as unsigned binary number. Prefix is not printed by
1270 * default. (Nonstandard extension.)
1271 *
1272 * - o Print value as unsigned octal number. Prefix is not printed by
1273 * default.
1274 *
1275 * - d, i Print signed decimal number. There is no difference between d
1276 * and i conversion.
1277 *
1278 * - u Print unsigned decimal number.
1279 *
1280 * - X, x Print hexadecimal number with upper- or lower-case. Prefix is
1281 * not printed by default.
1282 *
1283 * All other characters from fmt except the formatting directives are printed
1284 * verbatim.
1285 *
1286 * @param fmt Format NULL-terminated string.
1287 *
1288 * @return Number of characters printed, negative value on failure.
1289 *
1290 */
1291int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
1292{
1293 errno_t rc = EOK;
1294 size_t nxt = 0; /* Index of the next character from fmt */
1295
1296 size_t counter = 0; /* Number of characters printed */
1297
1298 while (rc == EOK) {
1299 /* Find the next specifier and write all the bytes before it. */
1300 const char *s = _strchrnul(&fmt[nxt], '%');
1301 size_t bytes = s - &fmt[nxt];
1302 rc = _write_bytes(&fmt[nxt], bytes, ps, &counter);
1303 if (rc != EOK)
1304 break;
1305
1306 nxt += bytes;
1307
1308 /* Check for end of string. */
1309 if (_eat_char(fmt, &nxt, 0))
1310 break;
1311
1312 /* We must be at the start of a specifier. */
1313 bool spec = _eat_char(fmt, &nxt, '%');
1314 assert(spec);
1315
1316 /* Parse modifiers */
1317 uint32_t flags = _parse_flags(fmt, &nxt);
1318
1319 /* Width & '*' operator */
1320 int width = -1;
1321 if (_eat_char(fmt, &nxt, '*')) {
1322 /* Get width value from argument list */
1323 width = va_arg(ap, int);
1324
1325 if (width < 0) {
1326 /* Negative width sets '-' flag */
1327 width = (width == INT_MIN) ? INT_MAX : -width;
1328 flags |= __PRINTF_FLAG_LEFTALIGNED;
1329 }
1330 } else {
1331 width = _read_num(fmt, &nxt);
1332 }
1333
1334 /* Precision and '*' operator */
1335 int precision = -1;
1336 if (_eat_char(fmt, &nxt, '.')) {
1337 if (_eat_char(fmt, &nxt, '*')) {
1338 /* Get precision value from the argument list */
1339 precision = va_arg(ap, int);
1340
1341 /* Negative is treated as omitted. */
1342 if (precision < 0)
1343 precision = -1;
1344 } else {
1345 precision = _read_num(fmt, &nxt);
1346 }
1347 }
1348
1349 qualifier_t qualifier = _read_qualifier(fmt, &nxt);
1350 unsigned int base = 10;
1351 char specifier = fmt[nxt++];
1352
1353 switch (specifier) {
1354 /*
1355 * String and character conversions.
1356 */
1357 case 's':
1358 if (qualifier == PrintfQualifierLong)
1359 rc = _format_wstr(va_arg(ap, char32_t *), width, precision, flags, ps, &counter);
1360 else
1361 rc = _format_cstr(va_arg(ap, char *), width, precision, flags, ps, &counter);
1362 continue;
1363
1364 case 'c':
1365 if (qualifier == PrintfQualifierLong)
1366 rc = _format_uchar(va_arg(ap, wint_t), width, flags, ps, &counter);
1367 else
1368 rc = _format_char(va_arg(ap, int), width, flags, ps, &counter);
1369 continue;
1370
1371 /*
1372 * Floating point values
1373 */
1374 case 'G':
1375 case 'g':
1376 case 'F':
1377 case 'f':
1378 case 'E':
1379 case 'e':;
1380#ifdef HAS_FLOAT
1381 rc = _format_double(va_arg(ap, double), specifier, precision,
1382 width, flags, ps, &counter);
1383#else
1384 rc = _format_cstr("<float unsupported>", width, -1, 0, ps, &counter);
1385#endif
1386 continue;
1387
1388 /*
1389 * Integer values
1390 */
1391 case 'P':
1392 /* Pointer */
1393 flags |= __PRINTF_FLAG_BIGCHARS;
1394 /* Fallthrough */
1395 case 'p':
1396 flags |= __PRINTF_FLAG_PREFIX;
1397 flags |= __PRINTF_FLAG_ZEROPADDED;
1398 base = 16;
1399 qualifier = PrintfQualifierPointer;
1400 break;
1401 case 'b':
1402 base = 2;
1403 break;
1404 case 'o':
1405 base = 8;
1406 break;
1407 case 'd':
1408 case 'i':
1409 flags |= __PRINTF_FLAG_SIGNED;
1410 break;
1411 case 'u':
1412 break;
1413 case 'X':
1414 flags |= __PRINTF_FLAG_BIGCHARS;
1415 /* Fallthrough */
1416 case 'x':
1417 base = 16;
1418 break;
1419
1420 case '%':
1421 /* Percentile itself */
1422 rc = _write_char('%', ps, &counter);
1423 continue;
1424
1425 /*
1426 * Bad formatting.
1427 */
1428 default:
1429 rc = EINVAL;
1430 continue;
1431 }
1432
1433 /* Print integers */
1434 uint64_t number;
1435
1436 switch (qualifier) {
1437 case PrintfQualifierByte:
1438 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
1439 break;
1440 case PrintfQualifierShort:
1441 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
1442 break;
1443 case PrintfQualifierInt:
1444 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
1445 break;
1446 case PrintfQualifierLong:
1447 number = PRINTF_GET_INT_ARGUMENT(long, ap, flags);
1448 break;
1449 case PrintfQualifierLongLong:
1450 number = PRINTF_GET_INT_ARGUMENT(long long, ap, flags);
1451 break;
1452 case PrintfQualifierPointer:
1453 precision = sizeof(void *) << 1;
1454 number = (uint64_t) (uintptr_t) va_arg(ap, void *);
1455 break;
1456 default:
1457 /* Unknown qualifier */
1458 rc = EINVAL;
1459 continue;
1460 }
1461
1462 rc = _format_number(number, width, precision, base, flags, ps, &counter);
1463 }
1464
1465 if (rc != EOK) {
1466 _set_errno(rc);
1467 return -1;
1468 }
1469
1470 if (counter > INT_MAX) {
1471 _set_errno(EOVERFLOW);
1472 return -1;
1473 }
1474
1475 return (int) counter;
1476}
1477
1478/** @}
1479 */
Note: See TracBrowser for help on using the repository browser.