xref: /PHP-8.0/main/spprintf.c (revision f91f7260)
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    | http://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 FALSE           0
99 #define TRUE            1
100 #define NUL             '\0'
101 #define INT_NULL        ((int *)0)
102 
103 #define S_NULL          "(null)"
104 #define S_NULL_LEN      6
105 
106 #define FLOAT_DIGITS    6
107 #define EXPONENT_LENGTH 10
108 
109 #include "zend_smart_str.h"
110 #include "zend_smart_string.h"
111 
112 /* {{{ macros */
113 
114 #define INS_CHAR(xbuf, ch, is_char) do { \
115 	if ((is_char)) { \
116 		smart_string_appendc((smart_string *)(xbuf), (ch)); \
117 	} else { \
118 		smart_str_appendc((smart_str *)(xbuf), (ch)); \
119 	} \
120 } while (0);
121 
122 #define INS_STRING(xbuf, str, len, is_char) do { \
123 	if ((is_char)) { \
124 		smart_string_appendl((smart_string *)(xbuf), (str), (len)); \
125 	} else { \
126 		smart_str_appendl((smart_str *)(xbuf), (str), (len)); \
127 	} \
128 } while (0);
129 
130 #define PAD_CHAR(xbuf, ch, count, is_char) do { \
131 	if ((is_char)) { \
132 		smart_string_alloc(((smart_string *)(xbuf)), (count), 0); \
133 		memset(((smart_string *)(xbuf))->c + ((smart_string *)(xbuf))->len, (ch), (count)); \
134 		((smart_string *)(xbuf))->len += (count); \
135 	} else { \
136 		smart_str_alloc(((smart_str *)(xbuf)), (count), 0); \
137 		memset(ZSTR_VAL(((smart_str *)(xbuf))->s) + ZSTR_LEN(((smart_str *)(xbuf))->s), (ch), (count)); \
138 		ZSTR_LEN(((smart_str *)(xbuf))->s) += (count); \
139 	} \
140 } while (0);
141 
142 /*
143  * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
144  * which can be at most max length of double
145  */
146 #define NUM_BUF_SIZE PHP_DOUBLE_MAX_LENGTH
147 
148 #define NUM(c) (c - '0')
149 
150 #define STR_TO_DEC(str, num) do {			\
151 	num = NUM(*str++);                  	\
152 	while (isdigit((int)*str)) {        	\
153 		num *= 10;                      	\
154 		num += NUM(*str++);             	\
155 		if (num >= INT_MAX / 10) {			\
156 			while (isdigit((int)*str++));	\
157 			break;							\
158 		}									\
159     }										\
160 } while (0)
161 
162 /*
163  * This macro does zero padding so that the precision
164  * requirement is satisfied. The padding is done by
165  * adding '0's to the left of the string that is going
166  * to be printed.
167  */
168 #define FIX_PRECISION(adjust, precision, s, s_len) do {	\
169     if (adjust)					                    	\
170 		while (s_len < (size_t)precision) {				\
171 			*--s = '0';                             	\
172 			s_len++;                                	\
173 		}												\
174 } while (0)
175 
176 /* }}} */
177 
178 #if !HAVE_STRNLEN
strnlen(const char * s,size_t maxlen)179 static size_t strnlen(const char *s, size_t maxlen) {
180 	char *r = memchr(s, '\0', maxlen);
181 	return r ? r-s : maxlen;
182 }
183 #endif
184 
185 /*
186  * Do format conversion placing the output in buffer
187  */
xbuf_format_converter(void * xbuf,zend_bool is_char,const char * fmt,va_list ap)188 static void xbuf_format_converter(void *xbuf, zend_bool is_char, const char *fmt, va_list ap) /* {{{ */
189 {
190 	char *s = NULL;
191 	size_t s_len;
192 	int free_zcopy;
193 	zval *zvp, zcopy;
194 
195 	int min_width = 0;
196 	int precision = 0;
197 	enum {
198 		LEFT, RIGHT
199 	} adjust;
200 	char pad_char;
201 	char prefix_char;
202 
203 	double fp_num;
204 	wide_int i_num = (wide_int) 0;
205 	u_wide_int ui_num = (u_wide_int) 0;
206 
207 	char num_buf[NUM_BUF_SIZE];
208 	char char_buf[2];			/* for printing %% and %<unknown> */
209 
210 #ifdef ZTS
211 	struct lconv lconv;
212 #else
213 	struct lconv *lconv = NULL;
214 #endif
215 
216 	/*
217 	 * Flag variables
218 	 */
219 	length_modifier_e modifier;
220 	boolean_e alternate_form;
221 	boolean_e print_sign;
222 	boolean_e print_blank;
223 	boolean_e adjust_precision;
224 	boolean_e adjust_width;
225 	bool_int is_negative;
226 
227 	while (*fmt) {
228 		if (*fmt != '%') {
229 			INS_CHAR(xbuf, *fmt, is_char);
230 		} else {
231 			/*
232 			 * Default variable settings
233 			 */
234 			adjust = RIGHT;
235 			alternate_form = print_sign = print_blank = NO;
236 			pad_char = ' ';
237 			prefix_char = NUL;
238 			free_zcopy = 0;
239 
240 			fmt++;
241 
242 			/*
243 			 * Try to avoid checking for flags, width or precision
244 			 */
245 			if (isascii((int)*fmt) && !islower((int)*fmt)) {
246 				/*
247 				 * Recognize flags: -, #, BLANK, +
248 				 */
249 				for (;; fmt++) {
250 					if (*fmt == '-')
251 						adjust = LEFT;
252 					else if (*fmt == '+')
253 						print_sign = YES;
254 					else if (*fmt == '#')
255 						alternate_form = YES;
256 					else if (*fmt == ' ')
257 						print_blank = YES;
258 					else if (*fmt == '0')
259 						pad_char = '0';
260 					else
261 						break;
262 				}
263 
264 				/*
265 				 * Check if a width was specified
266 				 */
267 				if (isdigit((int)*fmt)) {
268 					STR_TO_DEC(fmt, min_width);
269 					adjust_width = YES;
270 				} else if (*fmt == '*') {
271 					min_width = va_arg(ap, int);
272 					fmt++;
273 					adjust_width = YES;
274 					if (min_width < 0) {
275 						adjust = LEFT;
276 						min_width = -min_width;
277 					}
278 				} else
279 					adjust_width = NO;
280 
281 				/*
282 				 * Check if a precision was specified
283 				 */
284 				if (*fmt == '.') {
285 					adjust_precision = YES;
286 					fmt++;
287 					if (isdigit((int)*fmt)) {
288 						STR_TO_DEC(fmt, precision);
289 					} else if (*fmt == '*') {
290 						precision = va_arg(ap, int);
291 						fmt++;
292 						if (precision < -1)
293 							precision = -1;
294 					} else
295 						precision = 0;
296 
297 					if (precision > FORMAT_CONV_MAX_PRECISION) {
298 						precision = FORMAT_CONV_MAX_PRECISION;
299 					}
300 				} else
301 					adjust_precision = NO;
302 			} else
303 				adjust_precision = adjust_width = NO;
304 
305 			/*
306 			 * Modifier check
307 			 */
308 			switch (*fmt) {
309 				case 'L':
310 					fmt++;
311 					modifier = LM_LONG_DOUBLE;
312 					break;
313 				case 'l':
314 					fmt++;
315 #if SIZEOF_LONG_LONG
316 					if (*fmt == 'l') {
317 						fmt++;
318 						modifier = LM_LONG_LONG;
319 					} else
320 #endif
321 						modifier = LM_LONG;
322 					break;
323 				case 'z':
324 					fmt++;
325 					modifier = LM_SIZE_T;
326 					break;
327 				case 'j':
328 					fmt++;
329 #if SIZEOF_INTMAX_T
330 					modifier = LM_INTMAX_T;
331 #else
332 					modifier = LM_SIZE_T;
333 #endif
334 					break;
335 				case 't':
336 					fmt++;
337 #if SIZEOF_PTRDIFF_T
338 					modifier = LM_PTRDIFF_T;
339 #else
340 					modifier = LM_SIZE_T;
341 #endif
342 					break;
343 				case 'p': {
344 						char __next = *(fmt+1);
345 						if ('d' == __next || 'u' == __next || 'x' == __next || 'o' == __next) {
346 							fmt++;
347 							modifier = LM_PHP_INT_T;
348 						} else {
349 							modifier = LM_STD;
350 						}
351 					}
352 					break;
353 				case 'h':
354 					fmt++;
355 					if (*fmt == 'h') {
356 						fmt++;
357 					}
358 					/* these are promoted to int, so no break */
359 				default:
360 					modifier = LM_STD;
361 					break;
362 			}
363 
364 			/*
365 			 * Argument extraction and printing.
366 			 * First we determine the argument type.
367 			 * Then, we convert the argument to a string.
368 			 * On exit from the switch, s points to the string that
369 			 * must be printed, s_len has the length of the string
370 			 * The precision requirements, if any, are reflected in s_len.
371 			 *
372 			 * NOTE: pad_char may be set to '0' because of the 0 flag.
373 			 *   It is reset to ' ' by non-numeric formats
374 			 */
375 			switch (*fmt) {
376 				case 'Z': {
377 									zvp = (zval*) va_arg(ap, zval*);
378 					free_zcopy = zend_make_printable_zval(zvp, &zcopy);
379 					if (free_zcopy) {
380 						zvp = &zcopy;
381 					}
382 					s_len = Z_STRLEN_P(zvp);
383 					s = Z_STRVAL_P(zvp);
384 					if (adjust_precision && (size_t)precision < s_len) {
385 						s_len = precision;
386 					}
387 					break;
388 				}
389 				case 'u':
390 					switch(modifier) {
391 						default:
392 							i_num = (wide_int) va_arg(ap, unsigned int);
393 							break;
394 						case LM_LONG_DOUBLE:
395 							goto fmt_error;
396 						case LM_LONG:
397 							i_num = (wide_int) va_arg(ap, unsigned long int);
398 							break;
399 						case LM_SIZE_T:
400 							i_num = (wide_int) va_arg(ap, size_t);
401 							break;
402 #if SIZEOF_LONG_LONG
403 						case LM_LONG_LONG:
404 							i_num = (wide_int) va_arg(ap, u_wide_int);
405 							break;
406 #endif
407 #if SIZEOF_INTMAX_T
408 						case LM_INTMAX_T:
409 							i_num = (wide_int) va_arg(ap, uintmax_t);
410 							break;
411 #endif
412 #if SIZEOF_PTRDIFF_T
413 						case LM_PTRDIFF_T:
414 							i_num = (wide_int) va_arg(ap, ptrdiff_t);
415 							break;
416 #endif
417 						case LM_PHP_INT_T:
418 							i_num = (wide_int) va_arg(ap, zend_ulong);
419 							break;
420 					}
421 					/*
422 					 * The rest also applies to other integer formats, so fall
423 					 * into that case.
424 					 */
425 				case 'd':
426 				case 'i':
427 					/*
428 					 * Get the arg if we haven't already.
429 					 */
430 					if ((*fmt) != 'u') {
431 						switch(modifier) {
432 							default:
433 								i_num = (wide_int) va_arg(ap, int);
434 								break;
435 							case LM_LONG_DOUBLE:
436 								goto fmt_error;
437 							case LM_LONG:
438 								i_num = (wide_int) va_arg(ap, long int);
439 								break;
440 							case LM_SIZE_T:
441 #if SIZEOF_SSIZE_T
442 								i_num = (wide_int) va_arg(ap, ssize_t);
443 #else
444 								i_num = (wide_int) va_arg(ap, size_t);
445 #endif
446 								break;
447 #if SIZEOF_LONG_LONG
448 							case LM_LONG_LONG:
449 								i_num = (wide_int) va_arg(ap, wide_int);
450 								break;
451 #endif
452 #if SIZEOF_INTMAX_T
453 							case LM_INTMAX_T:
454 								i_num = (wide_int) va_arg(ap, intmax_t);
455 								break;
456 #endif
457 #if SIZEOF_PTRDIFF_T
458 							case LM_PTRDIFF_T:
459 								i_num = (wide_int) va_arg(ap, ptrdiff_t);
460 								break;
461 #endif
462 							case LM_PHP_INT_T:
463 								i_num = (wide_int) va_arg(ap, zend_long);
464 								break;
465 						}
466 					}
467 					s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
468 								&num_buf[NUM_BUF_SIZE], &s_len);
469 					FIX_PRECISION(adjust_precision, precision, s, s_len);
470 
471 					if (*fmt != 'u') {
472 						if (is_negative)
473 							prefix_char = '-';
474 						else if (print_sign)
475 							prefix_char = '+';
476 						else if (print_blank)
477 							prefix_char = ' ';
478 					}
479 					break;
480 
481 
482 				case 'o':
483 					switch(modifier) {
484 						default:
485 							ui_num = (u_wide_int) va_arg(ap, unsigned int);
486 							break;
487 						case LM_LONG_DOUBLE:
488 							goto fmt_error;
489 						case LM_LONG:
490 							ui_num = (u_wide_int) va_arg(ap, unsigned long int);
491 							break;
492 						case LM_SIZE_T:
493 							ui_num = (u_wide_int) va_arg(ap, size_t);
494 							break;
495 #if SIZEOF_LONG_LONG
496 						case LM_LONG_LONG:
497 							ui_num = (u_wide_int) va_arg(ap, u_wide_int);
498 							break;
499 #endif
500 #if SIZEOF_INTMAX_T
501 						case LM_INTMAX_T:
502 							ui_num = (u_wide_int) va_arg(ap, uintmax_t);
503 							break;
504 #endif
505 #if SIZEOF_PTRDIFF_T
506 						case LM_PTRDIFF_T:
507 							ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
508 							break;
509 #endif
510 						case LM_PHP_INT_T:
511 							ui_num = (u_wide_int) va_arg(ap, zend_ulong);
512 							break;
513 					}
514 					s = ap_php_conv_p2(ui_num, 3, *fmt,
515 								&num_buf[NUM_BUF_SIZE], &s_len);
516 					FIX_PRECISION(adjust_precision, precision, s, s_len);
517 					if (alternate_form && *s != '0') {
518 						*--s = '0';
519 						s_len++;
520 					}
521 					break;
522 
523 
524 				case 'x':
525 				case 'X':
526 					switch(modifier) {
527 						default:
528 							ui_num = (u_wide_int) va_arg(ap, unsigned int);
529 							break;
530 						case LM_LONG_DOUBLE:
531 							goto fmt_error;
532 						case LM_LONG:
533 							ui_num = (u_wide_int) va_arg(ap, unsigned long int);
534 							break;
535 						case LM_SIZE_T:
536 							ui_num = (u_wide_int) va_arg(ap, size_t);
537 							break;
538 #if SIZEOF_LONG_LONG
539 						case LM_LONG_LONG:
540 							ui_num = (u_wide_int) va_arg(ap, u_wide_int);
541 							break;
542 #endif
543 #if SIZEOF_INTMAX_T
544 						case LM_INTMAX_T:
545 							ui_num = (u_wide_int) va_arg(ap, uintmax_t);
546 							break;
547 #endif
548 #if SIZEOF_PTRDIFF_T
549 						case LM_PTRDIFF_T:
550 							ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
551 							break;
552 #endif
553 						case LM_PHP_INT_T:
554 							ui_num = (u_wide_int) va_arg(ap, zend_ulong);
555 							break;
556 					}
557 					s = ap_php_conv_p2(ui_num, 4, *fmt,
558 								&num_buf[NUM_BUF_SIZE], &s_len);
559 					FIX_PRECISION(adjust_precision, precision, s, s_len);
560 					if (alternate_form && ui_num != 0) {
561 						*--s = *fmt;	/* 'x' or 'X' */
562 						*--s = '0';
563 						s_len += 2;
564 					}
565 					break;
566 
567 
568 				case 's':
569 					s = va_arg(ap, char *);
570 					if (s != NULL) {
571 						if (!adjust_precision) {
572 							s_len = strlen(s);
573 						} else {
574 							s_len = strnlen(s, precision);
575 						}
576 					} else {
577 						s = S_NULL;
578 						s_len = S_NULL_LEN;
579 					}
580 					pad_char = ' ';
581 					break;
582 
583 
584 				case 'f':
585 				case 'F':
586 				case 'e':
587 				case 'E':
588 					switch(modifier) {
589 						case LM_LONG_DOUBLE:
590 							fp_num = (double) va_arg(ap, long double);
591 							break;
592 						case LM_STD:
593 							fp_num = va_arg(ap, double);
594 							break;
595 						default:
596 							goto fmt_error;
597 					}
598 
599 					if (zend_isnan(fp_num)) {
600 						s = "nan";
601 						s_len = 3;
602 					} else if (zend_isinf(fp_num)) {
603 						s = "inf";
604 						s_len = 3;
605 					} else {
606 #ifdef ZTS
607 						localeconv_r(&lconv);
608 #else
609 						if (!lconv) {
610 							lconv = localeconv();
611 						}
612 #endif
613 						s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
614 						 (adjust_precision == NO) ? FLOAT_DIGITS : precision,
615 						 (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
616 									&is_negative, &num_buf[1], &s_len);
617 						if (is_negative)
618 							prefix_char = '-';
619 						else if (print_sign)
620 							prefix_char = '+';
621 						else if (print_blank)
622 							prefix_char = ' ';
623 					}
624 					break;
625 
626 
627 				case 'g':
628 				case 'k':
629 				case 'G':
630 				case 'H':
631 					switch(modifier) {
632 						case LM_LONG_DOUBLE:
633 							fp_num = (double) va_arg(ap, long double);
634 							break;
635 						case LM_STD:
636 							fp_num = va_arg(ap, double);
637 							break;
638 						default:
639 							goto fmt_error;
640 					}
641 
642 					if (zend_isnan(fp_num)) {
643  						s = "NAN";
644  						s_len = 3;
645  						break;
646  					} else if (zend_isinf(fp_num)) {
647  						if (fp_num > 0) {
648  							s = "INF";
649  							s_len = 3;
650  						} else {
651  							s = "-INF";
652  							s_len = 4;
653  						}
654  						break;
655  					}
656 
657 					if (adjust_precision == NO)
658 						precision = FLOAT_DIGITS;
659 					else if (precision == 0)
660 						precision = 1;
661 					/*
662 					 * * We use &num_buf[ 1 ], so that we have room for the sign
663 					 */
664 #ifdef ZTS
665 					localeconv_r(&lconv);
666 #else
667 					if (!lconv) {
668 						lconv = localeconv();
669 					}
670 #endif
671 					s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
672 					if (*s == '-')
673 						prefix_char = *s++;
674 					else if (print_sign)
675 						prefix_char = '+';
676 					else if (print_blank)
677 						prefix_char = ' ';
678 
679 					s_len = strlen(s);
680 
681 					if (alternate_form && (strchr(s, '.')) == NULL)
682 						s[s_len++] = '.';
683 					break;
684 
685 
686 				case 'c':
687 					char_buf[0] = (char) (va_arg(ap, int));
688 					s = &char_buf[0];
689 					s_len = 1;
690 					pad_char = ' ';
691 					break;
692 
693 
694 				case '%':
695 					char_buf[0] = '%';
696 					s = &char_buf[0];
697 					s_len = 1;
698 					pad_char = ' ';
699 					break;
700 
701 
702 				case 'n':
703 					*(va_arg(ap, int *)) = is_char? (int)((smart_string *)xbuf)->len : (int)ZSTR_LEN(((smart_str *)xbuf)->s);
704 					goto skip_output;
705 
706 					/*
707 					 * Always extract the argument as a "char *" pointer. We
708 					 * should be using "void *" but there are still machines
709 					 * that don't understand it.
710 					 * If the pointer size is equal to the size of an unsigned
711 					 * integer we convert the pointer to a hex number, otherwise
712 					 * we print "%p" to indicate that we don't handle "%p".
713 					 */
714 				case 'p':
715 					if (sizeof(char *) <= sizeof(u_wide_int)) {
716 						ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
717 						s = ap_php_conv_p2(ui_num, 4, 'x',
718 								&num_buf[NUM_BUF_SIZE], &s_len);
719 						if (ui_num != 0) {
720 							*--s = 'x';
721 							*--s = '0';
722 							s_len += 2;
723 						}
724 					} else {
725 						s = "%p";
726 						s_len = 2;
727 					}
728 					pad_char = ' ';
729 					break;
730 
731 
732 				case NUL:
733 					/*
734 					 * The last character of the format string was %.
735 					 * We ignore it.
736 					 */
737 					continue;
738 
739 
740 fmt_error:
741 				php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
742 					/*
743 					 * The default case is for unrecognized %'s.
744 					 * We print %<char> to help the user identify what
745 					 * option is not understood.
746 					 * This is also useful in case the user wants to pass
747 					 * the output of format_converter to another function
748 					 * that understands some other %<char> (like syslog).
749 					 * Note that we can't point s inside fmt because the
750 					 * unknown <char> could be preceded by width etc.
751 					 */
752 				default:
753 					char_buf[0] = '%';
754 					char_buf[1] = *fmt;
755 					s = char_buf;
756 					s_len = 2;
757 					pad_char = ' ';
758 					break;
759 			}
760 
761 			if (prefix_char != NUL) {
762 				*--s = prefix_char;
763 				s_len++;
764 			}
765 			if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
766 				if (pad_char == '0' && prefix_char != NUL) {
767 					INS_CHAR(xbuf, *s, is_char);
768 					s++;
769 					s_len--;
770 					min_width--;
771 				}
772 				PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
773 			}
774 			/*
775 			 * Print the string s.
776 			 */
777 			INS_STRING(xbuf, s, s_len, is_char);
778 
779 			if (adjust_width && adjust == LEFT && (size_t)min_width > s_len) {
780 				PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char);
781 			}
782 
783 			if (free_zcopy) {
784 				zval_ptr_dtor_str(&zcopy);
785 			}
786 		}
787 skip_output:
788 		fmt++;
789 	}
790 	return;
791 }
792 /* }}} */
793 
php_printf_to_smart_string(smart_string * buf,const char * format,va_list ap)794 PHPAPI void php_printf_to_smart_string(smart_string *buf, const char *format, va_list ap) /* {{{ */
795 {
796 	xbuf_format_converter(buf, 1, format, ap);
797 }
798 /* }}} */
799 
php_printf_to_smart_str(smart_str * buf,const char * format,va_list ap)800 PHPAPI void php_printf_to_smart_str(smart_str *buf, const char *format, va_list ap) /* {{{ */
801 {
802 	xbuf_format_converter(buf, 0, format, ap);
803 }
804 /* }}} */
805