1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 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: Marcus Boerger <helly@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 /* This is the spprintf implementation.
22 * It has emerged from apache snprintf. See original header:
23 */
24
25 /* ====================================================================
26 * Copyright (c) 1995-1998 The Apache Group. All rights reserved.
27 *
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
30 * are met:
31 *
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 *
35 * 2. Redistributions in binary form must reproduce the above copyright
36 * notice, this list of conditions and the following disclaimer in
37 * the documentation and/or other materials provided with the
38 * distribution.
39 *
40 * 3. All advertising materials mentioning features or use of this
41 * software must display the following acknowledgment:
42 * "This product includes software developed by the Apache Group
43 * for use in the Apache HTTP server project (http://www.apache.org/)."
44 *
45 * 4. The names "Apache Server" and "Apache Group" must not be used to
46 * endorse or promote products derived from this software without
47 * prior written permission.
48 *
49 * 5. Redistributions of any form whatsoever must retain the following
50 * acknowledgment:
51 * "This product includes software developed by the Apache Group
52 * for use in the Apache HTTP server project (http://www.apache.org/)."
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
55 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
58 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
60 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
61 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
63 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
65 * OF THE POSSIBILITY OF SUCH DAMAGE.
66 * ====================================================================
67 *
68 * This software consists of voluntary contributions made by many
69 * individuals on behalf of the Apache Group and was originally based
70 * on public domain software written at the National Center for
71 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
72 * For more information on the Apache Group and the Apache HTTP server
73 * project, please see <http://www.apache.org/>.
74 *
75 * This code is based on, and used with the permission of, the
76 * SIO stdio-replacement strx_* functions by Panos Tsirigotis
77 * <panos@alumni.cs.colorado.edu> for xinetd.
78 */
79 #define _GNU_SOURCE
80 #include "php.h"
81
82 #include <stddef.h>
83 #include <stdio.h>
84 #include <ctype.h>
85 #include <sys/types.h>
86 #include <stdarg.h>
87 #include <string.h>
88 #include <stdlib.h>
89 #include <math.h>
90 #ifdef HAVE_INTTYPES_H
91 #include <inttypes.h>
92 #endif
93
94 #ifdef HAVE_LOCALE_H
95 #include <locale.h>
96 #ifdef ZTS
97 #include "ext/standard/php_string.h"
98 #define LCONV_DECIMAL_POINT (*lconv.decimal_point)
99 #else
100 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
101 #endif
102 #else
103 #define LCONV_DECIMAL_POINT '.'
104 #endif
105
106 #include "snprintf.h"
107
108 #define FALSE 0
109 #define TRUE 1
110 #define NUL '\0'
111 #define INT_NULL ((int *)0)
112
113 #define S_NULL "(null)"
114 #define S_NULL_LEN 6
115
116 #define FLOAT_DIGITS 6
117 #define EXPONENT_LENGTH 10
118
119 #include "zend_smart_str.h"
120 #include "zend_smart_string.h"
121
122 /* {{{ macros */
123
124 #define INS_CHAR(xbuf, ch, is_char) do { \
125 if ((is_char)) { \
126 smart_string_appendc((smart_string *)(xbuf), (ch)); \
127 } else { \
128 smart_str_appendc((smart_str *)(xbuf), (ch)); \
129 } \
130 } while (0);
131
132 #define INS_STRING(xbuf, str, len, is_char) do { \
133 if ((is_char)) { \
134 smart_string_appendl((smart_string *)(xbuf), (str), (len)); \
135 } else { \
136 smart_str_appendl((smart_str *)(xbuf), (str), (len)); \
137 } \
138 } while (0);
139
140 #define PAD_CHAR(xbuf, ch, count, is_char) do { \
141 if ((is_char)) { \
142 smart_string_alloc(((smart_string *)(xbuf)), (count), 0); \
143 memset(((smart_string *)(xbuf))->c + ((smart_string *)(xbuf))->len, (ch), (count)); \
144 ((smart_string *)(xbuf))->len += (count); \
145 } else { \
146 smart_str_alloc(((smart_str *)(xbuf)), (count), 0); \
147 memset(ZSTR_VAL(((smart_str *)(xbuf))->s) + ZSTR_LEN(((smart_str *)(xbuf))->s), (ch), (count)); \
148 ZSTR_LEN(((smart_str *)(xbuf))->s) += (count); \
149 } \
150 } while (0);
151
152 /*
153 * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
154 * which can be at most max length of double
155 */
156 #define NUM_BUF_SIZE PHP_DOUBLE_MAX_LENGTH
157
158 #define NUM(c) (c - '0')
159
160 #define STR_TO_DEC(str, num) do { \
161 num = NUM(*str++); \
162 while (isdigit((int)*str)) { \
163 num *= 10; \
164 num += NUM(*str++); \
165 if (num >= INT_MAX / 10) { \
166 while (isdigit((int)*str++)); \
167 break; \
168 } \
169 } \
170 } while (0)
171
172 /*
173 * This macro does zero padding so that the precision
174 * requirement is satisfied. The padding is done by
175 * adding '0's to the left of the string that is going
176 * to be printed.
177 */
178 #define FIX_PRECISION(adjust, precision, s, s_len) do { \
179 if (adjust) \
180 while (s_len < (size_t)precision) { \
181 *--s = '0'; \
182 s_len++; \
183 } \
184 } while (0)
185
186 /* }}} */
187
188 #if !HAVE_STRNLEN
strnlen(const char * s,size_t maxlen)189 static size_t strnlen(const char *s, size_t maxlen) {
190 char *r = memchr(s, '\0', maxlen);
191 return r ? r-s : maxlen;
192 }
193 #endif
194
195 /*
196 * Do format conversion placing the output in buffer
197 */
xbuf_format_converter(void * xbuf,zend_bool is_char,const char * fmt,va_list ap)198 static void xbuf_format_converter(void *xbuf, zend_bool is_char, const char *fmt, va_list ap) /* {{{ */
199 {
200 char *s = NULL;
201 size_t s_len;
202 int free_zcopy;
203 zval *zvp, zcopy;
204
205 int min_width = 0;
206 int precision = 0;
207 enum {
208 LEFT, RIGHT
209 } adjust;
210 char pad_char;
211 char prefix_char;
212
213 double fp_num;
214 wide_int i_num = (wide_int) 0;
215 u_wide_int ui_num = (u_wide_int) 0;
216
217 char num_buf[NUM_BUF_SIZE];
218 char char_buf[2]; /* for printing %% and %<unknown> */
219
220 #ifdef HAVE_LOCALE_H
221 #ifdef ZTS
222 struct lconv lconv;
223 #else
224 struct lconv *lconv = NULL;
225 #endif
226 #endif
227
228 /*
229 * Flag variables
230 */
231 length_modifier_e modifier;
232 boolean_e alternate_form;
233 boolean_e print_sign;
234 boolean_e print_blank;
235 boolean_e adjust_precision;
236 boolean_e adjust_width;
237 bool_int is_negative;
238
239 while (*fmt) {
240 if (*fmt != '%') {
241 INS_CHAR(xbuf, *fmt, is_char);
242 } else {
243 /*
244 * Default variable settings
245 */
246 adjust = RIGHT;
247 alternate_form = print_sign = print_blank = NO;
248 pad_char = ' ';
249 prefix_char = NUL;
250 free_zcopy = 0;
251
252 fmt++;
253
254 /*
255 * Try to avoid checking for flags, width or precision
256 */
257 if (isascii((int)*fmt) && !islower((int)*fmt)) {
258 /*
259 * Recognize flags: -, #, BLANK, +
260 */
261 for (;; fmt++) {
262 if (*fmt == '-')
263 adjust = LEFT;
264 else if (*fmt == '+')
265 print_sign = YES;
266 else if (*fmt == '#')
267 alternate_form = YES;
268 else if (*fmt == ' ')
269 print_blank = YES;
270 else if (*fmt == '0')
271 pad_char = '0';
272 else
273 break;
274 }
275
276 /*
277 * Check if a width was specified
278 */
279 if (isdigit((int)*fmt)) {
280 STR_TO_DEC(fmt, min_width);
281 adjust_width = YES;
282 } else if (*fmt == '*') {
283 min_width = va_arg(ap, int);
284 fmt++;
285 adjust_width = YES;
286 if (min_width < 0) {
287 adjust = LEFT;
288 min_width = -min_width;
289 }
290 } else
291 adjust_width = NO;
292
293 /*
294 * Check if a precision was specified
295 */
296 if (*fmt == '.') {
297 adjust_precision = YES;
298 fmt++;
299 if (isdigit((int)*fmt)) {
300 STR_TO_DEC(fmt, precision);
301 } else if (*fmt == '*') {
302 precision = va_arg(ap, int);
303 fmt++;
304 if (precision < -1)
305 precision = -1;
306 } else
307 precision = 0;
308
309 if (precision > FORMAT_CONV_MAX_PRECISION) {
310 precision = FORMAT_CONV_MAX_PRECISION;
311 }
312 } else
313 adjust_precision = NO;
314 } else
315 adjust_precision = adjust_width = NO;
316
317 /*
318 * Modifier check
319 */
320 switch (*fmt) {
321 case 'L':
322 fmt++;
323 modifier = LM_LONG_DOUBLE;
324 break;
325 case 'I':
326 fmt++;
327 #if SIZEOF_LONG_LONG
328 if (*fmt == '6' && *(fmt+1) == '4') {
329 fmt += 2;
330 modifier = LM_LONG_LONG;
331 } else
332 #endif
333 if (*fmt == '3' && *(fmt+1) == '2') {
334 fmt += 2;
335 modifier = LM_LONG;
336 } else {
337 #ifdef _WIN64
338 modifier = LM_LONG_LONG;
339 #else
340 modifier = LM_LONG;
341 #endif
342 }
343 break;
344 case 'l':
345 fmt++;
346 #if SIZEOF_LONG_LONG
347 if (*fmt == 'l') {
348 fmt++;
349 modifier = LM_LONG_LONG;
350 } else
351 #endif
352 modifier = LM_LONG;
353 break;
354 case 'z':
355 fmt++;
356 modifier = LM_SIZE_T;
357 break;
358 case 'j':
359 fmt++;
360 #if SIZEOF_INTMAX_T
361 modifier = LM_INTMAX_T;
362 #else
363 modifier = LM_SIZE_T;
364 #endif
365 break;
366 case 't':
367 fmt++;
368 #if SIZEOF_PTRDIFF_T
369 modifier = LM_PTRDIFF_T;
370 #else
371 modifier = LM_SIZE_T;
372 #endif
373 break;
374 case 'p': {
375 char __next = *(fmt+1);
376 if ('d' == __next || 'u' == __next || 'x' == __next || 'o' == __next) {
377 fmt++;
378 modifier = LM_PHP_INT_T;
379 } else {
380 modifier = LM_STD;
381 }
382 }
383 break;
384 case 'h':
385 fmt++;
386 if (*fmt == 'h') {
387 fmt++;
388 }
389 /* these are promoted to int, so no break */
390 default:
391 modifier = LM_STD;
392 break;
393 }
394
395 /*
396 * Argument extraction and printing.
397 * First we determine the argument type.
398 * Then, we convert the argument to a string.
399 * On exit from the switch, s points to the string that
400 * must be printed, s_len has the length of the string
401 * The precision requirements, if any, are reflected in s_len.
402 *
403 * NOTE: pad_char may be set to '0' because of the 0 flag.
404 * It is reset to ' ' by non-numeric formats
405 */
406 switch (*fmt) {
407 case 'Z': {
408 zvp = (zval*) va_arg(ap, zval*);
409 free_zcopy = zend_make_printable_zval(zvp, &zcopy);
410 if (free_zcopy) {
411 zvp = &zcopy;
412 }
413 s_len = Z_STRLEN_P(zvp);
414 s = Z_STRVAL_P(zvp);
415 if (adjust_precision && (size_t)precision < s_len) {
416 s_len = precision;
417 }
418 break;
419 }
420 case 'u':
421 switch(modifier) {
422 default:
423 i_num = (wide_int) va_arg(ap, unsigned int);
424 break;
425 case LM_LONG_DOUBLE:
426 goto fmt_error;
427 case LM_LONG:
428 i_num = (wide_int) va_arg(ap, unsigned long int);
429 break;
430 case LM_SIZE_T:
431 i_num = (wide_int) va_arg(ap, size_t);
432 break;
433 #if SIZEOF_LONG_LONG
434 case LM_LONG_LONG:
435 i_num = (wide_int) va_arg(ap, u_wide_int);
436 break;
437 #endif
438 #if SIZEOF_INTMAX_T
439 case LM_INTMAX_T:
440 i_num = (wide_int) va_arg(ap, uintmax_t);
441 break;
442 #endif
443 #if SIZEOF_PTRDIFF_T
444 case LM_PTRDIFF_T:
445 i_num = (wide_int) va_arg(ap, ptrdiff_t);
446 break;
447 #endif
448 case LM_PHP_INT_T:
449 i_num = (wide_int) va_arg(ap, zend_ulong);
450 break;
451 }
452 /*
453 * The rest also applies to other integer formats, so fall
454 * into that case.
455 */
456 case 'd':
457 case 'i':
458 /*
459 * Get the arg if we haven't already.
460 */
461 if ((*fmt) != 'u') {
462 switch(modifier) {
463 default:
464 i_num = (wide_int) va_arg(ap, int);
465 break;
466 case LM_LONG_DOUBLE:
467 goto fmt_error;
468 case LM_LONG:
469 i_num = (wide_int) va_arg(ap, long int);
470 break;
471 case LM_SIZE_T:
472 #if SIZEOF_SSIZE_T
473 i_num = (wide_int) va_arg(ap, ssize_t);
474 #else
475 i_num = (wide_int) va_arg(ap, size_t);
476 #endif
477 break;
478 #if SIZEOF_LONG_LONG
479 case LM_LONG_LONG:
480 i_num = (wide_int) va_arg(ap, wide_int);
481 break;
482 #endif
483 #if SIZEOF_INTMAX_T
484 case LM_INTMAX_T:
485 i_num = (wide_int) va_arg(ap, intmax_t);
486 break;
487 #endif
488 #if SIZEOF_PTRDIFF_T
489 case LM_PTRDIFF_T:
490 i_num = (wide_int) va_arg(ap, ptrdiff_t);
491 break;
492 #endif
493 case LM_PHP_INT_T:
494 i_num = (wide_int) va_arg(ap, zend_long);
495 break;
496 }
497 }
498 s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
499 &num_buf[NUM_BUF_SIZE], &s_len);
500 FIX_PRECISION(adjust_precision, precision, s, s_len);
501
502 if (*fmt != 'u') {
503 if (is_negative)
504 prefix_char = '-';
505 else if (print_sign)
506 prefix_char = '+';
507 else if (print_blank)
508 prefix_char = ' ';
509 }
510 break;
511
512
513 case 'o':
514 switch(modifier) {
515 default:
516 ui_num = (u_wide_int) va_arg(ap, unsigned int);
517 break;
518 case LM_LONG_DOUBLE:
519 goto fmt_error;
520 case LM_LONG:
521 ui_num = (u_wide_int) va_arg(ap, unsigned long int);
522 break;
523 case LM_SIZE_T:
524 ui_num = (u_wide_int) va_arg(ap, size_t);
525 break;
526 #if SIZEOF_LONG_LONG
527 case LM_LONG_LONG:
528 ui_num = (u_wide_int) va_arg(ap, u_wide_int);
529 break;
530 #endif
531 #if SIZEOF_INTMAX_T
532 case LM_INTMAX_T:
533 ui_num = (u_wide_int) va_arg(ap, uintmax_t);
534 break;
535 #endif
536 #if SIZEOF_PTRDIFF_T
537 case LM_PTRDIFF_T:
538 ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
539 break;
540 #endif
541 case LM_PHP_INT_T:
542 ui_num = (u_wide_int) va_arg(ap, zend_ulong);
543 break;
544 }
545 s = ap_php_conv_p2(ui_num, 3, *fmt,
546 &num_buf[NUM_BUF_SIZE], &s_len);
547 FIX_PRECISION(adjust_precision, precision, s, s_len);
548 if (alternate_form && *s != '0') {
549 *--s = '0';
550 s_len++;
551 }
552 break;
553
554
555 case 'x':
556 case 'X':
557 switch(modifier) {
558 default:
559 ui_num = (u_wide_int) va_arg(ap, unsigned int);
560 break;
561 case LM_LONG_DOUBLE:
562 goto fmt_error;
563 case LM_LONG:
564 ui_num = (u_wide_int) va_arg(ap, unsigned long int);
565 break;
566 case LM_SIZE_T:
567 ui_num = (u_wide_int) va_arg(ap, size_t);
568 break;
569 #if SIZEOF_LONG_LONG
570 case LM_LONG_LONG:
571 ui_num = (u_wide_int) va_arg(ap, u_wide_int);
572 break;
573 #endif
574 #if SIZEOF_INTMAX_T
575 case LM_INTMAX_T:
576 ui_num = (u_wide_int) va_arg(ap, uintmax_t);
577 break;
578 #endif
579 #if SIZEOF_PTRDIFF_T
580 case LM_PTRDIFF_T:
581 ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
582 break;
583 #endif
584 case LM_PHP_INT_T:
585 ui_num = (u_wide_int) va_arg(ap, zend_ulong);
586 break;
587 }
588 s = ap_php_conv_p2(ui_num, 4, *fmt,
589 &num_buf[NUM_BUF_SIZE], &s_len);
590 FIX_PRECISION(adjust_precision, precision, s, s_len);
591 if (alternate_form && ui_num != 0) {
592 *--s = *fmt; /* 'x' or 'X' */
593 *--s = '0';
594 s_len += 2;
595 }
596 break;
597
598
599 case 's':
600 case 'v':
601 s = va_arg(ap, char *);
602 if (s != NULL) {
603 if (!adjust_precision) {
604 s_len = strlen(s);
605 } else {
606 s_len = strnlen(s, precision);
607 }
608 } else {
609 s = S_NULL;
610 s_len = S_NULL_LEN;
611 }
612 pad_char = ' ';
613 break;
614
615
616 case 'f':
617 case 'F':
618 case 'e':
619 case 'E':
620 switch(modifier) {
621 case LM_LONG_DOUBLE:
622 fp_num = (double) va_arg(ap, long double);
623 break;
624 case LM_STD:
625 fp_num = va_arg(ap, double);
626 break;
627 default:
628 goto fmt_error;
629 }
630
631 if (zend_isnan(fp_num)) {
632 s = "nan";
633 s_len = 3;
634 } else if (zend_isinf(fp_num)) {
635 s = "inf";
636 s_len = 3;
637 } else {
638 #ifdef HAVE_LOCALE_H
639 #ifdef ZTS
640 localeconv_r(&lconv);
641 #else
642 if (!lconv) {
643 lconv = localeconv();
644 }
645 #endif
646 #endif
647 s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
648 (adjust_precision == NO) ? FLOAT_DIGITS : precision,
649 (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
650 &is_negative, &num_buf[1], &s_len);
651 if (is_negative)
652 prefix_char = '-';
653 else if (print_sign)
654 prefix_char = '+';
655 else if (print_blank)
656 prefix_char = ' ';
657 }
658 break;
659
660
661 case 'g':
662 case 'k':
663 case 'G':
664 case 'H':
665 switch(modifier) {
666 case LM_LONG_DOUBLE:
667 fp_num = (double) va_arg(ap, long double);
668 break;
669 case LM_STD:
670 fp_num = va_arg(ap, double);
671 break;
672 default:
673 goto fmt_error;
674 }
675
676 if (zend_isnan(fp_num)) {
677 s = "NAN";
678 s_len = 3;
679 break;
680 } else if (zend_isinf(fp_num)) {
681 if (fp_num > 0) {
682 s = "INF";
683 s_len = 3;
684 } else {
685 s = "-INF";
686 s_len = 4;
687 }
688 break;
689 }
690
691 if (adjust_precision == NO)
692 precision = FLOAT_DIGITS;
693 else if (precision == 0)
694 precision = 1;
695 /*
696 * * We use &num_buf[ 1 ], so that we have room for the sign
697 */
698 #ifdef HAVE_LOCALE_H
699 #ifdef ZTS
700 localeconv_r(&lconv);
701 #else
702 if (!lconv) {
703 lconv = localeconv();
704 }
705 #endif
706 #endif
707 s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
708 if (*s == '-')
709 prefix_char = *s++;
710 else if (print_sign)
711 prefix_char = '+';
712 else if (print_blank)
713 prefix_char = ' ';
714
715 s_len = strlen(s);
716
717 if (alternate_form && (strchr(s, '.')) == NULL)
718 s[s_len++] = '.';
719 break;
720
721
722 case 'c':
723 char_buf[0] = (char) (va_arg(ap, int));
724 s = &char_buf[0];
725 s_len = 1;
726 pad_char = ' ';
727 break;
728
729
730 case '%':
731 char_buf[0] = '%';
732 s = &char_buf[0];
733 s_len = 1;
734 pad_char = ' ';
735 break;
736
737
738 case 'n':
739 *(va_arg(ap, int *)) = is_char? (int)((smart_string *)xbuf)->len : (int)ZSTR_LEN(((smart_str *)xbuf)->s);
740 goto skip_output;
741
742 /*
743 * Always extract the argument as a "char *" pointer. We
744 * should be using "void *" but there are still machines
745 * that don't understand it.
746 * If the pointer size is equal to the size of an unsigned
747 * integer we convert the pointer to a hex number, otherwise
748 * we print "%p" to indicate that we don't handle "%p".
749 */
750 case 'p':
751 if (sizeof(char *) <= sizeof(u_wide_int)) {
752 ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
753 s = ap_php_conv_p2(ui_num, 4, 'x',
754 &num_buf[NUM_BUF_SIZE], &s_len);
755 if (ui_num != 0) {
756 *--s = 'x';
757 *--s = '0';
758 s_len += 2;
759 }
760 } else {
761 s = "%p";
762 s_len = 2;
763 }
764 pad_char = ' ';
765 break;
766
767
768 case NUL:
769 /*
770 * The last character of the format string was %.
771 * We ignore it.
772 */
773 continue;
774
775
776 fmt_error:
777 php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
778 /*
779 * The default case is for unrecognized %'s.
780 * We print %<char> to help the user identify what
781 * option is not understood.
782 * This is also useful in case the user wants to pass
783 * the output of format_converter to another function
784 * that understands some other %<char> (like syslog).
785 * Note that we can't point s inside fmt because the
786 * unknown <char> could be preceded by width etc.
787 */
788 default:
789 char_buf[0] = '%';
790 char_buf[1] = *fmt;
791 s = char_buf;
792 s_len = 2;
793 pad_char = ' ';
794 break;
795 }
796
797 if (prefix_char != NUL) {
798 *--s = prefix_char;
799 s_len++;
800 }
801 if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
802 if (pad_char == '0' && prefix_char != NUL) {
803 INS_CHAR(xbuf, *s, is_char);
804 s++;
805 s_len--;
806 min_width--;
807 }
808 PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
809 }
810 /*
811 * Print the string s.
812 */
813 INS_STRING(xbuf, s, s_len, is_char);
814
815 if (adjust_width && adjust == LEFT && (size_t)min_width > s_len) {
816 PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
817 }
818
819 if (free_zcopy) {
820 zval_dtor(&zcopy);
821 }
822 }
823 skip_output:
824 fmt++;
825 }
826 return;
827 }
828 /* }}} */
829
php_printf_to_smart_string(smart_string * buf,const char * format,va_list ap)830 PHPAPI void php_printf_to_smart_string(smart_string *buf, const char *format, va_list ap) /* {{{ */
831 {
832 xbuf_format_converter(buf, 1, format, ap);
833 }
834 /* }}} */
835
php_printf_to_smart_str(smart_str * buf,const char * format,va_list ap)836 PHPAPI void php_printf_to_smart_str(smart_str *buf, const char *format, va_list ap) /* {{{ */
837 {
838 xbuf_format_converter(buf, 0, format, ap);
839 }
840 /* }}} */
841
842 /*
843 * Local variables:
844 * tab-width: 4
845 * c-basic-offset: 4
846 * End:
847 * vim600: sw=4 ts=4 fdm=marker
848 * vim<600: sw=4 ts=4
849 */
850