Coverage Report

Created: 2025-09-05 06:52

/src/serenity/AK/PrintfImplementation.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/Format.h>
10
#include <AK/StdLibExtras.h>
11
#include <AK/Types.h>
12
#include <stdarg.h>
13
14
#ifndef KERNEL
15
#    include <math.h>
16
#    include <wchar.h>
17
#endif
18
19
#ifdef AK_OS_SERENITY
20
extern "C" size_t strlen(char const*);
21
#else
22
#    include <string.h>
23
#endif
24
25
namespace PrintfImplementation {
26
27
template<typename PutChFunc, typename T, typename CharType>
28
ALWAYS_INLINE int print_hex(PutChFunc putch, CharType*& bufptr, T number, bool upper_case, bool alternate_form, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
29
0
{
30
0
    constexpr char const* printf_hex_digits_lower = "0123456789abcdef";
31
0
    constexpr char const* printf_hex_digits_upper = "0123456789ABCDEF";
32
33
0
    u32 digits = 0;
34
0
    for (T n = number; n > 0; n >>= 4)
35
0
        ++digits;
36
0
    if (digits == 0)
37
0
        digits = 1;
38
39
0
    bool not_zero = number != 0;
40
41
0
    char buf[16];
42
0
    char* p = buf;
43
44
0
    if (!(has_precision && precision == 0 && !not_zero)) {
45
0
        if (number == 0) {
46
0
            (*p++) = '0';
47
0
            if (precision > 0)
48
0
                precision--;
49
0
        } else {
50
0
            u8 shift_count = digits * 4;
51
0
            while (shift_count) {
52
0
                shift_count -= 4;
53
0
                (*p++) = upper_case
54
0
                    ? printf_hex_digits_upper[(number >> shift_count) & 0x0f]
55
0
                    : printf_hex_digits_lower[(number >> shift_count) & 0x0f];
56
0
                if (precision > 0)
57
0
                    precision--;
58
0
            }
59
0
        }
60
0
    }
61
62
0
    size_t numlen = p - buf;
63
64
0
    if (!field_width || field_width < (numlen + has_precision * precision + (alternate_form * 2 * not_zero)))
65
0
        field_width = numlen + has_precision * precision + alternate_form * 2 * not_zero;
66
67
0
    if ((zero_pad && !has_precision) && (alternate_form && not_zero)) {
68
0
        putch(bufptr, '0');
69
0
        putch(bufptr, 'x');
70
0
    }
71
72
0
    if (!left_pad) {
73
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - alternate_form * 2 * not_zero; ++i) {
74
0
            putch(bufptr, (zero_pad && !has_precision) ? '0' : ' ');
75
0
        }
76
0
    }
77
78
0
    if (!(zero_pad && !has_precision) && (alternate_form && not_zero)) {
79
0
        putch(bufptr, '0');
80
0
        putch(bufptr, 'x');
81
0
    }
82
83
0
    if (has_precision) {
84
0
        for (u32 i = 0; i < precision; ++i) {
85
0
            putch(bufptr, '0');
86
0
        }
87
0
    }
88
89
0
    for (unsigned i = 0; i < numlen; ++i) {
90
0
        putch(bufptr, buf[i]);
91
0
    }
92
93
0
    if (left_pad) {
94
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - alternate_form * 2 * not_zero; ++i) {
95
0
            putch(bufptr, ' ');
96
0
        }
97
0
    }
98
99
0
    return field_width;
100
0
}
101
102
template<typename PutChFunc, typename CharType>
103
ALWAYS_INLINE int print_decimal(PutChFunc putch, CharType*& bufptr, u64 number, bool sign, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
104
0
{
105
0
    u64 divisor = 10000000000000000000LLU;
106
0
    char ch;
107
0
    char padding = 1;
108
0
    char buf[21];
109
0
    char* p = buf;
110
111
0
    if (!(has_precision && precision == 0 && number == 0)) {
112
0
        for (;;) {
113
0
            ch = '0' + (number / divisor);
114
0
            number %= divisor;
115
0
            if (ch != '0')
116
0
                padding = 0;
117
0
            if (!padding || divisor == 1) {
118
0
                *(p++) = ch;
119
0
                if (precision > 0)
120
0
                    precision--;
121
0
            }
122
0
            if (divisor == 1)
123
0
                break;
124
0
            divisor /= 10;
125
0
        }
126
0
    }
127
128
0
    size_t numlen = p - buf;
129
130
0
    if (!field_width || field_width < (numlen + has_precision * precision + (sign || always_sign)))
131
0
        field_width = numlen + has_precision * precision + (sign || always_sign);
132
133
0
    if ((zero_pad && !has_precision) && (sign || always_sign)) {
134
0
        putch(bufptr, sign ? '-' : '+');
135
0
    }
136
137
0
    if (!left_pad) {
138
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - (sign || always_sign); ++i) {
139
0
            putch(bufptr, (zero_pad && !has_precision) ? '0' : ' ');
140
0
        }
141
0
    }
142
143
0
    if (!(zero_pad && !has_precision) && (sign || always_sign)) {
144
0
        putch(bufptr, sign ? '-' : '+');
145
0
    }
146
147
0
    if (has_precision) {
148
0
        for (u32 i = 0; i < precision; ++i) {
149
0
            putch(bufptr, '0');
150
0
        }
151
0
    }
152
153
0
    for (unsigned i = 0; i < numlen; ++i) {
154
0
        putch(bufptr, buf[i]);
155
0
    }
156
157
0
    if (left_pad) {
158
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision - (sign || always_sign); ++i) {
159
0
            putch(bufptr, ' ');
160
0
        }
161
0
    }
