Coverage Report

Created: 2025-06-13 06:43

/src/php-src/main/spprintf.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | https://www.php.net/license/3_01.txt                                 |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Author: Marcus Boerger <helly@php.net>                               |
14
   +----------------------------------------------------------------------+
15
*/
16
17
/* This is the spprintf implementation.
18
 * It has emerged from apache snprintf. See original header:
19
 */
20
21
/* ====================================================================
22
 * Copyright (c) 1995-1998 The Apache Group.  All rights reserved.
23
 *
24
 * Redistribution and use in source and binary forms, with or without
25
 * modification, are permitted provided that the following conditions
26
 * are met:
27
 *
28
 * 1. Redistributions of source code must retain the above copyright
29
 *    notice, this list of conditions and the following disclaimer.
30
 *
31
 * 2. Redistributions in binary form must reproduce the above copyright
32
 *    notice, this list of conditions and the following disclaimer in
33
 *    the documentation and/or other materials provided with the
34
 *    distribution.
35
 *
36
 * 3. All advertising materials mentioning features or use of this
37
 *    software must display the following acknowledgment:
38
 *    "This product includes software developed by the Apache Group
39
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
40
 *
41
 * 4. The names "Apache Server" and "Apache Group" must not be used to
42
 *    endorse or promote products derived from this software without
43
 *    prior written permission.
44
 *
45
 * 5. Redistributions of any form whatsoever must retain the following
46
 *    acknowledgment:
47
 *    "This product includes software developed by the Apache Group
48
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
49
 *
50
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
51
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
53
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
54
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
56
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
57
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
59
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
61
 * OF THE POSSIBILITY OF SUCH DAMAGE.
62
 * ====================================================================
63
 *
64
 * This software consists of voluntary contributions made by many
65
 * individuals on behalf of the Apache Group and was originally based
66
 * on public domain software written at the National Center for
67
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
68
 * For more information on the Apache Group and the Apache HTTP server
69
 * project, please see <http://www.apache.org/>.
70
 *
71
 * This code is based on, and used with the permission of, the
72
 * SIO stdio-replacement strx_* functions by Panos Tsirigotis
73
 * <panos@alumni.cs.colorado.edu> for xinetd.
74
 */
75
#ifndef _GNU_SOURCE
76
# define _GNU_SOURCE
77
#endif
78
#include "php.h"
79
80
#include <stddef.h>
81
#include <stdio.h>
82
#include <ctype.h>
83
#include <sys/types.h>
84
#include <stdarg.h>
85
#include <string.h>
86
#include <stdlib.h>
87
#include <math.h>
88
#include <inttypes.h>
89
90
#include <locale.h>
91
#ifdef ZTS
92
#include "ext/standard/php_string.h"
93
#define LCONV_DECIMAL_POINT (*lconv.decimal_point)
94
#else
95
90.4k
#define LCONV_DECIMAL_POINT (*lconv->decimal_point)
96
#endif
97
98
#include "snprintf.h"
99
100
36.3M
#define NUL             '\0'
101
#define INT_NULL        ((int *)0)
102
103
0
#define S_NULL          "(null)"
104
0
#define S_NULL_LEN      6
105
106
0
#define FLOAT_DIGITS    6
107
#define EXPONENT_LENGTH 10
108
109
#include "zend_smart_str.h"
110
#include "zend_smart_string.h"
111
112
/* {{{ macros */
113
114
252M
#define INS_CHAR(xbuf, ch, is_char) do { \
115
252M
  if ((is_char)) { \
116
119M
    smart_string_appendc((smart_string *)(xbuf), (ch)); \
117
133M
  } else { \
118
133M
    smart_str_appendc((smart_str *)(xbuf), (ch)); \
119
133M
  } \
120
252M
} while (0);
121
122
18.1M
#define INS_STRING(xbuf, str, len, is_char) do { \
123
18.1M
  if ((is_char)) { \
124
10.7M
    smart_string_appendl((smart_string *)(xbuf), (str), (len)); \
125
10.7M
  } else { \
126
7.39M
    smart_str_appendl((smart_str *)(xbuf), (str), (len)); \
127
7.39M
  } \
