Coverage Report

Created: 2026-06-02 06:39

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
531
{
60
531
  char *s = NULL;
61
531
  char *p, *rve, c;
62
531
  size_t siz;
63
64
531
  if (ndigit < 0) {
65
0
    siz = -ndigit + 1;
66
531
  } else {
67
531
    siz = ndigit + 1;
68
531
  }
69
70
  /* __dtoa() doesn't allocate space for 0 so we do it by hand */
71
531
  if (value == 0.0) {
72
96
    *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
73
96
    *sign = 0;
74
96
    if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
75
0
      return(NULL);
76
0
    }
77
96
    *rve++ = '0';
78
96
    *rve = '\0';
79
96
    if (!ndigit) {
80
0
      return(s);
81
0
    }
82
435
  } else {
83
435
    p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
84
435
    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
435
    if (pad && fmode) {
93
429
      siz += *decpt;
94
429
    }
95
435
    if ((s = (char *)malloc(siz+1)) == NULL) {
96
0
      zend_freedtoa(p);
97
0
      return(NULL);
98
0
    }
99
435
    (void) strlcpy(s, p, siz);
100
435
    rve = s + (rve - p);
101
435
    zend_freedtoa(p);
102
435
  }
103
104
  /* Add trailing zeros */
105
531
  if (pad) {
106
531
    siz -= rve - s;
107
4.12k
    while (--siz) {
108
3.59k
      *rve++ = '0';
109
3.59k
    }
110
531
    *rve = '\0';
111
531
  }
112
113
531
  return(s);
114
531
}
115
/* }}} */
116
117
static inline char *php_ecvt(double value, int ndigit, int *decpt, bool *sign) /* {{{ */
118
6
{
119
6
  return(__cvt(value, ndigit, decpt, sign, 0, 1));
120
6
}
121
/* }}} */
122
123
static inline char *php_fcvt(double value, int ndigit, int *decpt, bool *sign) /* {{{ */
124
525
{
125
525
  return(__cvt(value, ndigit, decpt, sign, 1, 1));
126
525
}
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
268k
#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
3
#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
473k
{
212
473k
  char *p = buf_end;
213
473k
  uint64_t magnitude;
214
215
473k
  if (is_unsigned) {
216
18.2k
    magnitude = (uint64_t) num;
217
18.2k
    *is_negative = false;
218
455k
  } else {
219
455k
    *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
455k
    if (*is_negative) {
231
1.81k
      int64_t t = num + 1;
232
1.81k
      magnitude = ((uint64_t) - t) + 1;
233
453k
    } else {
234
453k
      magnitude = (uint64_t) num;
235
453k
    }
236
455k
  }
237
238
  /*
239
   * We use a do-while loop so that we write at least 1 digit
240
   */
241
681k
  do {
242
681k
    uint64_t new_magnitude = magnitude / 10;
243
244
681k
    *--p = (char)(magnitude - new_magnitude * 10 + '0');
245
681k
    magnitude = new_magnitude;
246
681k
  }
247
681k
  while (magnitude);
248
249
473k
  *len = buf_end - p;
250
473k
  return (p);
251
473k
}
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
849
#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
531
{
270
531
  char *s = buf;
271
531
  char *p, *p_orig;
272
531
  int decimal_point;
273
274
531
  if (precision >= NDIG - 1) {
275
0
    precision = NDIG - 2;
276
0
  }
277
278
531
  if (format == 'F') {
279
525
    p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
280
525
  } else {           /* either e or E format */
281
6
    p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
282
6
  }
283
284
  /*
285
   * Check for Infinity and NaN
286
   */
287
531
  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
531
  if (format == 'F') {
295
525
    if (decimal_point <= 0) {
296
207
      if (num != 0 || precision > 0) {
297
207
        *s++ = '0';
298
207
        if (precision > 0) {
299
207
          *s++ = dec_point;
300
468
          while (decimal_point++ < 0) {
301
261
            *s++ = '0';
302
261
          }
303
207
        } else if (add_dp) {
304
0
          *s++ = dec_point;
305
0
        }
306
207
      }
307
318
    } else {
308
318
      int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
309
318
      decimal_point -= addz;
310
1.48k
      while (decimal_point-- > 0) {
311
1.16k
        *s++ = *p++;
312
1.16k
      }
313
318
      while (addz-- > 0) {
314
0
        *s++ = '0';
315
0
      }
316
318
      if (precision > 0 || add_dp) {
317
312
        *s++ = dec_point;
318
312
      }
319
318
    }
320
525
  } else {
321
6
    *s++ = *p++;
322
6
    if (precision > 0 || add_dp) {
323
6
      *s++ = '.';
324
6
    }
325
6
  }
326
327
  /*
328
   * copy the rest of p, the NUL is NOT copied
329
   */
330
6.23k
  while (*p) {
331
5.70k
    *s++ = *p++;
332
5.70k
  }
333
334
531
  if (format != 'F') {
335
6
    char temp[EXPONENT_LENGTH];   /* for exponent conversion */
336
6
    size_t t_len;
337
6
    bool exponent_is_negative;
338
339
6
    *s++ = format;      /* either e or E */
340
6
    decimal_point--;
341
6
    if (decimal_point != 0) {
342
3
      p = ap_php_conv_10((int64_t) decimal_point, false, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len);
343
3
      *s++ = exponent_is_negative ? '-' : '+';
344
345
      /*
346
       * Make sure the exponent has at least 2 digits
347
       */
348
6
      while (t_len--) {
349
3
        *s++ = *p++;
350
3
      }
351
3
    } else {
352
3
      *s++ = '+';
353
3
      *s++ = '0';
354
3
    }
355
6
  }
356
531
  *len = s - buf;
357
531
  free(p_orig);
358
531
  return (buf);
359
531
}
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
4.46k
{
374
4.46k
  int mask = (1 << nbits) - 1;
375
4.46k
  char *p = buf_end;
376
4.46k
  static const char low_digits[] = "0123456789abcdef";
377
4.46k
  static const char upper_digits[] = "0123456789ABCDEF";
378
4.46k
  const char *digits = (format == 'X') ? upper_digits : low_digits;
379
380
12.9k
  do {
381
12.9k
    *--p = digits[num & mask];
382
12.9k
    num >>= nbits;
383
12.9k
  }
384
12.9k
  while (num);
385
386
4.46k
  *len = buf_end - p;
387
4.46k
  return (p);
388
4.46k
}
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
92.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
725k
  {                            \
424
725k
    if (sp < bep)            \
425
725k
    {                        \
426
725k
      *sp++ = c;           \
427
725k
    }                        \
428
725k
    cc++;                    \
429
725k
  }
