Changeset 97f6b71 in mainline for common/printf/printf_core.c


Ignore:
Timestamp:
2025-04-13T18:21:02Z (3 weeks ago)
Author:
Jiří Zárevúcky <zarevucky.jiri@…>
Branches:
master
Children:
163e34c
Parents:
28c39f3
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2025-04-12 10:47:26)
git-committer:
Jiří Zárevúcky <zarevucky.jiri@…> (2025-04-13 18:21:02)
Message:

Clean up printf_core() and make it obey the standard

The original implementation made an effort to count Unicode code points
rather than bytes (which is what standard printf() is defined as).
This makes printf() work incorrectly in some circumstances, especially
where its limits and return values are used to ensure memory is correctly
sized for the output.

I don't expect this change to break any HelenOS code, except potentially
for cosmetic issues where its used for aligning output on monospace display,
which in itself cannot possibly ever handle that scenario correctly, since
unicode code points and cells on the screen don't have any straightforward
correspondence. It works in some languages, but the same can be achieved by
calculating the necessary padding ahead of time and adding "%*s" with a width
argument and an empty string, which is a minor inconvenience at worst.

Other changes include general combing over the code and making it less
verbose by strategic utilization of added functions and a TRY() macro,
which some may recognize as being inspired by Rust error handling.
Also got rid of str_decode() in parsing the format string, since its
entirely unnecessary and just makes the parsing uglier.