128
18.1M
} while (0);
129
130
2.49M
#define PAD_CHAR(xbuf, ch, count, is_char) do { \
131
2.49M
  if ((is_char)) { \
132
2.46M
    smart_string_alloc(((smart_string *)(xbuf)), (count), 0); \
133
2.46M
    memset(((smart_string *)(xbuf))->c + ((smart_string *)(xbuf))->len, (ch), (count)); \
134
2.46M
    ((smart_string *)(xbuf))->len += (count); \
135
2.46M
  } else { \
136
22.9k
    smart_str_alloc(((smart_str *)(xbuf)), (count), 0); \
137
22.9k
    memset(ZSTR_VAL(((smart_str *)(xbuf))->s) + ZSTR_LEN(((smart_str *)(xbuf))->s), (ch), (count)); \
138
22.9k
    ZSTR_LEN(((smart_str *)(xbuf))->s) += (count); \
139
22.9k
  } \
140
2.49M
} while (0);
141
142
/*
143
 * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
144
 * which can be at most max length of double
145
 */
146
3.34M
#define NUM_BUF_SIZE ZEND_DOUBLE_MAX_LENGTH
147
148
76.2k
#define NUM(c) (c - '0')
149
150
60.8k
#define STR_TO_DEC(str, num) do {     \
151
60.8k
  num = NUM(*str++);                   \
152
60.8k
  while (isdigit((int)*str)) {         \
153
15.3k
    num *= 10;                        \
154
15.3k
    num += NUM(*str++);               \
155
15.3k
    if (num >= INT_MAX / 10) {     \
156
0
      while (isdigit((int)*str++));  \
157
0
      break;              \
158
0
    }                  \
159
15.3k
    }                    \
160
60.8k
} while (0)
161
162
/*
163
 * This macro does zero padding so that the precision
164
 * requirement is satisfied. The padding is done by
165
 * adding '0's to the left of the string that is going
166
 * to be printed.
167
 */
168
3.34M
#define FIX_PRECISION(adjust, precision, s, s_len) do { \
169
3.34M
    if (adjust)                               \
170
3.34M
    while (s_len < (size_t)precision) {       \
171
0
      *--s = '0';                               \
172
0
      s_len++;                                  \
173
0
    }                        \
174
3.34M
} while (0)
175
176
/* }}} */
177
178
/*
179
 * Do format conversion placing the output in buffer
180
 */
