1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Stig S�ther Bakken <ssb@php.net>                             |
16    +----------------------------------------------------------------------+
17  */
18 
19 #include <math.h>				/* modf() */
20 #include "php.h"
21 #include "ext/standard/head.h"
22 #include "php_string.h"
23 #include "zend_execute.h"
24 #include <stdio.h>
25 
26 #include <locale.h>
27 #ifdef ZTS
28 #include "ext/standard/php_string.h"
29 #define LCONV_DECIMAL_POINT (*lconv.decimal_point)
30 #else
31 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
32 #endif
33 
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 FLOAT_PRECISION 6
40 #define MAX_FLOAT_PRECISION 53
41 
42 #if 0
43 /* trick to control varargs functions through cpp */
44 # define PRINTF_DEBUG(arg) php_printf arg
45 #else
46 # define PRINTF_DEBUG(arg)
47 #endif
48 
49 static const char hexchars[] = "0123456789abcdef";
50 static const char HEXCHARS[] = "0123456789ABCDEF";
51 
52 /* php_spintf_appendchar() {{{ */
53 inline static void
php_sprintf_appendchar(zend_string **buffer, size_t *pos, char add)54 php_sprintf_appendchar(zend_string **buffer, size_t *pos, char add)
55 {
56 	if ((*pos + 1) >= ZSTR_LEN(*buffer)) {
57 		PRINTF_DEBUG(("%s(): ereallocing buffer to %d bytes\n", get_active_function_name(), ZSTR_LEN(*buffer)));
58 		*buffer = zend_string_extend(*buffer, ZSTR_LEN(*buffer) << 1, 0);
59 	}
60 	PRINTF_DEBUG(("sprintf: appending '%c', pos=\n", add, *pos));
61 	ZSTR_VAL(*buffer)[(*pos)++] = add;
62 }
63 /* }}} */
64 
65 /* php_spintf_appendchar() {{{ */
66 inline static void
php_sprintf_appendchars(zend_string **buffer, size_t *pos, char *add, size_t len)67 php_sprintf_appendchars(zend_string **buffer, size_t *pos, char *add, size_t len)
68 {
69 	if ((*pos + len) >= ZSTR_LEN(*buffer)) {
70 		size_t nlen = ZSTR_LEN(*buffer);
71 
72 		PRINTF_DEBUG(("%s(): ereallocing buffer to %d bytes\n", get_active_function_name(), ZSTR_LEN(*buffer)));
73 		do {
74 			nlen = nlen << 1;
75 		} while ((*pos + len) >= nlen);
76 		*buffer = zend_string_extend(*buffer, nlen, 0);
77 	}
78 	PRINTF_DEBUG(("sprintf: appending \"%s\", pos=\n", add, *pos));
79 	memcpy(ZSTR_VAL(*buffer) + (*pos), add, len);
80 	*pos += len;
81 }
82 /* }}} */
83 
84 /* php_spintf_appendstring() {{{ */
85 inline static void
php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add, size_t min_width, size_t max_width, char padding, size_t alignment, size_t len, int neg, int expprec, int always_sign)86 php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add,
87 						   size_t min_width, size_t max_width, char padding,
88 						   size_t alignment, size_t len, int neg, int expprec, int always_sign)
89 {
90 	register size_t npad;
91 	size_t req_size;
92 	size_t copy_len;
93 	size_t m_width;
94 
95 	copy_len = (expprec ? MIN(max_width, len) : len);
96 	npad = (min_width < copy_len) ? 0 : min_width - copy_len;
97 
98 	PRINTF_DEBUG(("sprintf: appendstring(%x, %d, %d, \"%s\", %d, '%c', %d)\n",
99 				  *buffer, *pos, ZSTR_LEN(*buffer), add, min_width, padding, alignment));
100 	m_width = MAX(min_width, copy_len);
101 
102 	if(m_width > INT_MAX - *pos - 1) {
103 		zend_error_noreturn(E_ERROR, "Field width %zd is too long", m_width);
104 	}
105 
106 	req_size = *pos + m_width + 1;
107 
108 	if (req_size > ZSTR_LEN(*buffer)) {
109 		size_t size = ZSTR_LEN(*buffer);
110 		while (req_size > size) {
111 			if (size > ZEND_SIZE_MAX/2) {
112 				zend_error_noreturn(E_ERROR, "Field width %zd is too long", req_size);
113 			}
114 			size <<= 1;
115 		}
116 		PRINTF_DEBUG(("sprintf ereallocing buffer to %d bytes\n", size));
117 		*buffer = zend_string_extend(*buffer, size, 0);
118 	}
119 	if (alignment == ALIGN_RIGHT) {
120 		if ((neg || always_sign) && padding=='0') {
121 			ZSTR_VAL(*buffer)[(*pos)++] = (neg) ? '-' : '+';
122 			add++;
123 			len--;
124 			copy_len--;
125 		}
126 		while (npad-- > 0) {
127 			ZSTR_VAL(*buffer)[(*pos)++] = padding;
128 		}
129 	}
130 	PRINTF_DEBUG(("sprintf: appending \"%s\"\n", add));
131 	memcpy(&ZSTR_VAL(*buffer)[*pos], add, copy_len + 1);
132 	*pos += copy_len;
133 	if (alignment == ALIGN_LEFT) {
134 		while (npad--) {
135 			ZSTR_VAL(*buffer)[(*pos)++] = padding;
136 		}
137 	}
138 }
139 /* }}} */
140 
141 /* php_spintf_appendint() {{{ */
142 inline static void
php_sprintf_appendint(zend_string **buffer, size_t *pos, zend_long number, size_t width, char padding, size_t alignment, int always_sign)143 php_sprintf_appendint(zend_string **buffer, size_t *pos, zend_long number,
144 						size_t width, char padding, size_t alignment,
145 						int always_sign)
146 {
147 	char numbuf[NUM_BUF_SIZE];
148 	register zend_ulong magn, nmagn;
149 	register unsigned int i = NUM_BUF_SIZE - 1, neg = 0;
150 
151 	PRINTF_DEBUG(("sprintf: appendint(%x, %x, %x, %d, %d, '%c', %d)\n",
152 				  *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment));
153 	if (number < 0) {
154 		neg = 1;
155 		magn = ((zend_ulong) -(number + 1)) + 1;
156 	} else {
157 		magn = (zend_ulong) number;
158 	}
159 
160 	/* Can't right-pad 0's on integers */
161 	if(alignment==0 && padding=='0') padding=' ';
162 
163 	numbuf[i] = '\0';
164 
165 	do {
166 		nmagn = magn / 10;
167 
168 		numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0';
169 		magn = nmagn;
170 	}
171 	while (magn > 0 && i > 1);
172 	if (neg) {
173 		numbuf[--i] = '-';
174 	} else if (always_sign) {
175 		numbuf[--i] = '+';
176 	}
177 	PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n",
178 				  number, &numbuf[i], i));
179 	php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0,
180 							 padding, alignment, (NUM_BUF_SIZE - 1) - i,
181 							 neg, 0, always_sign);
182 }
183 /* }}} */
184 
185 /* php_spintf_appenduint() {{{ */
186 inline static void
php_sprintf_appenduint(zend_string **buffer, size_t *pos, zend_ulong number, size_t width, char padding, size_t alignment)187 php_sprintf_appenduint(zend_string **buffer, size_t *pos,
188 					   zend_ulong number,
189 					   size_t width, char padding, size_t alignment)
190 {
191 	char numbuf[NUM_BUF_SIZE];
192 	register zend_ulong magn, nmagn;
193 	register unsigned int i = NUM_BUF_SIZE - 1;
194 
195 	PRINTF_DEBUG(("sprintf: appenduint(%x, %x, %x, %d, %d, '%c', %d)\n",
196 				  *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment));
197 	magn = (zend_ulong) number;
198 
199 	/* Can't right-pad 0's on integers */
200 	if (alignment == 0 && padding == '0') padding = ' ';
201 
202 	numbuf[i] = '\0';
203 
204 	do {
205 		nmagn = magn / 10;
206 
207 		numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0';
208 		magn = nmagn;
209 	} while (magn > 0 && i > 0);
210 
211 	PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n", number, &numbuf[i], i));
212 	php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0,
213 							 padding, alignment, (NUM_BUF_SIZE - 1) - i, 0, 0, 0);
214 }
215 /* }}} */
216 
217 /* php_spintf_appenddouble() {{{ */
218 inline static void
php_sprintf_appenddouble(zend_string **buffer, size_t *pos, double number, size_t width, char padding, size_t alignment, int precision, int adjust, char fmt, int always_sign )219 php_sprintf_appenddouble(zend_string **buffer, size_t *pos,
220 						 double number,
221 						 size_t width, char padding,
222 						 size_t alignment, int precision,
223 						 int adjust, char fmt,
224 						 int always_sign
225 						)
226 {
227 	char num_buf[NUM_BUF_SIZE];
228 	char *s = NULL;
229 	size_t s_len = 0;
230 	int is_negative = 0;
231 #ifdef ZTS
232 	struct lconv lconv;
233 #else
234 	struct lconv *lconv;
235 #endif
236 
237 	PRINTF_DEBUG(("sprintf: appenddouble(%x, %x, %x, %f, %d, '%c', %d, %c)\n",
238 				  *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, fmt));
239 	if ((adjust & ADJ_PRECISION) == 0) {
240 		precision = FLOAT_PRECISION;
241 	} else if (precision > MAX_FLOAT_PRECISION) {
242 		php_error_docref(NULL, E_NOTICE, "Requested precision of %d digits was truncated to PHP maximum of %d digits", precision, MAX_FLOAT_PRECISION);
243 		precision = MAX_FLOAT_PRECISION;
244 	}
245 
246 	if (zend_isnan(number)) {
247 		is_negative = (number<0);
248 		php_sprintf_appendstring(buffer, pos, "NaN", 3, 0, padding,
249 								 alignment, 3, is_negative, 0, always_sign);
250 		return;
251 	}
252 
253 	if (zend_isinf(number)) {
254 		is_negative = (number<0);
255 		php_sprintf_appendstring(buffer, pos, "INF", 3, 0, padding,
256 								 alignment, 3, is_negative, 0, always_sign);
257 		return;
258 	}
259 
260 	switch (fmt) {
261 		case 'e':
262 		case 'E':
263 		case 'f':
264 		case 'F':
265 #ifdef ZTS
266 			localeconv_r(&lconv);
267 #else
268 			lconv = localeconv();
269 #endif
270 			s = php_conv_fp((fmt == 'f')?'F':fmt, number, 0, precision,
271 						(fmt == 'f')?LCONV_DECIMAL_POINT:'.',
272 						&is_negative, &num_buf[1], &s_len);
273 			if (is_negative) {
274 				num_buf[0] = '-';
275 				s = num_buf;
276 				s_len++;
277 			} else if (always_sign) {
278 				num_buf[0] = '+';
279 				s = num_buf;
280 				s_len++;
281 			}
282 			break;
283 
284 		case 'g':
285 		case 'G':
286 			if (precision == 0)
287 				precision = 1;
288 			/*
289 			 * * We use &num_buf[ 1 ], so that we have room for the sign
290 			 */
291 #ifdef ZTS
292 			localeconv_r(&lconv);
293 #else
294 			lconv = localeconv();
295 #endif
296 			s = php_gcvt(number, precision, LCONV_DECIMAL_POINT, (fmt == 'G')?'E':'e', &num_buf[1]);
297 			is_negative = 0;
298 			if (*s == '-') {
299 				is_negative = 1;
300 				s = &num_buf[1];
301 			} else if (always_sign) {
302 				num_buf[0] = '+';
303 				s = num_buf;
304 			}
305 
306 			s_len = strlen(s);
307 			break;
308 	}
309 
310 	php_sprintf_appendstring(buffer, pos, s, width, 0, padding,
311 							 alignment, s_len, is_negative, 0, always_sign);
312 }
313 /* }}} */
314 
315 /* php_spintf_appendd2n() {{{ */
316 inline static void
php_sprintf_append2n(zend_string **buffer, size_t *pos, zend_long number, size_t width, char padding, size_t alignment, int n, const char *chartable, int expprec)317 php_sprintf_append2n(zend_string **buffer, size_t *pos, zend_long number,
318 					 size_t width, char padding, size_t alignment, int n,
319 					 const char *chartable, int expprec)
320 {
321 	char numbuf[NUM_BUF_SIZE];
322 	register zend_ulong num;
323 	register zend_ulong  i = NUM_BUF_SIZE - 1;
324 	register int andbits = (1 << n) - 1;
325 
326 	PRINTF_DEBUG(("sprintf: append2n(%x, %x, %x, %d, %d, '%c', %d, %d, %x)\n",
327 				  *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, n,
328 				  chartable));
329 	PRINTF_DEBUG(("sprintf: append2n 2^%d andbits=%x\n", n, andbits));
330 
331 	num = (zend_ulong) number;
332 	numbuf[i] = '\0';
333 
334 	do {
335 		numbuf[--i] = chartable[(num & andbits)];
336 		num >>= n;
337 	}
338 	while (num > 0);
339 
340 	php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0,
341 							 padding, alignment, (NUM_BUF_SIZE - 1) - i,
342 							 0, expprec, 0);
343 }
344 /* }}} */
345 
346 /* php_spintf_getnumber() {{{ */
347 inline static int
php_sprintf_getnumber(char **buffer, size_t *len)348 php_sprintf_getnumber(char **buffer, size_t *len)
349 {
350 	char *endptr;
351 	register zend_long num = ZEND_STRTOL(*buffer, &endptr, 10);
352 	register size_t i;
353 
354 	if (endptr != NULL) {
355 		i = (endptr - *buffer);
356 		*len -= i;
357 		*buffer = endptr;
358 	}
359 	PRINTF_DEBUG(("sprintf_getnumber: number was %d bytes long\n", i));
360 
361 	if (num >= INT_MAX || num < 0) {
362 		return -1;
363 	} else {
364 		return (int) num;
365 	}
366 }
367 /* }}} */
368 
369 /* php_formatted_print() {{{
370  * New sprintf implementation for PHP.
371  *
372  * Modifiers:
373  *
374  *  " "   pad integers with spaces
375  *  "-"   left adjusted field
376  *   n    field size
377  *  "."n  precision (floats only)
378  *  "+"   Always place a sign (+ or -) in front of a number
379  *
380  * Type specifiers:
381  *
382  *  "%"   literal "%", modifiers are ignored.
383  *  "b"   integer argument is printed as binary
384  *  "c"   integer argument is printed as a single character
385  *  "d"   argument is an integer
386  *  "f"   the argument is a float
387  *  "o"   integer argument is printed as octal
388  *  "s"   argument is a string
389  *  "x"   integer argument is printed as lowercase hexadecimal
390  *  "X"   integer argument is printed as uppercase hexadecimal
391  *
392  */
393 static zend_string *
php_formatted_print(zval *z_format, zval *args, int argc)394 php_formatted_print(zval *z_format, zval *args, int argc)
395 {
396 	size_t size = 240, outpos = 0;
397 	int alignment, currarg, adjusting, argnum, width, precision;
398 	char *format, *temppos, padding;
399 	zend_string *result;
400 	int always_sign;
401 	size_t format_len;
402 
403 	if (!try_convert_to_string(z_format)) {
404 		return NULL;
405 	}
406 
407 	format = Z_STRVAL_P(z_format);
408 	format_len = Z_STRLEN_P(z_format);
409 	result = zend_string_alloc(size, 0);
410 
411 	currarg = 0;
412 
413 	while (format_len) {
414 		int expprec;
415 		zval *tmp;
416 
417 		temppos = memchr(format, '%', format_len);
418 		if (!temppos) {
419 			php_sprintf_appendchars(&result, &outpos, format, format_len);
420 			break;
421 		} else if (temppos != format) {
422 			php_sprintf_appendchars(&result, &outpos, format, temppos - format);
423 			format_len -= temppos - format;
424 			format = temppos;
425 		}
426 		format++;			/* skip the '%' */
427 		format_len--;
428 
429 		if (*format == '%') {
430 			php_sprintf_appendchar(&result, &outpos, '%');
431 			format++;
432 			format_len--;
433 		} else {
434 			/* starting a new format specifier, reset variables */
435 			alignment = ALIGN_RIGHT;
436 			adjusting = 0;
437 			padding = ' ';
438 			always_sign = 0;
439 			expprec = 0;
440 
441 			PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%d\n",
442 						  *format, format - Z_STRVAL_P(z_format)));
443 			if (isalpha((int)*format)) {
444 				width = precision = 0;
445 				argnum = currarg++;
446 			} else {
447 				/* first look for argnum */
448 				temppos = format;
449 				while (isdigit((int)*temppos)) temppos++;
450 				if (*temppos == '$') {
451 					argnum = php_sprintf_getnumber(&format, &format_len);
452 
453 					if (argnum <= 0) {
454 						zend_string_efree(result);
455 						php_error_docref(NULL, E_WARNING, "Argument number must be greater than zero");
456 						return NULL;
457 					}
458 					argnum--;
459 					format++;  /* skip the '$' */
460 					format_len--;
461 				} else {
462 					argnum = currarg++;
463 				}
464 
465 				/* after argnum comes modifiers */
466 				PRINTF_DEBUG(("sprintf: looking for modifiers\n"
467 							  "sprintf: now looking at '%c', inpos=%d\n",
468 							  *format, format - Z_STRVAL_P(z_format)));
469 				for (;; format++, format_len--) {
470 					if (*format == ' ' || *format == '0') {
471 						padding = *format;
472 					} else if (*format == '-') {
473 						alignment = ALIGN_LEFT;
474 						/* space padding, the default */
475 					} else if (*format == '+') {
476 						always_sign = 1;
477 					} else if (*format == '\'' && format_len > 1) {
478 						format++;
479 						format_len--;
480 						padding = *format;
481 					} else {
482 						PRINTF_DEBUG(("sprintf: end of modifiers\n"));
483 						break;
484 					}
485 				}
486 				PRINTF_DEBUG(("sprintf: padding='%c'\n", padding));
487 				PRINTF_DEBUG(("sprintf: alignment=%s\n",
488 							  (alignment == ALIGN_LEFT) ? "left" : "right"));
489 
490 
491 				/* after modifiers comes width */
492 				if (isdigit((int)*format)) {
493 					PRINTF_DEBUG(("sprintf: getting width\n"));
494 					if ((width = php_sprintf_getnumber(&format, &format_len)) < 0) {
495 						efree(result);
496 						php_error_docref(NULL, E_WARNING, "Width must be greater than zero and less than %d", INT_MAX);
497 						return NULL;
498 					}
499 					adjusting |= ADJ_WIDTH;
500 				} else {
501 					width = 0;
502 				}
503 				PRINTF_DEBUG(("sprintf: width=%d\n", width));
504 
505 				/* after width and argnum comes precision */
506 				if (*format == '.') {
507 					format++;
508 					format_len--;
509 					PRINTF_DEBUG(("sprintf: getting precision\n"));
510 					if (isdigit((int)*format)) {
511 						if ((precision = php_sprintf_getnumber(&format, &format_len)) < 0) {
512 							efree(result);
513 							php_error_docref(NULL, E_WARNING, "Precision must be greater than zero and less than %d", INT_MAX);
514 							return NULL;
515 						}
516 						adjusting |= ADJ_PRECISION;
517 						expprec = 1;
518 					} else {
519 						precision = 0;
520 					}
521 				} else {
522 					precision = 0;
523 				}
524 				PRINTF_DEBUG(("sprintf: precision=%d\n", precision));
525 			}
526 
527 			if (argnum >= argc) {
528 				efree(result);
529 				php_error_docref(NULL, E_WARNING, "Too few arguments");
530 				return NULL;
531 			}
532 
533 			if (*format == 'l') {
534 				format++;
535 				format_len--;
536 			}
537 			PRINTF_DEBUG(("sprintf: format character='%c'\n", *format));
538 			/* now we expect to find a type specifier */
539 			tmp = &args[argnum];
540 			switch (*format) {
541 				case 's': {
542 					zend_string *t;
543 					zend_string *str = zval_get_tmp_string(tmp, &t);
544 					php_sprintf_appendstring(&result, &outpos,
545 											 ZSTR_VAL(str),
546 											 width, precision, padding,
547 											 alignment,
548 											 ZSTR_LEN(str),
549 											 0, expprec, 0);
550 					zend_tmp_string_release(t);
551 					break;
552 				}
553 
554 				case 'd':
555 					php_sprintf_appendint(&result, &outpos,
556 										  zval_get_long(tmp),
557 										  width, padding, alignment,
558 										  always_sign);
559 					break;
560 
561 				case 'u':
562 					php_sprintf_appenduint(&result, &outpos,
563 										  zval_get_long(tmp),
564 										  width, padding, alignment);
565 					break;
566 
567 				case 'g':
568 				case 'G':
569 				case 'e':
570 				case 'E':
571 				case 'f':
572 				case 'F':
573 					php_sprintf_appenddouble(&result, &outpos,
574 											 zval_get_double(tmp),
575 											 width, padding, alignment,
576 											 precision, adjusting,
577 											 *format, always_sign
578 											);
579 					break;
580 
581 				case 'c':
582 					php_sprintf_appendchar(&result, &outpos,
583 										(char) zval_get_long(tmp));
584 					break;
585 
586 				case 'o':
587 					php_sprintf_append2n(&result, &outpos,
588 										 zval_get_long(tmp),
589 										 width, padding, alignment, 3,
590 										 hexchars, expprec);
591 					break;
592 
593 				case 'x':
594 					php_sprintf_append2n(&result, &outpos,
595 										 zval_get_long(tmp),
596 										 width, padding, alignment, 4,
597 										 hexchars, expprec);
598 					break;
599 
600 				case 'X':
601 					php_sprintf_append2n(&result, &outpos,
602 										 zval_get_long(tmp),
603 										 width, padding, alignment, 4,
604 										 HEXCHARS, expprec);
605 					break;
606 
607 				case 'b':
608 					php_sprintf_append2n(&result, &outpos,
609 										 zval_get_long(tmp),
610 										 width, padding, alignment, 1,
611 										 hexchars, expprec);
612 					break;
613 
614 				case '%':
615 					php_sprintf_appendchar(&result, &outpos, '%');
616 
617 					break;
618 
619 				case '\0':
620 					if (!format_len) {
621 						goto exit;
622 					}
623 					break;
624 
625 				default:
626 					break;
627 			}
628 			format++;
629 			format_len--;
630 		}
631 	}
632 
633 exit:
634 	/* possibly, we have to make sure we have room for the terminating null? */
635 	ZSTR_VAL(result)[outpos]=0;
636 	ZSTR_LEN(result) = outpos;
637 	return result;
638 }
639 /* }}} */
640 
641 /* php_formatted_print_get_array() {{{ */
642 static zval*
php_formatted_print_get_array(zval *array, int *argc)643 php_formatted_print_get_array(zval *array, int *argc)
644 {
645 	zval *args, *zv;
646 	int n;
647 
648 	if (Z_TYPE_P(array) != IS_ARRAY) {
649 		convert_to_array(array);
650 	}
651 
652 	n = zend_hash_num_elements(Z_ARRVAL_P(array));
653 	args = (zval *)safe_emalloc(n, sizeof(zval), 0);
654 	n = 0;
655 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), zv) {
656 		ZVAL_COPY_VALUE(&args[n], zv);
657 		n++;
658 	} ZEND_HASH_FOREACH_END();
659 
660 	*argc = n;
661 	return args;
662 }
663 /* }}} */
664 
665 /* {{{ proto string sprintf(string format [, mixed arg1 [, mixed ...]])
666    Return a formatted string */
PHP_FUNCTIONnull667 PHP_FUNCTION(user_sprintf)
668 {
669 	zend_string *result;
670 	zval *format, *args;
671 	int argc;
672 
673 	ZEND_PARSE_PARAMETERS_START(1, -1)
674 		Z_PARAM_ZVAL(format)
675 		Z_PARAM_VARIADIC('*', args, argc)
676 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
677 
678 	result = php_formatted_print(format, args, argc);
679 	if (result == NULL) {
680 		RETURN_FALSE;
681 	}
682 	RETVAL_STR(result);
683 }
684 /* }}} */
685 
686 /* {{{ proto string vsprintf(string format, array args)
687    Return a formatted string */
PHP_FUNCTIONnull688 PHP_FUNCTION(vsprintf)
689 {
690 	zend_string *result;
691 	zval *format, *array, *args;
692 	int argc;
693 
694 	ZEND_PARSE_PARAMETERS_START(2, 2)
695 		Z_PARAM_ZVAL(format)
696 		Z_PARAM_ZVAL(array)
697 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
698 
699 	args = php_formatted_print_get_array(array, &argc);
700 
701 	result = php_formatted_print(format, args, argc);
702 	efree(args);
703 	if (result == NULL) {
704 		RETURN_FALSE;
705 	}
706 	RETVAL_STR(result);
707 }
708 /* }}} */
709 
710 /* {{{ proto int printf(string format [, mixed arg1 [, mixed ...]])
711    Output a formatted string */
PHP_FUNCTIONnull712 PHP_FUNCTION(user_printf)
713 {
714 	zend_string *result;
715 	size_t rlen;
716 	zval *format, *args;
717 	int argc;
718 
719 	ZEND_PARSE_PARAMETERS_START(1, -1)
720 		Z_PARAM_ZVAL(format)
721 		Z_PARAM_VARIADIC('*', args, argc)
722 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
723 
724 	result = php_formatted_print(format, args, argc);
725 	if (result == NULL) {
726 		RETURN_FALSE;
727 	}
728 	rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
729 	zend_string_efree(result);
730 	RETURN_LONG(rlen);
731 }
732 /* }}} */
733 
734 /* {{{ proto int vprintf(string format, array args)
735    Output a formatted string */
PHP_FUNCTIONnull736 PHP_FUNCTION(vprintf)
737 {
738 	zend_string *result;
739 	size_t rlen;
740 	zval *format, *array, *args;
741 	int argc;
742 
743 	ZEND_PARSE_PARAMETERS_START(2, 2)
744 		Z_PARAM_ZVAL(format)
745 		Z_PARAM_ZVAL(array)
746 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
747 
748 	args = php_formatted_print_get_array(array, &argc);
749 
750 	result = php_formatted_print(format, args, argc);
751 	efree(args);
752 	if (result == NULL) {
753 		RETURN_FALSE;
754 	}
755 	rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
756 	zend_string_efree(result);
757 	RETURN_LONG(rlen);
758 }
759 /* }}} */
760 
761 /* {{{ proto int fprintf(resource stream, string format [, mixed arg1 [, mixed ...]])
762    Output a formatted string into a stream */
PHP_FUNCTIONnull763 PHP_FUNCTION(fprintf)
764 {
765 	php_stream *stream;
766 	zval *arg1, *format, *args;
767 	int argc;
768 	zend_string *result;
769 
770 	if (ZEND_NUM_ARGS() < 2) {
771 		WRONG_PARAM_COUNT;
772 	}
773 
774 	ZEND_PARSE_PARAMETERS_START(2, -1)
775 		Z_PARAM_RESOURCE(arg1)
776 		Z_PARAM_ZVAL(format)
777 		Z_PARAM_VARIADIC('*', args, argc)
778 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
779 
780 	php_stream_from_zval(stream, arg1);
781 
782 	result = php_formatted_print(format, args, argc);
783 	if (result == NULL) {
784 		RETURN_FALSE;
785 	}
786 
787 	php_stream_write(stream, ZSTR_VAL(result), ZSTR_LEN(result));
788 
789 	RETVAL_LONG(ZSTR_LEN(result));
790 	zend_string_efree(result);
791 }
792 /* }}} */
793 
794 /* {{{ proto int vfprintf(resource stream, string format, array args)
795    Output a formatted string into a stream */
PHP_FUNCTIONnull796 PHP_FUNCTION(vfprintf)
797 {
798 	php_stream *stream;
799 	zval *arg1, *format, *array, *args;
800 	int argc;
801 	zend_string *result;
802 
803 	if (ZEND_NUM_ARGS() != 3) {
804 		WRONG_PARAM_COUNT;
805 	}
806 
807 	ZEND_PARSE_PARAMETERS_START(3, 3)
808 		Z_PARAM_RESOURCE(arg1)
809 		Z_PARAM_ZVAL(format)
810 		Z_PARAM_ZVAL(array)
811 	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
812 
813 	php_stream_from_zval(stream, arg1);
814 
815 	args = php_formatted_print_get_array(array, &argc);
816 
817 	result = php_formatted_print(format, args, argc);
818 	efree(args);
819 	if (result == NULL) {
820 		RETURN_FALSE;
821 	}
822 
823 	php_stream_write(stream, ZSTR_VAL(result), ZSTR_LEN(result));
824 
825 	RETVAL_LONG(ZSTR_LEN(result));
826 	zend_string_efree(result);
827 }
828 /* }}} */
829