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