Coverage Report

Created: 2026-10-28 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/main/snprintf.c
Line
Count
Source
1
/*
2
  +----------------------------------------------------------------------+
3
  | Copyright © The PHP Group and Contributors.                          |
4
  +----------------------------------------------------------------------+
5
  | This source file is subject to the Modified BSD License that is      |
6
  | bundled with this package in the file LICENSE, and is available      |
7
  | through the World Wide Web at <https://www.php.net/license/>.        |
8
  |                                                                      |
9
  | SPDX-License-Identifier: BSD-3-Clause                                |
10
  +----------------------------------------------------------------------+
11
*/
12
13
#ifndef _GNU_SOURCE
14
# define _GNU_SOURCE
15
#endif
16
#include "php.h"
17
18
#include <zend_strtod.h>
19
20
#include <stddef.h>
21
#include <stdio.h>
22
#include <ctype.h>
23
#include <sys/types.h>
24
#include <stdarg.h>
25
#include <string.h>
26
#include <stdlib.h>
27
#include <math.h>
28
#include <inttypes.h>
29
30
#include <locale.h>
31
#ifdef ZTS
32
#include "ext/standard/php_string.h"
33
#define LCONV_DECIMAL_POINT localeconv_decimal_point()
34
#else
35
0
#define LCONV_DECIMAL_POINT (*lconv->decimal_point)
36
#endif
37
38
/*
39
 * Copyright (c) 2002, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
40
 *
41
 * Permission to use, copy, modify, and distribute this software for any
42
 * purpose with or without fee is hereby granted, provided that the above
43
 * copyright notice and this permission notice appear in all copies.
44
 *
45
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
46
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
47
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
48
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
49
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
50
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
51
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
52
 *
53
 * Sponsored in part by the Defense Advanced Research Projects
54
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
55
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
56
 */
57
58
static char * __cvt(double value, int ndigit, int *decpt, bool *sign, int fmode, int pad) /* {{{ */
59
446
{
60
446
  char *s = NULL;
61
446
  char *p, *rve, c;
62
446
  size_t siz;
63
64
446
  if (ndigit < 0) {
65
0
    siz = -ndigit + 1;
66
446
  } else {
67
446
    siz = ndigit + 1;
68
446
  }
69
70
  /* __dtoa() doesn't allocate space for 0 so we do it by hand */
71
446
  if (value == 0.0) {
72
1
    *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
73
1
    *sign = 0;
74
1
    if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
75
0
      return(NULL);
76
0
    }
77
1
    *rve++ = '0';
78
1
    *rve = '\0';
79
1
    if (!ndigit) {
80
0
      return(s);
81
0
    }
82
445
  } else {
83
445
    p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
84
445
    if (*decpt == 9999) {
85
      /* Infinity or Nan, convert to inf or nan like printf */
86
0
      *decpt = 0;
87
0
      c = *p;
88
0
      zend_freedtoa(p);
89
0
      return strdup((c == 'I' ? "INF" : "NAN"));
90
0
    }
91
    /* Make a local copy and adjust rve to be in terms of s */
92
445
    if (pad && fmode) {
93
445
      siz += *decpt;
94
445
    }
95
445
    if ((s = (char *)malloc(siz+1)) == NULL) {
96
0
      zend_freedtoa(p);
97
0
      return(NULL);
98
0
    }
99
445
    (void) strlcpy(s, p, siz);
100
445
    rve = s + (rve - p);
101
445
    zend_freedtoa(p);
102
445
  }
103
104
  /* Add trailing zeros */
105
446
  if (pad) {
106
446
    siz -= rve - s;
107
896
    while (--siz) {
108
450
      *rve++ = '0';
109
450
    }
110
446
    *rve = '\0';
111
446
  }
112
113
446
  return(s);
