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