Coverage Report

Created: 2026-06-02 06:36

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 (*lconv.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
0
{
60
0
  char *s = NULL;
61
0
  char *p, *rve, c;
62
0
  size_t siz;
63
64
0
  if (ndigit < 0) {
65
0
    siz = -ndigit + 1;
66
0
  } else {
67
0
    siz = ndigit + 1;
68
0
  }
69
70
  /* __dtoa() doesn't allocate space for 0 so we do it by hand */
71
0
  if (value == 0.0) {
72
0
    *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
73
0
    *sign = 0;
74
0
    if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
75
0
      return(NULL);
76
0
    }
77
0
    *rve++ = '0';
78
0
    *rve = '\0';
79
0
    if (!ndigit) {
80
0
      return(s);
81
0
    }
82
0
  } else {
83
0
    p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
84
0
    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
0
    if (pad && fmode) {
93
0
      siz += *decpt;
94
0
    }
95
0
    if ((s = (char *)malloc(siz+1)) == NULL) {
96
0
      zend_freedtoa(p);
97
0
      return(NULL);
98
0
    }
99
0
    (void) strlcpy(s, p, siz);
100
0
    rve = s + (rve - p);
101
0
    zend_freedtoa(p);
102
0
  }
103
104
  /* Add trailing zeros */
105
0
  if (pad) {
106
0
    siz -= rve - s;
107
0
    while (--siz) {
108
0
      *rve++ = '0';
109
0
    }
110
0
    *rve = '\0';
111
0
  }
112
113
0
  return(s);
114
0
}
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
0
{
125
0
  return(__cvt(value, ndigit, decpt, sign, 1, 1));
126
0
}
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
76.5k
#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
80.0k
{
212
80.0k
  char *p = buf_end;
213
80.0k
  uint64_t magnitude;
214
215
80.0k
  if (is_unsigned) {
216
0
    magnitude = (uint64_t) num;
217
0
    *is_negative = false;
218
80.0k
  } else {
219
80.0k
    *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
80.0k
    if (*is_negative) {
231
225
      int64_t t = num + 1;
232
225
      magnitude = ((uint64_t) - t) + 1;
233
79.8k
    } else {
234
79.8k
      magnitude = (uint64_t) num;
235
79.8k
    }
236
80.0k
  }
237
238
  /*
239
   * We use a do-while loop so that we write at least 1 digit
240
   */
241
84.6k
  do {
242
84.6k
    uint64_t new_magnitude = magnitude / 10;
243
244
84.6k
    *--p = (char)(magnitude - new_magnitude * 10 + '0');
245
84.6k
    magnitude = new_magnitude;
246
84.6k
  }
247
84.6k
  while (magnitude);
248
249
80.0k
  *len = buf_end - p;
250
80.0k
  return (p);
251
80.0k
}
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
0
#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
0
{
270
0
  char *s = buf;
271
0
  char *p, *p_orig;
272
0
  int decimal_point;
273
274
0
  if (precision >= NDIG - 1) {
275
0
    precision = NDIG - 2;
276
0
  }
277
278
0
  if (format == 'F') {
279
0
    p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
280
0
  } 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
0
  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
0
  if (format == 'F') {
295
0
    if (decimal_point <= 0) {
296
0
      if (num != 0 || precision > 0) {
297
0
        *s++ = '0';
298
0
        if (precision > 0) {
299
0
          *s++ = dec_point;
300
0
          while (decimal_point++ < 0) {
301
0
            *s++ = '0';
302
0
          }
303
0
        } else if (add_dp) {
304
0
          *s++ = dec_point;
305
0
        }
306
0
      }
307
0
    } else {
308
0
      int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
309
0
      decimal_point -= addz;
310
0
      while (decimal_point-- > 0) {
311
0
        *s++ = *p++;
312
0
      }
313
0
      while (addz-- > 0) {
314
0
        *s++ = '0';
315
0
      }
316
0
      if (precision > 0 || add_dp) {
317
0
        *s++ = dec_point;
318
0
      }
319
0
    }
320
0
  } 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
0
  while (*p) {
331
0
    *s++ = *p++;
332
0
  }
333
334
0
  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
0
  *len = s - buf;
357
0
  free(p_orig);
358
0
  return (buf);
359
0
}
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
0
{
374
0
  int mask = (1 << nbits) - 1;
375
0
  char *p = buf_end;
376
0
  static const char low_digits[] = "0123456789abcdef";
377
0
  static const char upper_digits[] = "0123456789ABCDEF";
378
0
  const char *digits = (format == 'X') ? upper_digits : low_digits;
379
380
0
  do {
381
0
    *--p = digits[num & mask];
382
0
    num >>= nbits;
383
0
  }
384
0
  while (num);
385
386
0
  *len = buf_end - p;
387
0
  return (p);
388
0
}
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
38.2k
#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
459k
  {                            \
424
459k
    if (sp < bep)            \
425
459k
    {                        \
426
459k
      *sp++ = c;           \
427
459k
    }                        \
428
459k
    cc++;                    \
429
459k
  }
