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