Coverage Report

Created: 2022-10-14 11:20

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