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