114
446
}
115
/* }}} */
116
117
static inline char *php_ecvt(double value, int ndigit, int *decpt, bool *sign) /* {{{ */
118
0
{
119
0
  return(__cvt(value, ndigit, decpt, sign, 0, 1));
120
0
}
121
/* }}} */
122
123
static inline char *php_fcvt(double value, int ndigit, int *decpt, bool *sign) /* {{{ */
124
446
{
125
446
  return(__cvt(value, ndigit, decpt, sign, 1, 1));
126
446
}
127
/* }}} */
128
129
/* {{{ Apache license */
130
/* ====================================================================
131
 * Copyright (c) 1995-1998 The Apache Group.  All rights reserved.
132
 *
133
 * Redistribution and use in source and binary forms, with or without
134
 * modification, are permitted provided that the following conditions
135
 * are met:
136
 *
137
 * 1. Redistributions of source code must retain the above copyright
138
 *    notice, this list of conditions and the following disclaimer.
139
 *
140
 * 2. Redistributions in binary form must reproduce the above copyright
141
 *    notice, this list of conditions and the following disclaimer in
142
 *    the documentation and/or other materials provided with the
143
 *    distribution.
144
 *
145
 * 3. All advertising materials mentioning features or use of this
146
 *    software must display the following acknowledgment:
147
 *    "This product includes software developed by the Apache Group
148
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
149
 *
150
 * 4. The names "Apache Server" and "Apache Group" must not be used to
151
 *    endorse or promote products derived from this software without
152
 *    prior written permission.
153
 *
154
 * 5. Redistributions of any form whatsoever must retain the following
155
 *    acknowledgment:
156
 *    "This product includes software developed by the Apache Group
157
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
158
 *
159
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
160
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
162
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
163
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
164
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
165
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
166
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
167
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
168
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
169
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
170
 * OF THE POSSIBILITY OF SUCH DAMAGE.
171
 * ====================================================================
172
 *
173
 * This software consists of voluntary contributions made by many
174
 * individuals on behalf of the Apache Group and was originally based
175
 * on public domain software written at the National Center for
176
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
177
 * For more information on the Apache Group and the Apache HTTP server
178
 * project, please see <http://www.apache.org/>.
179
 *
180
 * This code is based on, and used with the permission of, the
181
 * SIO stdio-replacement strx_* functions by Panos Tsirigotis
182
 * <panos@alumni.cs.colorado.edu> for xinetd.
183
 */
184
/* }}} */
185
186
2.88M
#define NUL     '\0'
187
#define INT_NULL    ((int *)0)
188
189
0
#define S_NULL      "(null)"
190
0
#define S_NULL_LEN    6
191
192
0
#define FLOAT_DIGITS    6
193
0
#define EXPONENT_LENGTH   10
194
195
196
/*
197
 * Convert num to its decimal format.
198
 * Return value:
199
 *   - a pointer to a string containing the number (no sign)
200
 *   - len contains the length of the string
201
 *   - is_negative is set to true or false depending on the sign
202
 *     of the number (always set to false if is_unsigned is true)
203
 *
204
 * The caller provides a buffer for the string: that is the buf_end argument
205
 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
206
 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
207
 */
208
/* char * ap_php_conv_10() {{{ */
209
PHPAPI char * ap_php_conv_10(int64_t num, bool is_unsigned,
210
     bool * is_negative, char *buf_end, size_t *len)
211
7.02M
{
212
7.02M
  char *p = buf_end;
213
7.02M
  uint64_t magnitude;
214
215
7.02M
  if (is_unsigned) {
216
2.41M
    magnitude = (uint64_t) num;
217
2.41M
    *is_negative = false;
218
4.60M
  } else {
219
4.60M
    *is_negative = (num < 0);
220
221
    /*
222
     * On a 2's complement machine, negating the most negative integer
223
     * results in a number that cannot be represented as a signed integer.
224
     * Here is what we do to obtain the number's magnitude:
225
     *      a. add 1 to the number
226
     *      b. negate it (becomes positive)
227
     *      c. convert it to unsigned
228
     *      d. add 1
229
     */
230
4.60M
    if (*is_negative) {
231
406k
      int64_t t = num + 1;
232
406k
      magnitude = ((uint64_t) - t) + 1;
233
4.19M
    } else {
234
4.19M
      magnitude = (uint64_t) num;
235
4.19M
    }
236
4.60M
  }
237
238
  /*
239
   * We use a do-while loop so that we write at least 1 digit
240
   */
241
26.2M
  do {
242
26.2M
    uint64_t new_magnitude = magnitude / 10;
243
244
26.2M
    *--p = (char)(magnitude - new_magnitude * 10 + '0');
245
26.2M
    magnitude = new_magnitude;
246
26.2M
  }
247
26.2M
  while (magnitude);
248
249
7.02M
  *len = buf_end - p;
250
7.02M
  return (p);
251
7.02M
}
252
/* }}} */
253
254
/* If you change this value then also change bug24640.phpt.
255
 * Also NDIG must be reasonable smaller than NUM_BUF_SIZE.
256
 */
257
694
#define NDIG  320
258
259
260
/*
261
 * Convert a floating point number to a string formats 'f', 'e' or 'E'.
262
 * The result is placed in buf, and len denotes the length of the string
263
 * The sign is returned in the is_negative argument (and is not placed
264
 * in buf).
265
 */
266
/* PHPAPI char * php_conv_fp() {{{ */
267
PHPAPI char * php_conv_fp(char format, double num,
268
     bool add_dp, int precision, char dec_point, bool * is_negative, char *buf, size_t *len)
