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