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