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