269
446
{
270
446
  char *s = buf;
271
446
  char *p, *p_orig;
272
446
  int decimal_point;
273
274
446
  if (precision >= NDIG - 1) {
275
0
    precision = NDIG - 2;
276
0
  }
277
278
446
  if (format == 'F') {
279
446
    p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
280
446
  } else {           /* either e or E format */
281
0
    p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
282
0
  }
283
284
  /*
285
   * Check for Infinity and NaN
286
   */
287
446
  if (isalpha((unsigned char)*p)) {
288
0
    *len = strlen(p);
289
0
    memcpy(buf, p, *len + 1);
290
0
    *is_negative = false;
291
0
    free(p_orig);
292
0
    return (buf);
293
0
  }
294
446
  if (format == 'F') {
295
446
    if (decimal_point <= 0) {
296
198
      if (num != 0 || precision > 0) {
297
198
        *s++ = '0';
298
198
        if (precision > 0) {
299
198
          *s++ = dec_point;
300
464
          while (decimal_point++ < 0) {
301
266
            *s++ = '0';
302
266
          }
303
198
        } else if (add_dp) {
304
0
          *s++ = dec_point;
305
0
        }
306
198
      }
307
248
    } else {
308
248
      int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
309
248
      decimal_point -= addz;
310
2.61k
      while (decimal_point-- > 0) {
311
2.36k
        *s++ = *p++;
312
2.36k
      }
313
248
      while (addz-- > 0) {
314
0
        *s++ = '0';
315
0
      }
316
248
      if (precision > 0 || add_dp) {
317
246
        *s++ = dec_point;
318
246
      }
319
248
    }
320
446
  } else {
321
0
    *s++ = *p++;
322
0
    if (precision > 0 || add_dp) {
323
0
      *s++ = '.';
324
0
    }
325
0
  }
326
327
  /*
328
   * copy the rest of p, the NUL is NOT copied
329
   */
330
943
  while (*p) {
331
497
    *s++ = *p++;
332
497
  }
333
334
446
  if (format != 'F') {
335
0
    char temp[EXPONENT_LENGTH];   /* for exponent conversion */
336
0
    size_t t_len;
337
0
    bool exponent_is_negative;
338
339
0
    *s++ = format;      /* either e or E */
340
0
    decimal_point--;
341
0
    if (decimal_point != 0) {
342
0
      p = ap_php_conv_10((int64_t) decimal_point, false, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len);
343
0
      *s++ = exponent_is_negative ? '-' : '+';
344
345
      /*
346
       * Make sure the exponent has at least 2 digits
347
       */
348
0
      while (t_len--) {
349
0
        *s++ = *p++;
350
0
      }
351
0
    } else {
352
0
      *s++ = '+';
353
0
      *s++ = '0';
354
0
    }
355
0
  }
356
446
  *len = s - buf;
357
446
  free(p_orig);
358
446
  return (buf);
359
446
}
360
/* }}} */
361
362
/*
363
 * Convert num to a base X number where X is a power of 2. nbits determines X.
364
 * For example, if nbits is 3, we do base 8 conversion
365
 * Return value:
366
 *      a pointer to a string containing the number
367
 *
368
 * The caller provides a buffer for the string: that is the buf_end argument
369
 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
370
 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
371
 */
372
PHPAPI char * ap_php_conv_p2(uint64_t num, int nbits, char format, char *buf_end, size_t *len) /* {{{ */
373
391k
{
374
391k
  int mask = (1 << nbits) - 1;
375
391k
  char *p = buf_end;
376
391k
  static const char low_digits[] = "0123456789abcdef";
377
391k
  static const char upper_digits[] = "0123456789ABCDEF";
378
391k
  const char *digits = (format == 'X') ? upper_digits : low_digits;
379
380
1.74M
  do {
381
1.74M
    *--p = digits[num & mask];
382
1.74M
    num >>= nbits;
383
1.74M
  }
384
1.74M
  while (num);
385
386
391k
  *len = buf_end - p;
387
391k
  return (p);
388
391k
}
389
/* }}} */
390
391
/*
392
 * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
393
 *
394
 * XXX: this is a magic number; do not decrease it
395
 * Emax = 1023
396
 * NDIG = 320
397
 * NUM_BUF_SIZE >= strlen("-") + Emax + strlen(".") + NDIG + strlen("E+1023") + 1;
398
 */
399
1.41M
#define NUM_BUF_SIZE    2048
400
401
402
/*
403
 * Descriptor for buffer area
404
 */
405
struct buf_area {
406
  char *buf_end;
407
  char *nextb;        /* pointer to next byte to read/write   */
408
};
409
410
typedef struct buf_area buffy;
411
412
/*
413
 * The INS_CHAR macro inserts a character in the buffer and writes
414
 * the buffer back to disk if necessary
415
 * It uses the char pointers sp and bep:
416
 *      sp points to the next available character in the buffer
417
 *      bep points to the end-of-buffer+1
418
 * While using this macro, note that the nextb pointer is NOT updated.
419
 *
420
 * NOTE: Evaluation of the c argument should not have any side effects
421
 */
422
#define INS_CHAR(c, sp, bep, cc) \
423
12.9M
  {                            \
424
12.9M
    if (sp < bep)            \
425
12.9M
    {                        \
426
12.9M
      *sp++ = c;           \
427
12.9M
    }                        \
428
12.9M
    cc++;                    \
429
12.9M
  }