430
431
43.3k
#define NUM( c )      ( c - '0' )
432
433
#define STR_TO_DEC( str, num )    \
434
43.3k
    num = NUM( *(str)++ ) ;    \
435
43.3k
    while ( isdigit((unsigned char)*(str) ) )   \
436
43.3k
    {         \
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
92.2k
  if ( adjust )            \
449
92.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
31.3k
#define PAD( width, len, ch ) do    \
460
37.4k
  {         \
461
37.4k
      INS_CHAR( ch, sp, bep, cc ) ; \
462
37.4k
      width-- ;       \
463
37.4k
  }          \
464
37.4k
  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
85.2k
{
471
85.2k
  char *sp;
472
85.2k
  char *bep;
473
85.2k
  size_t cc = 0;
474
85.2k
  size_t i;
475
476
85.2k
  char *s = NULL;
477
85.2k
  size_t s_len;
478
479
85.2k
  int min_width = 0;
480
85.2k
  int precision = 0;
481
85.2k
  enum {
482
85.2k
    LEFT, RIGHT
483
85.2k
  } adjust;
484
85.2k
  char pad_char;
485
85.2k
  char prefix_char;
486
487
85.2k
  double fp_num;
488
85.2k
  int64_t i_num = (int64_t) 0;
489
85.2k
  uint64_t ui_num;
490
491
85.2k
  char num_buf[NUM_BUF_SIZE];
492
85.2k
  char char_buf[2];     /* for printing %% and %<unknown> */
493
494
#ifdef ZTS
495
  struct lconv lconv;
496
#else
497
85.2k
  struct lconv *lconv = NULL;
498
85.2k
#endif
499
500
  /*
501
   * Flag variables
502
   */
503
85.2k
  length_modifier_e modifier;
504
85.2k
  bool alternate_form;
505
85.2k
  bool print_sign;
506
85.2k
  bool print_blank;
507
85.2k
  bool adjust_precision;
508
85.2k
  bool adjust_width;
509
85.2k
  bool is_negative;
510
511
85.2k
  sp = odp->nextb;
512
85.2k
  bep = odp->buf_end;
513
514
714k
  while (*fmt) {
515
629k
    if (*fmt != '%') {
516
511k
      INS_CHAR(*fmt, sp, bep, cc);
517
511k
    } else {
518
      /*
519
       * Default variable settings
520
       */
521
118k
      zend_string *tmp_str = NULL;
522
118k
      adjust = RIGHT;
523
118k
      alternate_form = print_sign = print_blank = false;
524
118k
      pad_char = ' ';
525
118k
      prefix_char = NUL;
526
527
118k
      fmt++;
528
529
      /*
530
       * Try to avoid checking for flags, width or precision
531
       */
532
118k
      if (isascii((unsigned char)*fmt) && !islower((unsigned char)*fmt)) {
533
        /*
534
         * Recognize flags: -, #, BLANK, +
535
         */
536
83.1k
        for (;; fmt++) {
537
83.1k
          if (*fmt == '-')
538
0
            adjust = LEFT;
539
83.1k
          else if (*fmt == '+')
540
0
            print_sign = true;
541
83.1k
          else if (*fmt == '#')
542
0
            alternate_form = true;
543
83.1k
          else if (*fmt == ' ')
544
0
            print_blank = true;
545
83.1k
          else if (*fmt == '0')
546
39.7k
            pad_char = '0';
547
43.3k
          else
548
43.3k
            break;
549
83.1k
        }
550
551
        /*
552
         * Check if a width was specified
553
         */
554
43.3k
        if (isdigit((unsigned char)*fmt)) {
555
43.3k
          STR_TO_DEC(fmt, min_width);
556
43.3k
          adjust_width = true;
557
43.3k
        } 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
43.3k
        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
43.3k
          adjust_precision = false;
585
43.3k
      } else
586
75.2k
        adjust_precision = adjust_width = false;
587
588
      /*
589
       * Modifier check
590
       */
591
118k
      switch (*fmt) {
592
0
        case 'L':
593
0
          fmt++;
594
0
          modifier = LM_LONG_DOUBLE;
595
0
          break;
596
8.74k
        case 'l':
597
8.74k
          fmt++;
598
8.74k
#if SIZEOF_LONG_LONG
599
8.74k
          if (*fmt == 'l') {
600
189
            fmt++;
601
189
            modifier = LM_LONG_LONG;
602
189
          } else
603
8.55k
#endif
604
8.55k
            modifier = LM_LONG;
605
8.74k
          break;
606
12
        case 'z':
607
12
          fmt++;
608
12
          modifier = LM_SIZE_T;
609
12
          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
109k
        default:
636
109k
          modifier = LM_STD;
637
109k
          break;
638
118k
      }
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
118k
      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
4.07k
        case 'u':
663
4.07k
          switch(modifier) {
664
9
            default:
665
9
              i_num = (int64_t) va_arg(ap, unsigned int);
666
9
              break;
667
0
            case LM_LONG_DOUBLE:
668
0
              goto fmt_error;
669
4.05k
            case LM_LONG:
670
4.05k
              i_num = (int64_t) va_arg(ap, unsigned long int);
671
4.05k
              break;
672
12
            case LM_SIZE_T:
673
12
              i_num = (int64_t) va_arg(ap, size_t);
674
12
              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
4.07k
          }
687
          /*
688
           * The rest also applies to other integer formats, so fall
689
           * into that case.
690
           */
691
4.07k
          ZEND_FALLTHROUGH;
692
92.2k
        case 'd':
693
92.2k
        case 'i':
694
          /*
695
           * Get the arg if we haven't already.
696
           */
697
92.2k
          if ((*fmt) != 'u') {
698
88.1k
            switch(modifier) {
699
83.4k
              default:
700
83.4k
                i_num = (int64_t) va_arg(ap, int);
701
83.4k
                break;
702
0
              case LM_LONG_DOUBLE:
703
0
                goto fmt_error;
704
4.49k
              case LM_LONG:
705
4.49k
                i_num = (int64_t) va_arg(ap, long int);
706
4.49k
                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
189
              case LM_LONG_LONG:
712
189
                i_num = (int64_t) va_arg(ap, long long int);
713
189
                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
88.1k
            }
722
88.1k
          }
723
92.2k
          s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
724
92.2k
                &num_buf[NUM_BUF_SIZE], &s_len);
725
92.2k
          FIX_PRECISION(adjust_precision, precision, s, s_len);
726
727
92.2k
          if (*fmt != 'u') {
728
88.1k
            if (is_negative) {
729
0
              prefix_char = '-';
730
88.1k
            } else if (print_sign) {
731
0
              prefix_char = '+';
732
88.1k
            } else if (print_blank) {
733
0
              prefix_char = ' ';
734
0
            }
735
88.1k
          }
736
92.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
22.1k
        case 's':
810
22.1k
          s = va_arg(ap, char *);
811
22.1k
          if (s != NULL) {
812
22.1k
            s_len = strlen(s);
813
22.1k
            if (adjust_precision && (size_t)precision < s_len) {
814
0
              s_len = precision;
815
0
            }
816
22.1k
          } else {
817
0
            s = S_NULL;
818
0
            s_len = S_NULL_LEN;
819
0
          }
820
22.1k
          pad_char = ' ';
821
22.1k
          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
4.20k
        case 'c':
930
4.20k
          char_buf[0] = (char) (va_arg(ap, int));
931
4.20k
          s = &char_buf[0];
932
4.20k
          s_len = 1;
933
4.20k
          pad_char = ' ';
934
4.20k
          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
118k
      }
1004
1005
118k
      if (prefix_char != NUL) {
1006
0
        *--s = prefix_char;
1007
0
        s_len++;
1008
0
      }
1009
118k
      if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
1010
31.3k
        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
31.3k
        PAD(min_width, s_len, pad_char);
1017
31.3k
      }
1018
      /*
1019
       * Print the string s.
1020
       */
1021
295k
      for (i = s_len; i != 0; i--) {
1022
177k
        INS_CHAR(*s, sp, bep, cc);
1023
177k
        s++;
1024
177k
      }
1025
1026
118k
      if (adjust_width && adjust == LEFT && (size_t)min_width > s_len)
1027
0
        PAD(min_width, s_len, pad_char);
1028
118k
      zend_tmp_string_release(tmp_str);
1029
118k
    }
