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