1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Stig S�ther Bakken <ssb@php.net> |
14 +----------------------------------------------------------------------+
15 */
16
17 #include <math.h> /* modf() */
18 #include "php.h"
19 #include "zend_execute.h"
20
21 #include <locale.h>
22 #ifdef ZTS
23 #include "ext/standard/php_string.h" /* for localeconv_r() */
24 #define LCONV_DECIMAL_POINT (*lconv.decimal_point)
25 #else
26 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
27 #endif
28
29 #define ALIGN_LEFT 0
30 #define ALIGN_RIGHT 1
31 #define ADJ_WIDTH 1
32 #define ADJ_PRECISION 2
33 #define NUM_BUF_SIZE 500
34 #define FLOAT_PRECISION 6
35 #define MAX_FLOAT_PRECISION 53
36
37 #if 0
38 /* trick to control varargs functions through cpp */
39 # define PRINTF_DEBUG(arg) php_printf arg
40 #else
41 # define PRINTF_DEBUG(arg)
42 #endif
43
44 static const char hexchars[] = "0123456789abcdef";
45 static const char HEXCHARS[] = "0123456789ABCDEF";
46
47 /* php_spintf_appendchar() {{{ */
48 inline static void
php_sprintf_appendchar(zend_string ** buffer,size_t * pos,char add)49 php_sprintf_appendchar(zend_string **buffer, size_t *pos, char add)
50 {
51 if ((*pos + 1) >= ZSTR_LEN(*buffer)) {
52 PRINTF_DEBUG(("%s(): ereallocing buffer to %d bytes\n", get_active_function_name(), ZSTR_LEN(*buffer)));
53 *buffer = zend_string_extend(*buffer, ZSTR_LEN(*buffer) << 1, 0);
54 }
55 PRINTF_DEBUG(("sprintf: appending '%c', pos=\n", add, *pos));
56 ZSTR_VAL(*buffer)[(*pos)++] = add;
57 }
58 /* }}} */
59
60 /* php_spintf_appendchar() {{{ */
61 inline static void
php_sprintf_appendchars(zend_string ** buffer,size_t * pos,char * add,size_t len)62 php_sprintf_appendchars(zend_string **buffer, size_t *pos, char *add, size_t len)
63 {
64 if ((*pos + len) >= ZSTR_LEN(*buffer)) {
65 size_t nlen = ZSTR_LEN(*buffer);
66
67 PRINTF_DEBUG(("%s(): ereallocing buffer to %d bytes\n", get_active_function_name(), ZSTR_LEN(*buffer)));
68 do {
69 nlen = nlen << 1;
70 } while ((*pos + len) >= nlen);
71 *buffer = zend_string_extend(*buffer, nlen, 0);
72 }
73 PRINTF_DEBUG(("sprintf: appending \"%s\", pos=\n", add, *pos));
74 memcpy(ZSTR_VAL(*buffer) + (*pos), add, len);
75 *pos += len;
76 }
77 /* }}} */
78
79 /* php_spintf_appendstring() {{{ */
80 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,bool neg,int expprec,int always_sign)81 php_sprintf_appendstring(zend_string **buffer, size_t *pos, char *add,
82 size_t min_width, size_t max_width, char padding,
83 size_t alignment, size_t len, bool neg, int expprec, int always_sign)
84 {
85 size_t npad;
86 size_t req_size;
87 size_t copy_len;
88 size_t m_width;
89
90 copy_len = (expprec ? MIN(max_width, len) : len);
91 npad = (min_width < copy_len) ? 0 : min_width - copy_len;
92
93 PRINTF_DEBUG(("sprintf: appendstring(%x, %d, %d, \"%s\", %d, '%c', %d)\n",
94 *buffer, *pos, ZSTR_LEN(*buffer), add, min_width, padding, alignment));
95 m_width = MAX(min_width, copy_len);
96
97 if(m_width > INT_MAX - *pos - 1) {
98 zend_error_noreturn(E_ERROR, "Field width %zd is too long", m_width);
99 }
100
101 req_size = *pos + m_width + 1;
102
103 if (req_size > ZSTR_LEN(*buffer)) {
104 size_t size = ZSTR_LEN(*buffer);
105 while (req_size > size) {
106 if (size > ZEND_SIZE_MAX/2) {
107 zend_error_noreturn(E_ERROR, "Field width %zd is too long", req_size);
108 }
109 size <<= 1;
110 }
111 PRINTF_DEBUG(("sprintf ereallocing buffer to %d bytes\n", size));
112 *buffer = zend_string_extend(*buffer, size, 0);
113 }
114 if (alignment == ALIGN_RIGHT) {
115 if ((neg || always_sign) && padding=='0') {
116 ZSTR_VAL(*buffer)[(*pos)++] = (neg) ? '-' : '+';
117 add++;
118 len--;
119 copy_len--;
120 }
121 while (npad-- > 0) {
122 ZSTR_VAL(*buffer)[(*pos)++] = padding;
123 }
124 }
125 PRINTF_DEBUG(("sprintf: appending \"%s\"\n", add));
126 memcpy(&ZSTR_VAL(*buffer)[*pos], add, copy_len + 1);
127 *pos += copy_len;
128 if (alignment == ALIGN_LEFT) {
129 while (npad--) {
130 ZSTR_VAL(*buffer)[(*pos)++] = padding;
131 }
132 }
133 }
134 /* }}} */
135
136 /* php_spintf_appendint() {{{ */
137 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)138 php_sprintf_appendint(zend_string **buffer, size_t *pos, zend_long number,
139 size_t width, char padding, size_t alignment,
140 int always_sign)
141 {
142 char numbuf[NUM_BUF_SIZE];
143 zend_ulong magn, nmagn;
144 unsigned int i = NUM_BUF_SIZE - 1, neg = 0;
145
146 PRINTF_DEBUG(("sprintf: appendint(%x, %x, %x, %d, %d, '%c', %d)\n",
147 *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment));
148 if (number < 0) {
149 neg = 1;
150 magn = ((zend_ulong) -(number + 1)) + 1;
151 } else {
152 magn = (zend_ulong) number;
153 }
154
155 /* Can't right-pad 0's on integers */
156 if(alignment==0 && padding=='0') padding=' ';
157
158 numbuf[i] = '\0';
159
160 do {
161 nmagn = magn / 10;
162
163 numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0';
164 magn = nmagn;
165 }
166 while (magn > 0 && i > 1);
167 if (neg) {
168 numbuf[--i] = '-';
169 } else if (always_sign) {
170 numbuf[--i] = '+';
171 }
172 PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n",
173 number, &numbuf[i], i));
174 php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0,
175 padding, alignment, (NUM_BUF_SIZE - 1) - i,
176 neg, 0, always_sign);
177 }
178 /* }}} */
179
180 /* php_spintf_appenduint() {{{ */
181 inline static void
php_sprintf_appenduint(zend_string ** buffer,size_t * pos,zend_ulong number,size_t width,char padding,size_t alignment)182 php_sprintf_appenduint(zend_string **buffer, size_t *pos,
183 zend_ulong number,
184 size_t width, char padding, size_t alignment)
185 {
186 char numbuf[NUM_BUF_SIZE];
187 zend_ulong magn, nmagn;
188 unsigned int i = NUM_BUF_SIZE - 1;
189
190 PRINTF_DEBUG(("sprintf: appenduint(%x, %x, %x, %d, %d, '%c', %d)\n",
191 *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment));
192 magn = (zend_ulong) number;
193
194 /* Can't right-pad 0's on integers */
195 if (alignment == 0 && padding == '0') padding = ' ';
196
197 numbuf[i] = '\0';
198
199 do {
200 nmagn = magn / 10;
201
202 numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0';
203 magn = nmagn;
204 } while (magn > 0 && i > 0);
205
206 PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n", number, &numbuf[i], i));
207 php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0,
208 padding, alignment, (NUM_BUF_SIZE - 1) - i, /* neg */ false, 0, 0);
209 }
210 /* }}} */
211
212 /* php_spintf_appenddouble() {{{ */
213 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)214 php_sprintf_appenddouble(zend_string **buffer, size_t *pos,
215 double number,
216 size_t width, char padding,
217 size_t alignment, int precision,
218 int adjust, char fmt,
219 int always_sign
220 )
221 {
222 char num_buf[NUM_BUF_SIZE];
223 char *s = NULL;
224 size_t s_len = 0;
225 bool is_negative = false;
226 #ifdef ZTS
227 struct lconv lconv;
228 #else
229 struct lconv *lconv;
230 #endif
231
232 PRINTF_DEBUG(("sprintf: appenddouble(%x, %x, %x, %f, %d, '%c', %d, %c)\n",
233 *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, fmt));
234 if ((adjust & ADJ_PRECISION) == 0) {
235 precision = FLOAT_PRECISION;
236 } else if (precision > MAX_FLOAT_PRECISION) {
237 php_error_docref(NULL, E_NOTICE, "Requested precision of %d digits was truncated to PHP maximum of %d digits", precision, MAX_FLOAT_PRECISION);
238 precision = MAX_FLOAT_PRECISION;
239 }
240
241 if (zend_isnan(number)) {
242 is_negative = (number<0);
243 php_sprintf_appendstring(buffer, pos, "NaN", 3, 0, padding,
244 alignment, 3, is_negative, 0, always_sign);
245 return;
246 }
247
248 if (zend_isinf(number)) {
249 is_negative = (number<0);
250 php_sprintf_appendstring(buffer, pos, "INF", 3, 0, padding,
251 alignment, 3, is_negative, 0, always_sign);
252 return;
253 }
254
255 switch (fmt) {
256 case 'e':
257 case 'E':
258 case 'f':
259 case 'F':
260 #ifdef ZTS
261 localeconv_r(&lconv);
262 #else
263 lconv = localeconv();
264 #endif
265 s = php_conv_fp((fmt == 'f')?'F':fmt, number, 0, precision,
266 (fmt == 'f')?LCONV_DECIMAL_POINT:'.',
267 &is_negative, &num_buf[1], &s_len);
268 if (is_negative) {
269 num_buf[0] = '-';
270 s = num_buf;
271 s_len++;
272 } else if (always_sign) {
273 num_buf[0] = '+';
274 s = num_buf;
275 s_len++;
276 }
277 break;
278
279 case 'g':
280 case 'G':
281 case 'h':
282 case 'H':
283 {
284 if (precision == 0)
285 precision = 1;
286
287 char decimal_point = '.';
288 if (fmt == 'g' || fmt == 'G') {
289 #ifdef ZTS
290 localeconv_r(&lconv);
291 #else
292 lconv = localeconv();
293 #endif
294 decimal_point = LCONV_DECIMAL_POINT;
295 }
296
297 char exp_char = fmt == 'G' || fmt == 'H' ? 'E' : 'e';
298 /* We use &num_buf[ 1 ], so that we have room for the sign. */
299 s = zend_gcvt(number, precision, decimal_point, exp_char, &num_buf[1]);
300 is_negative = 0;
301 if (*s == '-') {
302 is_negative = 1;
303 s = &num_buf[1];
304 } else if (always_sign) {
305 num_buf[0] = '+';
306 s = num_buf;
307 }
308
309 s_len = strlen(s);
310 break;
311 }
312 }
313
314 php_sprintf_appendstring(buffer, pos, s, width, 0, padding,
315 alignment, s_len, is_negative, 0, always_sign);
316 }
317 /* }}} */
318
319 /* php_spintf_appendd2n() {{{ */
320 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)321 php_sprintf_append2n(zend_string **buffer, size_t *pos, zend_long number,
322 size_t width, char padding, size_t alignment, int n,
323 const char *chartable, int expprec)
324 {
325 char numbuf[NUM_BUF_SIZE];
326 zend_ulong num;
327 zend_ulong i = NUM_BUF_SIZE - 1;
328 int andbits = (1 << n) - 1;
329
330 PRINTF_DEBUG(("sprintf: append2n(%x, %x, %x, %d, %d, '%c', %d, %d, %x)\n",
331 *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, n,
332 chartable));
333 PRINTF_DEBUG(("sprintf: append2n 2^%d andbits=%x\n", n, andbits));
334
335 num = (zend_ulong) number;
336 numbuf[i] = '\0';
337
338 do {
339 numbuf[--i] = chartable[(num & andbits)];
340 num >>= n;
341 }
342 while (num > 0);
343
344 php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0,
345 padding, alignment, (NUM_BUF_SIZE - 1) - i,
346 /* neg */ false, expprec, 0);
347 }
348 /* }}} */
349
350 /* php_spintf_getnumber() {{{ */
351 inline static int
php_sprintf_getnumber(char ** buffer,size_t * len)352 php_sprintf_getnumber(char **buffer, size_t *len)
353 {
354 char *endptr;
355 zend_long num = ZEND_STRTOL(*buffer, &endptr, 10);
356 size_t i;
357
358 if (endptr != NULL) {
359 i = (endptr - *buffer);
360 *len -= i;
361 *buffer = endptr;
362 }
363 PRINTF_DEBUG(("sprintf_getnumber: number was %d bytes long\n", i));
364
365 if (num >= INT_MAX || num < 0) {
366 return -1;
367 } else {
368 return (int) num;
369 }
370 }
371 /* }}} */
372
373 #define ARG_NUM_NEXT -1
374 #define ARG_NUM_INVALID -2
375
php_sprintf_get_argnum(char ** format,size_t * format_len)376 int php_sprintf_get_argnum(char **format, size_t *format_len) {
377 char *temppos = *format;
378 while (isdigit((int) *temppos)) temppos++;
379 if (*temppos != '$') {
380 return ARG_NUM_NEXT;
381 }
382
383 int argnum = php_sprintf_getnumber(format, format_len);
384 if (argnum <= 0) {
385 zend_value_error("Argument number specifier must be greater than zero and less than %d", INT_MAX);
386 return ARG_NUM_INVALID;
387 }
388
389 (*format)++; /* skip the '$' */
390 (*format_len)--;
391 return argnum - 1;
392 }
393
394 /* php_formatted_print() {{{
395 * New sprintf implementation for PHP.
396 *
397 * Modifiers:
398 *
399 * " " pad integers with spaces
400 * "-" left adjusted field
401 * n field size
402 * "."n precision (floats only)
403 * "+" Always place a sign (+ or -) in front of a number
404 *
405 * Type specifiers:
406 *
407 * "%" literal "%", modifiers are ignored.
408 * "b" integer argument is printed as binary
409 * "c" integer argument is printed as a single character
410 * "d" argument is an integer
411 * "f" the argument is a float
412 * "o" integer argument is printed as octal
413 * "s" argument is a string
414 * "x" integer argument is printed as lowercase hexadecimal
415 * "X" integer argument is printed as uppercase hexadecimal
416 *
417 * nb_additional_parameters is used for throwing errors:
418 * - -1: ValueError is thrown (for vsprintf where args originates from an array)
419 * - 0 or more: ArgumentCountError is thrown
420 */
421 static zend_string *
php_formatted_print(char * format,size_t format_len,zval * args,int argc,int nb_additional_parameters)422 php_formatted_print(char *format, size_t format_len, zval *args, int argc, int nb_additional_parameters)
423 {
424 size_t size = 240, outpos = 0;
425 int alignment, currarg, adjusting, argnum, width, precision;
426 char *temppos, padding;
427 zend_string *result;
428 int always_sign;
429 int max_missing_argnum = -1;
430
431 result = zend_string_alloc(size, 0);
432
433 currarg = 0;
434 argnum = 0;
435
436 while (format_len) {
437 int expprec;
438 zval *tmp;
439
440 temppos = memchr(format, '%', format_len);
441 if (!temppos) {
442 php_sprintf_appendchars(&result, &outpos, format, format_len);
443 break;
444 } else if (temppos != format) {
445 php_sprintf_appendchars(&result, &outpos, format, temppos - format);
446 format_len -= temppos - format;
447 format = temppos;
448 }
449 format++; /* skip the '%' */
450 format_len--;
451
452 if (*format == '%') {
453 php_sprintf_appendchar(&result, &outpos, '%');
454 format++;
455 format_len--;
456 } else {
457 /* starting a new format specifier, reset variables */
458 alignment = ALIGN_RIGHT;
459 adjusting = 0;
460 padding = ' ';
461 always_sign = 0;
462 expprec = 0;
463
464 PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%d\n",
465 *format, format - Z_STRVAL_P(z_format)));
466 if (isalpha((int)*format)) {
467 width = precision = 0;
468 argnum = ARG_NUM_NEXT;
469 } else {
470 /* first look for argnum */
471 argnum = php_sprintf_get_argnum(&format, &format_len);
472 if (argnum == ARG_NUM_INVALID) {
473 goto fail;
474 }
475
476 /* after argnum comes modifiers */
477 PRINTF_DEBUG(("sprintf: looking for modifiers\n"
478 "sprintf: now looking at '%c', inpos=%d\n",
479 *format, format - Z_STRVAL_P(z_format)));
480 for (;; format++, format_len--) {
481 if (*format == ' ' || *format == '0') {
482 padding = *format;
483 } else if (*format == '-') {
484 alignment = ALIGN_LEFT;
485 /* space padding, the default */
486 } else if (*format == '+') {
487 always_sign = 1;
488 } else if (*format == '\'') {
489 if (format_len > 1) {
490 format++;
491 format_len--;
492 padding = *format;
493 } else {
494 zend_value_error("Missing padding character");
495 goto fail;
496 }
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 (*format == '*') {
509 format++;
510 format_len--;
511
512 int width_argnum = php_sprintf_get_argnum(&format, &format_len);
513 if (width_argnum == ARG_NUM_INVALID) {
514 goto fail;
515 }
516 if (width_argnum == ARG_NUM_NEXT) {
517 width_argnum = currarg++;
518 }
519 if (width_argnum >= argc) {
520 max_missing_argnum = MAX(max_missing_argnum, width_argnum);
521 continue;
522 }
523 tmp = &args[width_argnum];
524 ZVAL_DEREF(tmp);
525 if (Z_TYPE_P(tmp) != IS_LONG) {
526 zend_value_error("Width must be an integer");
527 goto fail;
528 }
529 if (Z_LVAL_P(tmp) < 0 || Z_LVAL_P(tmp) > INT_MAX) {
530 zend_value_error("Width must be greater than zero and less than %d", INT_MAX);
531 goto fail;
532 }
533 width = Z_LVAL_P(tmp);
534 adjusting |= ADJ_WIDTH;
535 } else if (isdigit((int)*format)) {
536 PRINTF_DEBUG(("sprintf: getting width\n"));
537 if ((width = php_sprintf_getnumber(&format, &format_len)) < 0) {
538 zend_value_error("Width must be greater than zero and less than %d", INT_MAX);
539 goto fail;
540 }
541 adjusting |= ADJ_WIDTH;
542 } else {
543 width = 0;
544 }
545 PRINTF_DEBUG(("sprintf: width=%d\n", width));
546
547 /* after width and argnum comes precision */
548 if (*format == '.') {
549 format++;
550 format_len--;
551 PRINTF_DEBUG(("sprintf: getting precision\n"));
552 if (*format == '*') {
553 format++;
554 format_len--;
555
556 int prec_argnum = php_sprintf_get_argnum(&format, &format_len);
557 if (prec_argnum == ARG_NUM_INVALID) {
558 goto fail;
559 }
560 if (prec_argnum == ARG_NUM_NEXT) {
561 prec_argnum = currarg++;
562 }
563 if (prec_argnum >= argc) {
564 max_missing_argnum = MAX(max_missing_argnum, prec_argnum);
565 continue;
566 }
567 tmp = &args[prec_argnum];
568 ZVAL_DEREF(tmp);
569 if (Z_TYPE_P(tmp) != IS_LONG) {
570 zend_value_error("Precision must be an integer");
571 goto fail;
572 }
573 if (Z_LVAL_P(tmp) < -1 || Z_LVAL_P(tmp) > INT_MAX) {
574 zend_value_error("Precision must be between -1 and %d", INT_MAX);
575 goto fail;
576 }
577 precision = Z_LVAL_P(tmp);
578 adjusting |= ADJ_PRECISION;
579 expprec = 1;
580 } else if (isdigit((int)*format)) {
581 if ((precision = php_sprintf_getnumber(&format, &format_len)) < 0) {
582 zend_value_error("Precision must be greater than zero and less than %d", INT_MAX);
583 goto fail;
584 }
585 adjusting |= ADJ_PRECISION;
586 expprec = 1;
587 } else {
588 precision = 0;
589 }
590 } else {
591 precision = 0;
592 }
593 PRINTF_DEBUG(("sprintf: precision=%d\n", precision));
594 }
595
596 if (*format == 'l') {
597 format++;
598 format_len--;
599 }
600 PRINTF_DEBUG(("sprintf: format character='%c'\n", *format));
601
602 if (argnum == ARG_NUM_NEXT) {
603 argnum = currarg++;
604 }
605 if (argnum >= argc) {
606 max_missing_argnum = MAX(max_missing_argnum, argnum);
607 continue;
608 }
609
610 if (expprec && precision == -1
611 && *format != 'g' && *format != 'G' && *format != 'h' && *format != 'H') {
612 zend_value_error("Precision -1 is only supported for %%g, %%G, %%h and %%H");
613 goto fail;
614 }
615
616 /* now we expect to find a type specifier */
617 tmp = &args[argnum];
618 switch (*format) {
619 case 's': {
620 zend_string *t;
621 zend_string *str = zval_get_tmp_string(tmp, &t);
622 php_sprintf_appendstring(&result, &outpos,
623 ZSTR_VAL(str),
624 width, precision, padding,
625 alignment,
626 ZSTR_LEN(str),
627 /* neg */ false, expprec, 0);
628 zend_tmp_string_release(t);
629 break;
630 }
631
632 case 'd':
633 php_sprintf_appendint(&result, &outpos,
634 zval_get_long(tmp),
635 width, padding, alignment,
636 always_sign);
637 break;
638
639 case 'u':
640 php_sprintf_appenduint(&result, &outpos,
641 zval_get_long(tmp),
642 width, padding, alignment);
643 break;
644
645 case 'e':
646 case 'E':
647 case 'f':
648 case 'F':
649 case 'g':
650 case 'G':
651 case 'h':
652 case 'H':
653 php_sprintf_appenddouble(&result, &outpos,
654 zval_get_double(tmp),
655 width, padding, alignment,
656 precision, adjusting,
657 *format, always_sign
658 );
659 break;
660
661 case 'c':
662 php_sprintf_appendchar(&result, &outpos,
663 (char) zval_get_long(tmp));
664 break;
665
666 case 'o':
667 php_sprintf_append2n(&result, &outpos,
668 zval_get_long(tmp),
669 width, padding, alignment, 3,
670 hexchars, expprec);
671 break;
672
673 case 'x':
674 php_sprintf_append2n(&result, &outpos,
675 zval_get_long(tmp),
676 width, padding, alignment, 4,
677 hexchars, expprec);
678 break;
679
680 case 'X':
681 php_sprintf_append2n(&result, &outpos,
682 zval_get_long(tmp),
683 width, padding, alignment, 4,
684 HEXCHARS, expprec);
685 break;
686
687 case 'b':
688 php_sprintf_append2n(&result, &outpos,
689 zval_get_long(tmp),
690 width, padding, alignment, 1,
691 hexchars, expprec);
692 break;
693
694 case '%':
695 php_sprintf_appendchar(&result, &outpos, '%');
696
697 break;
698
699 case '\0':
700 if (!format_len) {
701 zend_value_error("Missing format specifier at end of string");
702 goto fail;
703 }
704 ZEND_FALLTHROUGH;
705
706 default:
707 zend_value_error("Unknown format specifier \"%c\"", *format);
708 goto fail;
709 }
710 format++;
711 format_len--;
712 }
713 }
714
715 if (max_missing_argnum >= 0) {
716 if (nb_additional_parameters == -1) {
717 zend_value_error("The arguments array must contain %d items, %d given", max_missing_argnum + 1, argc);
718 } else {
719 zend_argument_count_error("%d arguments are required, %d given", max_missing_argnum + nb_additional_parameters + 1, argc + nb_additional_parameters);
720 }
721 goto fail;
722 }
723
724 /* possibly, we have to make sure we have room for the terminating null? */
725 ZSTR_VAL(result)[outpos]=0;
726 ZSTR_LEN(result) = outpos;
727 return result;
728
729 fail:
730 zend_string_efree(result);
731 return NULL;
732 }
733 /* }}} */
734
735 /* php_formatted_print_get_array() {{{ */
php_formatted_print_get_array(zend_array * array,int * argc)736 static zval *php_formatted_print_get_array(zend_array *array, int *argc)
737 {
738 zval *args, *zv;
739 int n;
740
741 n = zend_hash_num_elements(array);
742 args = (zval *)safe_emalloc(n, sizeof(zval), 0);
743 n = 0;
744 ZEND_HASH_FOREACH_VAL(array, zv) {
745 ZVAL_COPY_VALUE(&args[n], zv);
746 n++;
747 } ZEND_HASH_FOREACH_END();
748
749 *argc = n;
750 return args;
751 }
752 /* }}} */
753
754 /* {{{ Return a formatted string */
PHP_FUNCTION(sprintf)755 PHP_FUNCTION(sprintf)
756 {
757 zend_string *result;
758 char *format;
759 size_t format_len;
760 zval *args;
761 int argc;
762
763 ZEND_PARSE_PARAMETERS_START(1, -1)
764 Z_PARAM_STRING(format, format_len)
765 Z_PARAM_VARIADIC('*', args, argc)
766 ZEND_PARSE_PARAMETERS_END();
767
768 result = php_formatted_print(format, format_len, args, argc, 1);
769 if (result == NULL) {
770 RETURN_THROWS();
771 }
772 RETVAL_STR(result);
773 }
774 /* }}} */
775
776 /* {{{ Return a formatted string */
PHP_FUNCTION(vsprintf)777 PHP_FUNCTION(vsprintf)
778 {
779 zend_string *result;
780 char *format;
781 size_t format_len;
782 zval *args;
783 zend_array *array;
784 int argc;
785
786 ZEND_PARSE_PARAMETERS_START(2, 2)
787 Z_PARAM_STRING(format, format_len)
788 Z_PARAM_ARRAY_HT(array)
789 ZEND_PARSE_PARAMETERS_END();
790
791 args = php_formatted_print_get_array(array, &argc);
792
793 result = php_formatted_print(format, format_len, args, argc, -1);
794 efree(args);
795 if (result == NULL) {
796 RETURN_THROWS();
797 }
798 RETVAL_STR(result);
799 }
800 /* }}} */
801
802 /* {{{ Output a formatted string */
PHP_FUNCTION(printf)803 PHP_FUNCTION(printf)
804 {
805 zend_string *result;
806 size_t rlen;
807 char *format;
808 size_t format_len;
809 zval *args;
810 int argc;
811
812 ZEND_PARSE_PARAMETERS_START(1, -1)
813 Z_PARAM_STRING(format, format_len)
814 Z_PARAM_VARIADIC('*', args, argc)
815 ZEND_PARSE_PARAMETERS_END();
816
817 result = php_formatted_print(format, format_len, args, argc, 1);
818 if (result == NULL) {
819 RETURN_THROWS();
820 }
821 rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
822 zend_string_efree(result);
823 RETURN_LONG(rlen);
824 }
825 /* }}} */
826
827 /* {{{ Output a formatted string */
PHP_FUNCTION(vprintf)828 PHP_FUNCTION(vprintf)
829 {
830 zend_string *result;
831 size_t rlen;
832 char *format;
833 size_t format_len;
834 zval *args;
835 zend_array *array;
836 int argc;
837
838 ZEND_PARSE_PARAMETERS_START(2, 2)
839 Z_PARAM_STRING(format, format_len)
840 Z_PARAM_ARRAY_HT(array)
841 ZEND_PARSE_PARAMETERS_END();
842
843 args = php_formatted_print_get_array(array, &argc);
844
845 result = php_formatted_print(format, format_len, args, argc, -1);
846 efree(args);
847 if (result == NULL) {
848 RETURN_THROWS();
849 }
850 rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
851 zend_string_efree(result);
852 RETURN_LONG(rlen);
853 }
854 /* }}} */
855
856 /* {{{ Output a formatted string into a stream */
PHP_FUNCTION(fprintf)857 PHP_FUNCTION(fprintf)
858 {
859 php_stream *stream;
860 char *format;
861 size_t format_len;
862 zval *arg1, *args;
863 int argc;
864 zend_string *result;
865
866 ZEND_PARSE_PARAMETERS_START(2, -1)
867 Z_PARAM_RESOURCE(arg1)
868 Z_PARAM_STRING(format, format_len)
869 Z_PARAM_VARIADIC('*', args, argc)
870 ZEND_PARSE_PARAMETERS_END();
871
872 php_stream_from_zval(stream, arg1);
873
874 result = php_formatted_print(format, format_len, args, argc, 2);
875 if (result == NULL) {
876 RETURN_THROWS();
877 }
878
879 php_stream_write(stream, ZSTR_VAL(result), ZSTR_LEN(result));
880
881 RETVAL_LONG(ZSTR_LEN(result));
882 zend_string_efree(result);
883 }
884 /* }}} */
885
886 /* {{{ Output a formatted string into a stream */
PHP_FUNCTION(vfprintf)887 PHP_FUNCTION(vfprintf)
888 {
889 php_stream *stream;
890 char *format;
891 size_t format_len;
892 zval *arg1, *args;
893 zend_array *array;
894 int argc;
895 zend_string *result;
896
897 ZEND_PARSE_PARAMETERS_START(3, 3)
898 Z_PARAM_RESOURCE(arg1)
899 Z_PARAM_STRING(format, format_len)
900 Z_PARAM_ARRAY_HT(array)
901 ZEND_PARSE_PARAMETERS_END();
902
903 php_stream_from_zval(stream, arg1);
904
905 args = php_formatted_print_get_array(array, &argc);
906
907 result = php_formatted_print(format, format_len, args, argc, -1);
908 efree(args);
909 if (result == NULL) {
910 RETURN_THROWS();
911 }
912
913 php_stream_write(stream, ZSTR_VAL(result), ZSTR_LEN(result));
914
915 RETVAL_LONG(ZSTR_LEN(result));
916 zend_string_efree(result);
917 }
918 /* }}} */
919