Coverage Report

Created: 2025-07-23 06:33

/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
100k
#define LCONV_DECIMAL_POINT (*lconv->decimal_point)
96
#endif
97
98
#include "snprintf.h"
99
100
38.4M
#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
243M
#define INS_CHAR(xbuf, ch, is_char) do { \
115
243M
  if ((is_char)) { \
116
119M
    smart_string_appendc((smart_string *)(xbuf), (ch)); \
117
123M
  } else { \
118
123M
    smart_str_appendc((smart_str *)(xbuf), (ch)); \
119
123M
  } \
120
243M
} while (0);
121
122
19.2M
#define INS_STRING(xbuf, str, len, is_char) do { \
123
19.2M
  if ((is_char)) { \
124
11.0M
    smart_string_appendl((smart_string *)(xbuf), (str), (len)); \
125
11.0M
  } else { \
126
8.19M
    smart_str_appendl((smart_str *)(xbuf), (str), (len)); \
127
8.19M
  } \
128
19.2M
} while (0);
129
130
2.67M
#define PAD_CHAR(xbuf, ch, count, is_char) do { \
131
2.67M
  if ((is_char)) { \
132
2.64M
    smart_string_alloc(((smart_string *)(xbuf)), (count), 0); \
133
2.64M
    memset(((smart_string *)(xbuf))->c + ((smart_string *)(xbuf))->len, (ch), (count)); \
134
2.64M
    ((smart_string *)(xbuf))->len += (count); \
135
2.64M
  } else { \
136
22.6k
    smart_str_alloc(((smart_str *)(xbuf)), (count), 0); \
137
22.6k
    memset(ZSTR_VAL(((smart_str *)(xbuf))->s) + ZSTR_LEN(((smart_str *)(xbuf))->s), (ch), (count)); \
138
22.6k
    ZSTR_LEN(((smart_str *)(xbuf))->s) += (count); \
139
22.6k
  } \
140
2.67M
} 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.73M
#define NUM_BUF_SIZE ZEND_DOUBLE_MAX_LENGTH
147
148
75.8k
#define NUM(c) (c - '0')
149
150
60.4k
#define STR_TO_DEC(str, num) do {     \
151
60.4k
  num = NUM(*str++);                   \
152
60.4k
  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.4k
} 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.73M
#define FIX_PRECISION(adjust, precision, s, s_len) do { \
169
3.73M
    if (adjust)                               \
170
3.73M
    while (s_len < (size_t)precision) {       \
171
0
      *--s = '0';                               \
172
0
      s_len++;                                  \
173
0
    }                        \
