Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / zend-printf.cpp
blob5fb184ba970c8f7e619f072151e9afb33f276f7b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
20 #include <cmath>
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"
28 namespace HPHP {
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.
34 #define ALIGN_LEFT 0
35 #define ALIGN_RIGHT 1
36 #define ADJ_WIDTH 1
37 #define ADJ_PRECISION 2
38 #define NUM_BUF_SIZE 500
39 #define NDIG 320
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";
49 typedef enum {
50 LM_STD = 0,
51 LM_INTMAX_T,
52 LM_PTRDIFF_T,
53 LM_LONG_LONG,
54 LM_SIZE_T,
55 LM_LONG,
56 LM_LONG_DOUBLE
57 } length_modifier_e;
59 typedef enum {
60 NO = 0, YES = 1
61 } boolean_e;
63 #define NUM(c) (c - '0')
65 #define STR_TO_DEC(str, num) do { \
66 num = NUM(*str++); \
67 while (isdigit((int)*str)) { \
68 num *= 10; \
69 num += NUM(*str++); \
70 if (num >= INT_MAX / 10) { \
71 while (isdigit((int)*str++)) \
72 continue; \
73 break; \
74 } \
75 } \
76 } while (0)
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
82 * to be printed.
84 #define FIX_PRECISION(adjust, precision, s, s_len) do { \
85 if (adjust) \
86 while (s_len < precision) { \
87 *--s = '0'; \
88 s_len++; \
89 } \
90 } while (0)
92 typedef int64_t wide_int;
93 typedef uint64_t u_wide_int;
95 #define FALSE 0
96 #define TRUE 1
97 #define NUL '\0'
98 #define INT_NULL ((int *)0)
100 static const char* s_null = "(null)";
101 #define S_NULL_LEN 6
103 #define FLOAT_DIGITS 6
104 #define EXPONENT_LENGTH 10
106 #define HAVE_LOCALE_H 1
108 #ifdef HAVE_LOCALE_H
109 } // namespace HPHP
111 #include <locale.h>
113 namespace HPHP {
114 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
115 #else
116 #define LCONV_DECIMAL_POINT '.'
117 #endif
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;
143 char *p, *rve, c;
144 size_t siz;
146 if (ndigit < 0) {
147 siz = -ndigit + 1;
148 } else {
149 siz = ndigit + 1;
152 /* __dtoa() doesn't allocate space for 0 so we do it by hand */
153 if (value == 0.0) {
154 *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
155 *sign = 0;
156 if ((rve = s = (char *)req::malloc_noptrs(ndigit?siz:2)) == nullptr) {
157 return(nullptr);
159 *rve++ = '0';
160 *rve = '\0';
161 if (!ndigit) {
162 return(s);
164 } else {
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 */
168 *decpt = 0;
169 c = *p;
170 zend_freedtoa(p);
171 return strdup(c == 'I' ? "INF" : "NAN");
173 /* Make a local copy and adjust rve to be in terms of s */
174 if (pad && fmode) {
175 siz += *decpt;
177 if ((s = (char *)req::malloc_noptrs(siz+1)) == nullptr) {
178 zend_freedtoa(p);
179 return(nullptr);
181 (void)string_copy(s, p, siz);
182 rve = s + (rve - p);
183 zend_freedtoa(p);
186 /* Add trailing zeros */
187 if (pad) {
188 siz -= rve - s;
189 while (--siz) {
190 *rve++ = '0';
192 *rve = '\0';
195 return(s);
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;
209 int i, decpt, sign;
211 digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, nullptr);
212 if (decpt == 9999) {
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);
220 return (buf);
223 dst = buf;
224 if (sign) {
225 *dst++ = '-';
228 if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */
229 /* exponential format (e.g. 1.2345e+13) */
230 if (--decpt < 0) {
231 sign = 1;
232 decpt = -decpt;
233 } else {
234 sign = 0;
236 src = digits;
237 *dst++ = *src++;
238 *dst++ = dec_point;
239 if (*src == '\0') {
240 *dst++ = '0';
241 } else {
242 do {
243 *dst++ = *src++;
244 } while (*src != '\0');
246 *dst++ = exponent;
247 if (sign) {
248 *dst++ = '-';
249 } else {
250 *dst++ = '+';
252 if (decpt < 10) {
253 *dst++ = '0' + decpt;
254 *dst = '\0';
255 } else {
256 /* XXX - optimize */
257 for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
258 continue;
259 dst[i + 1] = '\0';
260 while (decpt != 0) {
261 dst[i--] = '0' + decpt % 10;
262 decpt /= 10;
265 } else if (decpt < 0) {
266 /* standard format 0. */
267 *dst++ = '0'; /* zero before decimal point */
268 *dst++ = dec_point;
269 do {
270 *dst++ = '0';
271 } while (++decpt < 0);
272 src = digits;
273 while (*src != '\0') {
274 *dst++ = *src++;
276 *dst = '\0';
277 } else {
278 /* standard format */
279 for (i = 0, src = digits; i < decpt; i++) {
280 if (*src != '\0') {
281 *dst++ = *src++;
282 } else {
283 *dst++ = '0';
286 if (*src != '\0') {
287 if (src == digits) {
288 *dst++ = '0'; /* zero before decimal point */
290 *dst++ = dec_point;
291 for (i = decpt; digits[i] != '\0'; i++) {
292 *dst++ = digits[i];
295 *dst = '\0';
297 zend_freedtoa(digits);
298 return (buf);
301 ///////////////////////////////////////////////////////////////////////////////
302 // Apache license
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
309 * are met:
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
317 * distribution.
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
329 * acknowledgment:
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
362 * Return value:
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;
378 do {
379 *--p = digits[num & mask];
380 num >>= nbits;
382 while (num);
384 *len = buf_end - p;
385 return (p);
389 * Convert num to its decimal format.
390 * Return value:
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,
402 register int *len) {
403 register char *p = buf_end;
404 register uint64_t magnitude;
406 if (is_unsigned) {
407 magnitude = (uint64_t) num;
408 *is_negative = 0;
409 } else {
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
419 * d. add 1
421 if (*is_negative) {
422 int64_t t = num + 1;
423 magnitude = ((uint64_t) - t) + 1;
424 } else {
425 magnitude = (uint64_t) num;
430 * We use a do-while loop so that we write at least 1 digit
432 do {
433 register uint64_t new_magnitude = magnitude / 10;
435 *--p = (char)(magnitude - new_magnitude * 10 + '0');
436 magnitude = new_magnitude;
438 while (magnitude);
440 *len = buf_end - p;
441 return (p);
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
449 * in buf).
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;
456 int decimal_point;
458 if (precision >= NDIG - 1) {
459 precision = NDIG - 2;
462 if (format == 'F') {
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)) {
470 *len = strlen(p);
471 memcpy(buf, p, *len + 1);
472 *is_negative = 0;
473 req::free(p_orig);
474 return (buf);
476 if (format == 'F') {
477 if (decimal_point <= 0) {
478 if (num != 0 || precision > 0) {
479 *s++ = '0';
480 if (precision > 0) {
481 *s++ = dec_point;
482 while (decimal_point++ < 0) {
483 *s++ = '0';
485 } else if (add_dp) {
486 *s++ = dec_point;
489 } else {
490 int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
491 decimal_point -= addz;
492 while (decimal_point-- > 0) {
493 *s++ = *p++;
495 while (addz-- > 0) {
496 *s++ = '0';
498 if (precision > 0 || add_dp) {
499 *s++ = dec_point;
502 } else {
503 *s++ = *p++;
504 if (precision > 0 || add_dp) {
505 *s++ = '.';
509 // copy the rest of p, the NUL is NOT copied
510 while (*p) {
511 *s++ = *p++;
514 if (format != 'F') {
515 char temp[EXPONENT_LENGTH]; // for exponent conversion
516 int t_len;
517 int exponent_is_negative;
519 *s++ = format; // either e or E
520 decimal_point--;
521 if (decimal_point != 0) {
522 p = ap_php_conv_10((int64_t) decimal_point, false,
523 &exponent_is_negative, &temp[EXPONENT_LENGTH],
524 &t_len);
525 *s++ = exponent_is_negative ? '-' : '+';
527 // Make sure the exponent has at least 2 digits
528 while (t_len--) {
529 *s++ = *p++;
531 } else {
532 *s++ = '+';
533 *s++ = '0';
536 *len = s - buf;
537 req::free(p_orig);
538 return (buf);
541 ///////////////////////////////////////////////////////////////////////////////
543 inline static void appendchar(char **buffer, int *pos, int *size, char add) {
544 if ((*pos + 1) >= *size) {
545 *size <<= 1;
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) {
557 *size <<= 1;
559 *buffer = (char *)realloc(*buffer, *size);
561 memcpy(&(*buffer)[*pos], add, len);
562 *pos += 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,
568 int always_sign) {
569 register int npad;
570 int req_size;
571 int copy_len;
573 copy_len = (expprec ? (max_width < len ? max_width : len) : len);
574 npad = min_width - copy_len;
576 if (npad < 0) {
577 npad = 0;
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) ? '-' : '+');
586 add++;
587 len--;
588 copy_len--;
590 while (npad-- > 0) {
591 buffer->append(padding);
594 buffer->append(add, copy_len);
595 if (alignment == ALIGN_LEFT) {
596 while (npad--) {
597 buffer->append(padding);
602 inline static void appendint(StringBuffer *buffer, long number,
603 int width, char padding, int alignment,
604 int always_sign) {
605 char numbuf[NUM_BUF_SIZE];
606 register unsigned long magn, nmagn;
607 register unsigned int i = NUM_BUF_SIZE - 1, neg = 0;
609 if (number < 0) {
610 neg = 1;
611 magn = ((unsigned long) -(number + 1)) + 1;
612 } else {
613 magn = (unsigned long) number;
616 /* Can't right-pad 0's on integers */
617 if (alignment==0 && padding=='0') padding=' ';
619 numbuf[i] = '\0';
621 do {
622 nmagn = magn / 10;
624 numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0';
625 magn = nmagn;
627 while (magn > 0 && i > 0);
628 if (neg) {
629 numbuf[--i] = '-';
630 } else if (always_sign) {
631 numbuf[--i] = '+';
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 = ' ';
650 numbuf[i] = '\0';
651 do {
652 nmagn = magn / 10;
653 numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0';
654 magn = nmagn;
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,
662 double number,
663 int width, char padding,
664 int alignment, int precision,
665 int adjust, char fmt,
666 int always_sign) {
667 char num_buf[NUM_BUF_SIZE];
668 char *s = nullptr;
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);
681 return;
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);
688 return;
691 #if defined(HAVE_LOCALE_H)
692 struct lconv *lconv;
693 lconv = localeconv();
694 # define APPENDDOUBLE_LCONV_DECIMAL_POINT (*lconv->decimal_point)
695 #else
696 # define APPENDDOUBLE_LCONV_DECIMAL_POINT '.'
697 #endif
699 switch (fmt) {
700 case 'e':
701 case 'E':
702 case 'f':
703 case 'F':
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);
708 if (is_negative) {
709 num_buf[0] = '-';
710 s = num_buf;
711 s_len++;
712 } else if (always_sign) {
713 num_buf[0] = '+';
714 s = num_buf;
715 s_len++;
717 break;
719 case 'g':
720 case 'G':
721 if (precision == 0)
722 precision = 1;
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',
729 &num_buf[1]);
730 is_negative = 0;
731 if (*s == '-') {
732 is_negative = 1;
733 s = &num_buf[1];
734 } else if (always_sign) {
735 num_buf[0] = '+';
736 s = num_buf;
739 s_len = strlen(s);
740 break;
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;
756 numbuf[i] = '\0';
758 do {
759 numbuf[--i] = chartable[(num & andbits)];
760 num >>= n;
762 while (num > 0);
764 appendstring(buffer, &numbuf[i], width, 0,
765 padding, alignment, (NUM_BUF_SIZE - 1) - i,
766 0, expprec, 0);
769 inline static int getnumber(const char *buffer, int *pos) {
770 char *endptr;
771 register long num = strtol(buffer + *pos, &endptr, 10);
772 register int i = 0;
774 if (endptr != nullptr) {
775 i = (endptr - buffer - *pos);
777 *pos += i;
779 if (num >= INT_MAX || num < 0) {
780 return -1;
782 return (int) num;
786 * New sprintf implementation for PHP.
788 * Modifiers:
790 * " " pad integers with spaces
791 * "-" left adjusted field
792 * n field size
793 * "."n precision (floats only)
794 * "+" Always place a sign (+ or -) in front of a number
796 * Type specifiers:
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) {
809 Array vargs = args;
810 if (!vargs.isNull() && !vargs->isVectorData()) {
811 vargs = Array::Create();
812 for (ArrayIter iter(args); iter; ++iter) {
813 vargs.append(iter.second());
817 if (len == 0) {
818 return empty_string();
821 int size = 240;
822 StringBuffer result(size);
824 int argnum = 0, currarg = 1;
825 for (int inpos = 0; inpos < len; ++inpos) {
826 char ch = format[inpos];
828 int expprec = 0;
829 if (ch != '%') {
830 result.append(ch);
831 continue;
834 if (format[inpos + 1] == '%') {
835 result.append('%');
836 inpos++;
837 continue;
840 /* starting a new format specifier, reset variables */
841 int alignment = ALIGN_RIGHT;
842 int adjusting = 0;
843 char padding = ' ';
844 int always_sign = 0;
845 int width, precision;
846 inpos++; /* skip the '%' */
847 ch = format[inpos];
849 if (isascii(ch) && !isalpha(ch)) {
850 /* first look for argnum */
851 int temppos = inpos;
852 while (isdigit((int)format[temppos])) temppos++;
853 if (format[temppos] == '$') {
854 argnum = getnumber(format, &inpos);
855 if (argnum <= 0) {
856 throw_invalid_argument("argnum: must be greater than zero");
857 return String();
859 inpos++; /* skip the '$' */
860 } else {
861 argnum = currarg++;
864 /* after argnum comes modifiers */
865 for (;; inpos++) {
866 ch = format[inpos];
868 if (ch == ' ' || ch == '0') {
869 padding = ch;
870 } else if (ch == '-') {
871 alignment = ALIGN_LEFT;
872 /* space padding, the default */
873 } else if (ch == '+') {
874 always_sign = 1;
875 } else if (ch == '\'' && inpos != len - 1) {
876 padding = format[++inpos];
877 } else {
878 break;
881 ch = format[inpos];
883 /* after modifiers comes width */
884 if (isdigit(ch)) {
885 if ((width = getnumber(format, &inpos)) < 0) {
886 throw_invalid_argument("width: must be greater than zero "
887 "and less than %d", INT_MAX);
888 return String();
890 adjusting |= ADJ_WIDTH;
891 } else {
892 width = 0;
894 ch = format[inpos];
896 /* after width and argnum comes precision */
897 if (ch == '.') {
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);
903 return String();
905 ch = format[inpos];
906 adjusting |= ADJ_PRECISION;
907 expprec = 1;
908 } else {
909 precision = 0;
911 } else {
912 precision = 0;
914 } else {
915 width = precision = 0;
916 argnum = currarg++;
919 if (argnum > vargs.size()) {
920 throw_invalid_argument("arguments: (too few)");
921 return String();
924 if (ch == 'l') {
925 ch = format[++inpos];
927 /* now we expect to find a type specifier */
928 Variant tmp = vargs[argnum-1];
930 switch (ch) {
931 case 's': {
932 String s = tmp.toString();
933 appendstring(&result, s.c_str(),
934 width, precision, padding, alignment, s.size(),
935 0, expprec, 0);
936 break;
938 case 'd':
939 appendint(&result, tmp.toInt64(),
940 width, padding, alignment, always_sign);
941 break;
942 case 'u':
943 appenduint(&result, tmp.toInt64(),
944 width, padding, alignment);
945 break;
947 case 'g':
948 case 'G':
949 case 'e':
950 case 'E':
951 case 'f':
952 case 'F':
953 appenddouble(&result, tmp.toDouble(),
954 width, padding, alignment, precision, adjusting,
955 ch, always_sign);
956 break;
958 case 'c':
959 result.append(tmp.toByte());
960 break;
962 case 'o':
963 append2n(&result, tmp.toInt64(),
964 width, padding, alignment, 3, hexchars, expprec);
965 break;
967 case 'x':
968 append2n(&result, tmp.toInt64(),
969 width, padding, alignment, 4, hexchars, expprec);
970 break;
972 case 'X':
973 append2n(&result, tmp.toInt64(),
974 width, padding, alignment, 4, HEXCHARS, expprec);
975 break;
977 case 'b':
978 append2n(&result, tmp.toInt64(),
979 width, padding, alignment, 1, hexchars, expprec);
980 break;
982 case '%':
983 result.append('%');
985 break;
986 default:
987 break;
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;
1000 char *q;
1001 int s_len;
1003 register int min_width = 0;
1004 int precision = 0;
1005 enum {
1006 LEFT, RIGHT
1007 } adjust;
1008 char pad_char;
1009 char prefix_char;
1011 double fp_num;
1012 wide_int i_num = (wide_int) 0;
1013 u_wide_int ui_num;
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;
1020 #endif
1023 * Flag variables
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;
1031 int is_negative;
1033 int size = 240;
1034 char *result = (char *)malloc(size);
1035 int outpos = 0;
1037 while (*fmt) {
1038 if (*fmt != '%') {
1039 appendchar(&result, &outpos, &size, *fmt);
1040 } else {
1042 * Default variable settings
1044 adjust = RIGHT;
1045 alternate_form = print_sign = print_blank = NO;
1046 pad_char = ' ';
1047 prefix_char = NUL;
1049 fmt++;
1052 * Try to avoid checking for flags, width or precision
1054 if (isascii((int)*fmt) && !islower((int)*fmt)) {
1056 * Recognize flags: -, #, BLANK, +
1058 for (;; fmt++) {
1059 if (*fmt == '-')
1060 adjust = LEFT;
1061 else if (*fmt == '+')
1062 print_sign = YES;
1063 else if (*fmt == '#')
1064 alternate_form = YES;
1065 else if (*fmt == ' ')
1066 print_blank = YES;
1067 else if (*fmt == '0')
1068 pad_char = '0';
1069 else
1070 break;
1074 * Check if a width was specified
1076 if (isdigit((int)*fmt)) {
1077 STR_TO_DEC(fmt, min_width);
1078 adjust_width = YES;
1079 } else if (*fmt == '*') {
1080 min_width = va_arg(ap, int);
1081 fmt++;
1082 adjust_width = YES;
1083 if (min_width < 0) {
1084 adjust = LEFT;
1085 min_width = -min_width;
1087 } else
1088 adjust_width = NO;
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.
1097 if (*fmt == '.') {
1098 adjust_precision = YES;
1099 fmt++;
1100 if (isdigit((int)*fmt)) {
1101 STR_TO_DEC(fmt, precision);
1102 } else if (*fmt == '*') {
1103 precision = va_arg(ap, int);
1104 fmt++;
1105 if (precision < 0)
1106 precision = 0;
1107 } else
1108 precision = 0;
1109 } else
1110 adjust_precision = NO;
1111 } else
1112 adjust_precision = adjust_width = NO;
1115 * Modifier check
1117 switch (*fmt) {
1118 case 'L':
1119 fmt++;
1120 modifier = LM_LONG_DOUBLE;
1121 break;
1122 case 'I':
1123 fmt++;
1124 #if SIZEOF_LONG_LONG
1125 if (*fmt == '6' && *(fmt+1) == '4') {
1126 fmt += 2;
1127 modifier = LM_LONG_LONG;
1128 } else
1129 #endif
1130 if (*fmt == '3' && *(fmt+1) == '2') {
1131 fmt += 2;
1132 modifier = LM_LONG;
1133 } else {
1134 #ifdef _WIN64
1135 modifier = LM_LONG_LONG;
1136 #else
1137 modifier = LM_LONG;
1138 #endif
1140 break;
1141 case 'l':
1142 fmt++;
1143 #if SIZEOF_LONG_LONG
1144 if (*fmt == 'l') {
1145 fmt++;
1146 modifier = LM_LONG_LONG;
1147 } else
1148 #endif
1149 modifier = LM_LONG;
1150 break;
1151 case 'z':
1152 fmt++;
1153 modifier = LM_SIZE_T;
1154 break;
1155 case 'j':
1156 fmt++;
1157 #if SIZEOF_INTMAX_T
1158 modifier = LM_INTMAX_T;
1159 #else
1160 modifier = LM_SIZE_T;
1161 #endif
1162 break;
1163 case 't':
1164 fmt++;
1165 #if SIZEOF_PTRDIFF_T
1166 modifier = LM_PTRDIFF_T;
1167 #else
1168 modifier = LM_SIZE_T;
1169 #endif
1170 break;
1171 case 'h':
1172 fmt++;
1173 if (*fmt == 'h') {
1174 fmt++;
1176 /* these are promoted to int, so no break */
1177 default:
1178 modifier = LM_STD;
1179 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
1193 switch (*fmt) {
1194 case 'u':
1195 switch(modifier) {
1196 default:
1197 i_num = (wide_int) va_arg(ap, unsigned int);
1198 break;
1199 case LM_LONG_DOUBLE:
1200 goto fmt_error;
1201 case LM_LONG:
1202 i_num = (wide_int) va_arg(ap, unsigned long int);
1203 break;
1204 case LM_SIZE_T:
1205 i_num = (wide_int) va_arg(ap, size_t);
1206 break;
1207 #if SIZEOF_LONG_LONG
1208 case LM_LONG_LONG:
1209 i_num = (wide_int) va_arg(ap, u_wide_int);
1210 break;
1211 #endif
1212 #if SIZEOF_INTMAX_T
1213 case LM_INTMAX_T:
1214 i_num = (wide_int) va_arg(ap, uintmax_t);
1215 break;
1216 #endif
1217 #if SIZEOF_PTRDIFF_T
1218 case LM_PTRDIFF_T:
1219 i_num = (wide_int) va_arg(ap, ptrdiff_t);
1220 break;
1221 #endif
1224 * The rest also applies to other integer formats, so fall
1225 * into that case.
1227 case 'd':
1228 case 'i':
1230 * Get the arg if we haven't already.
1232 if ((*fmt) != 'u') {
1233 switch(modifier) {
1234 default:
1235 i_num = (wide_int) va_arg(ap, int);
1236 break;
1237 case LM_LONG_DOUBLE:
1238 goto fmt_error;
1239 case LM_LONG:
1240 i_num = (wide_int) va_arg(ap, long int);
1241 break;
1242 case LM_SIZE_T:
1243 #if SIZEOF_SSIZE_T
1244 i_num = (wide_int) va_arg(ap, ssize_t);
1245 #else
1246 i_num = (wide_int) va_arg(ap, size_t);
1247 #endif
1248 break;
1249 #if SIZEOF_LONG_LONG
1250 case LM_LONG_LONG:
1251 i_num = (wide_int) va_arg(ap, wide_int);
1252 break;
1253 #endif
1254 #if SIZEOF_INTMAX_T
1255 case LM_INTMAX_T:
1256 i_num = (wide_int) va_arg(ap, intmax_t);
1257 break;
1258 #endif
1259 #if SIZEOF_PTRDIFF_T
1260 case LM_PTRDIFF_T:
1261 i_num = (wide_int) va_arg(ap, ptrdiff_t);
1262 break;
1263 #endif
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);
1270 if (*fmt != 'u') {
1271 if (is_negative)
1272 prefix_char = '-';
1273 else if (print_sign)
1274 prefix_char = '+';
1275 else if (print_blank)
1276 prefix_char = ' ';
1278 break;
1281 case 'o':
1282 switch(modifier) {
1283 default:
1284 ui_num = (u_wide_int) va_arg(ap, unsigned int);
1285 break;
1286 case LM_LONG_DOUBLE:
1287 goto fmt_error;
1288 case LM_LONG:
1289 ui_num = (u_wide_int) va_arg(ap, unsigned long int);
1290 break;
1291 case LM_SIZE_T:
1292 ui_num = (u_wide_int) va_arg(ap, size_t);
1293 break;
1294 #if SIZEOF_LONG_LONG
1295 case LM_LONG_LONG:
1296 ui_num = (u_wide_int) va_arg(ap, u_wide_int);
1297 break;
1298 #endif
1299 #if SIZEOF_INTMAX_T
1300 case LM_INTMAX_T:
1301 ui_num = (u_wide_int) va_arg(ap, uintmax_t);
1302 break;
1303 #endif
1304 #if SIZEOF_PTRDIFF_T
1305 case LM_PTRDIFF_T:
1306 ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
1307 break;
1308 #endif
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') {
1314 *--s = '0';
1315 s_len++;
1317 break;
1320 case 'x':
1321 case 'X':
1322 switch(modifier) {
1323 default:
1324 ui_num = (u_wide_int) va_arg(ap, unsigned int);
1325 break;
1326 case LM_LONG_DOUBLE:
1327 goto fmt_error;
1328 case LM_LONG:
1329 ui_num = (u_wide_int) va_arg(ap, unsigned long int);
1330 break;
1331 case LM_SIZE_T:
1332 ui_num = (u_wide_int) va_arg(ap, size_t);
1333 break;
1334 #if SIZEOF_LONG_LONG
1335 case LM_LONG_LONG:
1336 ui_num = (u_wide_int) va_arg(ap, u_wide_int);
1337 break;
1338 #endif
1339 #if SIZEOF_INTMAX_T
1340 case LM_INTMAX_T:
1341 ui_num = (u_wide_int) va_arg(ap, uintmax_t);
1342 break;
1343 #endif
1344 #if SIZEOF_PTRDIFF_T
1345 case LM_PTRDIFF_T:
1346 ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
1347 break;
1348 #endif
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' */
1355 *--s = '0';
1356 s_len += 2;
1358 break;
1361 case 's':
1362 case 'v':
1363 s = va_arg(ap, char *);
1364 if (s != nullptr) {
1365 s_len = strlen(s);
1366 if (adjust_precision && precision < s_len)
1367 s_len = precision;
1368 } else {
1369 s = const_cast<char*>(s_null);
1370 s_len = S_NULL_LEN;
1372 pad_char = ' ';
1373 break;
1376 case 'f':
1377 case 'F':
1378 case 'e':
1379 case 'E':
1380 switch(modifier) {
1381 case LM_LONG_DOUBLE:
1382 fp_num = (double) va_arg(ap, long double);
1383 break;
1384 case LM_STD:
1385 fp_num = va_arg(ap, double);
1386 break;
1387 default:
1388 goto fmt_error;
1391 if (std::isnan(fp_num)) {
1392 s = const_cast<char*>("nan");
1393 s_len = 3;
1394 } else if (std::isinf(fp_num)) {
1395 s = const_cast<char*>("inf");
1396 s_len = 3;
1397 } else {
1398 #ifdef HAVE_LOCALE_H
1399 if (!lconv) {
1400 lconv = localeconv();
1402 #endif
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);
1407 if (is_negative)
1408 prefix_char = '-';
1409 else if (print_sign)
1410 prefix_char = '+';
1411 else if (print_blank)
1412 prefix_char = ' ';
1414 break;
1417 case 'g':
1418 case 'k':
1419 case 'G':
1420 case 'H':
1421 switch(modifier) {
1422 case LM_LONG_DOUBLE:
1423 fp_num = (double) va_arg(ap, long double);
1424 break;
1425 case LM_STD:
1426 fp_num = va_arg(ap, double);
1427 break;
1428 default:
1429 goto fmt_error;
1432 if (std::isnan(fp_num)) {
1433 s = const_cast<char*>("NAN");
1434 s_len = 3;
1435 break;
1436 } else if (std::isinf(fp_num)) {
1437 if (fp_num > 0) {
1438 s = const_cast<char*>("INF");
1439 s_len = 3;
1440 } else {
1441 s = const_cast<char*>("-INF");
1442 s_len = 4;
1444 break;
1447 if (adjust_precision == NO)
1448 precision = FLOAT_DIGITS;
1449 else if (precision == 0)
1450 precision = 1;
1452 * * We use &num_buf[ 1 ], so that we have room for the sign
1454 #ifdef HAVE_LOCALE_H
1455 if (!lconv) {
1456 lconv = localeconv();
1458 #endif
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]);
1462 if (*s == '-')
1463 prefix_char = *s++;
1464 else if (print_sign)
1465 prefix_char = '+';
1466 else if (print_blank)
1467 prefix_char = ' ';
1469 s_len = strlen(s);
1471 if (alternate_form && (q = strchr(s, '.')) == nullptr)
1472 s[s_len++] = '.';
1473 break;
1476 case 'c':
1477 char_buf[0] = (char) (va_arg(ap, int));
1478 s = &char_buf[0];
1479 s_len = 1;
1480 pad_char = ' ';
1481 break;
1484 case '%':
1485 char_buf[0] = '%';
1486 s = &char_buf[0];
1487 s_len = 1;
1488 pad_char = ' ';
1489 break;
1492 case 'n':
1493 *(va_arg(ap, int *)) = outpos;
1494 goto skip_output;
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".
1504 case '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);
1509 if (ui_num != 0) {
1510 *--s = 'x';
1511 *--s = '0';
1512 s_len += 2;
1514 } else {
1515 s = const_cast<char*>("%p");
1516 s_len = 2;
1518 pad_char = ' ';
1519 break;
1522 case NUL:
1524 * The last character of the format string was %.
1525 * We ignore it.
1527 continue;
1530 fmt_error:
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.
1543 default:
1544 char_buf[0] = '%';
1545 char_buf[1] = *fmt;
1546 s = char_buf;
1547 s_len = 2;
1548 pad_char = ' ';
1549 break;
1552 if (prefix_char != NUL) {
1553 *--s = prefix_char;
1554 s_len++;
1556 if (adjust_width && adjust == RIGHT && min_width > s_len) {
1557 if (pad_char == '0' && prefix_char != NUL) {
1558 appendchar(&result, &outpos, &size, *s);
1559 s++;
1560 s_len--;
1561 min_width--;
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);
1578 skip_output:
1579 fmt++;
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;
1586 *outbuf = result;
1587 return outpos;
1591 * This is the general purpose conversion function.
1593 int vspprintf(char** pbuf, size_t /*max_len*/, const char* format, ...) {
1594 int len;
1595 va_list ap;
1596 va_start(ap, format);
1597 len = xbuf_format_converter(pbuf, format, ap);
1598 va_end(ap);
1599 return len;
1603 * Same as vspprintf but taking an va_list
1605 int vspprintf_ap(char** pbuf, size_t /*max_len*/, const char* format,
1606 va_list ap) {
1607 int len;
1608 len = xbuf_format_converter(pbuf, format, ap);
1609 return len;
1612 int spprintf(char **pbuf, size_t max_len, const char *format, ...)
1614 int cc;
1615 va_list ap;
1617 va_start(ap, format);
1618 cc = vspprintf(pbuf, max_len, format, ap);
1619 va_end(ap);
1620 return (cc);
1623 ///////////////////////////////////////////////////////////////////////////////