430
431
44.8k
#define NUM( c )      ( c - '0' )
432
433
#define STR_TO_DEC( str, num )    \
434
44.8k
    num = NUM( *(str)++ ) ;    \
435
44.8k
    while ( isdigit((unsigned char)*(str) ) )   \
436
44.8k
    {         \
437
0
  num *= 10 ;     \
438
0
  num += NUM( *(str)++ ) ;    \
439
0
    }
440
441
/*
442
 * This macro does zero padding so that the precision
443
 * requirement is satisfied. The padding is done by
444
 * adding '0's to the left of the string that is going
445
 * to be printed.
446
 */
447
#define FIX_PRECISION( adjust, precision, s, s_len )  \
448
1.41M
  if ( adjust )            \
449
1.41M
  while ( s_len < (size_t)precision )  \
450
0
  {                 \
451
0
      *--s = '0' ;          \
452
0
      s_len++ ;           \
453
0
  }
454
455
/*
456
 * Macro that does padding. The padding is done by printing
457
 * the character ch.
458
 */
459
27.5k
#define PAD( width, len, ch ) do    \
460
42.9k
  {         \
461
42.9k
      INS_CHAR( ch, sp, bep, cc ) ; \
462
42.9k
      width-- ;       \
463
42.9k
  }          \
464
42.9k
  while ( (size_t)width > len )
465
466
/*
467
 * Do format conversion placing the output in buffer
468
 */
