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 char *str = is_negative ? "-INF" : "INF";
251 php_sprintf_appendstring(buffer, pos, str, strlen(str), 0, padding,
252 alignment, strlen(str), is_negative, 0, always_sign);
253 return;
254 }
255
256 switch (fmt) {
257 case 'e':
258 case 'E':
259 case 'f':
260 case 'F':
261 #ifdef ZTS
262 localeconv_r(&lconv);
263 #else
264 lconv = localeconv();
265 #endif
266 s = php_conv_fp((fmt == 'f')?'F':fmt, number, 0, precision,
267 (fmt == 'f')?LCONV_DECIMAL_POINT:'.',
268 &is_negative, &num_buf[1], &s_len);
269 if (is_negative) {
270 num_buf[0] = '-';
271 s = num_buf;
272 s_len++;
273 } else if (always_sign) {
274 num_buf[0] = '+';
275 s = num_buf;
276 s_len++;
277 }
278 break;
279
280 case 'g':
281 case 'G':
282 case 'h':
283 case 'H':
284 {
285 if (precision == 0)
286 precision = 1;
287
288 char decimal_point = '.';
289 if (fmt == 'g' || fmt == 'G') {
290 #ifdef ZTS
291 localeconv_r(&lconv);
292 #else
293 lconv = localeconv();
294 #endif
295 decimal_point = LCONV_DECIMAL_POINT;
296 }
297
298 char exp_char = fmt == 'G' || fmt == 'H' ? 'E' : 'e';
299 /* We use &num_buf[ 1 ], so that we have room for the sign. */
300 s = zend_gcvt(number, precision, decimal_point, exp_char, &num_buf[1]);
301 is_negative = 0;
302 if (*s == '-') {
303 is_negative = 1;
304 s = &num_buf[1];
305 } else if (always_sign) {
306 num_buf[0] = '+';
307 s = num_buf;
308 }
309
310 s_len = strlen(s);
311 break;
312 }
313 }
314
315 php_sprintf_appendstring(buffer, pos, s, width, 0, padding,
316 alignment, s_len, is_negative, 0, always_sign);
317 }
318 /* }}} */
319
320 /* php_spintf_appendd2n() {{{ */
321 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)322 php_sprintf_append2n(zend_string **buffer, size_t *pos, zend_long number,
323 size_t width, char padding, size_t alignment, int n,
324 const char *chartable, int expprec)
325 {
326 char numbuf[NUM_BUF_SIZE];
327 zend_ulong num;
328 zend_ulong i = NUM_BUF_SIZE - 1;
329 int andbits = (1 << n) - 1;
330
331 PRINTF_DEBUG(("sprintf: append2n(%x, %x, %x, %d, %d, '%c', %d, %d, %x)\n",
332 *buffer, pos, &ZSTR_LEN(*buffer), number, width, padding, alignment, n,
333 chartable));
334 PRINTF_DEBUG(("sprintf: append2n 2^%d andbits=%x\n", n, andbits));
335
336 num = (zend_ulong) number;
337 numbuf[i] = '\0';
338
339 do {
340 numbuf[--i] = chartable[(num & andbits)];
341 num >>= n;
342 }
343 while (num > 0);
344
345 php_sprintf_appendstring(buffer, pos, &numbuf[i], width, 0,
346 padding, alignment, (NUM_BUF_SIZE - 1) - i,
347 /* neg */ false, expprec, 0);
348 }
349 /* }}} */
350
351 /* php_spintf_getnumber() {{{ */
352 inline static int
php_sprintf_getnumber(char ** buffer,size_t * len)353 php_sprintf_getnumber(char **buffer, size_t *len)
354 {
355 char *endptr;
356 zend_long num = ZEND_STRTOL(*buffer, &endptr, 10);
357 size_t i;
358
359 if (endptr != NULL) {
360 i = (endptr - *buffer);
361 *len -= i;
362 *buffer = endptr;
363 }
364 PRINTF_DEBUG(("sprintf_getnumber: number was %d bytes long\n", i));
365
366 if (num >= INT_MAX || num < 0) {
367 return -1;
368 } else {
369 return (int) num;
370 }
371 }
372 /* }}} */
373
374 #define ARG_NUM_NEXT -1
375 #define ARG_NUM_INVALID -2
376
php_sprintf_get_argnum(char ** format,size_t * format_len)377 int php_sprintf_get_argnum(char **format, size_t *format_len) {
378 char *temppos = *format;
379 while (isdigit((int) *temppos)) temppos++;
380 if (*temppos != '$') {
381 return ARG_NUM_NEXT;
382 }
383
384 int argnum = php_sprintf_getnumber(format, format_len);
385 if (argnum <= 0) {
386 zend_value_error("Argument number specifier must be greater than zero and less than %d", INT_MAX);
387 return ARG_NUM_INVALID;
388 }
389
390 (*format)++; /* skip the '$' */
391 (*format_len)--;
392 return argnum - 1;
393 }
394
395 /* php_formatted_print() {{{
396 * New sprintf implementation for PHP.
397 *
398 * Modifiers:
399 *
400 * " " pad integers with spaces
401 * "-" left adjusted field
402 * n field size
403 * "."n precision (floats only)
404 * "+" Always place a sign (+ or -) in front of a number
405 *
406 * Type specifiers:
407 *
408 * "%" literal "%", modifiers are ignored.
409 * "b" integer argument is printed as binary
410 * "c" integer argument is printed as a single character
411 * "d" argument is an integer
412 * "f" the argument is a float
413 * "o" integer argument is printed as octal
414 * "s" argument is a string
415 * "x" integer argument is printed as lowercase hexadecimal
416 * "X" integer argument is printed as uppercase hexadecimal
417 *
418 * nb_additional_parameters is used for throwing errors:
419 * - -1: ValueError is thrown (for vsprintf where args originates from an array)
420 * - 0 or more: ArgumentCountError is thrown
421 */
422 static zend_string *
php_formatted_print(char * format,size_t format_len,zval * args,int argc,int nb_additional_parameters)423 php_formatted_print(char *format, size_t format_len, zval *args, int argc, int nb_additional_parameters)
424 {
425 size_t size = 240, outpos = 0;
426 int alignment, currarg, adjusting, argnum, width, precision;
427 char *temppos, padding;
428 zend_string *result;
429 int always_sign;
430 int max_missing_argnum = -1;
431
432 result = zend_string_alloc(size, 0);
433
434 currarg = 0;
435 argnum = 0;
436
437 while (format_len) {
438 int expprec;
439 zval *tmp;
440
441 temppos = memchr(format, '%', format_len);
442 if (!temppos) {
443 php_sprintf_appendchars(&result, &outpos, format, format_len);
444 break;
445 } else if (temppos != format) {
446 php_sprintf_appendchars(&result, &outpos, format, temppos - format);
447 format_len -= temppos - format;
448 format = temppos;
449 }
450 format++; /* skip the '%' */
451 format_len--;
452
453 if (*format == '%') {
454 php_sprintf_appendchar(&result, &outpos, '%');
455 format++;
456 format_len--;
457 } else {
458 /* starting a new format specifier, reset variables */
459 alignment = ALIGN_RIGHT;
460 adjusting = 0;
461 padding = ' ';
462 always_sign = 0;
463 expprec = 0;
464
465 PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%d\n",
466 *format, format - Z_STRVAL_P(z_format)));
467 if (isalpha((int)*format)) {
468 width = precision = 0;
469 argnum = ARG_NUM_NEXT;
470 } else {
471 /* first look for argnum */
472 argnum = php_sprintf_get_argnum(&format, &format_len);
473 if (argnum == ARG_NUM_INVALID) {
474 goto fail;
475 }
476
477 /* after argnum comes modifiers */
478 PRINTF_DEBUG(("sprintf: looking for modifiers\n"
479 "sprintf: now looking at '%c', inpos=%d\n",
480 *format, format - Z_STRVAL_P(z_format)));
481 for (;; format++, format_len--) {
482 if (*format == ' ' || *format == '0') {
483 padding = *format;
484 } else if (*format == '-') {
485 alignment = ALIGN_LEFT;
486 /* space padding, the default */
487 } else if (*format == '+') {
488 always_sign = 1;
489 } else if (*format == '\'') {
490 if (format_len > 1) {
491 format++;
492 format_len--;
493 padding = *format;
494 } else {
495 zend_value_error("Missing padding character");
496 goto fail;
497 }
498 } else {
499 PRINTF_DEBUG(("sprintf: end of modifiers\n"));
500 break;
501 }
502 }
503 PRINTF_DEBUG(("sprintf: padding='%c'\n", padding));
504 PRINTF_DEBUG(("sprintf: alignment=%s\n",
505 (alignment == ALIGN_LEFT) ? "left" : "right"));
506
507
508 /* after modifiers comes width */
509 if (*format == '*') {
510 format++;
511 format_len--;
512
513 int width_argnum = php_sprintf_get_argnum(&format, &format_len);
514 if (width_argnum == ARG_NUM_INVALID) {
515 goto fail;
516 }
517 if (width_argnum == ARG_NUM_NEXT) {
518 width_argnum = currarg++;
519 }
520 if (width_argnum >= argc) {
521 max_missing_argnum = MAX(max_missing_argnum, width_argnum);
522 continue;
523 }
524 tmp = &args[width_argnum];
525 ZVAL_DEREF(tmp);
526 if (Z_TYPE_P(tmp) != IS_LONG) {
527 zend_value_error("Width must be an integer");
528 goto fail;
529 }
530 if (Z_LVAL_P(tmp) < 0 || Z_LVAL_P(tmp) > INT_MAX) {
531 zend_value_error("Width must be greater than zero and less than %d", INT_MAX);
532 goto fail;
533 }
534 width = Z_LVAL_P(tmp);
535 adjusting |= ADJ_WIDTH;
536 } else if (isdigit((int)*format)) {
537 PRINTF_DEBUG(("sprintf: getting width\n"));
538 if ((width = php_sprintf_getnumber(&format, &format_len)) < 0) {
539 zend_value_error("Width must be greater than zero and less than %d", INT_MAX);
540 goto fail;
541 }
542 adjusting |= ADJ_WIDTH;
543 } else {
544 width = 0;
545 }
546 PRINTF_DEBUG(("sprintf: width=%d\n", width));
547
548 /* after width and argnum comes precision */
549 if (*format == '.') {
550 format++;
551 format_len--;
552 PRINTF_DEBUG(("sprintf: getting precision\n"));
553 if (*format == '*') {
554 format++;
555 format_len--;
556
557 int prec_argnum = php_sprintf_get_argnum(&format, &format_len);
558 if (prec_argnum == ARG_NUM_INVALID) {
559 goto fail;
560 }
561 if (prec_argnum == ARG_NUM_NEXT) {
562 prec_argnum = currarg++;
563 }
564 if (prec_argnum >= argc) {
565 max_missing_argnum = MAX(max_missing_argnum, prec_argnum);
566 continue;
567 }
568 tmp = &args[prec_argnum];
569 ZVAL_DEREF(tmp);
570 if (Z_TYPE_P(tmp) != IS_LONG) {
571 zend_value_error("Precision must be an integer");
572 goto fail;
573 }
574 if (Z_LVAL_P(tmp) < -1 || Z_LVAL_P(tmp) > INT_MAX) {
575 zend_value_error("Precision must be between -1 and %d", INT_MAX);
576 goto fail;
577 }
578 precision = Z_LVAL_P(tmp);
579 adjusting |= ADJ_PRECISION;
580 expprec = 1;
581 } else if (isdigit((int)*format)) {
582 if ((precision = php_sprintf_getnumber(&format, &format_len)) < 0) {
583 zend_value_error("Precision must be greater than zero and less than %d", INT_MAX);
584 goto fail;
585 }
586 adjusting |= ADJ_PRECISION;
587 expprec = 1;
588 } else {
589 precision = 0;
590 }
591 } else {
592 precision = 0;
593 }
594 PRINTF_DEBUG(("sprintf: precision=%d\n", precision));
595 }
596
597 if (*format == 'l') {
598 format++;
599 format_len--;
600 }
601 PRINTF_DEBUG(("sprintf: format character='%c'\n", *format));
602
603 if (argnum == ARG_NUM_NEXT) {
604 argnum = currarg++;
605 }
606 if (argnum >= argc) {
607 max_missing_argnum = MAX(max_missing_argnum, argnum);
608 continue;
609 }
610
611 if (expprec && precision == -1
612 && *format != 'g' && *format != 'G' && *format != 'h' && *format != 'H') {
613 zend_value_error("Precision -1 is only supported for %%g, %%G, %%h and %%H");
614 goto fail;
615 }
616
617 /* now we expect to find a type specifier */
618 tmp = &args[argnum];
619 switch (*format) {
620 case 's': {
621 zend_string *t;
622 zend_string *str = zval_get_tmp_string(tmp, &t);
623 php_sprintf_appendstring(&result, &outpos,
624 ZSTR_VAL(str),
625 width, precision, padding,
626 alignment,
627 ZSTR_LEN(str),
628 /* neg */ false, expprec, 0);
629 zend_tmp_string_release(t);
630 break;
631 }
632
633 case 'd':
634 php_sprintf_appendint(&result, &outpos,
635 zval_get_long(tmp),
636 width, padding, alignment,
637 always_sign);
638 break;
639
640 case 'u':
641 php_sprintf_appenduint(&result, &outpos,
642 zval_get_long(tmp),
643 width, padding, alignment);
644 break;
645
646 case 'e':
647 case 'E':
648 case 'f':
649 case 'F':
650 case 'g':
651 case 'G':
652 case 'h':
653 case 'H':
654 php_sprintf_appenddouble(&result, &outpos,
655 zval_get_double(tmp),
656 width, padding, alignment,
657 precision, adjusting,
658 *format, always_sign
659 );
660 break;
661
662 case 'c':
663 php_sprintf_appendchar(&result, &outpos,
664 (char) zval_get_long(tmp));
665 break;
666
667 case 'o':
668 php_sprintf_append2n(&result, &outpos,
669 zval_get_long(tmp),
670 width, padding, alignment, 3,
671 hexchars, expprec);
672 break;
673
674 case 'x':
675 php_sprintf_append2n(&result, &outpos,
676 zval_get_long(tmp),
677 width, padding, alignment, 4,
678 hexchars, expprec);
679 break;
680
681 case 'X':
682 php_sprintf_append2n(&result, &outpos,
683 zval_get_long(tmp),
684 width, padding, alignment, 4,
685 HEXCHARS, expprec);
686 break;
687
688 case 'b':
689 php_sprintf_append2n(&result, &outpos,
690 zval_get_long(tmp),
691 width, padding, alignment, 1,
692 hexchars, expprec);
693 break;
694
695 case '%':
696 php_sprintf_appendchar(&result, &outpos, '%');
697
698 break;
699
700 case '\0':
701 if (!format_len) {
702 zend_value_error("Missing format specifier at end of string");
703 goto fail;
704 }
705 ZEND_FALLTHROUGH;
706
707 default:
708 zend_value_error("Unknown format specifier \"%c\"", *format);
709 goto fail;
710 }
711 format++;
712 format_len--;
713 }
714 }
715
716 if (max_missing_argnum >= 0) {
717 if (nb_additional_parameters == -1) {
718 zend_value_error("The arguments array must contain %d items, %d given", max_missing_argnum + 1, argc);
719 } else {
720 zend_argument_count_error("%d arguments are required, %d given", max_missing_argnum + nb_additional_parameters + 1, argc + nb_additional_parameters);
721 }
722 goto fail;
723 }
724
725 /* possibly, we have to make sure we have room for the terminating null? */
726 ZSTR_VAL(result)[outpos]=0;
727 ZSTR_LEN(result) = outpos;
728 return result;
729
730 fail:
731 zend_string_efree(result);
732 return NULL;
733 }
734 /* }}} */
735
736 /* php_formatted_print_get_array() {{{ */
php_formatted_print_get_array(zend_array * array,int * argc)737 static zval *php_formatted_print_get_array(zend_array *array, int *argc)
738 {
739 zval *args, *zv;
740 int n;
741
742 n = zend_hash_num_elements(array);
743 args = (zval *)safe_emalloc(n, sizeof(zval), 0);
744 n = 0;
745 ZEND_HASH_FOREACH_VAL(array, zv) {
746 ZVAL_COPY_VALUE(&args[n], zv);
747 n++;
748 } ZEND_HASH_FOREACH_END();
749
750 *argc = n;
751 return args;
752 }
753 /* }}} */
754
755 /* {{{ Return a formatted string */
PHP_FUNCTION(sprintf)756 PHP_FUNCTION(sprintf)
757 {
758 zend_string *result;
759 char *format;
760 size_t format_len;
761 zval *args;
762 int argc;
763
764 ZEND_PARSE_PARAMETERS_START(1, -1)
765 Z_PARAM_STRING(format, format_len)
766 Z_PARAM_VARIADIC('*', args, argc)
767 ZEND_PARSE_PARAMETERS_END();
768
769 result = php_formatted_print(format, format_len, args, argc, 1);
770 if (result == NULL) {
771 RETURN_THROWS();
772 }
773 RETVAL_STR(result);
774 }
775 /* }}} */
776
777 /* {{{ Return a formatted string */
PHP_FUNCTION(vsprintf)778 PHP_FUNCTION(vsprintf)
779 {
780 zend_string *result;
781 char *format;
782 size_t format_len;
783 zval *args;
784 zend_array *array;
785 int argc;
786
787 ZEND_PARSE_PARAMETERS_START(2, 2)
788 Z_PARAM_STRING(format, format_len)
789 Z_PARAM_ARRAY_HT(array)
790 ZEND_PARSE_PARAMETERS_END();
791
792 args = php_formatted_print_get_array(array, &argc);
793
794 result = php_formatted_print(format, format_len, args, argc, -1);
795 efree(args);
796 if (result == NULL) {
797 RETURN_THROWS();
798 }
799 RETVAL_STR(result);
800 }
801 /* }}} */
802
803 /* {{{ Output a formatted string */
PHP_FUNCTION(printf)804 PHP_FUNCTION(printf)
805 {
806 zend_string *result;
807 size_t rlen;
808 char *format;
809 size_t format_len;
810 zval *args;
811 int argc;
812
813 ZEND_PARSE_PARAMETERS_START(1, -1)
814 Z_PARAM_STRING(format, format_len)
815 Z_PARAM_VARIADIC('*', args, argc)
816 ZEND_PARSE_PARAMETERS_END();
817
818 result = php_formatted_print(format, format_len, args, argc, 1);
819 if (result == NULL) {
820 RETURN_THROWS();
821 }
822 rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
823 zend_string_efree(result);
824 RETURN_LONG(rlen);
825 }
826 /* }}} */
827
828 /* {{{ Output a formatted string */
PHP_FUNCTION(vprintf)829 PHP_FUNCTION(vprintf)
830 {
831 zend_string *result;
832 size_t rlen;
833 char *format;
834 size_t format_len;
835 zval *args;
836 zend_array *array;
837 int argc;
838
839 ZEND_PARSE_PARAMETERS_START(2, 2)
840 Z_PARAM_STRING(format, format_len)
841 Z_PARAM_ARRAY_HT(array)
842 ZEND_PARSE_PARAMETERS_END();
843
844 args = php_formatted_print_get_array(array, &argc);
845
846 result = php_formatted_print(format, format_len, args, argc, -1);
847 efree(args);
848 if (result == NULL) {
849 RETURN_THROWS();
850 }
851 rlen = PHPWRITE(ZSTR_VAL(result), ZSTR_LEN(result));
852 zend_string_efree(result);
853 RETURN_LONG(rlen);
854 }
855 /* }}} */
856
857 /* {{{ Output a formatted string into a stream */
PHP_FUNCTION(fprintf)858 PHP_FUNCTION(fprintf)
859 {
860 php_stream *stream;
861 char *format;
862 size_t format_len;
863 zval *arg1, *args;
864 int argc;
865 zend_string *result;
866
867 ZEND_PARSE_PARAMETERS_START(2, -1)
868 Z_PARAM_RESOURCE(arg1)
869 Z_PARAM_STRING(format, format_len)
870 Z_PARAM_VARIADIC('*', args, argc)
871 ZEND_PARSE_PARAMETERS_END();
872
873 php_stream_from_zval(stream, arg1);
874
875 result = php_formatted_print(format, format_len, args, argc, 2);
876 if (result == NULL) {
877 RETURN_THROWS();
878 }
879
880 php_stream_write(stream, ZSTR_VAL(result), ZSTR_LEN(result));
881
882 RETVAL_LONG(ZSTR_LEN(result));
883 zend_string_efree(result);
884 }
885 /* }}} */
886
887 /* {{{ Output a formatted string into a stream */
PHP_FUNCTION(vfprintf)888 PHP_FUNCTION(vfprintf)
889 {
890 php_stream *stream;
891 char *format;
892 size_t format_len;
893 zval *arg1, *args;
894 zend_array *array;
895 int argc;
896 zend_string *result;
897
898 ZEND_PARSE_PARAMETERS_START(3, 3)
899 Z_PARAM_RESOURCE(arg1)
900 Z_PARAM_STRING(format, format_len)
901 Z_PARAM_ARRAY_HT(array)
902 ZEND_PARSE_PARAMETERS_END();
903
904 php_stream_from_zval(stream, arg1);
905
906 args = php_formatted_print_get_array(array, &argc);
907
908 result = php_formatted_print(format, format_len, args, argc, -1);
909 efree(args);
910 if (result == NULL) {
911 RETURN_THROWS();
912 }
913
914 php_stream_write(stream, ZSTR_VAL(result), ZSTR_LEN(result));
915
916 RETVAL_LONG(ZSTR_LEN(result));
917 zend_string_efree(result);
918 }
919 /* }}} */
920