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