1030
629k
skip_output:
1031
629k
    fmt++;
1032
629k
  }
1033
85.2k
  odp->nextb = sp;
1034
85.2k
  return (cc);
1035
85.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
85.2k
{
1043
85.2k
  buffy od;
1044
85.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
85.2k
  if (len == 0) {
1052
0
    od.buf_end = (char *) ~0;
1053
0
    od.nextb   = (char *) ~0;
1054
85.2k
  } else {
1055
85.2k
    od.buf_end = &buf[len-1];
1056
85.2k
    od.nextb   = buf;
1057
85.2k
  }
1058
1059
  /*
1060
   * Do the conversion
1061
   */
1062
85.2k
  cc = format_converter(&od, format, ap);
1063
85.2k
  if (len != 0 && od.nextb <= od.buf_end) {
1064
85.2k
    *(od.nextb) = '\0';
1065
85.2k
  }
1066
85.2k
  return cc;
1067
85.2k
}
1068
/* }}} */
1069
1070
PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1071
85.2k
{
1072
85.2k
  size_t cc;
1073
85.2k
  va_list ap;
1074
1075
85.2k
  va_start(ap, format);
1076
85.2k
  cc = strx_printv(buf, len, format, ap);
1077
85.2k
  va_end(ap);
1078
85.2k
  if (cc >= len) {
1079
0
    cc = len -1;
1080
0
    buf[cc] = '\0';
1081
0
  }
1082
85.2k
  return (int) cc;
1083
85.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
63
{
1099
63
  size_t cc;
1100
63
  va_list ap;
1101
1102
63
  va_start(ap, format);
1103
63
  cc = strx_printv(buf, len, format, ap);
1104
63
  va_end(ap);
1105
63
  return (int) cc;
1106
63
}
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
/* }}} */