469
static size_t format_converter(buffy * odp, const char *fmt, va_list ap) /* {{{ */
470
896k
{
471
896k
  char *sp;
472
896k
  char *bep;
473
896k
  size_t cc = 0;
474
896k
  size_t i;
475
476
896k
  char *s = NULL;
477
896k
  size_t s_len;
478
479
896k
  int min_width = 0;
480
896k
  int precision = 0;
481
896k
  enum {
482
896k
    LEFT, RIGHT
483
896k
  } adjust;
484
896k
  char pad_char;
485
896k
  char prefix_char;
486
487
896k
  double fp_num;
488
896k
  int64_t i_num = (int64_t) 0;
489
896k
  uint64_t ui_num;
490
491
896k
  char num_buf[NUM_BUF_SIZE];
492
896k
  char char_buf[2];     /* for printing %% and %<unknown> */
493
494
896k
#ifndef ZTS
495
896k
  struct lconv *lconv = NULL;
496
896k
#endif
497
498
  /*
499
   * Flag variables
500
   */
501
896k
  length_modifier_e modifier;
502
896k
  bool alternate_form;
503
896k
  bool print_sign;
504
896k
  bool print_blank;
505
896k
  bool adjust_precision;
506
896k
  bool adjust_width;
507
896k
  bool is_negative;
508
509
896k
  sp = odp->nextb;
510
896k
  bep = odp->buf_end;
511
512
6.90M
  while (*fmt) {
513
6.00M
    if (*fmt != '%') {
514
4.57M
      INS_CHAR(*fmt, sp, bep, cc);
515
4.57M
    } else {
516
      /*
517
       * Default variable settings
518
       */
519
1.43M
      zend_string *tmp_str = NULL;
520
1.43M
      adjust = RIGHT;
521
1.43M
      alternate_form = print_sign = print_blank = false;
522
1.43M
      pad_char = ' ';
523
1.43M
      prefix_char = NUL;
524
525
1.43M
      fmt++;
526
527
      /*
528
       * Try to avoid checking for flags, width or precision
529
       */
530
1.43M
      if (isascii((unsigned char)*fmt) && !islower((unsigned char)*fmt)) {
531
        /*
532
         * Recognize flags: -, #, BLANK, +
533
         */
534
89.4k
        for (;; fmt++) {
535
89.4k
          if (*fmt == '-')
536
0
            adjust = LEFT;
537
89.4k
          else if (*fmt == '+')
538
0
            print_sign = true;
539
89.4k
          else if (*fmt == '#')
540
0
            alternate_form = true;
541
89.4k
          else if (*fmt == ' ')
542
0
            print_blank = true;
543
89.4k
          else if (*fmt == '0')
544
44.6k
            pad_char = '0';
545
44.8k
          else
546
44.8k
            break;
547
89.4k
        }
548
549
        /*
550
         * Check if a width was specified
551
         */
552
44.8k
        if (isdigit((unsigned char)*fmt)) {
553
44.8k
          STR_TO_DEC(fmt, min_width);
554
44.8k
          adjust_width = true;
555
44.8k
        } else if (*fmt == '*') {
556
0
          min_width = va_arg(ap, int);
557
0
          fmt++;
558
0
          adjust_width = true;
559
0
          if (min_width < 0) {
560
0
            adjust = LEFT;
561
0
            min_width = -min_width;
562
0
          }
563
0
        } else
564
0
          adjust_width = false;
565
566
        /*
567
         * Check if a precision was specified
568
         */
569
44.8k
        if (*fmt == '.') {
570
0
          adjust_precision = true;
571
0
          fmt++;
572
0
          if (isdigit((unsigned char)*fmt)) {
573
0
            STR_TO_DEC(fmt, precision);
574
0
          } else if (*fmt == '*') {
575
0
            precision = va_arg(ap, int);
576
0
            fmt++;
577
0
            if (precision < -1)
578
0
              precision = -1;
579
0
          } else
580
0
            precision = 0;
581
0
        } else
582
44.8k
          adjust_precision = false;
583
44.8k
      } else
584
1.38M
        adjust_precision = adjust_width = false;
585
586
      /*
587
       * Modifier check
588
       */
589
1.43M
      switch (*fmt) {
590
0
        case 'L':
591
0
          fmt++;
592
0
          modifier = LM_LONG_DOUBLE;
593
0
          break;
594
360
        case 'l':
595
360
          fmt++;
596
360
#if SIZEOF_LONG_LONG
597
360
          if (*fmt == 'l') {
598
16
            fmt++;
599
16
            modifier = LM_LONG_LONG;
600
16
          } else
601
344
#endif
602
344
            modifier = LM_LONG;
603
360
          break;
604
0
        case 'z':
605
0
          fmt++;
606
0
          modifier = LM_SIZE_T;
607
0
          break;
608
0
        case 'j':
609
0
          fmt++;
610
0
          modifier = LM_INTMAX_T;
611
0
          break;
612
0
        case 't':
613
0
          fmt++;
614
0
          modifier = LM_PTRDIFF_T;
615
0
          break;
616
0
        case 'p':
617
0
        {
618
0
          char __next = *(fmt+1);
619
0
          if ('d' == __next || 'u' == __next || 'x' == __next || 'o' == __next) {
620
0
            zend_error_noreturn(E_CORE_ERROR,
621
0
              "printf \"p\" modifier is no longer supported, use ZEND_LONG_FMT");
622
0
          }
623
0
          modifier = LM_STD;
624
0
          break;
625
0
        }
626
0
        case 'h':
627
0
          fmt++;
628
0
          if (*fmt == 'h') {
629
0
            fmt++;
630
0
          }
631
          /* these are promoted to int, so no break */
632
0
          ZEND_FALLTHROUGH;
633
1.43M
        default:
634
1.43M
          modifier = LM_STD;
635
1.43M
          break;
636
1.43M
      }
637
638
      /*
639
       * Argument extraction and printing.
640
       * First we determine the argument type.
641
       * Then, we convert the argument to a string.
642
       * On exit from the switch, s points to the string that
643
       * must be printed, s_len has the length of the string
644
       * The precision requirements, if any, are reflected in s_len.
645
       *
646
       * NOTE: pad_char may be set to '0' because of the 0 flag.
647
       *   It is reset to ' ' by non-numeric formats
648
       */
649
1.43M
      switch (*fmt) {
650
0
        case 'Z': {
651
0
          zval *zvp = va_arg(ap, zval*);
652
0
          zend_string *str = zval_get_tmp_string(zvp, &tmp_str);
653
0
          s_len = ZSTR_LEN(str);
654
0
          s = ZSTR_VAL(str);
655
0
          if (adjust_precision && (size_t)precision < s_len) {
656
0
            s_len = precision;
657
0
          }
658
0
          break;
659
0
        }
660
102k
        case 'u':
661
102k
          switch(modifier) {
662
102k
            default:
663
102k
              i_num = (int64_t) va_arg(ap, unsigned int);
664
102k
              break;
665
0
            case LM_LONG_DOUBLE:
666
0
              goto fmt_error;
667
134
            case LM_LONG:
668
134
              i_num = (int64_t) va_arg(ap, unsigned long int);
669
134
              break;
670
0
            case LM_SIZE_T:
671
0
              i_num = (int64_t) va_arg(ap, size_t);
672
0
              break;
673
0
#if SIZEOF_LONG_LONG
674
0
            case LM_LONG_LONG:
675
0
              i_num = (int64_t) va_arg(ap, unsigned long long int);
676
0
              break;
677
0
#endif
678
0
            case LM_INTMAX_T:
679
0
              i_num = (int64_t) va_arg(ap, uintmax_t);
680
0
              break;
681
0
            case LM_PTRDIFF_T:
682
0
              i_num = (int64_t) va_arg(ap, ptrdiff_t);
683
0
              break;
684
102k
          }
685
          /*
686
           * The rest also applies to other integer formats, so fall
687
           * into that case.
688
           */
689
102k
          ZEND_FALLTHROUGH;
690
411k
        case 'd':
691
1.37M
        case 'i':
692
          /*
693
           * Get the arg if we haven't already.
694
           */
695
1.37M
          if ((*fmt) != 'u') {
696
1.27M
            switch(modifier) {
697
1.27M
              default:
698
1.27M
                i_num = (int64_t) va_arg(ap, int);
699
1.27M
                break;
700
0
              case LM_LONG_DOUBLE:
701
0
                goto fmt_error;
702
210
              case LM_LONG:
703
210
                i_num = (int64_t) va_arg(ap, long int);
704
210
                break;
705
0
              case LM_SIZE_T:
706
0
                i_num = (int64_t) va_arg(ap, ssize_t);
707
0
                break;
708
0
#if SIZEOF_LONG_LONG
709
16
              case LM_LONG_LONG:
710
16
                i_num = (int64_t) va_arg(ap, long long int);
711
16
                break;
712
0
#endif
713
0
              case LM_INTMAX_T:
714
0
                i_num = (int64_t) va_arg(ap, intmax_t);
715
0
                break;
716
0
              case LM_PTRDIFF_T:
717
0
                i_num = (int64_t) va_arg(ap, ptrdiff_t);
718
0
                break;
719
1.27M
            }
720
1.27M
          }
721
1.37M
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
722
1.37M
                &num_buf[NUM_BUF_SIZE], &s_len);
723
1.37M
          FIX_PRECISION(adjust_precision, precision, s, s_len);
724
725
1.37M
          if (*fmt != 'u') {
726
1.27M
            if (is_negative) {
727
305k
              prefix_char = '-';
728
966k
            } else if (print_sign) {
729
0
              prefix_char = '+';
730
966k
            } else if (print_blank) {
731
0
              prefix_char = ' ';
732
0
            }
733
1.27M
          }
734
1.37M
          break;
735
736
737
0
        case 'o':
738
0
          switch(modifier) {
739
0
            default:
740
0
              ui_num = (uint64_t) va_arg(ap, unsigned int);
741
0
              break;
742
0
            case LM_LONG_DOUBLE:
743
0
              goto fmt_error;
744
0
            case LM_LONG:
745
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
746
0
              break;
747
0
            case LM_SIZE_T:
748
0
              ui_num = (uint64_t) va_arg(ap, size_t);
749
0
              break;
750
0
#if SIZEOF_LONG_LONG
751
0
            case LM_LONG_LONG:
752
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
753
0
              break;
754
0
#endif
755
0
            case LM_INTMAX_T:
756
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
757
0
              break;
758
0
            case LM_PTRDIFF_T:
759
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
760
0
              break;
761
0
          }
762
0
          s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
763
0
          FIX_PRECISION(adjust_precision, precision, s, s_len);
764
0
          if (alternate_form && *s != '0') {
765
0
            *--s = '0';
766
0
            s_len++;
767
0
          }
768
0
          break;
769
770
771
0
        case 'x':
772
42.7k
        case 'X':
773
42.7k
          switch(modifier) {
774
42.7k
            default:
775
42.7k
              ui_num = (uint64_t) va_arg(ap, unsigned int);
776
42.7k
              break;
777
0
            case LM_LONG_DOUBLE:
778
0
              goto fmt_error;
779
0
            case LM_LONG:
780
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
781
0
              break;
782
0
            case LM_SIZE_T:
783
0
              ui_num = (uint64_t) va_arg(ap, size_t);
784
0
              break;
785
0
#if SIZEOF_LONG_LONG
786
0
            case LM_LONG_LONG:
787
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
788
0
              break;
789
0
#endif
790
0
            case LM_INTMAX_T:
791
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
792
0
              break;
793
0
            case LM_PTRDIFF_T:
794
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
795
0
              break;
796
42.7k
          }
797
42.7k
          s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
798
42.7k
          FIX_PRECISION(adjust_precision, precision, s, s_len);
799
42.7k
          if (alternate_form && ui_num != 0) {
800
0
            *--s = *fmt;  /* 'x' or 'X' */
801
0
            *--s = '0';
802
0
            s_len += 2;
803
0
          }
804
42.7k
          break;
805
806
807
14.0k
        case 's':
808
14.0k
          s = va_arg(ap, char *);
809
14.0k
          if (s != NULL) {
810
14.0k
            s_len = strlen(s);
811
14.0k
            if (adjust_precision && (size_t)precision < s_len) {
812
0
              s_len = precision;
813
0
            }
814
14.0k
          } else {
815
0
            s = S_NULL;
816
0
            s_len = S_NULL_LEN;
817
0
          }
818
14.0k
          pad_char = ' ';
819
14.0k
          break;
820
821
822
0
        case 'f':
823
0
        case 'F':
824
0
        case 'e':
825
0
        case 'E':
826
0
          switch(modifier) {
827
0
            case LM_LONG_DOUBLE:
828
0
              fp_num = (double) va_arg(ap, long double);
829
0
              break;
830
0
            case LM_STD:
831
0
              fp_num = va_arg(ap, double);
832
0
              break;
833
0
            default:
834
0
              goto fmt_error;
835
0
          }
836
837
0
          if (zend_isnan(fp_num)) {
838
0
            s = "NAN";
839
0
            s_len = 3;
840
0
          } else if (zend_isinf(fp_num)) {
841
0
            s = "INF";
842
0
            s_len = 3;
843
0
          } else {
844
0
#ifndef ZTS
845
0
            if (!lconv) {
846
0
              lconv = localeconv();
847
0
            }
848
0
#endif
849
0
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
850
0
             (adjust_precision == false) ? FLOAT_DIGITS : precision,
851
0
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
852
0
                  &is_negative, &num_buf[1], &s_len);
853
0
            if (is_negative)
854
0
              prefix_char = '-';
855
0
            else if (print_sign)
856
0
              prefix_char = '+';
857
0
            else if (print_blank)
858
0
              prefix_char = ' ';
859
0
          }
860
0
          break;
861
862
863
0
        case 'g':
864
0
        case 'k':
865
0
        case 'G':
866
0
        case 'H':
867
0
          switch(modifier) {
868
0
            case LM_LONG_DOUBLE:
869
0
              fp_num = (double) va_arg(ap, long double);
870
0
              break;
871
0
            case LM_STD:
872
0
              fp_num = va_arg(ap, double);
873
0
              break;
874
0
            default:
875
0
              goto fmt_error;
876
0
          }
877
878
0
          if (zend_isnan(fp_num)) {
879
0
            s = "NAN";
880
0
            s_len = 3;
881
0
            break;
882
0
          } else if (zend_isinf(fp_num)) {
883
0
            if (fp_num > 0) {
884
0
              s = "INF";
885
0
              s_len = 3;
886
0
            } else {
887
0
              s = "-INF";
888
0
              s_len = 4;
889
0
            }
890
0
            break;
891
0
          }
892
893
0
          if (adjust_precision == false) {
894
0
            precision = FLOAT_DIGITS;
895
0
          } else if (precision == 0) {
896
0
            precision = 1;
897
0
          }
898
          /*
899
           * * We use &num_buf[ 1 ], so that we have room for the sign
900
           */
901
0
#ifndef ZTS
902
0
          if (!lconv) {
903
0
            lconv = localeconv();
904
0
          }
905
0
#endif
906
0
          s = zend_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
907
0
          if (*s == '-') {
908
0
            prefix_char = *s++;
909
0
          } else if (print_sign) {
910
0
            prefix_char = '+';
911
0
          } else if (print_blank) {
912
0
            prefix_char = ' ';
913
0
          }
914
915
0
          s_len = strlen(s);
916
917
0
          if (alternate_form && (strchr(s, '.')) == NULL) {
918
0
            s[s_len++] = '.';
919
0
          }
920
0
          break;
921
922
923
184
        case 'c':
924
184
          char_buf[0] = (char) (va_arg(ap, int));
925
184
          s = &char_buf[0];
926
184
          s_len = 1;
927
184
          pad_char = ' ';
928
184
          break;
929
930
931
0
        case '%':
932
0
          char_buf[0] = '%';
933
0
          s = &char_buf[0];
934
0
          s_len = 1;
935
0
          pad_char = ' ';
936
0
          break;
937
938
939
0
        case 'n':
940
0
          *(va_arg(ap, int *)) = cc;
941
0
          goto skip_output;
942
943
          /*
944
           * Always extract the argument as a "char *" pointer. We
945
           * should be using "void *" but there are still machines
946
           * that don't understand it.
947
           * If the pointer size is equal to the size of an unsigned
948
           * integer we convert the pointer to a hex number, otherwise
949
           * we print "%p" to indicate that we don't handle "%p".
950
           */
951
0
        case 'p':
952
0
          if (sizeof(char *) <= sizeof(uint64_t)) {
953
0
            ui_num = (uint64_t)((size_t) va_arg(ap, char *));
954
0
            s = ap_php_conv_p2(ui_num, 4, 'x',
955
0
                &num_buf[NUM_BUF_SIZE], &s_len);
956
0
            if (ui_num != 0) {
957
0
              *--s = 'x';
958
0
              *--s = '0';
959
0
              s_len += 2;
960
0
            }
961
0
          } else {
962
0
            s = "%p";
963
0
            s_len = 2;
964
0
          }
965
0
          pad_char = ' ';
966
0
          break;
967
968
969
0
        case NUL:
970
          /*
971
           * The last character of the format string was %.
972
           * We ignore it.
973
           */
974
0
          continue;
975
976
977
0
fmt_error:
978
0
        php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
979
          /*
980
           * The default case is for unrecognized %'s.
981
           * We print %<char> to help the user identify what
982
           * option is not understood.
983
           * This is also useful in case the user wants to pass
984
           * the output of format_converter to another function
985
           * that understands some other %<char> (like syslog).
986
           * Note that we can't point s inside fmt because the
987
           * unknown <char> could be preceded by width etc.
988
           */
989
0
          ZEND_FALLTHROUGH;
990
0
        default:
991
0
          char_buf[0] = '%';
992
0
          char_buf[1] = *fmt;
993
0
          s = char_buf;
994
0
          s_len = 2;
995
0
          pad_char = ' ';
996
0
          break;
997
1.43M
      }
998
999
1.43M
      if (prefix_char != NUL) {
1000
305k
        *--s = prefix_char;
1001
305k
        s_len++;
1002
305k
      }
1003
1.43M
      if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
1004
27.5k
        if (pad_char == '0' && prefix_char != NUL) {
1005
0
          INS_CHAR(*s, sp, bep, cc)
1006
0
            s++;
1007
0
          s_len--;
1008
0
          min_width--;
1009
0
        }
1010
27.5k
        PAD(min_width, s_len, pad_char);
1011
27.5k
      }
