Coverage Report

Created: 2025-04-03 08:40

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