xref: /PHP-7.3/main/spprintf.c (revision 8d3f8ca1)
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