1012
      /*
1013
       * Print the string s.
1014
       */
1015
9.76M
      for (i = s_len; i != 0; i--) {
1016
8.32M
        INS_CHAR(*s, sp, bep, cc);
1017
8.32M
        s++;
1018
8.32M
      }
1019
1020
1.43M
      if (adjust_width && adjust == LEFT && (size_t)min_width > s_len)
1021
0
        PAD(min_width, s_len, pad_char);
1022
1.43M
      zend_tmp_string_release(tmp_str);
1023
1.43M
    }
1024
6.00M
skip_output:
1025
6.00M
    fmt++;
1026
6.00M
  }
1027
896k
  odp->nextb = sp;
1028
896k
  return (cc);
1029
896k
}
1030
/* }}} */
1031
1032
/*
1033
 * This is the general purpose conversion function.
1034
 */
1035
static size_t strx_printv(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1036
896k
{
1037
896k
  buffy od;
1038
896k
  size_t cc;
1039
1040
  /*
1041
   * First initialize the descriptor
1042
   * Notice that if no length is given, we initialize buf_end to the
1043
   * highest possible address.
1044
   */
1045
896k
  if (len == 0) {
1046
0
    od.buf_end = (char *) ~0;
1047
0
    od.nextb   = (char *) ~0;
1048
896k
  } else {
1049
896k
    od.buf_end = &buf[len-1];
1050
896k
    od.nextb   = buf;
1051
896k
  }
1052
1053
  /*
1054
   * Do the conversion
1055
   */
1056
896k
  cc = format_converter(&od, format, ap);
1057
896k
  if (len != 0 && od.nextb <= od.buf_end) {
1058
896k
    *(od.nextb) = '\0';
1059
896k
  }
1060
896k
  return cc;
1061
896k
}
1062
/* }}} */
1063
1064
PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1065
308k
{
1066
308k
  size_t cc;
1067
308k
  va_list ap;
1068
1069
308k
  va_start(ap, format);
1070
308k
  cc = strx_printv(buf, len, format, ap);
1071
308k
  va_end(ap);
1072
308k
  if (cc >= len) {
1073
0
    cc = len -1;
1074
0
    buf[cc] = '\0';
1075
0
  }
1076
308k
  return (int) cc;
1077
308k
}
1078
/* }}} */
1079
1080
PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1081
0
{
1082
0
  size_t cc = strx_printv(buf, len, format, ap);
1083
0
  if (cc >= len) {
1084
0
    cc = len -1;
1085
0
    buf[cc] = '\0';
1086
0
  }
1087
0
  return (int) cc;
1088
0
}
1089
/* }}} */
1090
1091
PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1092
588k
{
1093
588k
  size_t cc;
1094
588k
  va_list ap;
1095
1096
588k
  va_start(ap, format);
1097
588k
  cc = strx_printv(buf, len, format, ap);
1098
588k
  va_end(ap);
1099
588k
  return (int) cc;
1100
588k
}
1101
/* }}} */
1102
1103
PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1104
0
{
1105
0
  size_t cc = strx_printv(buf, len, format, ap);
1106
0
  return (int) cc;
1107
0
}
1108
/* }}} */
1109
1110
PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */
1111
0
{
1112
0
  va_list ap2;
1113
0
  int cc;
1114
1115
0
  va_copy(ap2, ap);
1116
0
  cc = ap_php_vsnprintf(NULL, 0, format, ap2);
1117
0
  va_end(ap2);
1118
1119
0
  *buf = NULL;
1120
1121
0
  if (cc >= 0) {
1122
0
    if ((*buf = malloc(++cc)) != NULL) {
1123
0
      if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) {
1124
0
        free(*buf);
1125
0
        *buf = NULL;
1126
0
      }
1127
0
    }
1128
0
  }
1129
1130
0
  return cc;
1131
0
}
1132
/* }}} */
1133
1134
PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */
1135
0
{
1136
0
  int cc;
1137
0
  va_list ap;
1138
1139
0
  va_start(ap, format);
1140
0
  cc = vasprintf(buf, format, ap);
1141
  va_end(ap);
1142
0
  return cc;
1143
0
}
1144
/* }}} */