Coverage Report

Created: 2025-09-27 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/spprintf.c
Line
Count
Source
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.8k
#define LCONV_DECIMAL_POINT (*lconv->decimal_point)
96
#endif
97
98
#include "snprintf.h"
99
100
54.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
237M
#define INS_CHAR(xbuf, ch, is_char) do { \
115
237M
  if ((is_char)) { \
116
118M
    smart_string_appendc((smart_string *)(xbuf), (ch)); \
117
118M
  } else { \
118
118M
    smart_str_appendc((smart_str *)(xbuf), (ch)); \
119
118M
  } \
120
237M
} while (0);
121
122
27.1M
#define INS_STRING(xbuf, str, len, is_char) do { \
123
27.1M
  if ((is_char)) { \
124
18.1M
    smart_string_appendl((smart_string *)(xbuf), (str), (len)); \
125
18.1M
  } else { \
126
9.05M
    smart_str_appendl((smart_str *)(xbuf), (str), (len)); \
127
9.05M
  } \
128
27.1M
} while (0);
129
130
6.81M
#define PAD_CHAR(xbuf, ch, count, is_char) do { \
131
6.81M
  if ((is_char)) { \
132
6.78M
    smart_string_alloc(((smart_string *)(xbuf)), (count), 0); \
133
6.78M
    memset(((smart_string *)(xbuf))->c + ((smart_string *)(xbuf))->len, (ch), (count)); \
134
6.78M
    ((smart_string *)(xbuf))->len += (count); \
135
6.78M
  } else { \
136
21.5k
    smart_str_alloc(((smart_str *)(xbuf)), (count), 0); \
137
21.5k
    memset(ZSTR_VAL(((smart_str *)(xbuf))->s) + ZSTR_LEN(((smart_str *)(xbuf))->s), (ch), (count)); \
138
21.5k
    ZSTR_LEN(((smart_str *)(xbuf))->s) += (count); \
139
21.5k
  } \
140
6.81M
} 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
6.07M
#define NUM_BUF_SIZE ZEND_DOUBLE_MAX_LENGTH
147
148
71.5k
#define NUM(c) (c - '0')
149
150
57.1k
#define STR_TO_DEC(str, num) do {     \
151
57.1k
  num = NUM(*str++);                   \
152
57.1k
  while (isdigit((int)*str)) {         \
153
14.3k
    num *= 10;                        \
154
14.3k
    num += NUM(*str++);               \
155
14.3k
    if (num >= INT_MAX / 10) {     \
156
0
      while (isdigit((int)*str++));  \
157
0
      break;              \
158
0
    }                  \
159
14.3k
    }                    \
160
57.1k
} 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
6.07M
#define FIX_PRECISION(adjust, precision, s, s_len) do { \
169
6.07M
    if (adjust)                               \
170
6.07M
    while (s_len < (size_t)precision) {       \
171
0
      *--s = '0';                               \
172
0
      s_len++;                                  \
173
0
    }                        \
