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