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