174
6.07M
} 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
20.3M
{
183
20.3M
  char *s = NULL;
184
20.3M
  size_t s_len;
185
186
20.3M
  int min_width = 0;
187
20.3M
  int precision = 0;
188
20.3M
  enum {
189
20.3M
    LEFT, RIGHT
190
20.3M
  } adjust;
191
20.3M
  char pad_char;
192
20.3M
  char prefix_char;
193
194
20.3M
  double fp_num;
195
20.3M
  int64_t i_num = (int64_t) 0;
196
20.3M
  uint64_t ui_num = (uint64_t) 0;
197
198
20.3M
  char num_buf[NUM_BUF_SIZE];
199
20.3M
  char char_buf[2];     /* for printing %% and %<unknown> */
200
201
#ifdef ZTS
202
  struct lconv lconv;
203
#else
204
20.3M
  struct lconv *lconv = NULL;
205
20.3M
#endif
206
207
  /*
208
   * Flag variables
209
   */
210
20.3M
  length_modifier_e modifier;
211
20.3M
  bool alternate_form;
212
20.3M
  bool print_sign;
213
20.3M
  bool print_blank;
214
20.3M
  bool adjust_precision;
215
20.3M
  bool adjust_width;
216
20.3M
  bool is_negative;
217
218
284M
  while (*fmt) {
219
264M
    if (*fmt != '%') {
220
237M
      INS_CHAR(xbuf, *fmt, is_char);
221
237M
    } else {
222
      /*
223
       * Default variable settings
224
       */
225
27.1M
      zend_string *tmp_str = NULL;
226
27.1M
      adjust = RIGHT;
227
27.1M
      alternate_form = print_sign = print_blank = false;
228
27.1M
      pad_char = ' ';
229
27.1M
      prefix_char = NUL;
230
231
27.1M
      fmt++;
232
233
      /*
234
       * Try to avoid checking for flags, width or precision
235
       */
236
27.1M
      if (isascii((int)*fmt) && !islower((int)*fmt)) {
237
        /*
238
         * Recognize flags: -, #, BLANK, +
239
         */
240
10.7M
        for (;; fmt++) {
241
10.7M
          if (*fmt == '-')
242
0
            adjust = LEFT;
243
10.7M
          else if (*fmt == '+')
244
0
            print_sign = true;
245
10.7M
          else if (*fmt == '#')
246
0
            alternate_form = true;
247
10.7M
          else if (*fmt == ' ')
248
0
            print_blank = true;
249
10.7M
          else if (*fmt == '0')
250
42.7k
            pad_char = '0';
251
10.7M
          else
252
10.7M
            break;
253
10.7M
        }
254
255
        /*
256
         * Check if a width was specified
257
         */
258
10.7M
        if (isdigit((int)*fmt)) {
259
56.6k
          STR_TO_DEC(fmt, min_width);
260
56.6k
          adjust_width = true;
261
10.6M
        } else if (*fmt == '*') {
262
6.85M
          min_width = va_arg(ap, int);
263
6.85M
          fmt++;
264
6.85M
          adjust_width = true;
265
6.85M
          if (min_width < 0) {
266
0
            adjust = LEFT;
267
0
            min_width = -min_width;
268
0
          }
269
6.85M
        } else
270
3.83M
          adjust_width = false;
271
272
        /*
273
         * Check if a precision was specified
274
         */
275
10.7M
        if (*fmt == '.') {
276
99.8k
          adjust_precision = true;
277
99.8k
          fmt++;
278
99.8k
          if (isdigit((int)*fmt)) {
279
464
            STR_TO_DEC(fmt, precision);
280
99.3k
          } else if (*fmt == '*') {
281
99.3k
            precision = va_arg(ap, int);
282
99.3k
            fmt++;
283
99.3k
            if (precision < -1)
284
0
              precision = -1;
285
99.3k
          } else
286
0
            precision = 0;
287
99.8k
        } else
288
10.6M
          adjust_precision = false;
289
10.7M
      } else
290
16.4M
        adjust_precision = adjust_width = false;
291
292
      /*
293
       * Modifier check
294
       */
295
27.1M
      switch (*fmt) {
296
0
        case 'L':
297
0
          fmt++;
298
0
          modifier = LM_LONG_DOUBLE;
299
0
          break;
300
4.07M
        case 'l':
301
4.07M
          fmt++;
302
4.07M
#if SIZEOF_LONG_LONG
303
4.07M
          if (*fmt == 'l') {
304
0
            fmt++;
305
0
            modifier = LM_LONG_LONG;
306
0
          } else
307
4.07M
#endif
308
4.07M
            modifier = LM_LONG;
309
4.07M
          break;
310
522k
        case 'z':
311
522k
          fmt++;
312
522k
          modifier = LM_SIZE_T;
313
522k
          break;
314
0
        case 'j':
315
0
          fmt++;
316
0
          modifier = LM_INTMAX_T;
317
0
          break;
318
0
        case 't':
319
0
          fmt++;
320
0
          modifier = LM_PTRDIFF_T;
321
0
          break;
322
0
        case 'p':
323
0
        {
324
0
          char __next = *(fmt+1);
325
0
          if ('d' == __next || 'u' == __next || 'x' == __next || 'o' == __next) {
326
0
            zend_error_noreturn(E_CORE_ERROR,
327
0
              "printf \"p\" modifier is no longer supported, use ZEND_LONG_FMT");
328
0
          }
329
0
          modifier = LM_STD;
330
0
          break;
331
0
        }
332
0
        case 'h':
333
0
          fmt++;
334
0
          if (*fmt == 'h') {
335
0
            fmt++;
336
0
          }
337
          /* these are promoted to int, so no break */
338
0
          ZEND_FALLTHROUGH;
339
22.5M
        default:
340
22.5M
          modifier = LM_STD;
341
22.5M
          break;
342
27.1M
      }
343
344
      /*
345
       * Argument extraction and printing.
346
       * First we determine the argument type.
347
       * Then, we convert the argument to a string.
348
       * On exit from the switch, s points to the string that
349
       * must be printed, s_len has the length of the string
350
       * The precision requirements, if any, are reflected in s_len.
351
       *
352
       * NOTE: pad_char may be set to '0' because of the 0 flag.
353
       *   It is reset to ' ' by non-numeric formats
354
       */
355
27.1M
      switch (*fmt) {
356
0
        case 'Z': {
357
0
          zval *zvp = va_arg(ap, zval*);
358
0
          zend_string *str = zval_get_tmp_string(zvp, &tmp_str);
359
0
          s_len = ZSTR_LEN(str);
360
0
          s = ZSTR_VAL(str);
361
0
          if (adjust_precision && (size_t)precision < s_len) {
362
0
            s_len = precision;
363
0
          }
364
0
          break;
365
0
        }
366
3.73M
        case 'S': {
367
3.73M
          zend_string *str = va_arg(ap, zend_string*);
368
3.73M
          s_len = ZSTR_LEN(str);
369
3.73M
          s = ZSTR_VAL(str);
370
3.73M
          if (adjust_precision && (size_t)precision < s_len) {
371
0
            s_len = precision;
372
0
          }
373
3.73M
          break;
374
0
        }
375
734k
        case 'u':
376
734k
          switch(modifier) {
377
732k
            default:
378
732k
              i_num = (int64_t) va_arg(ap, unsigned int);
379
732k
              break;
380
0
            case LM_LONG_DOUBLE:
381
0
              goto fmt_error;
382
5
            case LM_LONG:
383
5
              i_num = (int64_t) va_arg(ap, unsigned long int);
384
5
              break;
385
1.60k
            case LM_SIZE_T:
386
1.60k
              i_num = (int64_t) va_arg(ap, size_t);
387
1.60k
              break;
388
0
#if SIZEOF_LONG_LONG
389
0
            case LM_LONG_LONG:
390
0
              i_num = (int64_t) va_arg(ap, unsigned long long int);
391
0
              break;
392
0
#endif
393
0
            case LM_INTMAX_T:
394
0
              i_num = (int64_t) va_arg(ap, uintmax_t);
395
0
              break;
396
0
            case LM_PTRDIFF_T:
397
0
              i_num = (int64_t) va_arg(ap, ptrdiff_t);
398
0
              break;
399
734k
          }
400
          /*
401
           * The rest also applies to other integer formats, so fall
402
           * into that case.
403
           */
404
734k
          ZEND_FALLTHROUGH;
405
5.94M
        case 'd':
406
5.94M
        case 'i':
407
          /*
408
           * Get the arg if we haven't already.
409
           */
410
5.94M
          if ((*fmt) != 'u') {
411
5.21M
            switch(modifier) {
412
623k
              default:
413
623k
                i_num = (int64_t) va_arg(ap, int);
414
623k
                break;
415
0
              case LM_LONG_DOUBLE:
416
0
                goto fmt_error;
417
4.07M
              case LM_LONG:
418
4.07M
                i_num = (int64_t) va_arg(ap, long int);
419
4.07M
                break;
420
520k
              case LM_SIZE_T:
421
520k
                i_num = (int64_t) va_arg(ap, ssize_t);
422
520k
                break;
423
0
#if SIZEOF_LONG_LONG
424
0
              case LM_LONG_LONG:
425
0
                i_num = (int64_t) va_arg(ap, long long int);
426
0
                break;
427
0
#endif
428
0
              case LM_INTMAX_T:
429
0
                i_num = (int64_t) va_arg(ap, intmax_t);
430
0
                break;
431
0
              case LM_PTRDIFF_T:
432
0
                i_num = (int64_t) va_arg(ap, ptrdiff_t);
433
0
                break;
434
5.21M
            }
435
5.21M
          }
436
5.94M
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
437
5.94M
                &num_buf[NUM_BUF_SIZE], &s_len);
438
5.94M
          FIX_PRECISION(adjust_precision, precision, s, s_len);
439
440
5.94M
          if (*fmt != 'u') {
441
5.21M
            if (is_negative)
442
30.4k
              prefix_char = '-';
443
5.18M
            else if (print_sign)
444
0
              prefix_char = '+';
445
5.18M
            else if (print_blank)
446
0
              prefix_char = ' ';
447
5.21M
          }
448
5.94M
          break;
449
450
451
0
        case 'o':
452
0
          switch(modifier) {
453
0
            default:
454
0
              ui_num = (uint64_t) va_arg(ap, unsigned int);
455
0
              break;
456
0
            case LM_LONG_DOUBLE:
457
0
              goto fmt_error;
458
0
            case LM_LONG:
459
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
460
0
              break;
461
0
            case LM_SIZE_T:
462
0
              ui_num = (uint64_t) va_arg(ap, size_t);
463
0
              break;
464
0
#if SIZEOF_LONG_LONG
465
0
            case LM_LONG_LONG:
466
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
467
0
              break;
468
0
#endif
469
0
            case LM_INTMAX_T:
470
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
471
0
              break;
472
0
            case LM_PTRDIFF_T:
473
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
474
0
              break;
475
0
          }
476
0
          s = ap_php_conv_p2(ui_num, 3, *fmt,
477
0
                &num_buf[NUM_BUF_SIZE], &s_len);
478
0
          FIX_PRECISION(adjust_precision, precision, s, s_len);
479
0
          if (alternate_form && *s != '0') {
480
0
            *--s = '0';
481
0
            s_len++;
482
0
          }
483
0
          break;
484
485
486
84.6k
        case 'x':
487
126k
        case 'X':
488
126k
          switch(modifier) {
489
126k
            default:
490
126k
              ui_num = (uint64_t) va_arg(ap, unsigned int);
491
126k
              break;
492
0
            case LM_LONG_DOUBLE:
493
0
              goto fmt_error;
494
0
            case LM_LONG:
495
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
496
0
              break;
497
250
            case LM_SIZE_T:
498
250
              ui_num = (uint64_t) va_arg(ap, size_t);
499
250
              break;
500
0
#if SIZEOF_LONG_LONG
501
0
            case LM_LONG_LONG:
502
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
503
0
              break;
504
0
#endif
505
0
            case LM_INTMAX_T:
506
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
507
0
              break;
508
0
            case LM_PTRDIFF_T:
509
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
510
0
              break;
511
126k
          }
512
126k
          s = ap_php_conv_p2(ui_num, 4, *fmt,
513
126k
                &num_buf[NUM_BUF_SIZE], &s_len);
514
126k
          FIX_PRECISION(adjust_precision, precision, s, s_len);
515
126k
          if (alternate_form && ui_num != 0) {
516
0
            *--s = *fmt;  /* 'x' or 'X' */
517
0
            *--s = '0';
518
0
            s_len += 2;
519
0
          }
520
126k
          break;
521
522
523
9.80M
        case 's':
524
9.80M
          s = va_arg(ap, char *);
525
9.80M
          if (s != NULL) {
526
9.80M
            if (!adjust_precision) {
527
9.80M
              s_len = strlen(s);
528
9.80M
            } else {
529
226
              s_len = zend_strnlen(s, precision);
530
226
            }
531
9.80M
          } else {
532
0
            s = S_NULL;
533
0
            s_len = S_NULL_LEN;
534
0
          }
535
9.80M
          pad_char = ' ';
536
9.80M
          break;
537
538
539
10
        case 'f':
540
464
        case 'F':
541
464
        case 'e':
542
464
        case 'E':
543
464
          switch(modifier) {
544
0
            case LM_LONG_DOUBLE:
545
0
              fp_num = (double) va_arg(ap, long double);
546
0
              break;
547
464
            case LM_STD:
548
464
              fp_num = va_arg(ap, double);
549
464
              break;
550
0
            default:
551
0
              goto fmt_error;
552
464
          }
553
554
464
          if (zend_isnan(fp_num)) {
555
6
            s = "nan";
556
6
            s_len = 3;
557
458
          } else if (zend_isinf(fp_num)) {
558
20
            s = "inf";
559
20
            s_len = 3;
560
438
          } else {
561
#ifdef ZTS
562
            localeconv_r(&lconv);
563
#else
564
438
            if (!lconv) {
565
430
              lconv = localeconv();
566
430
            }
567
438
#endif
568
438
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
569
438
             (adjust_precision == false) ? FLOAT_DIGITS : precision,
570
438
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
571
438
                  &is_negative, &num_buf[1], &s_len);
572
438
            if (is_negative)
573
45
              prefix_char = '-';
574
393
            else if (print_sign)
575
0
              prefix_char = '+';
576
393
            else if (print_blank)
577
0
              prefix_char = ' ';
578
438
          }
579
464
          break;
580
581
582
0
        case 'g':
583
0
        case 'k':
584
0
        case 'G':
585
99.1k
        case 'H':
586
99.1k
          switch(modifier) {
587
0
            case LM_LONG_DOUBLE:
588
0
              fp_num = (double) va_arg(ap, long double);
589
0
              break;
590
99.1k
            case LM_STD:
591
99.1k
              fp_num = va_arg(ap, double);
592
99.1k
              break;
593
0
            default:
594
0
              goto fmt_error;
595
99.1k
          }
596
597
99.1k
          if (zend_isnan(fp_num)) {
598
1.10k
            s = "NAN";
599
1.10k
            s_len = 3;
600
1.10k
            break;
601
98.0k
          } else if (zend_isinf(fp_num)) {
602
7.17k
            if (fp_num > 0) {
603
6.88k
              s = "INF";
604
6.88k
              s_len = 3;
605
6.88k
            } else {
606
293
              s = "-INF";
607
293
              s_len = 4;
608
293
            }
609
7.17k
            break;
610
7.17k
          }
611
612
90.8k
          if (adjust_precision == false)
613
0
            precision = FLOAT_DIGITS;
614
90.8k
          else if (precision == 0)
615
0
            precision = 1;
616
          /*
617
           * * We use &num_buf[ 1 ], so that we have room for the sign
618
           */
619
#ifdef ZTS
620
          localeconv_r(&lconv);
621
#else
622
90.8k
          if (!lconv) {
623
90.8k
            lconv = localeconv();
624
90.8k
          }
625
90.8k
#endif
626
90.8k
          s = zend_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
627
90.8k
          if (*s == '-')
628
18.3k
            prefix_char = *s++;
629
72.4k
          else if (print_sign)
630
0
            prefix_char = '+';
631
72.4k
          else if (print_blank)
632
0
            prefix_char = ' ';
633
634
90.8k
          s_len = strlen(s);
635
636
90.8k
          if (alternate_form && (strchr(s, '.')) == NULL)
637
0
            s[s_len++] = '.';
638
90.8k
          break;
639
640
641
7.44M
        case 'c':
642
7.44M
          char_buf[0] = (char) (va_arg(ap, int));
643
7.44M
          s = &char_buf[0];
644
7.44M
          s_len = 1;
645
7.44M
          pad_char = ' ';
646
7.44M
          break;
647
648
649
0
        case '%':
650
0
          char_buf[0] = '%';
651
0
          s = &char_buf[0];
652
0
          s_len = 1;
653
0
          pad_char = ' ';
654
0
          break;
655
656
657
0
        case 'n':
658
0
          *(va_arg(ap, int *)) = is_char? (int)((smart_string *)xbuf)->len : (int)ZSTR_LEN(((smart_str *)xbuf)->s);
659
0
          goto skip_output;
660
661
          /*
662
           * Always extract the argument as a "char *" pointer. We
663
           * should be using "void *" but there are still machines
664
           * that don't understand it.
665
           * If the pointer size is equal to the size of an unsigned
666
           * integer we convert the pointer to a hex number, otherwise
667
           * we print "%p" to indicate that we don't handle "%p".
668
           */
669
0
        case 'p':
670
0
          if (sizeof(char *) <= sizeof(uint64_t)) {
671
0
            ui_num = (uint64_t)((size_t) va_arg(ap, char *));
672
0
            s = ap_php_conv_p2(ui_num, 4, 'x',
673
0
                &num_buf[NUM_BUF_SIZE], &s_len);
674
0
            if (ui_num != 0) {
675
0
              *--s = 'x';
676
0
              *--s = '0';
677
0
              s_len += 2;
678
0
            }
679
0
          } else {
680
0
            s = "%p";
681
0
            s_len = 2;
682
0
          }
683
0
          pad_char = ' ';
684
0
          break;
685
686
687
0
        case NUL:
688
          /*
689
           * The last character of the format string was %.
690
           * We ignore it.
691
           */
692
0
          continue;
693
694
695
0
fmt_error:
696
0
        php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
697
          /*
698
           * The default case is for unrecognized %'s.
699
           * We print %<char> to help the user identify what
700
           * option is not understood.
701
           * This is also useful in case the user wants to pass
702
           * the output of format_converter to another function
703
           * that understands some other %<char> (like syslog).
704
           * Note that we can't point s inside fmt because the
705
           * unknown <char> could be preceded by width etc.
706
           */
707
0
          ZEND_FALLTHROUGH;
708
0
        default:
709
0
          char_buf[0] = '%';
710
0
          char_buf[1] = *fmt;
711
0
          s = char_buf;
712
0
          s_len = 2;
713
0
          pad_char = ' ';
714
0
          break;
715
27.1M
      }
716
717
27.1M
      if (prefix_char != NUL) {
718
48.8k
        *--s = prefix_char;
719
48.8k
        s_len++;
720
48.8k
      }
721
27.1M
      if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
722
6.81M
        if (pad_char == '0' && prefix_char != NUL) {
723
0
          INS_CHAR(xbuf, *s, is_char);
724
0
          s++;
725
0
          s_len--;
726
0
          min_width--;
727
0
        }
728
6.81M
        PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
729
6.81M
      }
730
      /*
731
       * Print the string s.
732
       */
733
27.1M
      INS_STRING(xbuf, s, s_len, is_char);
734
735
27.1M
      if (adjust_width && adjust == LEFT && (size_t)min_width > s_len) {
736
0
        PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
737
0
      }
738
739
27.1M
      zend_tmp_string_release(tmp_str);
740
27.1M
    }
741
264M
skip_output:
742
264M
    fmt++;
743
264M
  }
744
20.3M
  return;
745
20.3M
}
746
/* }}} */
747
748
PHPAPI void php_printf_to_smart_string(smart_string *buf, const char *format, va_list ap) /* {{{ */
749
15.9M
{
750
15.9M
  xbuf_format_converter(buf, 1, format, ap);
751
15.9M
}
752
/* }}} */
753
754
PHPAPI void php_printf_to_smart_str(smart_str *buf, const char *format, va_list ap) /* {{{ */
755
4.39M
{
756
4.39M
  xbuf_format_converter(buf, 0, format, ap);
757
4.39M
}
758
/* }}} */