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