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