174
3.73M
} 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.7M
{
183
16.7M
  char *s = NULL;
184
16.7M
  size_t s_len;
185
186
16.7M
  int min_width = 0;
187
16.7M
  int precision = 0;
188
16.7M
  enum {
189
16.7M
    LEFT, RIGHT
190
16.7M
  } adjust;
191
16.7M
  char pad_char;
192
16.7M
  char prefix_char;
193
194
16.7M
  double fp_num;
195
16.7M
  int64_t i_num = (int64_t) 0;
196
16.7M
  uint64_t ui_num = (uint64_t) 0;
197
198
16.7M
  char num_buf[NUM_BUF_SIZE];
199
16.7M
  char char_buf[2];     /* for printing %% and %<unknown> */
200
201
#ifdef ZTS
202
  struct lconv lconv;
203
#else
204
16.7M
  struct lconv *lconv = NULL;
205
16.7M
#endif
206
207
  /*
208
   * Flag variables
209
   */
210
16.7M
  length_modifier_e modifier;
211
16.7M
  bool alternate_form;
212
16.7M
  bool print_sign;
213
16.7M
  bool print_blank;
214
16.7M
  bool adjust_precision;
215
16.7M
  bool adjust_width;
216
16.7M
  bool is_negative;
217
218
279M
  while (*fmt) {
219
262M
    if (*fmt != '%') {
220
243M
      INS_CHAR(xbuf, *fmt, is_char);
221
243M
    } else {
222
      /*
223
       * Default variable settings
224
       */
225
19.2M
      zend_string *tmp_str = NULL;
226
19.2M
      adjust = RIGHT;
227
19.2M
      alternate_form = print_sign = print_blank = false;
228
19.2M
      pad_char = ' ';
229
19.2M
      prefix_char = NUL;
230
231
19.2M
      fmt++;
232
233
      /*
234
       * Try to avoid checking for flags, width or precision
235
       */
236
19.2M
      if (isascii((int)*fmt) && !islower((int)*fmt)) {
237
        /*
238
         * Recognize flags: -, #, BLANK, +
239
         */
240
5.51M
        for (;; fmt++) {
241
5.51M
          if (*fmt == '-')
242
0
            adjust = LEFT;
243
5.51M
          else if (*fmt == '+')
244
0
            print_sign = true;
245
5.51M
          else if (*fmt == '#')
246
0
            alternate_form = true;
247
5.51M
          else if (*fmt == ' ')
248
0
            print_blank = true;
249
5.51M
          else if (*fmt == '0')
250
45.1k
            pad_char = '0';
251
5.46M
          else
252
5.46M
            break;
253
5.51M
        }
254
255
        /*
256
         * Check if a width was specified
257
         */
258
5.46M
        if (isdigit((int)*fmt)) {
259
59.9k
          STR_TO_DEC(fmt, min_width);
260
59.9k
          adjust_width = true;
261
5.40M
        } else if (*fmt == '*') {
262
2.72M
          min_width = va_arg(ap, int);
263
2.72M
          fmt++;
264
2.72M
          adjust_width = true;
265
2.72M
          if (min_width < 0) {
266
0
            adjust = LEFT;
267
0
            min_width = -min_width;
268
0
          }
269
2.72M
        } else
270
2.68M
          adjust_width = false;
271
272
        /*
273
         * Check if a precision was specified
274
         */
275
5.46M
        if (*fmt == '.') {
276
109k
          adjust_precision = true;
277
109k
          fmt++;
278
109k
          if (isdigit((int)*fmt)) {
279
552
            STR_TO_DEC(fmt, precision);
280
108k
          } else if (*fmt == '*') {
281
108k
            precision = va_arg(ap, int);
282
108k
            fmt++;
283
108k
            if (precision < -1)
284
0
              precision = -1;
285
108k
          } else
286
0
            precision = 0;
287
109k
        } else
288
5.35M
          adjust_precision = false;
289
5.46M
      } else
290
13.7M
        adjust_precision = adjust_width = false;
291
292
      /*
293
       * Modifier check
294
       */
295
19.2M
      switch (*fmt) {
296
0
        case 'L':
297
0
          fmt++;
298
0
          modifier = LM_LONG_DOUBLE;
299
0
          break;
300
2.07M
        case 'l':
301
2.07M
          fmt++;
302
2.07M
#if SIZEOF_LONG_LONG
303
2.07M
          if (*fmt == 'l') {
304
0
            fmt++;
305
0
            modifier = LM_LONG_LONG;
306
0
          } else
307
2.07M
#endif
308
2.07M
            modifier = LM_LONG;
309
2.07M
          break;
310
467k
        case 'z':
311
467k
          fmt++;
312
467k
          modifier = LM_SIZE_T;
313
467k
          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
16.6M
        default:
340
16.6M
          modifier = LM_STD;
341
16.6M
          break;
342
19.2M
      }
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
19.2M
      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
2.57M
        case 'S': {
367
2.57M
          zend_string *str = va_arg(ap, zend_string*);
368
2.57M
          s_len = ZSTR_LEN(str);
369
2.57M
          s = ZSTR_VAL(str);
370
2.57M
          if (adjust_precision && (size_t)precision < s_len) {
371
0
            s_len = precision;
372
0
          }
373
2.57M
          break;
374
0
        }
375
158k
        case 'u':
376
158k
          switch(modifier) {
377
155k
            default:
378
155k
              i_num = (int64_t) va_arg(ap, unsigned int);
379
155k
              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
2.18k
            case LM_SIZE_T:
386
2.18k
              i_num = (int64_t) va_arg(ap, size_t);
387
2.18k
              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
158k
          }
400
          /*
401
           * The rest also applies to other integer formats, so fall
402
           * into that case.
403
           */
404
158k
          ZEND_FALLTHROUGH;
405
3.65M
        case 'd':
406
3.65M
        case 'i':
407
          /*
408
           * Get the arg if we haven't already.
409
           */
410
3.65M
          if ((*fmt) != 'u') {
411
3.49M
            switch(modifier) {
412
959k
              default:
413
959k
                i_num = (int64_t) va_arg(ap, int);
414
959k
                break;
415
0
              case LM_LONG_DOUBLE:
416
0
                goto fmt_error;
417
2.07M
              case LM_LONG:
418
2.07M
                i_num = (int64_t) va_arg(ap, long int);
419
2.07M
                break;
420
464k
              case LM_SIZE_T:
421
464k
                i_num = (int64_t) va_arg(ap, ssize_t);
422
464k
                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
3.49M
            }
435
3.49M
          }
436
3.65M
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
437
3.65M
                &num_buf[NUM_BUF_SIZE], &s_len);
438
3.65M
          FIX_PRECISION(adjust_precision, precision, s, s_len);
439
440
3.65M
          if (*fmt != 'u') {
441
3.49M
            if (is_negative)
442
47.4k
              prefix_char = '-';
443
3.44M
            else if (print_sign)
444
0
              prefix_char = '+';
445
3.44M
            else if (print_blank)
446
0
              prefix_char = ' ';
447
3.49M
          }
448
3.65M
          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
40.4k
        case 'x':
487
85.0k
        case 'X':
488
85.0k
          switch(modifier) {
489
84.8k
            default:
490
84.8k
              ui_num = (uint64_t) va_arg(ap, unsigned int);
491
84.8k
              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
85.0k
          }
512
85.0k
          s = ap_php_conv_p2(ui_num, 4, *fmt,
513
85.0k
                &num_buf[NUM_BUF_SIZE], &s_len);
514
85.0k
          FIX_PRECISION(adjust_precision, precision, s, s_len);
515
85.0k
          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
85.0k
          break;
521
522
523
9.34M
        case 's':
524
9.34M
          s = va_arg(ap, char *);
525
9.34M
          if (s != NULL) {
526
9.34M
            if (!adjust_precision) {
527
9.34M
              s_len = strlen(s);
528
9.34M
            } else {
529
236
              s_len = zend_strnlen(s, precision);
530
236
            }
531
9.34M
          } else {
532
0
            s = S_NULL;
533
0
            s_len = S_NULL_LEN;
534
0
          }
535
9.34M
          pad_char = ' ';
536
9.34M
          break;
537
538
539
0
        case 'f':
540
552
        case 'F':
541
552
        case 'e':
542
552
        case 'E':
543
552
          switch(modifier) {
544
0
            case LM_LONG_DOUBLE:
545
0
              fp_num = (double) va_arg(ap, long double);
546
0
              break;
547
552
            case LM_STD:
548
552
              fp_num = va_arg(ap, double);
549
552
              break;
550
0
            default:
551
0
              goto fmt_error;
552
552
          }
553
554
552
          if (zend_isnan(fp_num)) {
555
3
            s = "nan";
556
3
            s_len = 3;
557
549
          } else if (zend_isinf(fp_num)) {
558
24
            s = "inf";
559
24
            s_len = 3;
560
525
          } else {
561
#ifdef ZTS
562
            localeconv_r(&lconv);
563
#else
564
525
            if (!lconv) {
565
525
              lconv = localeconv();
566
525
            }
567
525
#endif
568
525
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
569
525
             (adjust_precision == false) ? FLOAT_DIGITS : precision,
570
525
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
571
525
                  &is_negative, &num_buf[1], &s_len);
572
525
            if (is_negative)
573
47
              prefix_char = '-';
574
478
            else if (print_sign)
575
0
              prefix_char = '+';
576
478
            else if (print_blank)
577
0
              prefix_char = ' ';
578
525
          }
579
552
          break;
580
581
582
0
        case 'g':
583
0
        case 'k':
584
0
        case 'G':
585
108k
        case 'H':
586
108k
          switch(modifier) {
587
0
            case LM_LONG_DOUBLE:
588
0
              fp_num = (double) va_arg(ap, long double);
589
0
              break;
590
108k
            case LM_STD:
591
108k
              fp_num = va_arg(ap, double);
592
108k
              break;
593
0
            default:
594
0
              goto fmt_error;
595
108k
          }
596
597
108k
          if (zend_isnan(fp_num)) {
598
1.33k
            s = "NAN";
599
1.33k
            s_len = 3;
600
1.33k
            break;
601
107k
          } else if (zend_isinf(fp_num)) {
602
6.45k
            if (fp_num > 0) {
603
6.20k
              s = "INF";
604
6.20k
              s_len = 3;
605
6.20k
            } else {
606
251
              s = "-INF";
607
251
              s_len = 4;
608
251
            }
609
6.45k
            break;
610
6.45k
          }
611
612
100k
          if (adjust_precision == false)
613
0
            precision = FLOAT_DIGITS;
614
100k
          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
100k
          if (!lconv) {
623
100k
            lconv = localeconv();
624
100k
          }
625
100k
#endif
626
100k
          s = zend_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
627
100k
          if (*s == '-')
628
19.1k
            prefix_char = *s++;
629
81.5k
          else if (print_sign)
630
0
            prefix_char = '+';
631
81.5k
          else if (print_blank)
632
0
            prefix_char = ' ';
633
634
100k
          s_len = strlen(s);
635
636
100k
          if (alternate_form && (strchr(s, '.')) == NULL)
637
0
            s[s_len++] = '.';
638
100k
          break;
639
640
641
3.44M
        case 'c':
642
3.44M
          char_buf[0] = (char) (va_arg(ap, int));
643
3.44M
          s = &char_buf[0];
644
3.44M
          s_len = 1;
645
3.44M
          pad_char = ' ';
646
3.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
19.2M
      }
716
717
19.2M
      if (prefix_char != NUL) {
718
66.6k
        *--s = prefix_char;
719
66.6k
        s_len++;
720
66.6k
      }
721
19.2M
      if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
722
2.67M
        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
2.67M
        PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
729
2.67M
      }
730
      /*
731
       * Print the string s.
732
       */
733
19.2M
      INS_STRING(xbuf, s, s_len, is_char);
734
735
19.2M
      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
19.2M
      zend_tmp_string_release(tmp_str);
740
19.2M
    }
741
262M
skip_output:
742
262M
    fmt++;
743
262M
  }
744
16.7M
  return;
745
16.7M
}
746
/* }}} */
747
748
PHPAPI void php_printf_to_smart_string(smart_string *buf, const char *format, va_list ap) /* {{{ */
749
11.5M
{
750
11.5M
  xbuf_format_converter(buf, 1, format, ap);
751
11.5M
}
752
/* }}} */
753
754
PHPAPI void php_printf_to_smart_str(smart_str *buf, const char *format, va_list ap) /* {{{ */
755
5.12M
{
756
5.12M
  xbuf_format_converter(buf, 0, format, ap);
757
5.12M
}
758
/* }}} */