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