430
431
0
#define NUM( c )      ( c - '0' )
432
433
#define STR_TO_DEC( str, num )    \
434
0
    num = NUM( *(str)++ ) ;    \
435
0
    while ( isdigit((unsigned char)*(str) ) )   \
436
0
    {         \
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
38.2k
  if ( adjust )            \
449
38.2k
  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
0
#define PAD( width, len, ch ) do    \
460
0
  {         \
461
0
      INS_CHAR( ch, sp, bep, cc ) ; \
462
0
      width-- ;       \
463
0
  }         \
464
0
  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
38.2k
{
471
38.2k
  char *sp;
472
38.2k
  char *bep;
473
38.2k
  size_t cc = 0;
474
38.2k
  size_t i;
475
476
38.2k
  char *s = NULL;
477
38.2k
  size_t s_len;
478
479
38.2k
  int min_width = 0;
480
38.2k
  int precision = 0;
481
38.2k
  enum {
482
38.2k
    LEFT, RIGHT
483
38.2k
  } adjust;
484
38.2k
  char pad_char;
485
38.2k
  char prefix_char;
486
487
38.2k
  double fp_num;
488
38.2k
  int64_t i_num = (int64_t) 0;
489
38.2k
  uint64_t ui_num;
490
491
38.2k
  char num_buf[NUM_BUF_SIZE];
492
38.2k
  char char_buf[2];     /* for printing %% and %<unknown> */
493
494
#ifdef ZTS
495
  struct lconv lconv;
496
#else
497
38.2k
  struct lconv *lconv = NULL;
498
38.2k
#endif
499
500
  /*
501
   * Flag variables
502
   */
503
38.2k
  length_modifier_e modifier;
504
38.2k
  bool alternate_form;
505
38.2k
  bool print_sign;
506
38.2k
  bool print_blank;
507
38.2k
  bool adjust_precision;
508
38.2k
  bool adjust_width;
509
38.2k
  bool is_negative;
510
511
38.2k
  sp = odp->nextb;
512
38.2k
  bep = odp->buf_end;
513
514
497k
  while (*fmt) {
515
459k
    if (*fmt != '%') {
516
421k
      INS_CHAR(*fmt, sp, bep, cc);
517
421k
    } else {
518
      /*
519
       * Default variable settings
520
       */
521
38.2k
      zend_string *tmp_str = NULL;
522
38.2k
      adjust = RIGHT;
523
38.2k
      alternate_form = print_sign = print_blank = false;
524
38.2k
      pad_char = ' ';
525
38.2k
      prefix_char = NUL;
526
527
38.2k
      fmt++;
528
529
      /*
530
       * Try to avoid checking for flags, width or precision
531
       */
532
38.2k
      if (isascii((unsigned char)*fmt) && !islower((unsigned char)*fmt)) {
533
        /*
534
         * Recognize flags: -, #, BLANK, +
535
         */
536
0
        for (;; fmt++) {
537
0
          if (*fmt == '-')
538
0
            adjust = LEFT;
539
0
          else if (*fmt == '+')
540
0
            print_sign = true;
541
0
          else if (*fmt == '#')
542
0
            alternate_form = true;
543
0
          else if (*fmt == ' ')
544
0
            print_blank = true;
545
0
          else if (*fmt == '0')
546
0
            pad_char = '0';
547
0
          else
548
0
            break;
549
0
        }
550
551
        /*
552
         * Check if a width was specified
553
         */
554
0
        if (isdigit((unsigned char)*fmt)) {
555
0
          STR_TO_DEC(fmt, min_width);
556
0
          adjust_width = true;
557
0
        } else if (*fmt == '*') {
558
0
          min_width = va_arg(ap, int);
559
0
          fmt++;
560
0
          adjust_width = true;
561
0
          if (min_width < 0) {
562
0
            adjust = LEFT;
563
0
            min_width = -min_width;
564
0
          }
565
0
        } else
566
0
          adjust_width = false;
567
568
        /*
569
         * Check if a precision was specified
570
         */
571
0
        if (*fmt == '.') {
572
0
          adjust_precision = true;
573
0
          fmt++;
574
0
          if (isdigit((unsigned char)*fmt)) {
575
0
            STR_TO_DEC(fmt, precision);
576
0
          } else if (*fmt == '*') {
577
0
            precision = va_arg(ap, int);
578
0
            fmt++;
579
0
            if (precision < -1)
580
0
              precision = -1;
581
0
          } else
582
0
            precision = 0;
583
0
        } else
584
0
          adjust_precision = false;
585
0
      } else
586
38.2k
        adjust_precision = adjust_width = false;
587
588
      /*
589
       * Modifier check
590
       */
591
38.2k
      switch (*fmt) {
592
0
        case 'L':
593
0
          fmt++;
594
0
          modifier = LM_LONG_DOUBLE;
595
0
          break;
596
0
        case 'l':
597
0
          fmt++;
598
0
#if SIZEOF_LONG_LONG
599
0
          if (*fmt == 'l') {
600
0
            fmt++;
601
0
            modifier = LM_LONG_LONG;
602
0
          } else
603
0
#endif
604
0
            modifier = LM_LONG;
605
0
          break;
606
0
        case 'z':
607
0
          fmt++;
608
0
          modifier = LM_SIZE_T;
609
0
          break;
610
0
        case 'j':
611
0
          fmt++;
612
0
          modifier = LM_INTMAX_T;
613
0
          break;
614
0
        case 't':
615
0
          fmt++;
616
0
          modifier = LM_PTRDIFF_T;
617
0
          break;
618
0
        case 'p':
619
0
        {
620
0
          char __next = *(fmt+1);
621
0
          if ('d' == __next || 'u' == __next || 'x' == __next || 'o' == __next) {
622
0
            zend_error_noreturn(E_CORE_ERROR,
623
0
              "printf \"p\" modifier is no longer supported, use ZEND_LONG_FMT");
624
0
          }
625
0
          modifier = LM_STD;
626
0
          break;
627
0
        }
628
0
        case 'h':
629
0
          fmt++;
630
0
          if (*fmt == 'h') {
631
0
            fmt++;
632
0
          }
633
          /* these are promoted to int, so no break */
634
0
          ZEND_FALLTHROUGH;
635
38.2k
        default:
636
38.2k
          modifier = LM_STD;
637
38.2k
          break;
638
38.2k
      }
639
640
      /*
641
       * Argument extraction and printing.
642
       * First we determine the argument type.
643
       * Then, we convert the argument to a string.
644
       * On exit from the switch, s points to the string that
645
       * must be printed, s_len has the length of the string
646
       * The precision requirements, if any, are reflected in s_len.
647
       *
648
       * NOTE: pad_char may be set to '0' because of the 0 flag.
649
       *   It is reset to ' ' by non-numeric formats
650
       */
651
38.2k
      switch (*fmt) {
652
0
        case 'Z': {
653
0
          zval *zvp = va_arg(ap, zval*);
654
0
          zend_string *str = zval_get_tmp_string(zvp, &tmp_str);
655
0
          s_len = ZSTR_LEN(str);
656
0
          s = ZSTR_VAL(str);
657
0
          if (adjust_precision && (size_t)precision < s_len) {
658
0
            s_len = precision;
659
0
          }
660
0
          break;
661
0
        }
662
0
        case 'u':
663
0
          switch(modifier) {
664
0
            default:
665
0
              i_num = (int64_t) va_arg(ap, unsigned int);
666
0
              break;
667
0
            case LM_LONG_DOUBLE:
668
0
              goto fmt_error;
669
0
            case LM_LONG:
670
0
              i_num = (int64_t) va_arg(ap, unsigned long int);
671
0
              break;
672
0
            case LM_SIZE_T:
673
0
              i_num = (int64_t) va_arg(ap, size_t);
674
0
              break;
675
0
#if SIZEOF_LONG_LONG
676
0
            case LM_LONG_LONG:
677
0
              i_num = (int64_t) va_arg(ap, unsigned long long int);
678
0
              break;
679
0
#endif
680
0
            case LM_INTMAX_T:
681
0
              i_num = (int64_t) va_arg(ap, uintmax_t);
682
0
              break;
683
0
            case LM_PTRDIFF_T:
684
0
              i_num = (int64_t) va_arg(ap, ptrdiff_t);
685
0
              break;
686
0
          }
687
          /*
688
           * The rest also applies to other integer formats, so fall
689
           * into that case.
690
           */
691
0
          ZEND_FALLTHROUGH;
692
38.2k
        case 'd':
693
38.2k
        case 'i':
694
          /*
695
           * Get the arg if we haven't already.
696
           */
697
38.2k
          if ((*fmt) != 'u') {
698
38.2k
            switch(modifier) {
699
38.2k
              default:
700
38.2k
                i_num = (int64_t) va_arg(ap, int);
701
38.2k
                break;
702
0
              case LM_LONG_DOUBLE:
703
0
                goto fmt_error;
704
0
              case LM_LONG:
705
0
                i_num = (int64_t) va_arg(ap, long int);
706
0
                break;
707
0
              case LM_SIZE_T:
708
0
                i_num = (int64_t) va_arg(ap, ssize_t);
709
0
                break;
710
0
#if SIZEOF_LONG_LONG
711
0
              case LM_LONG_LONG:
712
0
                i_num = (int64_t) va_arg(ap, long long int);
713
0
                break;
714
0
#endif
715
0
              case LM_INTMAX_T:
716
0
                i_num = (int64_t) va_arg(ap, intmax_t);
717
0
                break;
718
0
              case LM_PTRDIFF_T:
719
0
                i_num = (int64_t) va_arg(ap, ptrdiff_t);
720
0
                break;
721
38.2k
            }
722
38.2k
          }
723
38.2k
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
724
38.2k
                &num_buf[NUM_BUF_SIZE], &s_len);
725
38.2k
          FIX_PRECISION(adjust_precision, precision, s, s_len);
726
727
38.2k
          if (*fmt != 'u') {
728
38.2k
            if (is_negative) {
729
0
              prefix_char = '-';
730
38.2k
            } else if (print_sign) {
731
0
              prefix_char = '+';
732
38.2k
            } else if (print_blank) {
733
0
              prefix_char = ' ';
734
0
            }
735
38.2k
          }
736
38.2k
          break;
737
738
739
0
        case 'o':
740
0
          switch(modifier) {
741
0
            default:
742
0
              ui_num = (uint64_t) va_arg(ap, unsigned int);
743
0
              break;
744
0
            case LM_LONG_DOUBLE:
745
0
              goto fmt_error;
746
0
            case LM_LONG:
747
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
748
0
              break;
749
0
            case LM_SIZE_T:
750
0
              ui_num = (uint64_t) va_arg(ap, size_t);
751
0
              break;
752
0
#if SIZEOF_LONG_LONG
753
0
            case LM_LONG_LONG:
754
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
755
0
              break;
756
0
#endif
757
0
            case LM_INTMAX_T:
758
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
759
0
              break;
760
0
            case LM_PTRDIFF_T:
761
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
762
0
              break;
763
0
          }
764
0
          s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
765
0
          FIX_PRECISION(adjust_precision, precision, s, s_len);
766
0
          if (alternate_form && *s != '0') {
767
0
            *--s = '0';
768
0
            s_len++;
769
0
          }
770
0
          break;
771
772
773
0
        case 'x':
774
0
        case 'X':
775
0
          switch(modifier) {
776
0
            default:
777
0
              ui_num = (uint64_t) va_arg(ap, unsigned int);
778
0
              break;
779
0
            case LM_LONG_DOUBLE:
780
0
              goto fmt_error;
781
0
            case LM_LONG:
782
0
              ui_num = (uint64_t) va_arg(ap, unsigned long int);
783
0
              break;
784
0
            case LM_SIZE_T:
785
0
              ui_num = (uint64_t) va_arg(ap, size_t);
786
0
              break;
787
0
#if SIZEOF_LONG_LONG
788
0
            case LM_LONG_LONG:
789
0
              ui_num = (uint64_t) va_arg(ap, unsigned long long int);
790
0
              break;
791
0
#endif
792
0
            case LM_INTMAX_T:
793
0
              ui_num = (uint64_t) va_arg(ap, uintmax_t);
794
0
              break;
795
0
            case LM_PTRDIFF_T:
796
0
              ui_num = (uint64_t) va_arg(ap, ptrdiff_t);
797
0
              break;
798
0
          }
799
0
          s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
800
0
          FIX_PRECISION(adjust_precision, precision, s, s_len);
801
0
          if (alternate_form && i_num != 0) {
802
0
            *--s = *fmt;  /* 'x' or 'X' */
803
0
            *--s = '0';
804
0
            s_len += 2;
805
0
          }
806
0
          break;
807
808
809
24
        case 's':
810
24
          s = va_arg(ap, char *);
811
24
          if (s != NULL) {
812
24
            s_len = strlen(s);
813
24
            if (adjust_precision && (size_t)precision < s_len) {
814
0
              s_len = precision;
815
0
            }
816
24
          } else {
817
0
            s = S_NULL;
818
0
            s_len = S_NULL_LEN;
819
0
          }
820
24
          pad_char = ' ';
821
24
          break;
822
823
824
0
        case 'f':
825
0
        case 'F':
826
0
        case 'e':
827
0
        case 'E':
828
0
          switch(modifier) {
829
0
            case LM_LONG_DOUBLE:
830
0
              fp_num = (double) va_arg(ap, long double);
831
0
              break;
832
0
            case LM_STD:
833
0
              fp_num = va_arg(ap, double);
834
0
              break;
835
0
            default:
836
0
              goto fmt_error;
837
0
          }
838
839
0
          if (zend_isnan(fp_num)) {
840
0
            s = "NAN";
841
0
            s_len = 3;
842
0
          } else if (zend_isinf(fp_num)) {
843
0
            s = "INF";
844
0
            s_len = 3;
845
0
          } else {
846
#ifdef ZTS
847
            localeconv_r(&lconv);
848
#else
849
0
            if (!lconv) {
850
0
              lconv = localeconv();
851
0
            }
852
0
#endif
853
0
            s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
854
0
             (adjust_precision == false) ? FLOAT_DIGITS : precision,
855
0
             (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
856
0
                  &is_negative, &num_buf[1], &s_len);
857
0
            if (is_negative)
858
0
              prefix_char = '-';
859
0
            else if (print_sign)
860
0
              prefix_char = '+';
861
0
            else if (print_blank)
862
0
              prefix_char = ' ';
863
0
          }
864
0
          break;
865
866
867
0
        case 'g':
868
0
        case 'k':
869
0
        case 'G':
870
0
        case 'H':
871
0
          switch(modifier) {
872
0
            case LM_LONG_DOUBLE:
873
0
              fp_num = (double) va_arg(ap, long double);
874
0
              break;
875
0
            case LM_STD:
876
0
              fp_num = va_arg(ap, double);
877
0
              break;
878
0
            default:
879
0
              goto fmt_error;
880
0
          }
881
882
0
          if (zend_isnan(fp_num)) {
883
0
            s = "NAN";
884
0
            s_len = 3;
885
0
            break;
886
0
          } else if (zend_isinf(fp_num)) {
887
0
            if (fp_num > 0) {
888
0
              s = "INF";
889
0
              s_len = 3;
890
0
            } else {
891
0
              s = "-INF";
892
0
              s_len = 4;
893
0
            }
894
0
            break;
895
0
          }
896
897
0
          if (adjust_precision == false) {
898
0
            precision = FLOAT_DIGITS;
899
0
          } else if (precision == 0) {
900
0
            precision = 1;
901
0
          }
902
          /*
903
           * * We use &num_buf[ 1 ], so that we have room for the sign
904
           */
905
#ifdef ZTS
906
          localeconv_r(&lconv);
907
#else
908
0
          if (!lconv) {
909
0
            lconv = localeconv();
910
0
          }
911
0
#endif
912
0
          s = zend_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
913
0
          if (*s == '-') {
914
0
            prefix_char = *s++;
915
0
          } else if (print_sign) {
916
0
            prefix_char = '+';
917
0
          } else if (print_blank) {
918
0
            prefix_char = ' ';
919
0
          }
920
921
0
          s_len = strlen(s);
922
923
0
          if (alternate_form && (strchr(s, '.')) == NULL) {
924
0
            s[s_len++] = '.';
925
0
          }
926
0
          break;
927
928
929
0
        case 'c':
930
0
          char_buf[0] = (char) (va_arg(ap, int));
931
0
          s = &char_buf[0];
932
0
          s_len = 1;
933
0
          pad_char = ' ';
934
0
          break;
935
936
937
0
        case '%':
938
0
          char_buf[0] = '%';
939
0
          s = &char_buf[0];
940
0
          s_len = 1;
941
0
          pad_char = ' ';
942
0
          break;
943
944
945
0
        case 'n':
946
0
          *(va_arg(ap, int *)) = cc;
947
0
          goto skip_output;
948
949
          /*
950
           * Always extract the argument as a "char *" pointer. We
951
           * should be using "void *" but there are still machines
952
           * that don't understand it.
953
           * If the pointer size is equal to the size of an unsigned
954
           * integer we convert the pointer to a hex number, otherwise
955
           * we print "%p" to indicate that we don't handle "%p".
956
           */
957
0
        case 'p':
958
0
          if (sizeof(char *) <= sizeof(uint64_t)) {
959
0
            ui_num = (uint64_t)((size_t) va_arg(ap, char *));
960
0
            s = ap_php_conv_p2(ui_num, 4, 'x',
961
0
                &num_buf[NUM_BUF_SIZE], &s_len);
962
0
            if (ui_num != 0) {
963
0
              *--s = 'x';
964
0
              *--s = '0';
965
0
              s_len += 2;
966
0
            }
967
0
          } else {
968
0
            s = "%p";
969
0
            s_len = 2;
970
0
          }
971
0
          pad_char = ' ';
972
0
          break;
973
974
975
0
        case NUL:
976
          /*
977
           * The last character of the format string was %.
978
           * We ignore it.
979
           */
980
0
          continue;
981
982
983
0
fmt_error:
984
0
        php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
985
          /*
986
           * The default case is for unrecognized %'s.
987
           * We print %<char> to help the user identify what
988
           * option is not understood.
989
           * This is also useful in case the user wants to pass
990
           * the output of format_converter to another function
991
           * that understands some other %<char> (like syslog).
992
           * Note that we can't point s inside fmt because the
993
           * unknown <char> could be preceded by width etc.
994
           */
995
0
          ZEND_FALLTHROUGH;
996
0
        default:
997
0
          char_buf[0] = '%';
998
0
          char_buf[1] = *fmt;
999
0
          s = char_buf;
1000
0
          s_len = 2;
1001
0
          pad_char = ' ';
1002
0
          break;
1003
38.2k
      }
1004
1005
38.2k
      if (prefix_char != NUL) {
1006
0
        *--s = prefix_char;
1007
0
        s_len++;
1008
0
      }
1009
38.2k
      if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
1010
0
        if (pad_char == '0' && prefix_char != NUL) {
1011
0
          INS_CHAR(*s, sp, bep, cc)
1012
0
            s++;
1013
0
          s_len--;
1014
0
          min_width--;
1015
0
        }
1016
0
        PAD(min_width, s_len, pad_char);
1017
0
      }
1018
      /*
1019
       * Print the string s.
1020
       */
1021
76.8k
      for (i = s_len; i != 0; i--) {
1022
38.5k
        INS_CHAR(*s, sp, bep, cc);
1023
38.5k
        s++;
1024
38.5k
      }
1025
1026
38.2k
      if (adjust_width && adjust == LEFT && (size_t)min_width > s_len)
1027
0
        PAD(min_width, s_len, pad_char);
1028
38.2k
      zend_tmp_string_release(tmp_str);
1029
38.2k
    }
1030
459k
skip_output:
1031
459k
    fmt++;
1032
459k
  }
