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