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