Additionally, the implementation now only uses byte output.
Most consumers don't get any benefit from being served wide characters,
and there's almost no prints that output them.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • common/printf/printf_core.c

    r28c39f3 r97f6b71  
    33 * Copyright (c) 2006 Josef Cejka
    44 * Copyright (c) 2009 Martin Decky
     5 * Copyright (c) 2025 Jiří Zárevúcky
    56 * All rights reserved.
    67 *
     
    3738 */
    3839
    39 #include <stdio.h>
     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>
    4048#include <stddef.h>
     49#include <stdint.h>
    4150#include <stdlib.h>
    42 #include <printf_core.h>
    43 #include <ctype.h>
    4451#include <str.h>
    45 #include <assert.h>
    46 #include <macros.h>
    47 #include <uchar.h>
    4852
    4953/* Disable float support in kernel, because we usually disable floating operations there. */
     
    8892
    8993/**
    90  * Buffer big enough for 64-bit number printed in base 2, sign, prefix and 0
    91  * to terminate string... (last one is only for better testing end of buffer by
    92  * zero-filling subroutine)
    93  */
    94 #define PRINT_NUMBER_BUFFER_SIZE  (64 + 5)
     94 * Buffer big enough for 64-bit number printed in base 2.
     95 */
     96#define PRINT_NUMBER_BUFFER_SIZE  64
    9597
    9698/** Get signed or unsigned integer argument */
     
    122124        PrintfQualifierLongLong,
    123125        PrintfQualifierPointer,
    124         PrintfQualifierSize,
    125         PrintfQualifierMax
    126126} qualifier_t;
    127127
    128 static const char *nullstr = "(NULL)";
    129 static const char *digits_small = "0123456789abcdef";
    130 static const char *digits_big = "0123456789ABCDEF";
    131 static const char invalch = U_SPECIAL;
    132 
    133 /** Print one or more characters without adding newline.
    134  *
    135  * @param buf  Buffer holding characters with size of
    136  *             at least size bytes. NULL is not allowed!
    137  * @param size Size of the buffer in bytes.
    138  * @param ps   Output method and its data.
    139  *
    140  * @return Number of characters printed.
    141  *
    142  */
    143 static int printf_putnchars(const char *buf, size_t size,
    144     printf_spec_t *ps)
    145 {
    146         return ps->str_write((void *) buf, size, ps->data);
    147 }
    148 
    149 /** Print one or more wide characters without adding newline.
    150  *
    151  * @param buf  Buffer holding wide characters with size of
    152  *             at least size bytes. NULL is not allowed!
    153  * @param size Size of the buffer in bytes.
    154  * @param ps   Output method and its data.
    155  *
    156  * @return Number of wide characters printed.
    157  *
    158  */
    159 static int printf_wputnchars(const char32_t *buf, size_t size,
    160     printf_spec_t *ps)
    161 {
    162         return ps->wstr_write((void *) buf, size, ps->data);
    163 }
    164 
    165 /** Print string without adding a newline.
    166  *
    167  * @param str String to print.
    168  * @param ps  Write function specification and support data.
    169  *
    170  * @return Number of characters printed.
    171  *
    172  */
    173 static int printf_putstr(const char *str, printf_spec_t *ps)
    174 {
    175         if (str == NULL)
    176                 return printf_putnchars(nullstr, str_size(nullstr), ps);
    177 
    178         return ps->str_write((void *) str, str_size(str), ps->data);
    179 }
    180 
    181 /** Print one ASCII character.
    182  *
    183  * @param c  ASCII character to be printed.
    184  * @param ps Output method.
    185  *
    186  * @return Number of characters printed.
    187  *
    188  */
    189 static int printf_putchar(const char ch, printf_spec_t *ps)
    190 {
    191         if (!ascii_check(ch))
    192                 return ps->str_write((void *) &invalch, 1, ps->data);
    193 
    194         return ps->str_write(&ch, 1, ps->data);
    195 }
    196 
    197 /** Print one wide character.
    198  *
    199  * @param c  Wide character to be printed.
    200  * @param ps Output method.
    201  *
    202  * @return Number of characters printed.
    203  *
    204  */
    205 static int printf_putuchar(const char32_t ch, printf_spec_t *ps)
    206 {
    207         if (!chr_check(ch))
    208                 return ps->str_write((void *) &invalch, 1, ps->data);
    209 
    210         return ps->wstr_write(&ch, sizeof(char32_t), ps->data);
     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        int written = ps->str_write(buf, n, ps->data);
     204        if (written < 0)
     205                return EIO;
     206        _saturating_add(written_bytes, n);
     207        return EOK;
     208
     209        #if 0
     210        errno_t rc = ps->write(buf, &n, ps->data);
     211        _saturating_add(written_bytes, n);
     212        return rc;
     213        #endif
     214}
     215
     216/** Write one UTF-32 character. */
     217static errno_t _write_uchar(char32_t ch, printf_spec_t *ps,
     218    size_t *written_bytes)
     219{
     220        char utf8[4];
     221        size_t offset = 0;
     222
     223        if (chr_encode(ch, utf8, &offset, sizeof(utf8)) == EOK)
     224                return _write_bytes(utf8, offset, ps, written_bytes);
     225
     226        /* Invalid character. */
     227        return _write_bytes(_replacement, sizeof(_replacement) - 1, ps, written_bytes);
     228}
     229
     230/** Write n UTF-32 characters. */
     231static errno_t _write_chars(const char32_t *buf, size_t n, printf_spec_t *ps,
     232    size_t *written_bytes)
     233{
     234        for (size_t i = 0; i < n; i++)
     235                TRY(_write_uchar(buf[i], ps, written_bytes));
     236
     237        return EOK;
     238}
     239
     240static errno_t _write_char(char c, printf_spec_t *ps, size_t *written_bytes)
     241{
     242        return _write_bytes(&c, 1, ps, written_bytes);
     243}
     244
     245static errno_t _write_spaces(size_t n, printf_spec_t *ps, size_t *written_bytes)
     246{
     247        size_t max_spaces = sizeof(_spaces) - 1;
     248
     249        while (n > max_spaces) {
     250                TRY(_write_bytes(_spaces, max_spaces, ps, written_bytes));
     251                n -= max_spaces;
     252        }
     253
     254        return _write_bytes(_spaces, n, ps, written_bytes);
     255}
     256
     257static errno_t _write_zeros(size_t n, printf_spec_t *ps, size_t *written_bytes)
     258{
     259        size_t max_zeros = sizeof(_zeros) - 1;
     260
     261        while (n > max_zeros) {
     262                TRY(_write_bytes(_zeros, max_zeros, ps, written_bytes));
     263                n -= max_zeros;
     264        }
     265
     266        return _write_bytes(_zeros, n, ps, written_bytes);
    211267}
    212268
     
    216272 * @param width Width modifier.
    217273 * @param flags Flags that change the way the character is printed.
    218  *
    219  * @return Number of characters printed, negative value on failure.
    220  *
    221  */
    222 static int print_char(const char ch, int width, uint32_t flags, printf_spec_t *ps)
    223 {
    224         size_t counter = 0;
    225         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
    226                 while (--width > 0) {
    227                         /*
    228                          * One space is consumed by the character itself, hence
    229                          * the predecrement.
    230                          */
    231                         if (printf_putchar(' ', ps) > 0)
    232                                 counter++;
    233                 }
    234         }
    235 
    236         if (printf_putchar(ch, ps) > 0)
    237                 counter++;
    238 
    239         while (--width > 0) {
    240                 /*
    241                  * One space is consumed by the character itself, hence
    242                  * the predecrement.
    243                  */
    244                 if (printf_putchar(' ', ps) > 0)
    245                         counter++;
    246         }
    247 
    248         return (int) (counter);
     274 */
     275static errno_t _format_char(const char c, size_t width, uint32_t flags,
     276    printf_spec_t *ps, size_t *written_bytes)
     277{
     278        size_t bytes = 1;
     279
     280        if (width <= bytes)
     281                return _write_char(c, ps, written_bytes);
     282
     283        if (flags & __PRINTF_FLAG_LEFTALIGNED) {
     284                TRY(_write_char(c, ps, written_bytes));
     285                TRY(_write_spaces(width - bytes, ps, written_bytes));
     286        } else {
     287                TRY(_write_spaces(width - bytes, ps, written_bytes));
     288                TRY(_write_char(c, ps, written_bytes));
     289        }
     290
     291        return EOK;
    249292}
    250293
     
    254297 * @param width Width modifier.
    255298 * @param flags Flags that change the way the character is printed.
    256  *
    257  * @return Number of characters printed, negative value on failure.
    258  *
    259  */
    260 static int print_wchar(const char32_t ch, int width, uint32_t flags, printf_spec_t *ps)
    261 {
    262         size_t counter = 0;
    263         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
    264                 while (--width > 0) {
    265                         /*
    266                          * One space is consumed by the character itself, hence
    267                          * the predecrement.
    268                          */
    269                         if (printf_putchar(' ', ps) > 0)
    270                                 counter++;
    271                 }
    272         }
    273 
    274         if (printf_putuchar(ch, ps) > 0)
    275                 counter++;
    276 
    277         while (--width > 0) {
    278                 /*
    279                  * One space is consumed by the character itself, hence
    280                  * the predecrement.
    281                  */
    282                 if (printf_putchar(' ', ps) > 0)
    283                         counter++;
    284         }
    285 
    286         return (int) (counter);
     299 */
     300static errno_t _format_uchar(const char32_t ch, size_t width, uint32_t flags,
     301    printf_spec_t *ps, size_t *written_bytes)
     302{
     303        /*
     304         * All widths in printf() are specified in bytes. It might seem nonsensical
     305         * with unicode text, but that's the way the function is defined. The width
     306     * is barely useful if you want column alignment in terminal, but keep in
     307     * mind that counting code points is only marginally better for that.
     308     * Characters can span more than one unicode code point, even in languages
     309     * based on latin alphabet, and a single unicode code point can occupy two
     310     * spaces in east asian scripts.
     311     *
     312     * What the width can actually be useful for is padding, when you need the
     313     * output to fill an exact number of bytes in a file. That use would break
     314     * if we did our own thing here.
     315     */
     316
     317    size_t bytes = _utf8_bytes(ch);
     318
     319        if (width <= bytes)
     320                return _write_uchar(ch, ps, written_bytes);
     321
     322        if (flags & __PRINTF_FLAG_LEFTALIGNED) {
     323                TRY(_write_uchar(ch, ps, written_bytes));
     324                TRY(_write_spaces(width - bytes, ps, written_bytes));
     325        } else {
     326                TRY(_write_spaces(width - bytes, ps, written_bytes));
     327                TRY(_write_uchar(ch, ps, written_bytes));
     328        }
     329
     330        return EOK;
    287331}
    288332
     
    293337 * @param precision Precision modifier.
    294338 * @param flags     Flags that modify the way the string is printed.
    295  *
    296  * @return Number of characters printed, negative value on failure.
    297  */
    298 static int print_str(char *str, int width, unsigned int precision,
    299     uint32_t flags, printf_spec_t *ps)
     339 */
     340static errno_t _format_cstr(const char *str, size_t width, int precision,
     341    uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
    300342{
    301343        if (str == NULL)
    302                 return printf_putstr(nullstr, ps);
    303 
    304         size_t strw = str_length(str);
    305 
    306         /* Precision unspecified - print everything. */
    307         if ((precision == 0) || (precision > strw))
    308                 precision = strw;
    309 
    310         /* Left padding */
    311         size_t counter = 0;
    312         width -= precision;
    313         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
    314                 while (width-- > 0) {
    315                         if (printf_putchar(' ', ps) == 1)
    316                                 counter++;
    317                 }
    318         }
    319 
    320         /* Part of @a str fitting into the alloted space. */
    321         int retval;
    322         size_t size = str_lsize(str, precision);
    323         if ((retval = printf_putnchars(str, size, ps)) < 0)
    324                 return -counter;
    325 
    326         counter += retval;
    327 
    328         /* Right padding */
    329         while (width-- > 0) {
    330                 if (printf_putchar(' ', ps) == 1)
    331                         counter++;
    332         }
    333 
    334         return ((int) counter);
    335 
     344                str = _nullstr;
     345
     346        /* Negative precision == unspecified. */
     347        size_t max_bytes = (precision < 0) ? SIZE_MAX : (size_t) precision;
     348        size_t bytes = str_nsize(str, max_bytes);
     349
     350        if (width <= bytes)
     351                return _write_bytes(str, bytes, ps, written_bytes);
     352
     353        if (flags & __PRINTF_FLAG_LEFTALIGNED) {
     354                TRY(_write_bytes(str, bytes, ps, written_bytes));
     355                TRY(_write_spaces(width - bytes, ps, written_bytes));
     356        } else {
     357                TRY(_write_spaces(width - bytes, ps, written_bytes));
     358                TRY(_write_bytes(str, bytes, ps, written_bytes));
     359        }
     360
     361        return EOK;
    336362}
    337363
     
    342368 * @param precision Precision modifier.
    343369 * @param flags     Flags that modify the way the string is printed.
    344  *
    345  * @return Number of wide characters printed, negative value on failure.
    346  */
    347 static int print_wstr(char32_t *str, int width, unsigned int precision,
    348     uint32_t flags, printf_spec_t *ps)
    349 {
    350         if (str == NULL)
    351                 return printf_putstr(nullstr, ps);
    352 
    353         size_t strw = wstr_length(str);
    354 
    355         /* Precision not specified - print everything. */
    356         if ((precision == 0) || (precision > strw))
    357                 precision = strw;
    358 
    359         /* Left padding */
    360         size_t counter = 0;
    361         width -= precision;
    362         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
    363                 while (width-- > 0) {
    364                         if (printf_putchar(' ', ps) == 1)
    365                                 counter++;
    366                 }
    367         }
    368 
    369         /* Part of @a wstr fitting into the alloted space. */
    370         int retval;
    371         size_t size = wstr_lsize(str, precision);
    372         if ((retval = printf_wputnchars(str, size, ps)) < 0)
    373                 return -counter;
    374 
    375         counter += retval;
    376 
    377         /* Right padding */
    378         while (width-- > 0) {
    379                 if (printf_putchar(' ', ps) == 1)
    380                         counter++;
    381         }
    382 
    383         return ((int) counter);
     370 */
     371static errno_t _format_wstr(char32_t *str, size_t width, int precision,
     372    uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
     373{
     374        if (!str)
     375                return _format_cstr(_nullstr, width, precision, flags, ps, written_bytes);
     376
     377        /* Width and precision are always byte-based. See _format_uchar() */
     378        /* Negative precision == unspecified. */
     379        size_t max_bytes = (precision < 0) ? SIZE_MAX : (size_t) precision;
     380
     381        size_t len;
     382        size_t bytes = _utf8_wstr_bytes_len(str, max_bytes, &len);
     383
     384        if (width <= bytes)
     385                return _write_chars(str, len, ps, written_bytes);
     386
     387        if (flags & __PRINTF_FLAG_LEFTALIGNED) {
     388                TRY(_write_chars(str, len, ps, written_bytes));
     389                TRY(_write_spaces(width - bytes, ps, written_bytes));
     390        } else {
     391                TRY(_write_spaces(width - bytes, ps, written_bytes));
     392                TRY(_write_chars(str, len, ps, written_bytes));
     393        }
     394
     395        return EOK;
     396}
     397
     398static char _sign(uint32_t flags)
     399{
     400        if (!(flags & __PRINTF_FLAG_SIGNED))
     401                return 0;
     402
     403        if (flags & __PRINTF_FLAG_NEGATIVE)
     404                return '-';
     405
     406        if (flags & __PRINTF_FLAG_SHOWPLUS)
     407                return '+';
     408
     409        if (flags & __PRINTF_FLAG_SPACESIGN)
     410                return ' ';
     411
     412        return 0;
    384413}
    385414
     
    393422 * @param base      Base to print the number in (must be between 2 and 16).
    394423 * @param flags     Flags that modify the way the number is printed.
    395  *
    396  * @return Number of characters printed.
    397  *
    398  */
    399 static int print_number(uint64_t num, int width, int precision, int base,
    400     uint32_t flags, printf_spec_t *ps)
    401 {
    402         /* Precision not specified. */
    403         if (precision < 0) {
    404                 precision = 0;
    405         }
    406 
    407         const char *digits;
    408         if (flags & __PRINTF_FLAG_BIGCHARS)
    409                 digits = digits_big;
    410         else
    411                 digits = digits_small;
    412 
    413         char data[PRINT_NUMBER_BUFFER_SIZE];
    414         char *ptr = &data[PRINT_NUMBER_BUFFER_SIZE - 1];
    415 
    416         /* Size of number with all prefixes and signs */
    417         int size = 0;
    418 
    419         /* Put zero at end of string */
    420         *ptr-- = 0;
    421 
    422         if (num == 0) {
    423                 *ptr-- = '0';
    424                 size++;
    425         } else {
    426                 do {
    427                         *ptr-- = digits[num % base];
    428                         size++;
    429                 } while (num /= base);
    430         }
    431 
    432         /* Size of plain number */
    433         int number_size = size;
    434 
    435         /*
    436          * Collect the sum of all prefixes/signs/etc. to calculate padding and
    437          * leading zeroes.
    438          */
    439         if (flags & __PRINTF_FLAG_PREFIX) {
    440                 switch (base) {
    441                 case 2:
    442                         /* Binary formating is not standard, but usefull */
    443                         size += 2;
    444                         break;
    445                 case 8:
    446                         size++;
    447                         break;
    448                 case 16:
    449                         size += 2;
    450                         break;
    451                 }
    452         }
    453 
    454         char sgn = 0;
    455         if (flags & __PRINTF_FLAG_SIGNED) {
    456                 if (flags & __PRINTF_FLAG_NEGATIVE) {
    457                         sgn = '-';
    458                         size++;
    459                 } else if (flags & __PRINTF_FLAG_SHOWPLUS) {
    460                         sgn = '+';
    461                         size++;
    462                 } else if (flags & __PRINTF_FLAG_SPACESIGN) {
    463                         sgn = ' ';
    464                         size++;
    465                 }
    466         }
    467 
    468         if (flags & __PRINTF_FLAG_LEFTALIGNED)
    469                 flags &= ~__PRINTF_FLAG_ZEROPADDED;
    470 
    471         /*
    472          * If the number is left-aligned or precision is specified then
    473          * padding with zeros is ignored.
    474          */
    475         if (flags & __PRINTF_FLAG_ZEROPADDED) {
    476                 if ((precision == 0) && (width > size))
    477                         precision = width - size + number_size;
    478         }
    479 
    480         /* Print leading spaces */
    481         if (number_size > precision) {
    482                 /* Print the whole number, not only a part */
    483                 precision = number_size;
    484         }
    485 
    486         width -= precision + size - number_size;
    487         size_t counter = 0;
    488 
    489         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
    490                 while (width-- > 0) {
    491                         if (printf_putchar(' ', ps) == 1)
    492                                 counter++;
    493                 }
    494         }
    495 
    496         /* Print sign */
    497         if (sgn) {
    498                 if (printf_putchar(sgn, ps) == 1)
    499                         counter++;
    500         }
    501 
    502         /* Print prefix */
    503         if (flags & __PRINTF_FLAG_PREFIX) {
    504                 switch (base) {
    505                 case 2:
    506                         /* Binary formating is not standard, but useful */
    507                         if (printf_putchar('0', ps) == 1)
    508                                 counter++;
    509                         if (flags & __PRINTF_FLAG_BIGCHARS) {
    510                                 if (printf_putchar('B', ps) == 1)
    511                                         counter++;
    512                         } else {
    513                                 if (printf_putchar('b', ps) == 1)
    514                                         counter++;
    515                         }
    516                         break;
    517                 case 8:
    518                         if (printf_putchar('o', ps) == 1)
    519                                 counter++;
    520                         break;
    521                 case 16:
    522                         if (printf_putchar('0', ps) == 1)
    523                                 counter++;
    524                         if (flags & __PRINTF_FLAG_BIGCHARS) {
    525                                 if (printf_putchar('X', ps) == 1)
    526                                         counter++;
    527                         } else {
    528                                 if (printf_putchar('x', ps) == 1)
    529                                         counter++;
    530                         }
    531                         break;
    532                 }
    533         }
    534 
    535         /* Print leading zeroes */
    536         precision -= number_size;
    537         while (precision-- > 0) {
    538                 if (printf_putchar('0', ps) == 1)
    539                         counter++;
    540         }
    541 
    542         /* Print the number itself */
    543         int retval;
    544         if ((retval = printf_putstr(++ptr, ps)) > 0)
    545                 counter += retval;
    546 
    547         /* Print trailing spaces */
    548 
    549         while (width-- > 0) {
    550                 if (printf_putchar(' ', ps) == 1)
    551                         counter++;
    552         }
    553 
    554         return ((int) counter);
     424 */
     425static errno_t _format_number(uint64_t num, size_t width, int precision, int base,
     426    uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
     427{
     428        assert(base >= 2 && base <= 16);
     429
     430        /* Default precision for numeric output is 1. */
     431        size_t min_digits = (precision < 0) ? 1 : precision;
     432
     433        bool bigchars = flags & __PRINTF_FLAG_BIGCHARS;
     434        bool prefix = flags & __PRINTF_FLAG_PREFIX;
     435        bool left_aligned = flags & __PRINTF_FLAG_LEFTALIGNED;
     436        bool zero_padded = flags & __PRINTF_FLAG_ZEROPADDED;
     437
     438        const char *digits = bigchars ? _digits_big : _digits_small;
     439
     440        char buffer[PRINT_NUMBER_BUFFER_SIZE];
     441        char *end = &buffer[PRINT_NUMBER_BUFFER_SIZE];
     442
     443        /* Write number to the buffer. */
     444        int offset = 0;
     445        while (num > 0) {
     446                end[--offset] = digits[num % base];
     447                num /= base;
     448        }
     449
     450        char *number = &end[offset];
     451        size_t number_len = end - number;
     452        char sign = _sign(flags);
     453
     454        if (left_aligned) {
     455                /* Space padded right-aligned. */
     456                size_t real_size = max(number_len, min_digits);
     457
     458                if (sign) {
     459                        TRY(_write_char(sign, ps, written_bytes));
     460                        real_size++;
     461                }
     462
     463                if (prefix && base == 2 && number_len > 0) {
     464                        TRY(_write_bytes(bigchars ? "0B" : "0b", 2, ps, written_bytes));
     465                        real_size += 2;
     466                }
     467
     468                if (prefix && base == 16 && number_len > 0) {
     469                        TRY(_write_bytes(bigchars ? "0X" : "0x", 2, ps, written_bytes));
     470                        real_size += 2;
     471                }
     472
     473                if (min_digits > number_len) {
     474                        TRY(_write_zeros(min_digits - number_len, ps, written_bytes));
     475                } else if (prefix && base == 8) {
     476                        TRY(_write_zeros(1, ps, written_bytes));
     477                        real_size++;
     478                }
     479
     480                TRY(_write_bytes(number, number_len, ps, written_bytes));
     481
     482                if (width > real_size)
     483                        TRY(_write_spaces(width - real_size, ps, written_bytes));
     484
     485                return EOK;
     486        }
     487
     488        /* Zero padded number (ignored when left aligned or if precision is specified). */
     489        if (precision < 0 && zero_padded) {
     490                size_t real_size = number_len;
     491
     492                if (sign) {
     493                        TRY(_write_char(sign, ps, written_bytes));
     494                        real_size++;
     495                }
     496
     497                if (prefix && base == 2 && number_len > 0) {
     498                        TRY(_write_bytes(bigchars ? "0B" : "0b", 2, ps, written_bytes));
     499                        real_size += 2;
     500                }
     501
     502                if (prefix && base == 16 && number_len > 0) {
     503                        TRY(_write_bytes(bigchars ? "0X" : "0x", 2, ps, written_bytes));
     504                        real_size += 2;
     505                }
     506
     507                if (width > real_size)
     508                        TRY(_write_zeros(width - real_size, ps, written_bytes));
     509                else if (number_len == 0 || (prefix && base == 8))
     510                        TRY(_write_char('0', ps, written_bytes));
     511
     512                return _write_bytes(number, number_len, ps, written_bytes);
     513        }
     514
     515        /* Space padded right-aligned. */
     516        size_t real_size = max(number_len, min_digits);
     517        if (sign)
     518                real_size++;
     519
     520        if (prefix && (base == 2 || base == 16) && number_len > 0)
     521                real_size += 2;
     522
     523        if (prefix && base == 8 && number_len >= min_digits)
     524                real_size += 1;
     525
     526        if (width > real_size)
     527                TRY(_write_spaces(width - real_size, ps, written_bytes));
     528
     529        if (sign)
     530                TRY(_write_char(sign, ps, written_bytes));
     531
     532        if (prefix && base == 2 && number_len > 0)
     533                TRY(_write_bytes(bigchars ? "0B" : "0b", 2, ps, written_bytes));
     534
     535        if (prefix && base == 16 && number_len > 0)
     536                TRY(_write_bytes(bigchars ? "0X" : "0x", 2, ps, written_bytes));
     537
     538        if (min_digits > number_len)
     539                TRY(_write_zeros(min_digits - number_len, ps, written_bytes));
     540        else if (prefix && base == 8)
     541                TRY(_write_char('0', ps, written_bytes));
     542
     543        return _write_bytes(number, number_len, ps, written_bytes);
    555544}
    556545
     
    570559
    571560/** Returns the sign character or 0 if no sign should be printed. */
    572 static int get_sign_char(bool negative, uint32_t flags)
     561static char _get_sign_char(bool negative, uint32_t flags)
    573562{
    574563        if (negative) {
     
    583572}
    584573
    585 /** Prints count times character ch. */
    586 static int print_padding(char ch, int count, printf_spec_t *ps)
    587 {
    588         for (int i = 0; i < count; ++i) {
    589                 if (ps->str_write(&ch, 1, ps->data) < 0) {
    590                         return -1;
    591                 }
    592         }
    593 
    594         return count;
    595 }
    596 
    597574/** Prints a special double (ie NaN, infinity) padded to width characters. */
    598 static int print_special(ieee_double_t val, int width, uint32_t flags,
    599     printf_spec_t *ps)
     575static errno_t _format_special(ieee_double_t val, int width, uint32_t flags,
     576    printf_spec_t *ps, size_t *written_bytes)
    600577{
    601578        assert(val.is_special);
    602579
    603         char sign = get_sign_char(val.is_negative, flags);
     580        char sign = _get_sign_char(val.is_negative, flags);
    604581
    605582        const int str_len = 3;
     
    614591        int padding_len = max(0, width - ((sign ? 1 : 0) + str_len));
    615592
    616         int counter = 0;
    617         int ret;
    618 
    619593        /* Leading padding. */
    620         if (!(flags & __PRINTF_FLAG_LEFTALIGNED)) {
    621                 if ((ret = print_padding(' ', padding_len, ps)) < 0)
    622                         return -1;
    623 
    624                 counter += ret;
    625         }
    626 
    627         if (sign) {
    628                 if ((ret = ps->str_write(&sign, 1, ps->data)) < 0)
    629                         return -1;
    630 
    631                 counter += ret;
    632         }
    633 
    634         if ((ret = ps->str_write(str, str_len, ps->data)) < 0)
    635                 return -1;
    636 
    637         counter += ret;
     594        if (!(flags & __PRINTF_FLAG_LEFTALIGNED))
     595                TRY(_write_spaces(padding_len, ps, written_bytes));
     596
     597        if (sign)
     598                TRY(_write_char(sign, ps, written_bytes));
     599
     600        TRY(_write_bytes(str, str_len, ps, written_bytes));
    638601
    639602        /* Trailing padding. */
    640         if (flags & __PRINTF_FLAG_LEFTALIGNED) {
    641                 if ((ret = print_padding(' ', padding_len, ps)) < 0)
    642                         return -1;
    643 
    644                 counter += ret;
    645         }
    646 
    647         return counter;
     603        if (flags & __PRINTF_FLAG_LEFTALIGNED)
     604                TRY(_write_spaces(padding_len, ps, written_bytes));
     605
     606        return EOK;
    648607}
    649608
    650609/** Trims trailing zeros but leaves a single "0" intact. */
    651 static void fp_trim_trailing_zeros(char *buf, int *len, int *dec_exp)
     610static void _fp_trim_trailing_zeros(char *buf, int *len, int *dec_exp)
    652611{
    653612        /* Cut the zero off by adjusting the exponent. */
     
    659618
    660619/** Textually round up the last digit thereby eliminating it. */
    661 static void fp_round_up(char *buf, int *len, int *dec_exp)
     620static void _fp_round_up(char *buf, int *len, int *dec_exp)
    662621{
    663622        assert(1 <= *len);
     
    703662 *  to the %f specifier.
    704663 */
    705 static int print_double_str_fixed(double_str_t *val_str, int precision, int width,
    706     uint32_t flags, printf_spec_t *ps)
     664static errno_t _format_double_str_fixed(double_str_t *val_str, int precision, int width,
     665    uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
    707666{
    708667        int len = val_str->len;
     
    717676        int int_len = max(1, len + dec_exp);
    718677
    719         char sign = get_sign_char(val_str->neg, flags);
     678        char sign = _get_sign_char(val_str->neg, flags);
    720679
    721680        /* Fractional portion lengths. */
     
    726685        char *buf_frac = buf + len - signif_frac_figs;
    727686
    728         if (flags & __PRINTF_FLAG_NOFRACZEROS) {
     687        if (flags & __PRINTF_FLAG_NOFRACZEROS)
    729688                trailing_frac_zeros = 0;
    730         }
    731689
    732690        int frac_len = leading_frac_zeros + signif_frac_figs + trailing_frac_zeros;
     
    738696
    739697        int padding_len = max(0, width - num_len);
    740         int ret = 0;
    741         int counter = 0;
    742698
    743699        /* Leading padding and sign. */
    744700
    745         if (!(flags & (__PRINTF_FLAG_LEFTALIGNED | __PRINTF_FLAG_ZEROPADDED))) {
    746                 if ((ret = print_padding(' ', padding_len, ps)) < 0)
    747                         return -1;
    748 
    749                 counter += ret;
    750         }
    751 
    752         if (sign) {
    753                 if ((ret = ps->str_write(&sign, 1, ps->data)) < 0)
    754                         return -1;
    755 
    756                 counter += ret;
    757         }
    758 
    759         if (flags & __PRINTF_FLAG_ZEROPADDED) {
    760                 if ((ret = print_padding('0', padding_len, ps)) < 0)
    761                         return -1;
    762 
    763                 counter += ret;
    764         }
     701        if (!(flags & (__PRINTF_FLAG_LEFTALIGNED | __PRINTF_FLAG_ZEROPADDED)))
     702                TRY(_write_spaces(padding_len, ps, written_bytes));
     703
     704        if (sign)
     705                TRY(_write_char(sign, ps, written_bytes));
     706
     707        if (flags & __PRINTF_FLAG_ZEROPADDED)
     708                TRY(_write_zeros(padding_len, ps, written_bytes));
    765709
    766710        /* Print the intergral part of the buffer. */
     
    769713
    770714        if (0 < buf_int_len) {
    771                 if ((ret = ps->str_write(buf, buf_int_len, ps->data)) < 0)
    772                         return -1;
    773 
    774                 counter += ret;
     715                TRY(_write_bytes(buf, buf_int_len, ps, written_bytes));
    775716
    776717                /* Print trailing zeros of the integral part of the number. */
    777                 if ((ret = print_padding('0', int_len - buf_int_len, ps)) < 0)
    778                         return -1;
     718                TRY(_write_zeros(int_len - buf_int_len, ps, written_bytes));
    779719        } else {
    780720                /* Single leading integer 0. */
    781                 char ch = '0';
    782                 if ((ret = ps->str_write(&ch, 1, ps->data)) < 0)
    783                         return -1;
    784         }
    785 
    786         counter += ret;
     721                TRY(_write_char('0', ps, written_bytes));
     722        }
    787723
    788724        /* Print the decimal point and the fractional part. */
    789725        if (has_decimal_pt) {
    790                 char ch = '.';
    791 
    792                 if ((ret = ps->str_write(&ch, 1, ps->data)) < 0)
    793                         return -1;
    794 
    795                 counter += ret;
     726                TRY(_write_char('.', ps, written_bytes));
    796727
    797728                /* Print leading zeros of the fractional part of the number. */
    798                 if ((ret = print_padding('0', leading_frac_zeros, ps)) < 0)
    799                         return -1;
    800 
    801                 counter += ret;
     729                TRY(_write_zeros(leading_frac_zeros, ps, written_bytes));
    802730
    803731                /* Print significant digits of the fractional part of the number. */
    804                 if (0 < signif_frac_figs) {
    805                         if ((ret = ps->str_write(buf_frac, signif_frac_figs, ps->data)) < 0)
    806                                 return -1;
    807 
    808                         counter += ret;
    809                 }
     732                if (0 < signif_frac_figs)
     733                        TRY(_write_bytes(buf_frac, signif_frac_figs, ps, written_bytes));
    810734
    811735                /* Print trailing zeros of the fractional part of the number. */
    812                 if ((ret = print_padding('0', trailing_frac_zeros, ps)) < 0)
    813                         return -1;
    814 
    815                 counter += ret;
     736                TRY(_write_zeros(trailing_frac_zeros, ps, written_bytes));
    816737        }
    817738
    818739        /* Trailing padding. */
    819         if (flags & __PRINTF_FLAG_LEFTALIGNED) {
    820                 if ((ret = print_padding(' ', padding_len, ps)) < 0)
    821                         return -1;
    822 
    823                 counter += ret;
    824         }
    825 
    826         return counter;
     740        if (flags & __PRINTF_FLAG_LEFTALIGNED)
     741                TRY(_write_spaces(padding_len, ps, written_bytes));
     742
     743        return EOK;
    827744}
    828745
     
    837754 * @param flags Printf flags.
    838755 * @param ps    Printing functions.
    839  *
    840  * @return The number of characters printed; negative on failure.
    841  */
    842 static int print_double_fixed(double g, int precision, int width, uint32_t flags,
    843     printf_spec_t *ps)
     756 */
     757static errno_t _format_double_fixed(double g, int precision, int width,
     758        uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
    844759{
    845760        if (flags & __PRINTF_FLAG_LEFTALIGNED) {
     
    854769
    855770        if (val.is_special) {
    856                 return print_special(val, width, flags, ps);
     771                return _format_special(val, width, flags, ps, written_bytes);
    857772        }
    858773
     
    877792                 * digit is definitely inaccurate so also round to get rid of it.
    878793                 */
    879                 fp_round_up(buf, &val_str.len, &val_str.dec_exp);
     794                _fp_round_up(buf, &val_str.len, &val_str.dec_exp);
    880795
    881796                /* Rounding could have introduced trailing zeros. */
    882797                if (flags & __PRINTF_FLAG_NOFRACZEROS) {
    883                         fp_trim_trailing_zeros(buf, &val_str.len, &val_str.dec_exp);
     798                        _fp_trim_trailing_zeros(buf, &val_str.len, &val_str.dec_exp);
    884799                }
    885800        } else {
     
    891806        }
    892807
    893         return print_double_str_fixed(&val_str, precision, width, flags, ps);
     808        return _format_double_str_fixed(&val_str, precision, width, flags, ps, written_bytes);
    894809}
    895810
    896811/** Prints the decimal exponent part of a %e specifier formatted number. */
    897 static int print_exponent(int exp_val, uint32_t flags, printf_spec_t *ps)
    898 {
    899         int counter = 0;
    900         int ret;
    901 
     812static errno_t _format_exponent(int exp_val, uint32_t flags, printf_spec_t *ps,
     813    size_t *written_bytes)
     814{
    902815        char exp_ch = (flags & __PRINTF_FLAG_BIGCHARS) ? 'E' : 'e';
    903 
    904         if ((ret = ps->str_write(&exp_ch, 1, ps->data)) < 0)
    905                 return -1;
    906 
    907         counter += ret;
     816        TRY(_write_char(exp_ch, ps, written_bytes));
    908817
    909818        char exp_sign = (exp_val < 0) ? '-' : '+';
    910 
    911         if ((ret = ps->str_write(&exp_sign, 1, ps->data)) < 0)
    912                 return -1;
    913 
    914         counter += ret;
     819        TRY(_write_char(exp_sign, ps, written_bytes));
    915820
    916821        /* Print the exponent. */
     
    926831        const char *exp_str_start = &exp_str[3] - exp_len;
    927832
    928         if ((ret = ps->str_write(exp_str_start, exp_len, ps->data)) < 0)
    929                 return -1;
    930 
    931         counter += ret;
    932 
    933         return counter;
     833        return _write_bytes(exp_str_start, exp_len, ps, written_bytes);
    934834}
    935835
     
    937837 *  to the %e specifier.
    938838 */
    939 static int print_double_str_scient(double_str_t *val_str, int precision,
    940     int width, uint32_t flags, printf_spec_t *ps)
     839static errno_t _format_double_str_scient(double_str_t *val_str, int precision,
     840    int width, uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
    941841{
    942842        int len = val_str->len;
     
    946846        assert(0 < len);
    947847
    948         char sign = get_sign_char(val_str->neg, flags);
     848        char sign = _get_sign_char(val_str->neg, flags);
    949849        bool has_decimal_pt = (0 < precision) || (flags & __PRINTF_FLAG_DECIMALPT);
    950850        int dec_pt_len = has_decimal_pt ? 1 : 0;
     
    968868
    969869        int padding_len = max(0, width - num_len);
    970         int ret = 0;
    971         int counter = 0;
    972 
    973         if (!(flags & (__PRINTF_FLAG_LEFTALIGNED | __PRINTF_FLAG_ZEROPADDED))) {
    974                 if ((ret = print_padding(' ', padding_len, ps)) < 0)
    975                         return -1;
    976 
    977                 counter += ret;
    978         }
    979 
    980         if (sign) {
    981                 if ((ret = ps->str_write(&sign, 1, ps->data)) < 0)
    982                         return -1;
    983 
    984                 counter += ret;
    985         }
    986 
    987         if (flags & __PRINTF_FLAG_ZEROPADDED) {
    988                 if ((ret = print_padding('0', padding_len, ps)) < 0)
    989                         return -1;
    990 
    991                 counter += ret;
    992         }
     870
     871        if (!(flags & (__PRINTF_FLAG_LEFTALIGNED | __PRINTF_FLAG_ZEROPADDED)))
     872                TRY(_write_spaces(padding_len, ps, written_bytes));
     873
     874        if (sign)
     875                TRY(_write_char(sign, ps, written_bytes));
     876
     877        if (flags & __PRINTF_FLAG_ZEROPADDED)
     878                TRY(_write_zeros(padding_len, ps, written_bytes));
    993879
    994880        /* Single leading integer. */
    995         if ((ret = ps->str_write(buf, 1, ps->data)) < 0)
    996                 return -1;
    997 
    998         counter += ret;
     881        TRY(_write_char(buf[0], ps, written_bytes));
    999882
    1000883        /* Print the decimal point and the fractional part. */
    1001884        if (has_decimal_pt) {
    1002                 char ch = '.';
    1003 
    1004                 if ((ret = ps->str_write(&ch, 1, ps->data)) < 0)
    1005                         return -1;
    1006 
    1007                 counter += ret;
     885                TRY(_write_char('.', ps, written_bytes));
    1008886
    1009887                /* Print significant digits of the fractional part of the number. */
    1010                 if (0 < signif_frac_figs) {
    1011                         if ((ret = ps->str_write(buf + 1, signif_frac_figs, ps->data)) < 0)
    1012                                 return -1;
    1013 
    1014                         counter += ret;
    1015                 }
     888                if (0 < signif_frac_figs)
     889                        TRY(_write_bytes(buf + 1, signif_frac_figs, ps, written_bytes));
    1016890
    1017891                /* Print trailing zeros of the fractional part of the number. */
    1018                 if ((ret = print_padding('0', trailing_frac_zeros, ps)) < 0)
    1019                         return -1;
    1020 
    1021                 counter += ret;
     892                TRY(_write_zeros(trailing_frac_zeros, ps, written_bytes));
    1022893        }
    1023894
    1024895        /* Print the exponent. */
    1025         if ((ret = print_exponent(exp_val, flags, ps)) < 0)
    1026                 return -1;
    1027 
    1028         counter += ret;
    1029 
    1030         if (flags & __PRINTF_FLAG_LEFTALIGNED) {
    1031                 if ((ret = print_padding(' ', padding_len, ps)) < 0)
    1032                         return -1;
    1033 
    1034                 counter += ret;
    1035         }
    1036 
    1037         return counter;
     896        TRY(_format_exponent(exp_val, flags, ps, written_bytes));
     897
     898        if (flags & __PRINTF_FLAG_LEFTALIGNED)
     899                TRY(_write_spaces(padding_len, ps, written_bytes));
     900
     901        return EOK;
    1038902}
    1039903
     
    1057921 * @param flags Printf flags.
    1058922 * @param ps    Printing functions.
    1059  *
    1060  * @return The number of characters printed; negative on failure.
    1061  */
    1062 static int print_double_scientific(double g, int precision, int width,
    1063     uint32_t flags, printf_spec_t *ps)
    1064 {
    1065         if (flags & __PRINTF_FLAG_LEFTALIGNED) {
     923 */
     924static errno_t _format_double_scientific(double g, int precision, int width,
     925    uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
     926{
     927        if (flags & __PRINTF_FLAG_LEFTALIGNED)
    1066928                flags &= ~__PRINTF_FLAG_ZEROPADDED;
    1067         }
    1068929
    1069930        ieee_double_t val = extract_ieee_double(g);
    1070931
    1071         if (val.is_special) {
    1072                 return print_special(val, width, flags, ps);
    1073         }
     932        if (val.is_special)
     933                return _format_special(val, width, flags, ps, written_bytes);
    1074934
    1075935        char buf[MAX_DOUBLE_STR_BUF_SIZE];
     
    1094954                 * digit is definitely inaccurate so also round to get rid of it.
    1095955                 */
    1096                 fp_round_up(buf, &val_str.len, &val_str.dec_exp);
     956                _fp_round_up(buf, &val_str.len, &val_str.dec_exp);
    1097957
    1098958                /* Rounding could have introduced trailing zeros. */
    1099959                if (flags & __PRINTF_FLAG_NOFRACZEROS) {
    1100                         fp_trim_trailing_zeros(buf, &val_str.len, &val_str.dec_exp);
     960                        _fp_trim_trailing_zeros(buf, &val_str.len, &val_str.dec_exp);
    1101961                }
    1102962        } else {
     
    1108968        }
    1109969
    1110         return print_double_str_scient(&val_str, precision, width, flags, ps);
     970        return _format_double_str_scient(&val_str, precision, width, flags, ps, written_bytes);
    1111971}
    1112972
     
    1126986 * @return The number of characters printed; negative on failure.
    1127987 */
    1128 static int print_double_generic(double g, int precision, int width,
    1129     uint32_t flags, printf_spec_t *ps)
     988static errno_t _format_double_generic(double g, int precision, int width,
     989    uint32_t flags, printf_spec_t *ps, size_t *written_bytes)
    1130990{
    1131991        ieee_double_t val = extract_ieee_double(g);
    1132992
    1133         if (val.is_special) {
    1134                 return print_special(val, width, flags, ps);
    1135         }
     993        if (val.is_special)
     994                return _format_special(val, width, flags, ps, written_bytes);
    1136995
    1137996        char buf[MAX_DOUBLE_STR_BUF_SIZE];
     
    11531012                if (-4 <= dec_exp && dec_exp < precision) {
    11541013                        precision = precision - (dec_exp + 1);
    1155                         return print_double_fixed(g, precision, width,
    1156                             flags | __PRINTF_FLAG_NOFRACZEROS, ps);
     1014                        return _format_double_fixed(g, precision, width,
     1015                            flags | __PRINTF_FLAG_NOFRACZEROS, ps, written_bytes);
    11571016                } else {
    11581017                        --precision;
    1159                         return print_double_scientific(g, precision, width,
    1160                             flags | __PRINTF_FLAG_NOFRACZEROS, ps);
     1018                        return _format_double_scientific(g, precision, width,
     1019                            flags | __PRINTF_FLAG_NOFRACZEROS, ps, written_bytes);
    11611020                }
    11621021        } else {
     
    11821041                        /* Precision needed for the last significant digit. */
    11831042                        precision = max(0, -val_str.dec_exp);
    1184                         return print_double_str_fixed(&val_str, precision, width, flags, ps);
     1043                        return _format_double_str_fixed(&val_str, precision, width, flags, ps, written_bytes);
    11851044                } else {
    11861045                        /* Use all produced digits. */
    11871046                        precision = val_str.len - 1;
    1188                         return print_double_str_scient(&val_str, precision, width, flags, ps);
     1047                        return _format_double_str_scient(&val_str, precision, width, flags, ps, written_bytes);
    11891048                }
    11901049        }
     
    12071066 * @param flags Printf flags.
    12081067 * @param ps    Printing functions.
    1209  *
    1210  * @return The number of characters printed; negative on failure.
    1211  */
    1212 static int print_double(double g, char spec, int precision, int width,
    1213     uint32_t flags, printf_spec_t *ps)
     1068 */
     1069static errno_t _format_double(double g, char spec, int precision, int width,
     1070    uint32_t flags, printf_spec_t *ps, size_t *written_chars)
    12141071{
    12151072        switch (spec) {
     
    12191076        case 'f':
    12201077                precision = (precision < 0) ? 6 : precision;
    1221                 return print_double_fixed(g, precision, width, flags, ps);
     1078                return _format_double_fixed(g, precision, width, flags, ps, written_chars);
    12221079
    12231080        case 'E':
     
    12261083        case 'e':
    12271084                precision = (precision < 0) ? 6 : precision;
    1228                 return print_double_scientific(g, precision, width, flags, ps);
     1085                return _format_double_scientific(g, precision, width, flags, ps, written_chars);
    12291086
    12301087        case 'G':
     
    12321089                /* Fallthrough */
    12331090        case 'g':
    1234                 return print_double_generic(g, precision, width, flags, ps);
     1091                return _format_double_generic(g, precision, width, flags, ps, written_chars);
    12351092
    12361093        default:
     
    12411098
    12421099#endif
     1100
     1101static const char *_strchrnul(const char *s, int c)
     1102{
     1103        while (*s != c && *s != 0)
     1104                s++;
     1105        return s;
     1106}
     1107
     1108/** Read a sequence of digits from the format string as a number.
     1109 * If the number has too many digits to fit in int, returns INT_MAX.
     1110 */
     1111static int _read_num(const char *fmt, size_t *i)
     1112{
     1113        const char *s;
     1114        unsigned n = 0;
     1115
     1116        for (s = &fmt[*i]; isdigit(*s); s++) {
     1117                unsigned digit = (*s - '0');
     1118
     1119                /* Check for overflow */
     1120                if (n > INT_MAX / 10 || n * 10 > INT_MAX - digit) {
     1121                        n = INT_MAX;
     1122                        while (isdigit(*s))
     1123                                s++;
     1124                        break;
     1125                }
     1126
     1127                n = n * 10 + digit;
     1128        }
     1129
     1130        *i = s - fmt;
     1131        return n;
     1132}
     1133
     1134static uint32_t _parse_flags(const char *fmt, size_t *i)
     1135{
     1136        uint32_t flags = 0;
     1137
     1138        while (true) {
     1139                switch (fmt[(*i)++]) {
     1140                case '#':
     1141                        flags |= __PRINTF_FLAG_PREFIX;
     1142                        flags |= __PRINTF_FLAG_DECIMALPT;
     1143                        continue;
     1144                case '-':
     1145                        flags |= __PRINTF_FLAG_LEFTALIGNED;
     1146                        continue;
     1147                case '+':
     1148                        flags |= __PRINTF_FLAG_SHOWPLUS;
     1149                        continue;
     1150                case ' ':
     1151                        flags |= __PRINTF_FLAG_SPACESIGN;
     1152                        continue;
     1153                case '0':
     1154                        flags |= __PRINTF_FLAG_ZEROPADDED;
     1155                        continue;
     1156                }
     1157
     1158                --*i;
     1159                break;
     1160        }
     1161
     1162        return flags;
     1163}
     1164
     1165static bool _eat_char(const char *s, size_t *idx, int c)
     1166{
     1167        if (s[*idx] != c)
     1168                return false;
     1169
     1170        (*idx)++;
     1171        return true;
     1172}
     1173
     1174static qualifier_t _read_qualifier(const char *s, size_t *idx)
     1175{
     1176        switch (s[(*idx)++]) {
     1177        case 't': /* ptrdiff_t */
     1178        case 'z': /* size_t */
     1179                if (sizeof(ptrdiff_t) == sizeof(int))
     1180                        return PrintfQualifierInt;
     1181                else
     1182                        return PrintfQualifierLong;
     1183
     1184        case 'h':
     1185                if (_eat_char(s, idx, 'h'))
     1186                        return PrintfQualifierByte;
     1187                else
     1188                        return PrintfQualifierShort;
     1189
     1190        case 'l':
     1191                if (_eat_char(s, idx, 'l'))
     1192                        return PrintfQualifierLongLong;
     1193                else
     1194                        return PrintfQualifierLong;
     1195
     1196        case 'j':
     1197                return PrintfQualifierLongLong;
     1198
     1199        default:
     1200                --*idx;
     1201
     1202                /* Unspecified */
     1203                return PrintfQualifierInt;
     1204        }
     1205}
    12431206
    12441207/** Print formatted string.
     
    13331296int printf_core(const char *fmt, printf_spec_t *ps, va_list ap)
    13341297{
    1335         size_t i;        /* Index of the currently processed character from fmt */
     1298        errno_t rc = EOK;
    13361299        size_t nxt = 0;  /* Index of the next character from fmt */
    1337         size_t j = 0;    /* Index to the first not printed nonformating character */
    13381300
    13391301        size_t counter = 0;   /* Number of characters printed */
    1340         int retval;           /* Return values from nested functions */
    1341 
    1342         while (true) {
    1343                 i = nxt;
    1344                 char32_t uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1345 
    1346                 if (uc == 0)
    1347                         break;
    1348 
    1349                 /* Control character */
    1350                 if (uc == '%') {
    1351                         /* Print common characters if any processed */
    1352                         if (i > j) {
    1353                                 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
    1354                                         /* Error */
    1355                                         counter = -counter;
    1356                                         goto out;
    1357                                 }
    1358                                 counter += retval;
     1302
     1303        while (rc == EOK) {
     1304                /* Find the next specifier and write all the bytes before it. */
     1305                const char *s = _strchrnul(&fmt[nxt], '%');
     1306                size_t bytes = s - &fmt[nxt];
     1307                rc = _write_bytes(&fmt[nxt], bytes, ps, &counter);
     1308                if (rc != EOK)
     1309                        break;
     1310
     1311                nxt += bytes;
     1312
     1313                /* Check for end of string. */
     1314                if (_eat_char(fmt, &nxt, 0))
     1315                        break;
     1316
     1317                /* We must be at the start of a specifier. */
     1318                bool spec = _eat_char(fmt, &nxt, '%');
     1319                assert(spec);
     1320
     1321                /* Parse modifiers */
     1322                uint32_t flags = _parse_flags(fmt, &nxt);
     1323
     1324                /* Width & '*' operator */
     1325                int width = -1;
     1326                if (_eat_char(fmt, &nxt, '*')) {
     1327                        /* Get width value from argument list */
     1328                        width = va_arg(ap, int);
     1329
     1330                        if (width < 0) {
     1331                                /* Negative width sets '-' flag */
     1332                                width = (width == INT_MIN) ? INT_MAX : -width;
     1333                                flags |= __PRINTF_FLAG_LEFTALIGNED;
    13591334                        }
    1360 
    1361                         j = i;
    1362 
    1363                         /* Parse modifiers */
    1364                         uint32_t flags = 0;
    1365                         bool end = false;
    1366 
    1367                         do {
    1368                                 i = nxt;
    1369                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1370                                 switch (uc) {
    1371                                 case '#':
    1372                                         flags |= __PRINTF_FLAG_PREFIX;
    1373                                         flags |= __PRINTF_FLAG_DECIMALPT;
    1374                                         break;
    1375                                 case '-':
    1376                                         flags |= __PRINTF_FLAG_LEFTALIGNED;
    1377                                         break;
    1378                                 case '+':
    1379                                         flags |= __PRINTF_FLAG_SHOWPLUS;
    1380                                         break;
    1381                                 case ' ':
    1382                                         flags |= __PRINTF_FLAG_SPACESIGN;
    1383                                         break;
    1384                                 case '0':
    1385                                         flags |= __PRINTF_FLAG_ZEROPADDED;
    1386                                         break;
    1387                                 default:
    1388                                         end = true;
    1389                                 }
    1390                         } while (!end);
    1391 
    1392                         /* Width & '*' operator */
    1393                         int width = 0;
    1394                         if (isdigit(uc)) {
    1395                                 while (true) {
    1396                                         width *= 10;
    1397                                         width += uc - '0';
    1398 
    1399                                         i = nxt;
    1400                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1401                                         if (uc == 0)
    1402                                                 break;
    1403                                         if (!isdigit(uc))
    1404                                                 break;
    1405                                 }
    1406                         } else if (uc == '*') {
    1407                                 /* Get width value from argument list */
    1408                                 i = nxt;
    1409                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1410                                 width = (int) va_arg(ap, int);
    1411                                 if (width < 0) {
    1412                                         /* Negative width sets '-' flag */
    1413                                         width *= -1;
    1414                                         flags |= __PRINTF_FLAG_LEFTALIGNED;
    1415                                 }
     1335                } else {
     1336                        width = _read_num(fmt, &nxt);
     1337                }
     1338
     1339                /* Precision and '*' operator */
     1340                int precision = -1;
     1341                if (_eat_char(fmt, &nxt, '.')) {
     1342                        if (_eat_char(fmt, &nxt, '*')) {
     1343                                /* Get precision value from the argument list */
     1344                                precision = va_arg(ap, int);
     1345
     1346                                /* Negative is treated as omitted. */
     1347                                if (precision < 0)
     1348                                        precision = -1;
     1349                        } else {
     1350                                precision = _read_num(fmt, &nxt);
    14161351                        }
    1417 
    1418                         /* Precision and '*' operator */
    1419                         int precision = -1;
    1420                         if (uc == '.') {
    1421                                 i = nxt;
    1422                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1423                                 if (isdigit(uc)) {
    1424                                         precision = 0;
    1425                                         while (true) {
    1426                                                 precision *= 10;
    1427                                                 precision += uc - '0';
    1428 
    1429                                                 i = nxt;
    1430                                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1431                                                 if (uc == 0)
    1432                                                         break;
    1433                                                 if (!isdigit(uc))
    1434                                                         break;
    1435                                         }
    1436                                 } else if (uc == '*') {
    1437                                         /* Get precision value from the argument list */
    1438                                         i = nxt;
    1439                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1440                                         precision = (int) va_arg(ap, int);
    1441                                         if (precision < 0) {
    1442                                                 /* Ignore negative precision - use default instead */
    1443                                                 precision = -1;
    1444                                         }
    1445                                 }
    1446                         }
    1447 
    1448                         qualifier_t qualifier;
    1449 
    1450                         switch (uc) {
    1451                         case 't':
    1452                                 /* ptrdiff_t */
    1453                                 if (sizeof(ptrdiff_t) == sizeof(int32_t))
    1454                                         qualifier = PrintfQualifierInt;
    1455                                 else
    1456                                         qualifier = PrintfQualifierLongLong;
    1457                                 i = nxt;
    1458                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1459                                 break;
    1460                         case 'h':
    1461                                 /* Char or short */
    1462                                 qualifier = PrintfQualifierShort;
    1463                                 i = nxt;
    1464                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1465                                 if (uc == 'h') {
    1466                                         i = nxt;
    1467                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1468                                         qualifier = PrintfQualifierByte;
    1469                                 }
    1470                                 break;
    1471                         case 'l':
    1472                                 /* Long or long long */
    1473                                 qualifier = PrintfQualifierLong;
    1474                                 i = nxt;
    1475                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1476                                 if (uc == 'l') {
    1477                                         i = nxt;
    1478                                         uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1479                                         qualifier = PrintfQualifierLongLong;
    1480                                 }
    1481                                 break;
    1482                         case 'z':
    1483                                 qualifier = PrintfQualifierSize;
    1484                                 i = nxt;
    1485                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1486                                 break;
    1487                         case 'j':
    1488                                 qualifier = PrintfQualifierMax;
    1489                                 i = nxt;
    1490                                 uc = str_decode(fmt, &nxt, STR_NO_LIMIT);
    1491                                 break;
    1492                         default:
    1493                                 /* Default type */
    1494                                 qualifier = PrintfQualifierInt;
    1495                         }
    1496 
    1497                         unsigned int base = 10;
    1498 
    1499                         switch (uc) {
    1500                                 /*
    1501                                  * String and character conversions.
    1502                                  */
    1503                         case 's':
    1504                                 precision = max(0,  precision);
    1505 
    1506                                 if (qualifier == PrintfQualifierLong)
    1507                                         retval = print_wstr(va_arg(ap, char32_t *), width, precision, flags, ps);
    1508                                 else
    1509                                         retval = print_str(va_arg(ap, char *), width, precision, flags, ps);
    1510 
    1511                                 if (retval < 0) {
    1512                                         counter = -counter;
    1513                                         goto out;
    1514                                 }
    1515 
    1516                                 counter += retval;
    1517                                 j = nxt;
    1518                                 continue;
    1519                         case 'c':
    1520                                 if (qualifier == PrintfQualifierLong)
    1521                                         retval = print_wchar(va_arg(ap, wint_t), width, flags, ps);
    1522                                 else
    1523                                         retval = print_char(va_arg(ap, unsigned int), width, flags, ps);
    1524 
    1525                                 if (retval < 0) {
    1526                                         counter = -counter;
    1527                                         goto out;
    1528                                 }
    1529 
    1530                                 counter += retval;
    1531                                 j = nxt;
    1532                                 continue;
    1533 
     1352                }
     1353
     1354                qualifier_t qualifier = _read_qualifier(fmt, &nxt);
     1355                unsigned int base = 10;
     1356                char specifier = fmt[nxt++];
     1357
     1358                switch (specifier) {
     1359                /*
     1360                 * String and character conversions.
     1361                 */
     1362                case 's':
     1363                        if (qualifier == PrintfQualifierLong)
     1364                                rc = _format_wstr(va_arg(ap, char32_t *), width, precision, flags, ps, &counter);
     1365                        else
     1366                                rc = _format_cstr(va_arg(ap, char *), width, precision, flags, ps, &counter);
     1367                        continue;
     1368
     1369                case 'c':
     1370                        if (qualifier == PrintfQualifierLong)
     1371                                rc = _format_uchar(va_arg(ap, wint_t), width, flags, ps, &counter);
     1372                        else
     1373                                rc = _format_char(va_arg(ap, int), width, flags, ps, &counter);
     1374                        continue;
     1375
     1376                /*
     1377                 * Floating point values
     1378                 */
     1379                case 'G':
     1380                case 'g':
     1381                case 'F':
     1382                case 'f':
     1383                case 'E':
     1384                case 'e':;
    15341385#ifdef HAS_FLOAT
    1535                                 /*
    1536                                  * Floating point values
    1537                                  */
    1538                         case 'G':
    1539                         case 'g':
    1540                         case 'F':
    1541                         case 'f':
    1542                         case 'E':
    1543                         case 'e':
    1544                                 retval = print_double(va_arg(ap, double), uc, precision,
    1545                                     width, flags, ps);
    1546 
    1547                                 if (retval < 0) {
    1548                                         counter = -counter;
    1549                                         goto out;
    1550                                 }
    1551 
    1552                                 counter += retval;
    1553                                 j = nxt;
    1554                                 continue;
     1386                        rc = _format_double(va_arg(ap, double), specifier, precision,
     1387                            width, flags, ps, &counter);
     1388#else
     1389                        rc = _format_cstr("<float unsupported>", width, -1, 0, ps, &counter);
    15551390#endif
    1556 
    1557                                 /*
    1558                                  * Integer values
    1559                                  */
    1560                         case 'P':
    1561                                 /* Pointer */
    1562                                 flags |= __PRINTF_FLAG_BIGCHARS;
    1563                                 /* Fallthrough */
    1564                         case 'p':
    1565                                 flags |= __PRINTF_FLAG_PREFIX;
    1566                                 flags |= __PRINTF_FLAG_ZEROPADDED;
    1567                                 base = 16;
    1568                                 qualifier = PrintfQualifierPointer;
    1569                                 break;
    1570                         case 'b':
    1571                                 base = 2;
    1572                                 break;
    1573                         case 'o':
    1574                                 base = 8;
    1575                                 break;
    1576                         case 'd':
    1577                         case 'i':
    1578                                 flags |= __PRINTF_FLAG_SIGNED;
    1579                                 /* Fallthrough */
    1580                         case 'u':
    1581                                 break;
    1582                         case 'X':
    1583                                 flags |= __PRINTF_FLAG_BIGCHARS;
    1584                                 /* Fallthrough */
    1585                         case 'x':
    1586                                 base = 16;
    1587                                 break;
    1588 
    1589                         case '%':
    1590                                 /* Percentile itself */
    1591                                 j = i;
    1592                                 continue;
    1593 
    1594                                 /*
    1595                                  * Bad formatting.
    1596                                  */
    1597                         default:
    1598                                 /*
    1599                                  * Unknown format. Now, j is the index of '%'
    1600                                  * so we will print whole bad format sequence.
    1601                                  */
    1602                                 continue;
    1603                         }
    1604 
    1605                         /* Print integers */
    1606                         size_t size;
    1607                         uint64_t number;
    1608 
    1609                         switch (qualifier) {
    1610                         case PrintfQualifierByte:
    1611                                 size = sizeof(unsigned char);
    1612                                 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
    1613                                 break;
    1614                         case PrintfQualifierShort:
    1615                                 size = sizeof(unsigned short);
    1616                                 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
    1617                                 break;
    1618                         case PrintfQualifierInt:
    1619                                 size = sizeof(unsigned int);
    1620                                 number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
    1621                                 break;
    1622                         case PrintfQualifierLong:
    1623                                 size = sizeof(unsigned long);
    1624                                 number = PRINTF_GET_INT_ARGUMENT(long, ap, flags);
    1625                                 break;
    1626                         case PrintfQualifierLongLong:
    1627                                 size = sizeof(unsigned long long);
    1628                                 number = PRINTF_GET_INT_ARGUMENT(long long, ap, flags);
    1629                                 break;
    1630                         case PrintfQualifierPointer:
    1631                                 size = sizeof(void *);
    1632                                 precision = size << 1;
    1633                                 number = (uint64_t) (uintptr_t) va_arg(ap, void *);
    1634                                 break;
    1635                         case PrintfQualifierSize:
    1636                                 size = sizeof(size_t);
    1637                                 number = (uint64_t) va_arg(ap, size_t);
    1638                                 break;
    1639                         case PrintfQualifierMax:
    1640                                 size = sizeof(uintmax_t);
    1641                                 number = (uint64_t) va_arg(ap, uintmax_t);
    1642                                 break;
    1643                         default:
    1644                                 /* Unknown qualifier */
    1645                                 counter = -counter;
    1646                                 goto out;
    1647                         }
    1648 
    1649                         if ((retval = print_number(number, width, precision,
    1650                             base, flags, ps)) < 0) {
    1651                                 counter = -counter;
    1652                                 goto out;
    1653                         }
    1654 
    1655                         counter += retval;
    1656                         j = nxt;
    1657                 }
    1658         }
    1659 
    1660         if (i > j) {
    1661                 if ((retval = printf_putnchars(&fmt[j], i - j, ps)) < 0) {
    1662                         /* Error */
    1663                         counter = -counter;
    1664                         goto out;
    1665                 }
    1666                 counter += retval;
    1667         }
    1668 
    1669 out:
    1670         return ((int) counter);
     1391                        continue;
     1392
     1393                /*
     1394                 * Integer values
     1395                 */
     1396                case 'P':
     1397                        /* Pointer */
     1398                        flags |= __PRINTF_FLAG_BIGCHARS;
     1399                        /* Fallthrough */
     1400                case 'p':
     1401                        flags |= __PRINTF_FLAG_PREFIX;
     1402                        flags |= __PRINTF_FLAG_ZEROPADDED;
     1403                        base = 16;
     1404                        qualifier = PrintfQualifierPointer;
     1405                        break;
     1406                case 'b':
     1407                        base = 2;
     1408                        break;
     1409                case 'o':
     1410                        base = 8;
     1411                        break;
     1412                case 'd':
     1413                case 'i':
     1414                        flags |= __PRINTF_FLAG_SIGNED;
     1415                        break;
     1416                case 'u':
     1417                        break;
     1418                case 'X':
     1419                        flags |= __PRINTF_FLAG_BIGCHARS;
     1420                        /* Fallthrough */
     1421                case 'x':
     1422                        base = 16;
     1423                        break;
     1424
     1425                case '%':
     1426                        /* Percentile itself */
     1427                        rc = _write_char('%', ps, &counter);
     1428                        continue;
     1429
     1430                /*
     1431                 * Bad formatting.
     1432                 */
     1433                default:
     1434                        rc = EINVAL;
     1435                        continue;
     1436                }
     1437
     1438                /* Print integers */
     1439                uint64_t number;
     1440
     1441                switch (qualifier) {
     1442                case PrintfQualifierByte:
     1443                        number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
     1444                        break;
     1445                case PrintfQualifierShort:
     1446                        number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
     1447                        break;
     1448                case PrintfQualifierInt:
     1449                        number = PRINTF_GET_INT_ARGUMENT(int, ap, flags);
     1450                        break;
     1451                case PrintfQualifierLong:
     1452                        number = PRINTF_GET_INT_ARGUMENT(long, ap, flags);
     1453                        break;
     1454                case PrintfQualifierLongLong:
     1455                        number = PRINTF_GET_INT_ARGUMENT(long long, ap, flags);
     1456                        break;
     1457                case PrintfQualifierPointer:
     1458                        precision = sizeof(void *) << 1;
     1459                        number = (uint64_t) (uintptr_t) va_arg(ap, void *);
     1460                        break;
     1461                default:
     1462                        /* Unknown qualifier */
     1463                        rc = EINVAL;
     1464                        continue;
     1465                }
     1466
     1467                rc = _format_number(number, width, precision, base, flags, ps, &counter);
     1468                if (rc != EOK)
     1469                        continue;
     1470        }
     1471
     1472        if (rc != EOK) {
     1473                _set_errno(rc);
     1474                return -1;
     1475        }
     1476
     1477        if (counter > INT_MAX) {
     1478                _set_errno(EOVERFLOW);
     1479                return -1;
     1480        }
     1481
     1482        return (int) counter;
    16711483}
    16721484
Note: See TracChangeset for help on using the changeset viewer.