1033
38.2k
  odp->nextb = sp;
1034
38.2k
  return (cc);
1035
38.2k
}
1036
/* }}} */
1037
1038
/*
1039
 * This is the general purpose conversion function.
1040
 */
1041
static size_t strx_printv(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1042
38.2k
{
1043
38.2k
  buffy od;
1044
38.2k
  size_t cc;
1045
1046
  /*
1047
   * First initialize the descriptor
1048
   * Notice that if no length is given, we initialize buf_end to the
1049
   * highest possible address.
1050
   */
1051
38.2k
  if (len == 0) {
1052
0
    od.buf_end = (char *) ~0;
1053
0
    od.nextb   = (char *) ~0;
1054
38.2k
  } else {
1055
38.2k
    od.buf_end = &buf[len-1];
1056
38.2k
    od.nextb   = buf;
1057
38.2k
  }
1058
1059
  /*
1060
   * Do the conversion
1061
   */
1062
38.2k
  cc = format_converter(&od, format, ap);
1063
38.2k
  if (len != 0 && od.nextb <= od.buf_end) {
1064
38.2k
    *(od.nextb) = '\0';
1065
38.2k
  }
1066
38.2k
  return cc;
1067
38.2k
}
1068
/* }}} */
1069
1070
PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1071
38.2k
{
1072
38.2k
  size_t cc;
1073
38.2k
  va_list ap;
1074
1075
38.2k
  va_start(ap, format);
1076
38.2k
  cc = strx_printv(buf, len, format, ap);
1077
38.2k
  va_end(ap);
1078
38.2k
  if (cc >= len) {
1079
0
    cc = len -1;
1080
0
    buf[cc] = '\0';
1081
0
  }
1082
38.2k
  return (int) cc;
1083
38.2k
}
1084
/* }}} */
1085
1086
PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1087
0
{
1088
0
  size_t cc = strx_printv(buf, len, format, ap);
1089
0
  if (cc >= len) {
1090
0
    cc = len -1;
1091
0
    buf[cc] = '\0';
1092
0
  }
1093
0
  return (int) cc;
1094
0
}
1095
/* }}} */
1096
1097
PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1098
10
{
1099
10
  size_t cc;
1100
10
  va_list ap;
1101
1102
10
  va_start(ap, format);
1103
10
  cc = strx_printv(buf, len, format, ap);
1104
10
  va_end(ap);
1105
10
  return (int) cc;
1106
10
}
1107
/* }}} */
1108
1109
PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1110
0
{
1111
0
  size_t cc = strx_printv(buf, len, format, ap);
1112
0
  return (int) cc;
1113
0
}
1114
/* }}} */
1115
1116
PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */
1117
0
{
1118
0
  va_list ap2;
1119
0
  int cc;
1120
1121
0
  va_copy(ap2, ap);
1122
0
  cc = ap_php_vsnprintf(NULL, 0, format, ap2);
1123
0
  va_end(ap2);
1124
1125
0
  *buf = NULL;
1126
1127
0
  if (cc >= 0) {
1128
0
    if ((*buf = malloc(++cc)) != NULL) {
1129
0
      if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) {
1130
0
        free(*buf);
1131
0
        *buf = NULL;
1132
0
      }
1133
0
    }
1134
0
  }
1135
1136
0
  return cc;
1137
0
}
1138
/* }}} */
1139
1140
PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */
1141
0
{
1142
0
  int cc;
1143
0
  va_list ap;
1144
1145
0
  va_start(ap, format);
1146
0
  cc = vasprintf(buf, format, ap);
1147
  va_end(ap);
1148
0
  return cc;
1149
0
}
1150
/* }}} */