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