181
static void xbuf_format_converter(void *xbuf, bool is_char, const char *fmt, va_list ap) /* {{{ */
182
16.0M
{
183
16.0M
  char *s = NULL;
184
16.0M
  size_t s_len;
185
186
16.0M
  int min_width = 0;
187
16.0M
  int precision = 0;
188
16.0M
  enum {
189
16.0M
    LEFT, RIGHT
190
16.0M
  } adjust;
191
16.0M
  char pad_char;
192
16.0M
  char prefix_char;
193
194
16.0M
  double fp_num;
195
16.0M
  int64_t i_num = (int64_t) 0;
196
16.0M
  uint64_t ui_num = (uint64_t) 0;
197
198
16.0M
  char num_buf[NUM_BUF_SIZE];
199
16.0M
  char char_buf[2];     /* for printing %% and %<unknown> */
200
201
#ifdef ZTS
202
  struct lconv lconv;
203
#else
204
16.0M
  struct lconv *lconv = NULL;
205
16.0M
#endif
206
207
  /*
208
   * Flag variables
209
   */
210
16.0M
  length_modifier_e modifier;
211
16.0M
  bool alternate_form;
212
16.0M
  bool print_sign;
213
16.0M
  bool print_blank;
214
16.0M
  bool adjust_precision;
215
16.0M
  bool adjust_width;
216
16.0M
  bool is_negative;
217
218
286M
  while (*fmt) {
219
270M
    if (*fmt != '%') {
220
252M
      INS_CHAR(xbuf, *fmt, is_char);
221
252M
    } else {
222
      /*
223
       * Default variable settings
224
       */
225
18.1M
      zend_string *tmp_str = NULL;
226
18.1M
      adjust = RIGHT;
227
18.1M
      alternate_form = print_sign = print_blank = false;
228
18.1M
      pad_char = ' ';
229
18.1M
      prefix_char = NUL;
230
231
18.1M
      fmt++;
232
233
      /*
234
       * Try to avoid checking for flags, width or precision
235
       */
236
18.1M
      if (isascii((int)*fmt) && !islower((int)*fmt)) {
237
        /*
238
         * Recognize flags: -, #, BLANK, +
239
         */
240
5.30M
        for (;; fmt++) {
241
5.30M
          if (*fmt == '-')
242
0
            adjust = LEFT;
243
5.30M
          else if (*fmt == '+')
244
0
            print_sign = true;
245
5.30M
          else if (*fmt == '#')
246
0
            alternate_form = true;
247
5.30M
          else if (*fmt == ' ')
248
0
            print_blank = true;
249
5.30M
          else if (*fmt == '0')
250
45.5k
            pad_char = '0';
251
5.26M
          else
252
5.26M
            break;
253
5.30M
        }
254
255
        /*
256
         * Check if a width was specified
257
         */
258
5.26M
        if (isdigit((int)*fmt)) {
259
60.4k
          STR_TO_DEC(fmt, min_width);
260
60.4k
          adjust_width = true;
261
5.20M
        } else if (*fmt == '*') {
262
2.54M
          min_width = va_arg(ap, int);
263
2.54M
          fmt++;
264
2.54M
          adjust_width = true;
265
2.54M
          if (min_width < 0) {
266
0
            adjust = LEFT;
267
0
            min_width = -min_width;
268
0
          }
269
2.54M
        } else
270
2.65M
          adjust_width = false;
271
272
        /*
273
         * Check if a precision was specified
274
         */
275
5.26M
        if (*fmt == '.') {
276
99.9k
          adjust_precision = true;
277
99.9k
          fmt++;
278
99.9k
          if (isdigit((int)*fmt)) {
279
473
            STR_TO_DEC(fmt, precision);
280
99.4k
          } else if (*fmt == '*') {
281
99.4k
            precision = va_arg(ap, int);
282
99.4k
            fmt++;
283
99.4k
            if (precision < -1)
284
0
              precision = -1;
285
99.4k
          } else
286
0
            precision = 0;
287
99.9k
        } else
288
5.16M
          adjust_precision = false;
289
5.26M
      } else
290
12.8M
        adjust_precision = adjust_width = false;
291
292
      /*
293
       * Modifier check
294
       */
295
18.1M
      switch (*fmt) {
296
0
        case 'L':
297
0
          fmt++;
298
0
          modifier = LM_LONG_DOUBLE;
299
0
          break;
300
1.76M
        case 'l':
301
1.76M
          fmt++;
302
1.76M
#if SIZEOF_LONG_LONG
303
1.76M
          if (*fmt == 'l') {
304
0
            fmt++;
305
0
            modifier = LM_LONG_LONG;
306
0
          } else
307
1.76M
#endif
308
1.76M
            modifier = LM_LONG;
309
1.76M
          break;
310
545k
        case 'z':
311
545k
          fmt++;
312
545k
          modifier = LM_SIZE_T;
313
545k
          break;
314
0
        case 'j':
315
0
          fmt++;
316
0
#if SIZEOF_INTMAX_T
317
0
          modifier = LM_INTMAX_T;
318
#else
319
          modifier = LM_SIZE_T;
320
#endif
321
0
          break;
322
0
        case 't':
323
0
          fmt++;
324
0
#if SIZEOF_PTRDIFF_T
325
0
          modifier = LM_PTRDIFF_T;
326
#else
327
          modifier = LM_SIZE_T;
328
#endif
329
0
          break;
330
0
        case 'p':
331
0
        {
332
0
          char __next = *(fmt+1);
333
0
          if ('d' == __next || 'u' == __next || 'x' == __next || 'o' == __next) {
334
0
            zend_error_noreturn(E_CORE_ERROR,
335
0
              "printf \"p\" modifier is no longer supported, use ZEND_LONG_FMT");
336
0
          }
337
0
          modifier = LM_STD;
338
0
          break;
339
0
        }
340
0
        case 'h':
341
0
          fmt++;
342
0
          if (*fmt == 'h') {
343
0
            fmt++;
344
0
          }
345
          /* these are promoted to int, so no break */
346
0
          ZEND_FALLTHROUGH;
347
15.8M
        default:
348
15.8M
          modifier = LM_STD;
349
15.8M
          break;
350
18.1M
      }
351
352
      /*
353
       * Argument extraction and printing.
354
       * First we determine the argument type.
355
       * Then, we convert the argument to a string.
356
       * On exit from the switch, s points to the string that
357
       * must be printed, s_len has the length of the string
358
       * The precision requirements, if any, are reflected in s_len.
359
       *
360
       * NOTE: pad_char may be set to '0' because of the 0 flag.
361
       *   It is reset to ' ' by non-numeric formats
362
       */
363
18.1M
      switch (*fmt) {
364
0
        case 'Z': {
365
0
          zval *zvp = va_arg(ap, zval*);
366
0
          zend_string *str = zval_get_tmp_string(zvp, &tmp_str);
367
0
          s_len = ZSTR_LEN(str);
368
0
          s = ZSTR_VAL(str);
369
0
          if (adjust_precision && (size_t)precision < s_len) {
370
0
            s_len = precision;
371
0
          }
372
0
          break;
373
0
        }
374
2.55M
        case 'S': {
375
2.55M
          zend_string *str = va_arg(ap, zend_string*);
376
2.55M
          s_len = ZSTR_LEN(str);
377
2.55M
          s = ZSTR_VAL(str);
378
2.55M
          if (adjust_precision && (size_t)precision < s_len) {
379
0
            s_len = precision;
380
0
          }
381
2.55M
          break;
382
0
        }
383
75.8k
        case 'u':
384
75.8k
          switch(modifier) {
385
73.5k
            default:
386
73.5k
              i_num = (int64_t) va_arg(ap, unsigned int);
387
73.5k
              break;
388
0
            case LM_LONG_DOUBLE:
389
0
              goto fmt_error;
390
17
            case LM_LONG:
391
17
              i_num = (int64_t) va_arg(ap, unsigned long int);
392
17
              break;
393
2.33k
            case LM_SIZE_T:
394
2.33k
              i_num = (int64_t) va_arg(ap, size_t);
395
2.33k
              break;
396
0
#if SIZEOF_LONG_LONG
397
0
            case LM_LONG_LONG:
398
0
              i_num = (int64_t) va_arg(ap, unsigned long long int);
399
0
              break;
400
0
#endif
401
0
#if SIZEOF_INTMAX_T
402
0
            case LM_INTMAX_T:
403
0
              i_num = (int64_t) va_arg(ap, uintmax_t);
404
0
              break;
405
0
#endif
406
0
#if SIZEOF_PTRDIFF_T
407
0
            case LM_PTRDIFF_T:
408
0
              i_num = (int64_t) va_arg(ap, ptrdiff_t);
409
0
              break;
410
75.8k
#endif
411
75.8k
          }
412
          /*
413
           * The rest also applies to other integer formats, so fall
414
           * into that case.
415
           */
416
75.8k
          ZEND_FALLTHROUGH;
417
3.26M
        case 'd':
418
3.26M
        case 'i':
419
          /*
420
           * Get the arg if we haven't already.
421
           */
422
3.26M
          if ((*fmt) != 'u') {
423
3.19M
            switch(modifier) {
424
882k
              default:
425
882k
                i_num = (int64_t) va_arg(ap, int);
426
882k
                break;
427
0
              case LM_LONG_DOUBLE:
428
0
                goto fmt_error;
429
1.76M
              case LM_LONG:
430
1.76M
                i_num = (int64_t) va_arg(ap, long int);
431
1.76M
                break;
432
542k
              case LM_SIZE_T:
433
542k
#if SIZEOF_SSIZE_T
434
542k
                i_num = (int64_t) va_arg(ap, ssize_t);
435
#else
436
                i_num = (int64_t) va_arg(ap, size_t);
437
#endif
438
542k
                break;
439
0
#if SIZEOF_LONG_LONG
440
0
              case LM_LONG_LONG:
441
0
                i_num = (int64_t) va_arg(ap, long long int);
442
0
                break;
443
0
#endif
444
0
#if SIZEOF_INTMAX_T
445
0
              case LM_INTMAX_T:
446
0
                i_num = (int64_t) va_arg(ap, intmax_t);
447
0
                break;
448
0
#endif
449
0
#if SIZEOF_PTRDIFF_T
450
0
              case LM_PTRDIFF_T:
451
0
                i_num = (int64_t) va_arg(ap, ptrdiff_t);
452
0
                break;
453
3.19M
#endif
454
3.19M
            }
455
3.19M
          }
456
3.26M
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
457
3.26M
                &num_buf[NUM_BUF_SIZE], &s_len);
458
3.26M
          FIX_PRECISION(adjust_precision, precision, s, s_len);
459
460
3.26M
          if (*fmt != 'u') {
461
3.19M
            if (is_negative)
462
30.1k
              prefix_char = '-';
463
3.16M
            else if (print_sign)
464
0
              prefix_char = '+';
465
3.16M
            else if (print_blank)
466
0
              prefix_char = ' ';
467
3.19M
          }
468
3.26M
          break;
469
470
471
0
        case 'o':
472
0
          switch(modifier) {
473
0
            default:
474
0
              ui_num = (uint64_t) va_arg(ap, unsigned int);
475
0
              break;
476
0
            case LM_LONG_DOUBLE:
477
0
              goto fmt_error;
478
0
            case LM_LONG:
479
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
480
0
              break;
481
0
            case LM_SIZE_T:
482
0
              ui_num = (uint64_t) va_arg(ap, size_t);
483
0
              break;
484
0
#if SIZEOF_LONG_LONG
485
0
            case LM_LONG_LONG:
486
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
487
0
              break;
488
0
#endif
489
0
#if SIZEOF_INTMAX_T
490
0
            case LM_INTMAX_T:
491
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
492
0
              break;
493
0
#endif
494
0
#if SIZEOF_PTRDIFF_T
495
0
            case LM_PTRDIFF_T:
496
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
497
0
              break;
498
0
#endif
499
0
          }
500
0
          s = ap_php_conv_p2(ui_num, 3, *fmt,
501
0
                &num_buf[NUM_BUF_SIZE], &s_len);
502
0
          FIX_PRECISION(adjust_precision, precision, s, s_len);
503
0
          if (alternate_form && *s != '0') {
504
0
            *--s = '0';
505
0
            s_len++;
506
0
          }
507
0
          break;
508
509
510
29.3k
        case 'x':
511
74.4k
        case 'X':
512
74.4k
          switch(modifier) {
513
74.1k
            default:
514
74.1k
              ui_num = (uint64_t) va_arg(ap, unsigned int);
515
74.1k
              break;
516
0
            case LM_LONG_DOUBLE:
517
0
              goto fmt_error;
518
0
            case LM_LONG:
519
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
520
0
              break;
521
248
            case LM_SIZE_T:
522
248
              ui_num = (uint64_t) va_arg(ap, size_t);
523
248
              break;
524
0
#if SIZEOF_LONG_LONG
525
0
            case LM_LONG_LONG:
526
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
527
0
              break;
528
0
#endif
529
0
#if SIZEOF_INTMAX_T
530
0
            case LM_INTMAX_T:
531
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
532
0
              break;
533
0
#endif
534
0
#if SIZEOF_PTRDIFF_T
535
0
            case LM_PTRDIFF_T:
536
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
537
0
              break;
538
74.4k
#endif
539
74.4k
          }
540
74.4k
          s = ap_php_conv_p2(ui_num, 4, *fmt,
541
74.4k
                &num_buf[NUM_BUF_SIZE], &s_len);
542
74.4k
          FIX_PRECISION(adjust_precision, precision, s, s_len);
543
74.4k
          if (alternate_form && ui_num != 0) {
544
0
            *--s = *fmt;  /* 'x' or 'X' */
545
0
            *--s = '0';
546
0
            s_len += 2;
547
0
          }
548
74.4k
          break;
549
550
551
8.82M
        case 's':
552
8.82M
          s = va_arg(ap, char *);
553
8.82M
          if (s != NULL) {
554
8.82M
            if (!adjust_precision) {
555
8.82M
              s_len = strlen(s);
556
8.82M
            } else {
557
226
              s_len = zend_strnlen(s, precision);
558
226
            }
559
8.82M
          } else {
560
0
            s = S_NULL;
561
0
            s_len = S_NULL_LEN;
562
0
          }
563
8.82M
          pad_char = ' ';
564
8.82M
          break;
565
566
567
0
        case 'f':
568
473
        case 'F':
569
473
        case 'e':
570
473
        case 'E':
571
473
          switch(modifier) {
572
0
            case LM_LONG_DOUBLE:
573
0
              fp_num = (double) va_arg(ap, long double);
574
0
              break;
575
473
            case LM_STD:
576
473
              fp_num = va_arg(ap, double);
577
473
              break;
578
0
            default:
579
0
              goto fmt_error;
580
473
          }
581
582
473
          if (zend_isnan(fp_num)) {
583
5
            s = "nan";
584
5
            s_len = 3;
585
468
          } else if (zend_isinf(fp_num)) {
586
19
            s = "inf";
587
19
            s_len = 3;
588
449
          } else {
589
#ifdef ZTS
590
            localeconv_r(&lconv);
591
#else
592
449
            if (!lconv) {
593
449
              lconv = localeconv();
594
449
            }
595
449
#endif
596
449
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
597
449
             (adjust_precision == false) ? FLOAT_DIGITS : precision,
598
449
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
599
449
                  &is_negative, &num_buf[1], &s_len);
600
449
            if (is_negative)
601
48
              prefix_char = '-';
602
401
            else if (print_sign)
603
0
              prefix_char = '+';
604
401
            else if (print_blank)
605
0
              prefix_char = ' ';
606
449
          }
607
473
          break;
608
609
610
0
        case 'g':
611
0
        case 'k':
612
0
        case 'G':
613
99.2k
        case 'H':
614
99.2k
          switch(modifier) {
615
0
            case LM_LONG_DOUBLE:
616
0
              fp_num = (double) va_arg(ap, long double);
617
0
              break;
618
99.2k
            case LM_STD:
619
99.2k
              fp_num = va_arg(ap, double);
620
99.2k
              break;
621
0
            default:
622
0
              goto fmt_error;
623
99.2k
          }
624
625
99.2k
          if (zend_isnan(fp_num)) {
626
2.81k
            s = "NAN";
627
2.81k
            s_len = 3;
628
2.81k
            break;
629
96.4k
          } else if (zend_isinf(fp_num)) {
630
5.99k
            if (fp_num > 0) {
631
5.74k
              s = "INF";
632
5.74k
              s_len = 3;
633
5.74k
            } else {
634
256
              s = "-INF";
635
256
              s_len = 4;
636
256
            }
637
5.99k
            break;
638
5.99k
          }
639
640
90.4k
          if (adjust_precision == false)
641
0
            precision = FLOAT_DIGITS;
642
90.4k
          else if (precision == 0)
643
0
            precision = 1;
644
          /*
645
           * * We use &num_buf[ 1 ], so that we have room for the sign
646
           */
647
#ifdef ZTS
648
          localeconv_r(&lconv);
649
#else
650
90.4k
          if (!lconv) {
651
90.4k
            lconv = localeconv();
652
90.4k
          }
653
90.4k
#endif
654
90.4k
          s = zend_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
655
90.4k
          if (*s == '-')
656
18.4k
            prefix_char = *s++;
657
71.9k
          else if (print_sign)
658
0
            prefix_char = '+';
659
71.9k
          else if (print_blank)
660
0
            prefix_char = ' ';
661
662
90.4k
          s_len = strlen(s);
663
664
90.4k
          if (alternate_form && (strchr(s, '.')) == NULL)
665
0
            s[s_len++] = '.';
666
90.4k
          break;
667
668
669
3.32M
        case 'c':
670
3.32M
          char_buf[0] = (char) (va_arg(ap, int));
671
3.32M
          s = &char_buf[0];
672
3.32M
          s_len = 1;
673
3.32M
          pad_char = ' ';
674
3.32M
          break;
675
676
677
0
        case '%':
678
0
          char_buf[0] = '%';
679
0
          s = &char_buf[0];
680
0
          s_len = 1;
681
0
          pad_char = ' ';
682
0
          break;
683
684
685
0
        case 'n':
686
0
          *(va_arg(ap, int *)) = is_char? (int)((smart_string *)xbuf)->len : (int)ZSTR_LEN(((smart_str *)xbuf)->s);
687
0
          goto skip_output;
688
689
          /*
690
           * Always extract the argument as a "char *" pointer. We
691
           * should be using "void *" but there are still machines
692
           * that don't understand it.
693
           * If the pointer size is equal to the size of an unsigned
694
           * integer we convert the pointer to a hex number, otherwise
695
           * we print "%p" to indicate that we don't handle "%p".
696
           */
697
0
        case 'p':
698
0
          if (sizeof(char *) <= sizeof(uint64_t)) {
699
0
            ui_num = (uint64_t)((size_t) va_arg(ap, char *));
700
0
            s = ap_php_conv_p2(ui_num, 4, 'x',
701
0
                &num_buf[NUM_BUF_SIZE], &s_len);
702
0
            if (ui_num != 0) {
703
0
              *--s = 'x';
704
0
              *--s = '0';
705
0
              s_len += 2;
706
0
            }
707
0
          } else {
708
0
            s = "%p";
709
0
            s_len = 2;
710
0
          }
711
0
          pad_char = ' ';
712
0
          break;
713
714
715
0
        case NUL:
716
          /*
717
           * The last character of the format string was %.
718
           * We ignore it.
719
           */
720
0
          continue;
721
722
723
0
fmt_error:
724
0
        php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
725
          /*
726
           * The default case is for unrecognized %'s.
727
           * We print %<char> to help the user identify what
728
           * option is not understood.
729
           * This is also useful in case the user wants to pass
730
           * the output of format_converter to another function
731
           * that understands some other %<char> (like syslog).
732
           * Note that we can't point s inside fmt because the
733
           * unknown <char> could be preceded by width etc.
734
           */
735
0
          ZEND_FALLTHROUGH;
736
0
        default:
737
0
          char_buf[0] = '%';
738
0
          char_buf[1] = *fmt;
739
0
          s = char_buf;
740
0
          s_len = 2;
741
0
          pad_char = ' ';
742
0
          break;
743
18.1M
      }
744
745
18.1M
      if (prefix_char != NUL) {
746
48.6k
        *--s = prefix_char;
747
48.6k
        s_len++;
748
48.6k
      }
749
18.1M
      if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
750
2.49M
        if (pad_char == '0' && prefix_char != NUL) {
751
0
          INS_CHAR(xbuf, *s, is_char);
752
0
          s++;
753
0
          s_len--;
754
0
          min_width--;
755
0
        }
756
2.49M
        PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
757
2.49M
      }
758
      /*
759
       * Print the string s.
760
       */
761
18.1M
      INS_STRING(xbuf, s, s_len, is_char);
762
763
18.1M
      if (adjust_width && adjust == LEFT && (size_t)min_width > s_len) {
764
0
        PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
765
0
      }
766
767
18.1M
      zend_tmp_string_release(tmp_str);
768
18.1M
    }
769
270M
skip_output:
770
270M
    fmt++;
771
270M
  }
772
16.0M
  return;
773
16.0M
}
774
/* }}} */
775
776
PHPAPI void php_printf_to_smart_string(smart_string *buf, const char *format, va_list ap) /* {{{ */
777
11.4M
{
778
11.4M
  xbuf_format_converter(buf, 1, format, ap);
779
11.4M
}
780
/* }}} */
781
782
PHPAPI void php_printf_to_smart_str(smart_str *buf, const char *format, va_list ap) /* {{{ */
783
4.62M
{
784
4.62M
  xbuf_format_converter(buf, 0, format, ap);
785
4.62M
}
786
/* }}} */