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