162
163
0
    return field_width;
164
0
}
165
#ifndef KERNEL
166
template<typename PutChFunc, typename CharType>
167
ALWAYS_INLINE int print_double(PutChFunc putch, CharType*& bufptr, double number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, u32 precision, bool trailing_zeros)
168
0
{
169
0
    int length = 0;
170
171
0
    u32 whole_width = (field_width >= precision + 1) ? field_width - precision - 1 : 0;
172
173
0
    bool sign = signbit(number);
174
0
    bool nan = isnan(number);
175
0
    bool inf = isinf(number);
176
177
0
    if (nan || inf) {
178
0
        for (unsigned i = 0; i < field_width - 3 - sign; i++) {
179
0
            putch(bufptr, ' ');
180
0
            length++;
181
0
        }
182
0
        if (sign) {
183
0
            putch(bufptr, '-');
184
0
            length++;
185
0
        }
186
0
        if (nan) {
187
0
            putch(bufptr, 'n');
188
0
            putch(bufptr, 'a');
189
0
            putch(bufptr, 'n');
190
0
        } else {
191
0
            putch(bufptr, 'i');
192
0
            putch(bufptr, 'n');
193
0
            putch(bufptr, 'f');
194
0
        }
195
0
        return length + 3;
196
0
    }
197
198
0
    if (sign)
199
0
        number = -number;
200
201
0
    length = print_decimal(putch, bufptr, (i64)number, sign, always_sign, left_pad, zero_pad, whole_width, false, 1);
202
0
    if (precision > 0) {
203
0
        double fraction = number - (i64)number;
204
205
0
        for (u32 i = 0; i < precision; ++i)
206
0
            fraction = fraction * 10;
207
0
        if (trailing_zeros || fraction) {
208
0
            length++;
209
0
            putch(bufptr, '.');
210
211
0
            i64 ifraction = fraction;
212
0
            while (!trailing_zeros && ifraction % 10 == 0) {
213
0
                ifraction /= 10;
214
0
                precision--;
215
0
            }
216
217
0
            return length + print_decimal(putch, bufptr, ifraction, false, false, false, true, precision, false, 1);
218
0
        }
219
0
    }
220
221
0
    return length;
222
0
}
223
#endif
224
template<typename PutChFunc, typename CharType>
225
ALWAYS_INLINE int print_octal_number(PutChFunc putch, CharType*& bufptr, u64 number, bool alternate_form, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
226
0
{
227
0
    u32 divisor = 134217728;
228
0
    char ch;
229
0
    char padding = 1;
230
0
    char buf[32];
231
0
    char* p = buf;
232
233
0
    if (alternate_form) {
234
0
        (*p++) = '0';
235
0
        if (precision > 0)
236
0
            precision--;
237
0
    }
238
239
0
    if (!(has_precision && precision == 0 && number == 0)) {
240
0
        for (;;) {
241
0
            ch = '0' + (number / divisor);
242
0
            number %= divisor;
243
0
            if (ch != '0')
244
0
                padding = 0;
245
0
            if (!padding || divisor == 1) {
246
0
                *(p++) = ch;
247
0
                if (precision > 0)
248
0
                    precision--;
249
0
            }
250
0
            if (divisor == 1)
251
0
                break;
252
0
            divisor /= 8;
253
0
        }
254
0
    }
255
256
0
    size_t numlen = p - buf;
257
258
0
    if (!field_width || field_width < (numlen + has_precision * precision))
259
0
        field_width = numlen + has_precision * precision;
260
261
0
    if (!left_pad) {
262
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision; ++i) {
263
0
            putch(bufptr, (zero_pad && !has_precision) ? '0' : ' ');
264
0
        }
265
0
    }
266
267
0
    if (has_precision) {
268
0
        for (u32 i = 0; i < precision; ++i) {
269
0
            putch(bufptr, '0');
270
0
        }
271
0
    }
272
273
0
    for (unsigned i = 0; i < numlen; ++i) {
274
0
        putch(bufptr, buf[i]);
275
0
    }
276
277
0
    if (left_pad) {
278
0
        for (unsigned i = 0; i < field_width - numlen - has_precision * precision; ++i) {
279
0
            putch(bufptr, ' ');
280
0
        }
281
0
    }
282
283
0
    return field_width;
284
0
}
285
286
template<typename PutChFunc, typename T, typename CharType>
287
ALWAYS_INLINE int print_string(PutChFunc putch, CharType*& bufptr, T str, size_t len, bool left_pad, size_t field_width, bool dot, size_t precision, bool has_fraction)
288
0
{
289
0
    if (has_fraction)
290
0
        len = min(len, precision);
291
292
0
    if (!dot && (!field_width || field_width < len))
293
0
        field_width = len;
294
295
0
    if (has_fraction && !field_width)
296
0
        field_width = len;
297
298
0
    size_t pad_amount = field_width > len ? field_width - len : 0;
299
300
0
    if (!left_pad) {
301
0
        for (size_t i = 0; i < pad_amount; ++i)
302
0
            putch(bufptr, ' ');
303
0
    }
304
0
    for (size_t i = 0; i < min(len, field_width); ++i) {
305
0
        putch(bufptr, str[i]);
306
0
    }
307
0
    if (left_pad) {
308
0
        for (size_t i = 0; i < pad_amount; ++i)
309
0
            putch(bufptr, ' ');
310
0
    }
311
0
    return field_width;
312
0
}
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_string<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, char*, unsigned long, bool, unsigned long, bool, unsigned long, bool)
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_string<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, wchar_t const*, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, wchar_t const*, unsigned long, bool, unsigned long, bool, unsigned long, bool)
Unexecuted instantiation: StringBuilder.cpp:int PrintfImplementation::print_string<AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char const*, char>(AK::StringBuilder::appendvf(char const*, __va_list_tag*)::$_0, char*&, char const*, unsigned long, bool, unsigned long, bool, unsigned long, bool)
313
314
template<typename PutChFunc, typename CharType>
315
ALWAYS_INLINE int print_signed_number(PutChFunc putch, CharType*& bufptr, i64 number, bool always_sign, bool left_pad, bool zero_pad, u32 field_width, bool has_precision, u32 precision)
316
0
{
317
    // FIXME: `0 - number` overflows if we are trying to negate the smallest possible value.
318
0
    return print_decimal(putch, bufptr, (number < 0) ? 0 - number : number, number < 0, always_sign, left_pad, zero_pad, field_width, has_precision, precision);
319
0
}
320
321
struct ModifierState {
322
    bool left_pad { false };
323
    bool zero_pad { false };
324
    bool dot { false };
325
    unsigned field_width { 0 };
326
    bool has_precision { false };
327
    unsigned precision { 6 };
328
    unsigned short_qualifiers { 0 }; // TODO: Unimplemented.
329
    unsigned long_qualifiers { 0 };
330
    bool intmax_qualifier { false };      // TODO: Unimplemented.
331
    bool ptrdiff_qualifier { false };     // TODO: Unimplemented.
332
    bool long_double_qualifier { false }; // TODO: Unimplemented.
333
    bool size_qualifier { false };        // TODO: Unimplemented.
334
    bool alternate_form { 0 };
335
    bool always_sign { false };
336
};
337
338
template<typename PutChFunc, typename ArgumentListRefT, template<typename T, typename U = ArgumentListRefT> typename NextArgument, typename CharType = char>
339
struct PrintfImpl {
340
    ALWAYS_INLINE PrintfImpl(PutChFunc& putch, CharType*& bufptr, int const& nwritten)
341
0
        : m_bufptr(bufptr)
342
0
        , m_nwritten(nwritten)
343
0
        , m_putch(putch)
344
0
    {
345
0
    }
346
347
    ALWAYS_INLINE int format_s(ModifierState const& state, ArgumentListRefT ap) const
348
0
    {
349
        // FIXME: Narrow characters should be converted to wide characters on the fly and vice versa.
350
        // https://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html
351
        // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wprintf.html
352
0
#ifndef KERNEL
353
0
        if (state.long_qualifiers) {
354
0
            wchar_t const* sp = NextArgument<wchar_t const*>()(ap);
355
0
            if (!sp)
356
0
                sp = L"(null)";
357
0
            return print_string(m_putch, m_bufptr, sp, wcslen(sp), state.left_pad, state.field_width, state.dot, state.precision, state.has_precision);
358
0
        }
359
0
#endif
360
0
        char const* sp = NextArgument<char const*>()(ap);
361
0
        if (!sp)
362
0
            sp = "(null)";
363
0
        return print_string(m_putch, m_bufptr, sp, strlen(sp), state.left_pad, state.field_width, state.dot, state.precision, state.has_precision);
364
0
    }
365
    ALWAYS_INLINE int format_d(ModifierState const& state, ArgumentListRefT ap) const
366
0
    {
367
0
        i64 number = [&]() -> i64 {
368
0
            if (state.long_qualifiers >= 2)
369
0
                return NextArgument<long long int>()(ap);
370
0
            if (state.long_qualifiers == 1)
371
0
                return NextArgument<long int>()(ap);
372
0
            return NextArgument<int>()(ap);
373
0
        }();
374
375
0
        return print_signed_number(m_putch, m_bufptr, number, state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
376
0
    }
377
    ALWAYS_INLINE int format_i(ModifierState const& state, ArgumentListRefT ap) const
378
0
    {
379
0
        return format_d(state, ap);
380
0
    }
381
    ALWAYS_INLINE int format_u(ModifierState const& state, ArgumentListRefT ap) const
382
0
    {
383
0
        u64 number = [&]() -> u64 {
384
0
            if (state.long_qualifiers >= 2)
385
0
                return NextArgument<unsigned long long int>()(ap);
386
0
            if (state.long_qualifiers == 1)
387
0
                return NextArgument<unsigned long int>()(ap);
388
0
            return NextArgument<unsigned int>()(ap);
389
0
        }();
390
391
0
        return print_decimal(m_putch, m_bufptr, number, false, false, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
392
0
    }
393
    ALWAYS_INLINE int format_Q(ModifierState const& state, ArgumentListRefT ap) const
394
0
    {
395
0
        return print_decimal(m_putch, m_bufptr, NextArgument<u64>()(ap), false, false, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
396
0
    }
397
    ALWAYS_INLINE int format_q(ModifierState const& state, ArgumentListRefT ap) const
398
0
    {
399
0
        return print_hex(m_putch, m_bufptr, NextArgument<u64>()(ap), false, false, state.left_pad, state.zero_pad, 16, false, 1);
400
0
    }
401
#ifndef KERNEL
402
    ALWAYS_INLINE int format_g(ModifierState const& state, ArgumentListRefT ap) const
403
0
    {
404
        // FIXME: Exponent notation
405
0
        return print_double(m_putch, m_bufptr, NextArgument<double>()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision, false);
406
0
    }
407
    ALWAYS_INLINE int format_f(ModifierState const& state, ArgumentListRefT ap) const
408
0
    {
409
0
        return print_double(m_putch, m_bufptr, NextArgument<double>()(ap), state.always_sign, state.left_pad, state.zero_pad, state.field_width, state.precision, true);
410
0
    }
411
#endif
412
    ALWAYS_INLINE int format_o(ModifierState const& state, ArgumentListRefT ap) const
413
0
    {
414
0
        return print_octal_number(m_putch, m_bufptr, NextArgument<u32>()(ap), state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
415
0
    }
416
    ALWAYS_INLINE int format_unsigned_hex(ModifierState const& state, ArgumentListRefT ap, bool uppercase) const
417
0
    {
418
0
        u64 number = [&]() -> u64 {
419
0
            if (state.long_qualifiers >= 2)
420
0
                return NextArgument<unsigned long long int>()(ap);
421
0
            if (state.long_qualifiers == 1)
422
0
                return NextArgument<unsigned long int>()(ap);
423
0
            return NextArgument<unsigned int>()(ap);
424
0
        }();
425
426
0
        return print_hex(m_putch, m_bufptr, number, uppercase, state.alternate_form, state.left_pad, state.zero_pad, state.field_width, state.has_precision, state.precision);
427
0
    }
428
    ALWAYS_INLINE int format_x(ModifierState const& state, ArgumentListRefT ap) const
429
0
    {
430
0
        return format_unsigned_hex(state, ap, false);
431
0
    }
432
    ALWAYS_INLINE int format_X(ModifierState const& state, ArgumentListRefT ap) const
433
0
    {
434
0
        return format_unsigned_hex(state, ap, true);
435
0
    }
436
    ALWAYS_INLINE int format_n(ModifierState const&, ArgumentListRefT ap) const
437
0
    {
438
0
        *NextArgument<int*>()(ap) = m_nwritten;
439
0
        return 0;
440
0
    }
441
    ALWAYS_INLINE int format_p(ModifierState const&, ArgumentListRefT ap) const
442
0
    {
443
0
        return print_hex(m_putch, m_bufptr, NextArgument<FlatPtr>()(ap), false, true, false, true, 8, false, 1);
444
0
    }
445
    ALWAYS_INLINE int format_P(ModifierState const&, ArgumentListRefT ap) const
446
0
    {
447
0
        return print_hex(m_putch, m_bufptr, NextArgument<FlatPtr>()(ap), true, true, false, true, 8, false, 1);
448
0
    }
449
    ALWAYS_INLINE int format_percent(ModifierState const&, ArgumentListRefT) const
450
0
    {
451
0
        m_putch(m_bufptr, '%');
452
0
        return 1;
453
0
    }
454
    ALWAYS_INLINE int format_c(ModifierState const& state, ArgumentListRefT ap) const
455
0
    {
456
0
        char c = NextArgument<int>()(ap);
457
0
        return print_string(m_putch, m_bufptr, &c, 1, state.left_pad, state.field_width, state.dot, state.precision, state.has_precision);
458
0
    }
459
    ALWAYS_INLINE int format_unrecognized(CharType format_op, CharType const* fmt, ModifierState const&, ArgumentListRefT) const
460
0
    {
461
0
        dbgln("printf_internal: Unimplemented format specifier {} (fmt: {})", format_op, fmt);
462
0
        return 0;
463
0
    }
464
465
protected:
466
    CharType*& m_bufptr;
467
    int const& m_nwritten;
468
    PutChFunc& m_putch;
469
};
470
471
template<typename T, typename V>
472
struct VaArgNextArgument {
473
    ALWAYS_INLINE T operator()(V ap) const
474
0
    {
475
#ifdef AK_OS_WINDOWS
476
        // GCC on msys2 complains about the type of ap,
477
        // so let's force the compiler to believe it's a
478
        // va_list.
479
        return va_arg((va_list&)ap, T);
480
#else
481
0
        return va_arg(ap, T);
482
0
#endif
483
0
    }
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<int, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<unsigned long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<unsigned long long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<unsigned int, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<long long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<long, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<double, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<int*, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<wchar_t const*, __va_list_tag*&>::operator()(__va_list_tag*&) const
Unexecuted instantiation: PrintfImplementation::VaArgNextArgument<char const*, __va_list_tag*&>::operator()(__va_list_tag*&) const
484
};
485
486
#define PRINTF_IMPL_DELEGATE_TO_IMPL(c)    \
487
0
    case *#c:                              \
488
0
        ret += impl.format_##c(state, ap); \
489
0
        break;
490
491
template<typename PutChFunc, template<typename T, typename U, template<typename X, typename Y> typename V, typename C = char> typename Impl = PrintfImpl, typename ArgumentListT = va_list, template<typename T, typename V = decltype(declval<ArgumentListT&>())> typename NextArgument = VaArgNextArgument, typename CharType = char>
492
ALWAYS_INLINE int printf_internal(PutChFunc putch, IdentityType<CharType>* buffer, CharType const*& fmt, ArgumentListT ap)
493
0
{
494
0
    int ret = 0;
495
0
    CharType* bufptr = buffer;
496
497
0
    Impl<PutChFunc, ArgumentListT&, NextArgument, CharType> impl { putch, bufptr, ret };
498
499
0
    for (CharType const* p = fmt; *p; ++p) {
500
0
        ModifierState state;
501
0
        if (*p == '%' && *(p + 1)) {
502
0
        one_more:
503
0
            ++p;
504
0
            if (*p == '.') {
505
0
                state.dot = true;
506
0
                state.has_precision = true;
507
0
                state.precision = 0;
508
0
                if (*(p + 1))
509
0
                    goto one_more;
510
0
            }
511
0
            if (*p == '-') {
512
0
                state.left_pad = true;
513
0
                if (*(p + 1))
514
0
                    goto one_more;
515
0
            }
516
0
            if (*p == '+') {
517
0
                state.always_sign = true;
518
0
                if (*(p + 1))
519
0
                    goto one_more;
520
0
            }
521
0
            if (!state.zero_pad && !state.field_width && !state.dot && *p == '0') {
522
0
                state.zero_pad = true;
523
0
                if (*(p + 1))
524
0
                    goto one_more;
525
0
            }
526
0
            if (*p >= '0' && *p <= '9') {
527
0
                if (!state.dot) {
528
0
                    state.field_width *= 10;
529
0
                    state.field_width += *p - '0';
530
0
                    if (*(p + 1))
531
0
                        goto one_more;
532
0
                } else {
533
0
                    state.precision *= 10;
534
0
                    state.precision += *p - '0';
535
0
                    if (*(p + 1))
536
0
                        goto one_more;
537
0
                }
538
0
            }
539
0
            if (*p == '*') {
540
0
                if (state.dot) {
541
0
                    state.zero_pad = true;
542
0
                    state.precision = NextArgument<int>()(ap);
543
0
                } else {
544
0
                    state.field_width = NextArgument<int>()(ap);
545
0
                }
546
547
0
                if (*(p + 1))
548
0
                    goto one_more;
549
0
            }
550
0
            if (*p == 'h') {
551
0
                ++state.short_qualifiers;
552
0
                if (*(p + 1))
553
0
                    goto one_more;
554
0
            }
555
0
            if (*p == 'l') {
556
0
                ++state.long_qualifiers;
557
0
                if (*(p + 1))
558
0
                    goto one_more;
559
0
            }
560
0
            if (*p == 'j') {
561
0
                state.intmax_qualifier = true;
562
0
                if (*(p + 1))
563
0
                    goto one_more;
564
0
            }
565
0
            if (*p == 't') {
566
0
                state.ptrdiff_qualifier = true;
567
0
                if (*(p + 1))
568
0
                    goto one_more;
569
0
            }
570
0
            if (*p == 'L') {
571
0
                state.long_double_qualifier = true;
572
0
                if (*(p + 1))
573
0
                    goto one_more;
574
0
            }
575
0
            if (*p == 'z') {
576
0
                state.size_qualifier = true;
577
0
                if (*(p + 1))
578
0
                    goto one_more;
579
0
            }
580
0
            if (*p == '#') {
581
0
                state.alternate_form = true;
582
0
                if (*(p + 1))
583
0
                    goto one_more;
584
0
            }
585
0
            switch (*p) {
586
0
            case '%':
587
0
                ret += impl.format_percent(state, ap);
588
0
                break;
589
590
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(P);
591
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(Q);
592
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(X);
593
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(c);
594
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(d);
595
0
#ifndef KERNEL
596
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(f);
597
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(g);
598
0
#endif
599
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(i);
600
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(n);
601
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(o);
602
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(p);
603
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(q);
604
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(s);
605
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(u);
606
0
                PRINTF_IMPL_DELEGATE_TO_IMPL(x);
607
0
            default:
608
0
                ret += impl.format_unrecognized(*p, fmt, state, ap);
609
0
                break;
610
0
            }
611
0
        } else {
612
0
            putch(bufptr, *p);
613
0
            ++ret;
614
0
        }
615
0
    }
616
0
    return ret;
617
0
}
618
619
#undef PRINTF_IMPL_DELEGATE_TO_IMPL
620
621
}
622
623
using PrintfImplementation::printf_internal;