2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 2.00 of the Zend license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.zend.com/license/2_00.txt. |
12 | If you did not receive a copy of the Zend license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@zend.com so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/base/zend-printf.h"
22 #include "hphp/runtime/base/array-iterator.h"
23 #include "hphp/runtime/base/builtin-functions.h"
24 #include "hphp/runtime/base/string-buffer.h"
25 #include "hphp/runtime/base/zend-string.h"
26 #include "hphp/runtime/base/zend-strtod.h"
30 /* These definitions are coped from the Zend formatted output conversion
31 files so that we only need to make minimal changes to the Zend formatted
32 output conversion functions that are incorporated here.
37 #define ADJ_PRECISION 2
38 #define NUM_BUF_SIZE 500
40 #define FLOAT_DIGITS 6
41 #define FLOAT_PRECISION 6
42 #define MAX_FLOAT_DIGITS 38
43 #define MAX_FLOAT_PRECISION 40
44 #define EXPONENT_LENGTH 10
46 static char hexchars
[] = "0123456789abcdef";
47 static char HEXCHARS
[] = "0123456789ABCDEF";
63 #define NUM(c) (c - '0')
65 #define STR_TO_DEC(str, num) do { \
67 while (isdigit((int)*str)) { \
70 if (num >= INT_MAX / 10) { \
71 while (isdigit((int)*str++)) \
79 * This macro does zero padding so that the precision
80 * requirement is satisfied. The padding is done by
81 * adding '0's to the left of the string that is going
84 #define FIX_PRECISION(adjust, precision, s, s_len) do { \
86 while (s_len < precision) { \
92 typedef int64_t wide_int
;
93 typedef uint64_t u_wide_int
;
98 #define INT_NULL ((int *)0)
100 static const char* s_null
= "(null)";
103 #define FLOAT_DIGITS 6
104 #define EXPONENT_LENGTH 10
106 #define HAVE_LOCALE_H 1
114 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
116 #define LCONV_DECIMAL_POINT '.'
119 ///////////////////////////////////////////////////////////////////////////////
121 * Copyright (c) 2002, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
123 * Permission to use, copy, modify, and distribute this software for any
124 * purpose with or without fee is hereby granted, provided that the above
125 * copyright notice and this permission notice appear in all copies.
127 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
128 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
130 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
131 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
132 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
133 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
135 * Sponsored in part by the Defense Advanced Research Projects
136 * Agency (DARPA) and Air Force Research Laboratory, Air Force
137 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
140 static char * __cvt(double value
, int ndigit
, int *decpt
, int *sign
,
141 int fmode
, int pad
) {
142 register char *s
= nullptr;
152 /* __dtoa() doesn't allocate space for 0 so we do it by hand */
154 *decpt
= 1 - fmode
; /* 1 for 'e', 0 for 'f' */
156 if ((rve
= s
= (char *)req::malloc_noptrs(ndigit
?siz
:2)) == nullptr) {
165 p
= zend_dtoa(value
, fmode
+ 2, ndigit
, decpt
, sign
, &rve
);
166 if (*decpt
== 9999) {
167 /* Infinity or Nan, convert to inf or nan like printf */
171 return strdup(c
== 'I' ? "INF" : "NAN");
173 /* Make a local copy and adjust rve to be in terms of s */
177 if ((s
= (char *)req::malloc_noptrs(siz
+1)) == nullptr) {
181 (void)string_copy(s
, p
, siz
);
186 /* Add trailing zeros */
198 static inline char *php_ecvt(double value
, int ndigit
, int *decpt
, int *sign
) {
199 return(__cvt(value
, ndigit
, decpt
, sign
, 0, 1));
202 static inline char *php_fcvt(double value
, int ndigit
, int *decpt
, int *sign
) {
203 return(__cvt(value
, ndigit
, decpt
, sign
, 1, 1));
206 static char *php_gcvt(double value
, int ndigit
, char dec_point
,
207 char exponent
, char *buf
) {
208 char *digits
, *dst
, *src
;
211 digits
= zend_dtoa(value
, 2, ndigit
, &decpt
, &sign
, nullptr);
214 * Infinity or NaN, convert to inf or nan with sign.
215 * We assume the buffer is at least ndigit long.
217 snprintf(buf
, ndigit
+ 1, "%s%s", (sign
&& *digits
== 'I') ? "-" : "",
218 *digits
== 'I' ? "INF" : "NAN");
219 zend_freedtoa(digits
);
228 if ((decpt
>= 0 && decpt
> ndigit
) || decpt
< -3) { /* use E-style */
229 /* exponential format (e.g. 1.2345e+13) */
244 } while (*src
!= '\0');
253 *dst
++ = '0' + decpt
;
257 for (sign
= decpt
, i
= 0; (sign
/= 10) != 0; i
++)
261 dst
[i
--] = '0' + decpt
% 10;
265 } else if (decpt
< 0) {
266 /* standard format 0. */
267 *dst
++ = '0'; /* zero before decimal point */
271 } while (++decpt
< 0);
273 while (*src
!= '\0') {
278 /* standard format */
279 for (i
= 0, src
= digits
; i
< decpt
; i
++) {
288 *dst
++ = '0'; /* zero before decimal point */
291 for (i
= decpt
; digits
[i
] != '\0'; i
++) {
297 zend_freedtoa(digits
);
301 ///////////////////////////////////////////////////////////////////////////////
304 /* ====================================================================
305 * Copyright (c) 1995-1998 The Apache Group. All rights reserved.
307 * Redistribution and use in source and binary forms, with or without
308 * modification, are permitted provided that the following conditions
311 * 1. Redistributions of source code must retain the above copyright
312 * notice, this list of conditions and the following disclaimer.
314 * 2. Redistributions in binary form must reproduce the above copyright
315 * notice, this list of conditions and the following disclaimer in
316 * the documentation and/or other materials provided with the
319 * 3. All advertising materials mentioning features or use of this
320 * software must display the following acknowledgment:
321 * "This product includes software developed by the Apache Group
322 * for use in the Apache HTTP server project (http://www.apache.org/)."
324 * 4. The names "Apache Server" and "Apache Group" must not be used to
325 * endorse or promote products derived from this software without
326 * prior written permission.
328 * 5. Redistributions of any form whatsoever must retain the following
330 * "This product includes software developed by the Apache Group
331 * for use in the Apache HTTP server project (http://www.apache.org/)."
333 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
334 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
335 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
336 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
337 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
338 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
339 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
340 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
341 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
342 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
343 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
344 * OF THE POSSIBILITY OF SUCH DAMAGE.
345 * ====================================================================
347 * This software consists of voluntary contributions made by many
348 * individuals on behalf of the Apache Group and was originally based
349 * on public domain software written at the National Center for
350 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
351 * For more information on the Apache Group and the Apache HTTP server
352 * project, please see <http://www.apache.org/>.
354 * This code is based on, and used with the permission of, the
355 * SIO stdio-replacement strx_* functions by Panos Tsirigotis
356 * <panos@alumni.cs.colorado.edu> for xinetd.
360 * Convert num to a base X number where X is a power of 2. nbits determines X.
361 * For example, if nbits is 3, we do base 8 conversion
363 * a pointer to a string containing the number
365 * The caller provides a buffer for the string: that is the buf_end argument
366 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
367 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
369 char * ap_php_conv_p2(register uint64_t num
, register int nbits
,
370 char format
, char *buf_end
, register int *len
)
372 register int mask
= (1 << nbits
) - 1;
373 register char *p
= buf_end
;
374 static char low_digits
[] = "0123456789abcdef";
375 static char upper_digits
[] = "0123456789ABCDEF";
376 register char *digits
= (format
== 'X') ? upper_digits
: low_digits
;
379 *--p
= digits
[num
& mask
];
389 * Convert num to its decimal format.
391 * - a pointer to a string containing the number (no sign)
392 * - len contains the length of the string
393 * - is_negative is set to TRUE or FALSE depending on the sign
394 * of the number (always set to FALSE if is_unsigned is TRUE)
396 * The caller provides a buffer for the string: that is the buf_end argument
397 * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
398 * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
400 char * ap_php_conv_10(register int64_t num
, register bool is_unsigned
,
401 register int * is_negative
, char *buf_end
,
403 register char *p
= buf_end
;
404 register uint64_t magnitude
;
407 magnitude
= (uint64_t) num
;
410 *is_negative
= (num
< 0);
413 * On a 2's complement machine, negating the most negative integer
414 * results in a number that cannot be represented as a signed integer.
415 * Here is what we do to obtain the number's magnitude:
416 * a. add 1 to the number
417 * b. negate it (becomes positive)
418 * c. convert it to unsigned
423 magnitude
= ((uint64_t) - t
) + 1;
425 magnitude
= (uint64_t) num
;
430 * We use a do-while loop so that we write at least 1 digit
433 register uint64_t new_magnitude
= magnitude
/ 10;
435 *--p
= (char)(magnitude
- new_magnitude
* 10 + '0');
436 magnitude
= new_magnitude
;
444 ///////////////////////////////////////////////////////////////////////////////
446 * Convert a floating point number to a string formats 'f', 'e' or 'E'.
447 * The result is placed in buf, and len denotes the length of the string
448 * The sign is returned in the is_negative argument (and is not placed
451 char * php_conv_fp(register char format
, register double num
,
452 bool add_dp
, int precision
, char dec_point
,
453 int *is_negative
, char *buf
, int *len
) {
454 register char *s
= buf
;
455 register char *p
, *p_orig
;
458 if (precision
>= NDIG
- 1) {
459 precision
= NDIG
- 2;
463 p_orig
= p
= php_fcvt(num
, precision
, &decimal_point
, is_negative
);
464 } else { // either e or E format
465 p_orig
= p
= php_ecvt(num
, precision
+ 1, &decimal_point
, is_negative
);
468 // Check for Infinity and NaN
469 if (isalpha((int)*p
)) {
471 memcpy(buf
, p
, *len
+ 1);
477 if (decimal_point
<= 0) {
478 if (num
!= 0 || precision
> 0) {
482 while (decimal_point
++ < 0) {
490 int addz
= decimal_point
>= NDIG
? decimal_point
- NDIG
+ 1 : 0;
491 decimal_point
-= addz
;
492 while (decimal_point
-- > 0) {
498 if (precision
> 0 || add_dp
) {
504 if (precision
> 0 || add_dp
) {
509 // copy the rest of p, the NUL is NOT copied
515 char temp
[EXPONENT_LENGTH
]; // for exponent conversion
517 int exponent_is_negative
;
519 *s
++ = format
; // either e or E
521 if (decimal_point
!= 0) {
522 p
= ap_php_conv_10((int64_t) decimal_point
, false,
523 &exponent_is_negative
, &temp
[EXPONENT_LENGTH
],
525 *s
++ = exponent_is_negative
? '-' : '+';
527 // Make sure the exponent has at least 2 digits
541 ///////////////////////////////////////////////////////////////////////////////
543 inline static void appendchar(char **buffer
, int *pos
, int *size
, char add
) {
544 if ((*pos
+ 1) >= *size
) {
546 *buffer
= (char*)realloc(*buffer
, *size
);
548 (*buffer
)[(*pos
)++] = add
;
551 inline static void appendsimplestring(char **buffer
, int *pos
, int *size
,
552 const char *add
, int len
) {
553 int req_size
= *pos
+ len
;
555 if (req_size
> *size
) {
556 while (req_size
> *size
) {
559 *buffer
= (char *)realloc(*buffer
, *size
);
561 memcpy(&(*buffer
)[*pos
], add
, len
);
565 inline static void appendstring(StringBuffer
*buffer
, const char *add
,
566 int min_width
, int max_width
, char padding
,
567 int alignment
, int len
, int neg
, int expprec
,
573 copy_len
= (expprec
? (max_width
< len
? max_width
: len
) : len
);
574 npad
= min_width
- copy_len
;
580 req_size
= buffer
->size() + (min_width
> copy_len
? min_width
: copy_len
);
582 buffer
->appendCursor(req_size
);
583 if (alignment
== ALIGN_RIGHT
) {
584 if ((neg
|| always_sign
) && padding
=='0') {
585 buffer
->append((neg
) ? '-' : '+');
591 buffer
->append(padding
);
594 buffer
->append(add
, copy_len
);
595 if (alignment
== ALIGN_LEFT
) {
597 buffer
->append(padding
);
602 inline static void appendint(StringBuffer
*buffer
, long number
,
603 int width
, char padding
, int alignment
,
605 char numbuf
[NUM_BUF_SIZE
];
606 register unsigned long magn
, nmagn
;
607 register unsigned int i
= NUM_BUF_SIZE
- 1, neg
= 0;
611 magn
= ((unsigned long) -(number
+ 1)) + 1;
613 magn
= (unsigned long) number
;
616 /* Can't right-pad 0's on integers */
617 if (alignment
==0 && padding
=='0') padding
=' ';
624 numbuf
[--i
] = (unsigned char)(magn
- (nmagn
* 10)) + '0';
627 while (magn
> 0 && i
> 0);
630 } else if (always_sign
) {
633 appendstring(buffer
, &numbuf
[i
], width
, 0,
634 padding
, alignment
, (NUM_BUF_SIZE
- 1) - i
,
635 neg
, 0, always_sign
);
638 inline static void appenduint(StringBuffer
*buffer
,
639 unsigned long number
,
640 int width
, char padding
, int alignment
) {
641 char numbuf
[NUM_BUF_SIZE
];
642 register unsigned long magn
, nmagn
;
643 register unsigned int i
= NUM_BUF_SIZE
- 1;
645 magn
= (unsigned long) number
;
647 /* Can't right-pad 0's on integers */
648 if (alignment
== 0 && padding
== '0') padding
= ' ';
653 numbuf
[--i
] = (unsigned char)(magn
- (nmagn
* 10)) + '0';
655 } while (magn
> 0 && i
> 0);
657 appendstring(buffer
, &numbuf
[i
], width
, 0,
658 padding
, alignment
, (NUM_BUF_SIZE
- 1) - i
, 0, 0, 0);
661 inline static void appenddouble(StringBuffer
*buffer
,
663 int width
, char padding
,
664 int alignment
, int precision
,
665 int adjust
, char fmt
,
667 char num_buf
[NUM_BUF_SIZE
];
669 int s_len
= 0, is_negative
= 0;
671 if ((adjust
& ADJ_PRECISION
) == 0) {
672 precision
= FLOAT_PRECISION
;
673 } else if (precision
> MAX_FLOAT_PRECISION
) {
674 precision
= MAX_FLOAT_PRECISION
;
677 if (std::isnan(number
)) {
678 is_negative
= (number
<0);
679 appendstring(buffer
, "NaN", 3, 0, padding
,
680 alignment
, 3, is_negative
, 0, always_sign
);
684 if (std::isinf(number
)) {
685 is_negative
= (number
<0);
686 appendstring(buffer
, "INF", 3, 0, padding
,
687 alignment
, 3, is_negative
, 0, always_sign
);
691 #if defined(HAVE_LOCALE_H)
693 lconv
= localeconv();
694 # define APPENDDOUBLE_LCONV_DECIMAL_POINT (*lconv->decimal_point)
696 # define APPENDDOUBLE_LCONV_DECIMAL_POINT '.'
704 s
= php_conv_fp((fmt
== 'f')?'F':fmt
,
705 number
, 0, precision
,
706 (fmt
== 'f')?APPENDDOUBLE_LCONV_DECIMAL_POINT
:'.',
707 &is_negative
, &num_buf
[1], &s_len
);
712 } else if (always_sign
) {
724 * * We use &num_buf[ 1 ], so that we have room for the sign
726 s
= php_gcvt(number
, precision
,
727 APPENDDOUBLE_LCONV_DECIMAL_POINT
,
728 (fmt
== 'G')?'E':'e',
734 } else if (always_sign
) {
743 appendstring(buffer
, s
, width
, 0, padding
,
744 alignment
, s_len
, is_negative
, 0, always_sign
);
747 inline static void append2n(StringBuffer
*buffer
, long number
,
748 int width
, char padding
, int alignment
, int n
,
749 char *chartable
, int expprec
) {
750 char numbuf
[NUM_BUF_SIZE
];
751 register unsigned long num
;
752 register unsigned int i
= NUM_BUF_SIZE
- 1;
753 register int andbits
= (1 << n
) - 1;
755 num
= (unsigned long) number
;
759 numbuf
[--i
] = chartable
[(num
& andbits
)];
764 appendstring(buffer
, &numbuf
[i
], width
, 0,
765 padding
, alignment
, (NUM_BUF_SIZE
- 1) - i
,
769 inline static int getnumber(const char *buffer
, int *pos
) {
771 register long num
= strtol(buffer
+ *pos
, &endptr
, 10);
774 if (endptr
!= nullptr) {
775 i
= (endptr
- buffer
- *pos
);
779 if (num
>= INT_MAX
|| num
< 0) {
786 * New sprintf implementation for PHP.
790 * " " pad integers with spaces
791 * "-" left adjusted field
793 * "."n precision (floats only)
794 * "+" Always place a sign (+ or -) in front of a number
798 * "%" literal "%", modifiers are ignored.
799 * "b" integer argument is printed as binary
800 * "c" integer argument is printed as a single character
801 * "d" argument is an integer
802 * "f" the argument is a float
803 * "o" integer argument is printed as octal
804 * "s" argument is a string
805 * "x" integer argument is printed as lowercase hexadecimal
806 * "X" integer argument is printed as uppercase hexadecimal
808 String
string_printf(const char *format
, int len
, const Array
& args
) {
810 if (!vargs
.isNull() && !vargs
->isVectorData()) {
811 vargs
= Array::Create();
812 for (ArrayIter
iter(args
); iter
; ++iter
) {
813 vargs
.append(iter
.second());
818 return empty_string();
822 StringBuffer
result(size
);
824 int argnum
= 0, currarg
= 1;
825 for (int inpos
= 0; inpos
< len
; ++inpos
) {
826 char ch
= format
[inpos
];
834 if (format
[inpos
+ 1] == '%') {
840 /* starting a new format specifier, reset variables */
841 int alignment
= ALIGN_RIGHT
;
845 int width
, precision
;
846 inpos
++; /* skip the '%' */
849 if (isascii(ch
) && !isalpha(ch
)) {
850 /* first look for argnum */
852 while (isdigit((int)format
[temppos
])) temppos
++;
853 if (format
[temppos
] == '$') {
854 argnum
= getnumber(format
, &inpos
);
856 throw_invalid_argument("argnum: must be greater than zero");
859 inpos
++; /* skip the '$' */
864 /* after argnum comes modifiers */
868 if (ch
== ' ' || ch
== '0') {
870 } else if (ch
== '-') {
871 alignment
= ALIGN_LEFT
;
872 /* space padding, the default */
873 } else if (ch
== '+') {
875 } else if (ch
== '\'' && inpos
!= len
- 1) {
876 padding
= format
[++inpos
];
883 /* after modifiers comes width */
885 if ((width
= getnumber(format
, &inpos
)) < 0) {
886 throw_invalid_argument("width: must be greater than zero "
887 "and less than %d", INT_MAX
);
890 adjusting
|= ADJ_WIDTH
;
896 /* after width and argnum comes precision */
898 ch
= format
[++inpos
];
899 if (isdigit((int)ch
)) {
900 if ((precision
= getnumber(format
, &inpos
)) < 0) {
901 throw_invalid_argument("precision: must be greater than zero "
902 "and less than %d", INT_MAX
);
906 adjusting
|= ADJ_PRECISION
;
915 width
= precision
= 0;
919 if (argnum
> vargs
.size()) {
920 throw_invalid_argument("arguments: (too few)");
925 ch
= format
[++inpos
];
927 /* now we expect to find a type specifier */
928 Variant tmp
= vargs
[argnum
-1];
932 String s
= tmp
.toString();
933 appendstring(&result
, s
.c_str(),
934 width
, precision
, padding
, alignment
, s
.size(),
939 appendint(&result
, tmp
.toInt64(),
940 width
, padding
, alignment
, always_sign
);
943 appenduint(&result
, tmp
.toInt64(),
944 width
, padding
, alignment
);
953 appenddouble(&result
, tmp
.toDouble(),
954 width
, padding
, alignment
, precision
, adjusting
,
959 result
.append(tmp
.toByte());
963 append2n(&result
, tmp
.toInt64(),
964 width
, padding
, alignment
, 3, hexchars
, expprec
);
968 append2n(&result
, tmp
.toInt64(),
969 width
, padding
, alignment
, 4, hexchars
, expprec
);
973 append2n(&result
, tmp
.toInt64(),
974 width
, padding
, alignment
, 4, HEXCHARS
, expprec
);
978 append2n(&result
, tmp
.toInt64(),
979 width
, padding
, alignment
, 1, hexchars
, expprec
);
991 return result
.detach();
995 * Do format conversion placing the output in buffer
997 static int xbuf_format_converter(char **outbuf
, const char *fmt
, va_list ap
)
999 register char *s
= nullptr;
1003 register int min_width
= 0;
1012 wide_int i_num
= (wide_int
) 0;
1015 char num_buf
[NUM_BUF_SIZE
];
1016 char char_buf
[2]; /* for printing %% and %<unknown> */
1018 #ifdef HAVE_LOCALE_H
1019 struct lconv
*lconv
= nullptr;
1025 length_modifier_e modifier
;
1026 boolean_e alternate_form
;
1027 boolean_e print_sign
;
1028 boolean_e print_blank
;
1029 boolean_e adjust_precision
;
1030 boolean_e adjust_width
;
1034 char *result
= (char *)malloc(size
);
1039 appendchar(&result
, &outpos
, &size
, *fmt
);
1042 * Default variable settings
1045 alternate_form
= print_sign
= print_blank
= NO
;
1052 * Try to avoid checking for flags, width or precision
1054 if (isascii((int)*fmt
) && !islower((int)*fmt
)) {
1056 * Recognize flags: -, #, BLANK, +
1061 else if (*fmt
== '+')
1063 else if (*fmt
== '#')
1064 alternate_form
= YES
;
1065 else if (*fmt
== ' ')
1067 else if (*fmt
== '0')
1074 * Check if a width was specified
1076 if (isdigit((int)*fmt
)) {
1077 STR_TO_DEC(fmt
, min_width
);
1079 } else if (*fmt
== '*') {
1080 min_width
= va_arg(ap
, int);
1083 if (min_width
< 0) {
1085 min_width
= -min_width
;
1091 * Check if a precision was specified
1093 * XXX: an unreasonable amount of precision may be specified
1094 * resulting in overflow of num_buf. Currently we
1095 * ignore this possibility.
1098 adjust_precision
= YES
;
1100 if (isdigit((int)*fmt
)) {
1101 STR_TO_DEC(fmt
, precision
);
1102 } else if (*fmt
== '*') {
1103 precision
= va_arg(ap
, int);
1110 adjust_precision
= NO
;
1112 adjust_precision
= adjust_width
= NO
;
1120 modifier
= LM_LONG_DOUBLE
;
1124 #if SIZEOF_LONG_LONG
1125 if (*fmt
== '6' && *(fmt
+1) == '4') {
1127 modifier
= LM_LONG_LONG
;
1130 if (*fmt
== '3' && *(fmt
+1) == '2') {
1135 modifier
= LM_LONG_LONG
;
1143 #if SIZEOF_LONG_LONG
1146 modifier
= LM_LONG_LONG
;
1153 modifier
= LM_SIZE_T
;
1158 modifier
= LM_INTMAX_T
;
1160 modifier
= LM_SIZE_T
;
1165 #if SIZEOF_PTRDIFF_T
1166 modifier
= LM_PTRDIFF_T
;
1168 modifier
= LM_SIZE_T
;
1176 /* these are promoted to int, so no break */
1183 * Argument extraction and printing.
1184 * First we determine the argument type.
1185 * Then, we convert the argument to a string.
1186 * On exit from the switch, s points to the string that
1187 * must be printed, s_len has the length of the string
1188 * The precision requirements, if any, are reflected in s_len.
1190 * NOTE: pad_char may be set to '0' because of the 0 flag.
1191 * It is reset to ' ' by non-numeric formats
1197 i_num
= (wide_int
) va_arg(ap
, unsigned int);
1199 case LM_LONG_DOUBLE
:
1202 i_num
= (wide_int
) va_arg(ap
, unsigned long int);
1205 i_num
= (wide_int
) va_arg(ap
, size_t);
1207 #if SIZEOF_LONG_LONG
1209 i_num
= (wide_int
) va_arg(ap
, u_wide_int
);
1214 i_num
= (wide_int
) va_arg(ap
, uintmax_t);
1217 #if SIZEOF_PTRDIFF_T
1219 i_num
= (wide_int
) va_arg(ap
, ptrdiff_t);
1224 * The rest also applies to other integer formats, so fall
1230 * Get the arg if we haven't already.
1232 if ((*fmt
) != 'u') {
1235 i_num
= (wide_int
) va_arg(ap
, int);
1237 case LM_LONG_DOUBLE
:
1240 i_num
= (wide_int
) va_arg(ap
, long int);
1244 i_num
= (wide_int
) va_arg(ap
, ssize_t
);
1246 i_num
= (wide_int
) va_arg(ap
, size_t);
1249 #if SIZEOF_LONG_LONG
1251 i_num
= (wide_int
) va_arg(ap
, wide_int
);
1256 i_num
= (wide_int
) va_arg(ap
, intmax_t);
1259 #if SIZEOF_PTRDIFF_T
1261 i_num
= (wide_int
) va_arg(ap
, ptrdiff_t);
1266 s
= ap_php_conv_10(i_num
, (*fmt
) == 'u', &is_negative
,
1267 &num_buf
[NUM_BUF_SIZE
], &s_len
);
1268 FIX_PRECISION(adjust_precision
, precision
, s
, s_len
);
1273 else if (print_sign
)
1275 else if (print_blank
)
1284 ui_num
= (u_wide_int
) va_arg(ap
, unsigned int);
1286 case LM_LONG_DOUBLE
:
1289 ui_num
= (u_wide_int
) va_arg(ap
, unsigned long int);
1292 ui_num
= (u_wide_int
) va_arg(ap
, size_t);
1294 #if SIZEOF_LONG_LONG
1296 ui_num
= (u_wide_int
) va_arg(ap
, u_wide_int
);
1301 ui_num
= (u_wide_int
) va_arg(ap
, uintmax_t);
1304 #if SIZEOF_PTRDIFF_T
1306 ui_num
= (u_wide_int
) va_arg(ap
, ptrdiff_t);
1310 s
= ap_php_conv_p2(ui_num
, 3, *fmt
,
1311 &num_buf
[NUM_BUF_SIZE
], &s_len
);
1312 FIX_PRECISION(adjust_precision
, precision
, s
, s_len
);
1313 if (alternate_form
&& *s
!= '0') {
1324 ui_num
= (u_wide_int
) va_arg(ap
, unsigned int);
1326 case LM_LONG_DOUBLE
:
1329 ui_num
= (u_wide_int
) va_arg(ap
, unsigned long int);
1332 ui_num
= (u_wide_int
) va_arg(ap
, size_t);
1334 #if SIZEOF_LONG_LONG
1336 ui_num
= (u_wide_int
) va_arg(ap
, u_wide_int
);
1341 ui_num
= (u_wide_int
) va_arg(ap
, uintmax_t);
1344 #if SIZEOF_PTRDIFF_T
1346 ui_num
= (u_wide_int
) va_arg(ap
, ptrdiff_t);
1350 s
= ap_php_conv_p2(ui_num
, 4, *fmt
,
1351 &num_buf
[NUM_BUF_SIZE
], &s_len
);
1352 FIX_PRECISION(adjust_precision
, precision
, s
, s_len
);
1353 if (alternate_form
&& i_num
!= 0) {
1354 *--s
= *fmt
; /* 'x' or 'X' */
1363 s
= va_arg(ap
, char *);
1366 if (adjust_precision
&& precision
< s_len
)
1369 s
= const_cast<char*>(s_null
);
1381 case LM_LONG_DOUBLE
:
1382 fp_num
= (double) va_arg(ap
, long double);
1385 fp_num
= va_arg(ap
, double);
1391 if (std::isnan(fp_num
)) {
1392 s
= const_cast<char*>("nan");
1394 } else if (std::isinf(fp_num
)) {
1395 s
= const_cast<char*>("inf");
1398 #ifdef HAVE_LOCALE_H
1400 lconv
= localeconv();
1403 s
= php_conv_fp((*fmt
== 'f')?'F':*fmt
, fp_num
, alternate_form
,
1404 (adjust_precision
== NO
) ? FLOAT_DIGITS
: precision
,
1405 (*fmt
== 'f')?LCONV_DECIMAL_POINT
:'.',
1406 &is_negative
, &num_buf
[1], &s_len
);
1409 else if (print_sign
)
1411 else if (print_blank
)
1422 case LM_LONG_DOUBLE
:
1423 fp_num
= (double) va_arg(ap
, long double);
1426 fp_num
= va_arg(ap
, double);
1432 if (std::isnan(fp_num
)) {
1433 s
= const_cast<char*>("NAN");
1436 } else if (std::isinf(fp_num
)) {
1438 s
= const_cast<char*>("INF");
1441 s
= const_cast<char*>("-INF");
1447 if (adjust_precision
== NO
)
1448 precision
= FLOAT_DIGITS
;
1449 else if (precision
== 0)
1452 * * We use &num_buf[ 1 ], so that we have room for the sign
1454 #ifdef HAVE_LOCALE_H
1456 lconv
= localeconv();
1459 s
= php_gcvt(fp_num
, precision
,
1460 (*fmt
=='H' || *fmt
== 'k') ? '.' : LCONV_DECIMAL_POINT
,
1461 (*fmt
== 'G' || *fmt
== 'H')?'E':'e', &num_buf
[1]);
1464 else if (print_sign
)
1466 else if (print_blank
)
1471 if (alternate_form
&& (q
= strchr(s
, '.')) == nullptr)
1477 char_buf
[0] = (char) (va_arg(ap
, int));
1493 *(va_arg(ap
, int *)) = outpos
;
1497 * Always extract the argument as a "char *" pointer. We
1498 * should be using "void *" but there are still machines
1499 * that don't understand it.
1500 * If the pointer size is equal to the size of an unsigned
1501 * integer we convert the pointer to a hex number, otherwise
1502 * we print "%p" to indicate that we don't handle "%p".
1505 if (sizeof(char *) <= sizeof(u_wide_int
)) {
1506 ui_num
= (u_wide_int
)((size_t) va_arg(ap
, char *));
1507 s
= ap_php_conv_p2(ui_num
, 4, 'x',
1508 &num_buf
[NUM_BUF_SIZE
], &s_len
);
1515 s
= const_cast<char*>("%p");
1524 * The last character of the format string was %.
1531 throw Exception("Illegal length modifier specified '%c'", *fmt
);
1534 * The default case is for unrecognized %'s.
1535 * We print %<char> to help the user identify what
1536 * option is not understood.
1537 * This is also useful in case the user wants to pass
1538 * the output of format_converter to another function
1539 * that understands some other %<char> (like syslog).
1540 * Note that we can't point s inside fmt because the
1541 * unknown <char> could be preceded by width etc.
1552 if (prefix_char
!= NUL
) {
1556 if (adjust_width
&& adjust
== RIGHT
&& min_width
> s_len
) {
1557 if (pad_char
== '0' && prefix_char
!= NUL
) {
1558 appendchar(&result
, &outpos
, &size
, *s
);
1563 for (int i
= 0; i
< min_width
- s_len
; i
++) {
1564 appendchar(&result
, &outpos
, &size
, pad_char
);
1568 * Print the (for now) non-null terminated string s.
1570 appendsimplestring(&result
, &outpos
, &size
, s
, s_len
);
1572 if (adjust_width
&& adjust
== LEFT
&& min_width
> s_len
) {
1573 for (int i
= 0; i
< min_width
- s_len
; i
++) {
1574 appendchar(&result
, &outpos
, &size
, pad_char
);
1582 * Add the terminating null here since it wasn't added incrementally above
1583 * once the whole string has been composed.
1585 result
[outpos
] = NUL
;
1591 * This is the general purpose conversion function.
1593 int vspprintf(char** pbuf
, size_t /*max_len*/, const char* format
, ...) {
1596 va_start(ap
, format
);
1597 len
= xbuf_format_converter(pbuf
, format
, ap
);
1603 * Same as vspprintf but taking an va_list
1605 int vspprintf_ap(char** pbuf
, size_t /*max_len*/, const char* format
,
1608 len
= xbuf_format_converter(pbuf
, format
, ap
);
1612 int spprintf(char **pbuf
, size_t max_len
, const char *format
, ...)
1617 va_start(ap
, format
);
1618 cc
= vspprintf(pbuf
, max_len
, format
, ap
);
1623 ///////////////////////////////////////////////////////////////////////////////