xref: /PHP-7.4/main/snprintf.c (revision be36d922)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 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:                                                              |
16   +----------------------------------------------------------------------+
17 */
18 
19 #define _GNU_SOURCE
20 #include "php.h"
21 
22 #include <zend_strtod.h>
23 
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <math.h>
32 
33 #ifdef HAVE_INTTYPES_H
34 #include <inttypes.h>
35 #endif
36 
37 #include <locale.h>
38 #ifdef ZTS
39 #include "ext/standard/php_string.h"
40 #define LCONV_DECIMAL_POINT (*lconv.decimal_point)
41 #else
42 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
43 #endif
44 
45 /*
46  * Copyright (c) 2002, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
47  *
48  * Permission to use, copy, modify, and distribute this software for any
49  * purpose with or without fee is hereby granted, provided that the above
50  * copyright notice and this permission notice appear in all copies.
51  *
52  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
53  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
54  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
55  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
56  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
57  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
58  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
59  *
60  * Sponsored in part by the Defense Advanced Research Projects
61  * Agency (DARPA) and Air Force Research Laboratory, Air Force
62  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
63  */
64 
__cvt(double value,int ndigit,int * decpt,int * sign,int fmode,int pad)65 static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) /* {{{ */
66 {
67 	register char *s = NULL;
68 	char *p, *rve, c;
69 	size_t siz;
70 
71 	if (ndigit < 0) {
72 		siz = -ndigit + 1;
73 	} else {
74 		siz = ndigit + 1;
75 	}
76 
77 	/* __dtoa() doesn't allocate space for 0 so we do it by hand */
78 	if (value == 0.0) {
79 		*decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
80 		*sign = 0;
81 		if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
82 			return(NULL);
83 		}
84 		*rve++ = '0';
85 		*rve = '\0';
86 		if (!ndigit) {
87 			return(s);
88 		}
89 	} else {
90 		p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
91 		if (*decpt == 9999) {
92 			/* Infinity or Nan, convert to inf or nan like printf */
93 			*decpt = 0;
94 			c = *p;
95 			zend_freedtoa(p);
96 			return strdup((c == 'I' ? "INF" : "NAN"));
97 		}
98 		/* Make a local copy and adjust rve to be in terms of s */
99 		if (pad && fmode) {
100 			siz += *decpt;
101 		}
102 		if ((s = (char *)malloc(siz+1)) == NULL) {
103 			zend_freedtoa(p);
104 			return(NULL);
105 		}
106 		(void) strlcpy(s, p, siz);
107 		rve = s + (rve - p);
108 		zend_freedtoa(p);
109 	}
110 
111 	/* Add trailing zeros */
112 	if (pad) {
113 		siz -= rve - s;
114 		while (--siz) {
115 			*rve++ = '0';
116 		}
117 		*rve = '\0';
118 	}
119 
120 	return(s);
121 }
122 /* }}} */
123 
php_ecvt(double value,int ndigit,int * decpt,int * sign)124 static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
125 {
126 	return(__cvt(value, ndigit, decpt, sign, 0, 1));
127 }
128 /* }}} */
129 
php_fcvt(double value,int ndigit,int * decpt,int * sign)130 static inline char *php_fcvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
131 {
132     return(__cvt(value, ndigit, decpt, sign, 1, 1));
133 }
134 /* }}} */
135 
php_gcvt(double value,int ndigit,char dec_point,char exponent,char * buf)136 PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) /* {{{ */
137 {
138 	char *digits, *dst, *src;
139 	int i, decpt, sign;
140 	int mode = ndigit >= 0 ? 2 : 0;
141 
142 	if (mode == 0) {
143 		ndigit = 17;
144 	}
145 	digits = zend_dtoa(value, mode, ndigit, &decpt, &sign, NULL);
146 	if (decpt == 9999) {
147 		/*
148 		 * Infinity or NaN, convert to inf or nan with sign.
149 		 * We assume the buffer is at least ndigit long.
150 		 */
151 		snprintf(buf, ndigit + 1, "%s%s", (sign && *digits == 'I') ? "-" : "", *digits == 'I' ? "INF" : "NAN");
152 		zend_freedtoa(digits);
153 		return (buf);
154 	}
155 
156 	dst = buf;
157 	if (sign) {
158 		*dst++ = '-';
159 	}
160 
161 	if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */
162 		/* exponential format (e.g. 1.2345e+13) */
163 		if (--decpt < 0) {
164 			sign = 1;
165 			decpt = -decpt;
166 		} else {
167 			sign = 0;
168 		}
169 		src = digits;
170 		*dst++ = *src++;
171 		*dst++ = dec_point;
172 		if (*src == '\0') {
173 			*dst++ = '0';
174 		} else {
175 			do {
176 				*dst++ = *src++;
177 			} while (*src != '\0');
178 		}
179 		*dst++ = exponent;
180 		if (sign) {
181 			*dst++ = '-';
182 		} else {
183 			*dst++ = '+';
184 		}
185 		if (decpt < 10) {
186 			*dst++ = '0' + decpt;
187 			*dst = '\0';
188 		} else {
189 			/* XXX - optimize */
190 			for (sign = decpt, i = 0; (sign /= 10) != 0; i++);
191 			dst[i + 1] = '\0';
192 			while (decpt != 0) {
193 				dst[i--] = '0' + decpt % 10;
194 				decpt /= 10;
195 			}
196 		}
197 	} else if (decpt < 0) {
198 		/* standard format 0. */
199 		*dst++ = '0';   /* zero before decimal point */
200 		*dst++ = dec_point;
201 		do {
202 			*dst++ = '0';
203 		} while (++decpt < 0);
204 		src = digits;
205 		while (*src != '\0') {
206 			*dst++ = *src++;
207 		}
208 		*dst = '\0';
209 	} else {
210 		/* standard format */
211 		for (i = 0, src = digits; i < decpt; i++) {
212 			if (*src != '\0') {
213 				*dst++ = *src++;
214 			} else {
215 				*dst++ = '0';
216 			}
217 		}
218 		if (*src != '\0') {
219 			if (src == digits) {
220 				*dst++ = '0';   /* zero before decimal point */
221 			}
222 			*dst++ = dec_point;
223 			for (i = decpt; digits[i] != '\0'; i++) {
224                 *dst++ = digits[i];
225             }
226         }
227         *dst = '\0';
228     }
229     zend_freedtoa(digits);
230     return (buf);
231 }
232 /* }}} */
233 
234 /* {{{ Apache license */
235 /* ====================================================================
236  * Copyright (c) 1995-1998 The Apache Group.  All rights reserved.
237  *
238  * Redistribution and use in source and binary forms, with or without
239  * modification, are permitted provided that the following conditions
240  * are met:
241  *
242  * 1. Redistributions of source code must retain the above copyright
243  *    notice, this list of conditions and the following disclaimer.
244  *
245  * 2. Redistributions in binary form must reproduce the above copyright
246  *    notice, this list of conditions and the following disclaimer in
247  *    the documentation and/or other materials provided with the
248  *    distribution.
249  *
250  * 3. All advertising materials mentioning features or use of this
251  *    software must display the following acknowledgment:
252  *    "This product includes software developed by the Apache Group
253  *    for use in the Apache HTTP server project (http://www.apache.org/)."
254  *
255  * 4. The names "Apache Server" and "Apache Group" must not be used to
256  *    endorse or promote products derived from this software without
257  *    prior written permission.
258  *
259  * 5. Redistributions of any form whatsoever must retain the following
260  *    acknowledgment:
261  *    "This product includes software developed by the Apache Group
262  *    for use in the Apache HTTP server project (http://www.apache.org/)."
263  *
264  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
265  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
266  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
267  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
268  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
269  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
270  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
271  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
272  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
273  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
274  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
275  * OF THE POSSIBILITY OF SUCH DAMAGE.
276  * ====================================================================
277  *
278  * This software consists of voluntary contributions made by many
279  * individuals on behalf of the Apache Group and was originally based
280  * on public domain software written at the National Center for
281  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
282  * For more information on the Apache Group and the Apache HTTP server
283  * project, please see <http://www.apache.org/>.
284  *
285  * This code is based on, and used with the permission of, the
286  * SIO stdio-replacement strx_* functions by Panos Tsirigotis
287  * <panos@alumni.cs.colorado.edu> for xinetd.
288  */
289 /* }}} */
290 
291 #define FALSE			0
292 #define TRUE			1
293 #define NUL			'\0'
294 #define INT_NULL		((int *)0)
295 
296 #define S_NULL			"(null)"
297 #define S_NULL_LEN		6
298 
299 #define FLOAT_DIGITS		6
300 #define EXPONENT_LENGTH		10
301 
302 
303 /*
304  * Convert num to its decimal format.
305  * Return value:
306  *   - a pointer to a string containing the number (no sign)
307  *   - len contains the length of the string
308  *   - is_negative is set to TRUE or FALSE depending on the sign
309  *     of the number (always set to FALSE if is_unsigned is TRUE)
310  *
311  * The caller provides a buffer for the string: that is the buf_end argument
312  * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
313  * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
314  */
315 /* char * ap_php_conv_10() {{{ */
ap_php_conv_10(register wide_int num,register bool_int is_unsigned,register bool_int * is_negative,char * buf_end,register size_t * len)316 PHPAPI char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,
317 	   register bool_int * is_negative, char *buf_end, register size_t *len)
318 {
319 	register char *p = buf_end;
320 	register u_wide_int magnitude;
321 
322 	if (is_unsigned) {
323 		magnitude = (u_wide_int) num;
324 		*is_negative = FALSE;
325 	} else {
326 		*is_negative = (num < 0);
327 
328 		/*
329 		 * On a 2's complement machine, negating the most negative integer
330 		 * results in a number that cannot be represented as a signed integer.
331 		 * Here is what we do to obtain the number's magnitude:
332 		 *      a. add 1 to the number
333 		 *      b. negate it (becomes positive)
334 		 *      c. convert it to unsigned
335 		 *      d. add 1
336 		 */
337 		if (*is_negative) {
338 			wide_int t = num + 1;
339 			magnitude = ((u_wide_int) - t) + 1;
340 		} else {
341 			magnitude = (u_wide_int) num;
342 		}
343 	}
344 
345 	/*
346 	 * We use a do-while loop so that we write at least 1 digit
347 	 */
348 	do {
349 		register u_wide_int new_magnitude = magnitude / 10;
350 
351 		*--p = (char)(magnitude - new_magnitude * 10 + '0');
352 		magnitude = new_magnitude;
353 	}
354 	while (magnitude);
355 
356 	*len = buf_end - p;
357 	return (p);
358 }
359 /* }}} */
360 
361 /* If you change this value then also change bug24640.phpt.
362  * Also NDIG must be reasonable smaller than NUM_BUF_SIZE.
363  */
364 #define	NDIG	320
365 
366 
367 /*
368  * Convert a floating point number to a string formats 'f', 'e' or 'E'.
369  * The result is placed in buf, and len denotes the length of the string
370  * The sign is returned in the is_negative argument (and is not placed
371  * in buf).
372  */
373 /* PHPAPI char * php_conv_fp() {{{ */
php_conv_fp(register char format,register double num,boolean_e add_dp,int precision,char dec_point,bool_int * is_negative,char * buf,size_t * len)374 PHPAPI char * php_conv_fp(register char format, register double num,
375 		 boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, size_t *len)
376 {
377 	register char *s = buf;
378 	register char *p, *p_orig;
379 	int decimal_point;
380 
381 	if (precision >= NDIG - 1) {
382 		precision = NDIG - 2;
383 	}
384 
385 	if (format == 'F') {
386 		p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
387 	} else {						/* either e or E format */
388 		p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
389 	}
390 
391 	/*
392 	 * Check for Infinity and NaN
393 	 */
394 	if (isalpha((int)*p)) {
395 		*len = strlen(p);
396 		memcpy(buf, p, *len + 1);
397 		*is_negative = FALSE;
398 		free(p_orig);
399 		return (buf);
400 	}
401 	if (format == 'F') {
402 		if (decimal_point <= 0) {
403 			if (num != 0 || precision > 0) {
404 				*s++ = '0';
405 				if (precision > 0) {
406 					*s++ = dec_point;
407 					while (decimal_point++ < 0) {
408 						*s++ = '0';
409 					}
410 				} else if (add_dp) {
411 					*s++ = dec_point;
412 				}
413 			}
414 		} else {
415 			int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
416 			decimal_point -= addz;
417 			while (decimal_point-- > 0) {
418 				*s++ = *p++;
419 			}
420 			while (addz-- > 0) {
421 				*s++ = '0';
422 			}
423 			if (precision > 0 || add_dp) {
424 				*s++ = dec_point;
425 			}
426 		}
427 	} else {
428 		*s++ = *p++;
429 		if (precision > 0 || add_dp) {
430 			*s++ = '.';
431 		}
432 	}
433 
434 	/*
435 	 * copy the rest of p, the NUL is NOT copied
436 	 */
437 	while (*p) {
438 		*s++ = *p++;
439 	}
440 
441 	if (format != 'F') {
442 		char temp[EXPONENT_LENGTH];		/* for exponent conversion */
443 		size_t t_len;
444 		bool_int exponent_is_negative;
445 
446 		*s++ = format;			/* either e or E */
447 		decimal_point--;
448 		if (decimal_point != 0) {
449 			p = ap_php_conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len);
450 			*s++ = exponent_is_negative ? '-' : '+';
451 
452 			/*
453 			 * Make sure the exponent has at least 2 digits
454 			 */
455 			while (t_len--) {
456 				*s++ = *p++;
457 			}
458 		} else {
459 			*s++ = '+';
460 			*s++ = '0';
461 		}
462 	}
463 	*len = s - buf;
464 	free(p_orig);
465 	return (buf);
466 }
467 /* }}} */
468 
469 /*
470  * Convert num to a base X number where X is a power of 2. nbits determines X.
471  * For example, if nbits is 3, we do base 8 conversion
472  * Return value:
473  *      a pointer to a string containing the number
474  *
475  * The caller provides a buffer for the string: that is the buf_end argument
476  * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
477  * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
478  */
ap_php_conv_p2(register u_wide_int num,register int nbits,char format,char * buf_end,register size_t * len)479 PHPAPI char * ap_php_conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register size_t *len) /* {{{ */
480 {
481 	register int mask = (1 << nbits) - 1;
482 	register char *p = buf_end;
483 	static const char low_digits[] = "0123456789abcdef";
484 	static const char upper_digits[] = "0123456789ABCDEF";
485 	register const char *digits = (format == 'X') ? upper_digits : low_digits;
486 
487 	do {
488 		*--p = digits[num & mask];
489 		num >>= nbits;
490 	}
491 	while (num);
492 
493 	*len = buf_end - p;
494 	return (p);
495 }
496 /* }}} */
497 
498 /*
499  * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
500  *
501  * XXX: this is a magic number; do not decrease it
502  * Emax = 1023
503  * NDIG = 320
504  * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1;
505  */
506 #define NUM_BUF_SIZE		2048
507 
508 
509 /*
510  * Descriptor for buffer area
511  */
512 struct buf_area {
513 	char *buf_end;
514 	char *nextb;				/* pointer to next byte to read/write   */
515 };
516 
517 typedef struct buf_area buffy;
518 
519 /*
520  * The INS_CHAR macro inserts a character in the buffer and writes
521  * the buffer back to disk if necessary
522  * It uses the char pointers sp and bep:
523  *      sp points to the next available character in the buffer
524  *      bep points to the end-of-buffer+1
525  * While using this macro, note that the nextb pointer is NOT updated.
526  *
527  * NOTE: Evaluation of the c argument should not have any side-effects
528  */
529 #define INS_CHAR(c, sp, bep, cc) \
530 	{                            \
531 		if (sp < bep)            \
532 		{                        \
533 			*sp++ = c;           \
534 		}                        \
535 		cc++;                    \
536 	}
537 
538 #define NUM( c )			( c - '0' )
539 
540 #define STR_TO_DEC( str, num )		\
541     num = NUM( *str++ ) ;		\
542     while ( isdigit((int)*str ) )		\
543     {					\
544 	num *= 10 ;			\
545 	num += NUM( *str++ ) ;		\
546     }
547 
548 /*
549  * This macro does zero padding so that the precision
550  * requirement is satisfied. The padding is done by
551  * adding '0's to the left of the string that is going
552  * to be printed.
553  */
554 #define FIX_PRECISION( adjust, precision, s, s_len )	\
555     if ( adjust )						\
556 	while ( s_len < (size_t)precision )	\
557 	{									\
558 	    *--s = '0' ;					\
559 	    s_len++ ;						\
560 	}
561 
562 /*
563  * Macro that does padding. The padding is done by printing
564  * the character ch.
565  */
566 #define PAD( width, len, ch )	do		\
567 	{					\
568 	    INS_CHAR( ch, sp, bep, cc ) ;	\
569 	    width-- ;				\
570 	}					\
571 	while ( (size_t)width > len )
572 
573 /*
574  * Prefix the character ch to the string str
575  * Increase length
576  * Set the has_prefix flag
577  */
578 #define PREFIX( str, length, ch )	 *--str = ch ; length++ ; has_prefix = YES
579 
580 
581 /*
582  * Do format conversion placing the output in buffer
583  */
format_converter(register buffy * odp,const char * fmt,va_list ap)584 static int format_converter(register buffy * odp, const char *fmt, va_list ap) /* {{{ */
585 {
586 	char *sp;
587 	char *bep;
588 	int cc = 0;
589 	size_t i;
590 
591 	char *s = NULL;
592 	size_t s_len;
593 	int free_zcopy;
594 	zval *zvp, zcopy;
595 
596 	int min_width = 0;
597 	int precision = 0;
598 	enum {
599 		LEFT, RIGHT
600 	} adjust;
601 	char pad_char;
602 	char prefix_char;
603 
604 	double fp_num;
605 	wide_int i_num = (wide_int) 0;
606 	u_wide_int ui_num;
607 
608 	char num_buf[NUM_BUF_SIZE];
609 	char char_buf[2];			/* for printing %% and %<unknown> */
610 
611 #ifdef ZTS
612 	struct lconv lconv;
613 #else
614 	struct lconv *lconv = NULL;
615 #endif
616 
617 	/*
618 	 * Flag variables
619 	 */
620 	length_modifier_e modifier;
621 	boolean_e alternate_form;
622 	boolean_e print_sign;
623 	boolean_e print_blank;
624 	boolean_e adjust_precision;
625 	boolean_e adjust_width;
626 	bool_int is_negative;
627 
628 	sp = odp->nextb;
629 	bep = odp->buf_end;
630 
631 	while (*fmt) {
632 		if (*fmt != '%') {
633 			INS_CHAR(*fmt, sp, bep, cc);
634 		} else {
635 			/*
636 			 * Default variable settings
637 			 */
638 			adjust = RIGHT;
639 			alternate_form = print_sign = print_blank = NO;
640 			pad_char = ' ';
641 			prefix_char = NUL;
642 			free_zcopy = 0;
643 
644 			fmt++;
645 
646 			/*
647 			 * Try to avoid checking for flags, width or precision
648 			 */
649 			if (isascii((int)*fmt) && !islower((int)*fmt)) {
650 				/*
651 				 * Recognize flags: -, #, BLANK, +
652 				 */
653 				for (;; fmt++) {
654 					if (*fmt == '-')
655 						adjust = LEFT;
656 					else if (*fmt == '+')
657 						print_sign = YES;
658 					else if (*fmt == '#')
659 						alternate_form = YES;
660 					else if (*fmt == ' ')
661 						print_blank = YES;
662 					else if (*fmt == '0')
663 						pad_char = '0';
664 					else
665 						break;
666 				}
667 
668 				/*
669 				 * Check if a width was specified
670 				 */
671 				if (isdigit((int)*fmt)) {
672 					STR_TO_DEC(fmt, min_width);
673 					adjust_width = YES;
674 				} else if (*fmt == '*') {
675 					min_width = va_arg(ap, int);
676 					fmt++;
677 					adjust_width = YES;
678 					if (min_width < 0) {
679 						adjust = LEFT;
680 						min_width = -min_width;
681 					}
682 				} else
683 					adjust_width = NO;
684 
685 				/*
686 				 * Check if a precision was specified
687 				 */
688 				if (*fmt == '.') {
689 					adjust_precision = YES;
690 					fmt++;
691 					if (isdigit((int)*fmt)) {
692 						STR_TO_DEC(fmt, precision);
693 					} else if (*fmt == '*') {
694 						precision = va_arg(ap, int);
695 						fmt++;
696 						if (precision < 0)
697 							precision = 0;
698 					} else
699 						precision = 0;
700 
701 					if (precision > FORMAT_CONV_MAX_PRECISION) {
702 						precision = FORMAT_CONV_MAX_PRECISION;
703 					}
704 				} else
705 					adjust_precision = NO;
706 			} else
707 				adjust_precision = adjust_width = NO;
708 
709 			/*
710 			 * Modifier check
711 			 */
712 			switch (*fmt) {
713 				case 'L':
714 					fmt++;
715 					modifier = LM_LONG_DOUBLE;
716 					break;
717 				case 'I':
718 					fmt++;
719 #if SIZEOF_LONG_LONG
720 					if (*fmt == '6' && *(fmt+1) == '4') {
721 						fmt += 2;
722 						modifier = LM_LONG_LONG;
723 					} else
724 #endif
725 						if (*fmt == '3' && *(fmt+1) == '2') {
726 							fmt += 2;
727 							modifier = LM_LONG;
728 						} else {
729 #ifdef _WIN64
730 							modifier = LM_LONG_LONG;
731 #else
732 							modifier = LM_LONG;
733 #endif
734 						}
735 					break;
736 				case 'l':
737 					fmt++;
738 #if SIZEOF_LONG_LONG
739 					if (*fmt == 'l') {
740 						fmt++;
741 						modifier = LM_LONG_LONG;
742 					} else
743 #endif
744 						modifier = LM_LONG;
745 					break;
746 				case 'z':
747 					fmt++;
748 					modifier = LM_SIZE_T;
749 					break;
750 				case 'j':
751 					fmt++;
752 #if SIZEOF_INTMAX_T
753 					modifier = LM_INTMAX_T;
754 #else
755 					modifier = LM_SIZE_T;
756 #endif
757 					break;
758 				case 't':
759 					fmt++;
760 #if SIZEOF_PTRDIFF_T
761 					modifier = LM_PTRDIFF_T;
762 #else
763 					modifier = LM_SIZE_T;
764 #endif
765 					break;
766 				case 'p':
767 					fmt++;
768 					modifier = LM_PHP_INT_T;
769 					break;
770 				case 'h':
771 					fmt++;
772 					if (*fmt == 'h') {
773 						fmt++;
774 					}
775 					/* these are promoted to int, so no break */
776 				default:
777 					modifier = LM_STD;
778 					break;
779 			}
780 
781 			/*
782 			 * Argument extraction and printing.
783 			 * First we determine the argument type.
784 			 * Then, we convert the argument to a string.
785 			 * On exit from the switch, s points to the string that
786 			 * must be printed, s_len has the length of the string
787 			 * The precision requirements, if any, are reflected in s_len.
788 			 *
789 			 * NOTE: pad_char may be set to '0' because of the 0 flag.
790 			 *   It is reset to ' ' by non-numeric formats
791 			 */
792 			switch (*fmt) {
793 				case 'Z': {
794 					zvp = (zval*) va_arg(ap, zval*);
795 					free_zcopy = zend_make_printable_zval(zvp, &zcopy);
796 					if (free_zcopy) {
797 						zvp = &zcopy;
798 					}
799 					s_len = Z_STRLEN_P(zvp);
800 					s = Z_STRVAL_P(zvp);
801 					if (adjust_precision && (size_t)precision < s_len) {
802 						s_len = precision;
803 					}
804 					break;
805 				}
806 				case 'u':
807 					switch(modifier) {
808 						default:
809 							i_num = (wide_int) va_arg(ap, unsigned int);
810 							break;
811 						case LM_LONG_DOUBLE:
812 							goto fmt_error;
813 						case LM_LONG:
814 							i_num = (wide_int) va_arg(ap, unsigned long int);
815 							break;
816 						case LM_SIZE_T:
817 							i_num = (wide_int) va_arg(ap, size_t);
818 							break;
819 #if SIZEOF_LONG_LONG
820 						case LM_LONG_LONG:
821 							i_num = (wide_int) va_arg(ap, u_wide_int);
822 							break;
823 #endif
824 #if SIZEOF_INTMAX_T
825 						case LM_INTMAX_T:
826 							i_num = (wide_int) va_arg(ap, uintmax_t);
827 							break;
828 #endif
829 #if SIZEOF_PTRDIFF_T
830 						case LM_PTRDIFF_T:
831 							i_num = (wide_int) va_arg(ap, ptrdiff_t);
832 							break;
833 #endif
834 						case LM_PHP_INT_T:
835 							i_num = (wide_int) va_arg(ap, zend_ulong);
836 							break;
837 					}
838 					/*
839 					 * The rest also applies to other integer formats, so fall
840 					 * into that case.
841 					 */
842 				case 'd':
843 				case 'i':
844 					/*
845 					 * Get the arg if we haven't already.
846 					 */
847 					if ((*fmt) != 'u') {
848 						switch(modifier) {
849 							default:
850 								i_num = (wide_int) va_arg(ap, int);
851 								break;
852 							case LM_LONG_DOUBLE:
853 								goto fmt_error;
854 							case LM_LONG:
855 								i_num = (wide_int) va_arg(ap, long int);
856 								break;
857 							case LM_SIZE_T:
858 #if SIZEOF_SSIZE_T
859 								i_num = (wide_int) va_arg(ap, ssize_t);
860 #else
861 								i_num = (wide_int) va_arg(ap, size_t);
862 #endif
863 								break;
864 #if SIZEOF_LONG_LONG
865 							case LM_LONG_LONG:
866 								i_num = (wide_int) va_arg(ap, wide_int);
867 								break;
868 #endif
869 #if SIZEOF_INTMAX_T
870 							case LM_INTMAX_T:
871 								i_num = (wide_int) va_arg(ap, intmax_t);
872 								break;
873 #endif
874 #if SIZEOF_PTRDIFF_T
875 							case LM_PTRDIFF_T:
876 								i_num = (wide_int) va_arg(ap, ptrdiff_t);
877 								break;
878 #endif
879 							case LM_PHP_INT_T:
880 								i_num = (wide_int) va_arg(ap, zend_long);
881 								break;
882 						}
883 					}
884 					s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
885 								&num_buf[NUM_BUF_SIZE], &s_len);
886 					FIX_PRECISION(adjust_precision, precision, s, s_len);
887 
888 					if (*fmt != 'u') {
889 						if (is_negative) {
890 							prefix_char = '-';
891 						} else if (print_sign) {
892 							prefix_char = '+';
893 						} else if (print_blank) {
894 							prefix_char = ' ';
895 						}
896 					}
897 					break;
898 
899 
900 				case 'o':
901 					switch(modifier) {
902 						default:
903 							ui_num = (u_wide_int) va_arg(ap, unsigned int);
904 							break;
905 						case LM_LONG_DOUBLE:
906 							goto fmt_error;
907 						case LM_LONG:
908 							ui_num = (u_wide_int) va_arg(ap, unsigned long int);
909 							break;
910 						case LM_SIZE_T:
911 							ui_num = (u_wide_int) va_arg(ap, size_t);
912 							break;
913 #if SIZEOF_LONG_LONG
914 						case LM_LONG_LONG:
915 							ui_num = (u_wide_int) va_arg(ap, u_wide_int);
916 							break;
917 #endif
918 #if SIZEOF_INTMAX_T
919 						case LM_INTMAX_T:
920 							ui_num = (u_wide_int) va_arg(ap, uintmax_t);
921 							break;
922 #endif
923 #if SIZEOF_PTRDIFF_T
924 						case LM_PTRDIFF_T:
925 							ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
926 							break;
927 #endif
928 						case LM_PHP_INT_T:
929 							ui_num = (u_wide_int) va_arg(ap, zend_ulong);
930 							break;
931 					}
932 					s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
933 					FIX_PRECISION(adjust_precision, precision, s, s_len);
934 					if (alternate_form && *s != '0') {
935 						*--s = '0';
936 						s_len++;
937 					}
938 					break;
939 
940 
941 				case 'x':
942 				case 'X':
943 					switch(modifier) {
944 						default:
945 							ui_num = (u_wide_int) va_arg(ap, unsigned int);
946 							break;
947 						case LM_LONG_DOUBLE:
948 							goto fmt_error;
949 						case LM_LONG:
950 							ui_num = (u_wide_int) va_arg(ap, unsigned long int);
951 							break;
952 						case LM_SIZE_T:
953 							ui_num = (u_wide_int) va_arg(ap, size_t);
954 							break;
955 #if SIZEOF_LONG_LONG
956 						case LM_LONG_LONG:
957 							ui_num = (u_wide_int) va_arg(ap, u_wide_int);
958 							break;
959 #endif
960 #if SIZEOF_INTMAX_T
961 						case LM_INTMAX_T:
962 							ui_num = (u_wide_int) va_arg(ap, uintmax_t);
963 							break;
964 #endif
965 #if SIZEOF_PTRDIFF_T
966 						case LM_PTRDIFF_T:
967 							ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
968 							break;
969 #endif
970 						case LM_PHP_INT_T:
971 							ui_num = (u_wide_int) va_arg(ap, zend_ulong);
972 							break;
973 					}
974 					s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
975 					FIX_PRECISION(adjust_precision, precision, s, s_len);
976 					if (alternate_form && i_num != 0) {
977 						*--s = *fmt;	/* 'x' or 'X' */
978 						*--s = '0';
979 						s_len += 2;
980 					}
981 					break;
982 
983 
984 				case 's':
985 				case 'v':
986 					s = va_arg(ap, char *);
987 					if (s != NULL) {
988 						s_len = strlen(s);
989 						if (adjust_precision && (size_t)precision < s_len) {
990 							s_len = precision;
991 						}
992 					} else {
993 						s = S_NULL;
994 						s_len = S_NULL_LEN;
995 					}
996 					pad_char = ' ';
997 					break;
998 
999 
1000 				case 'f':
1001 				case 'F':
1002 				case 'e':
1003 				case 'E':
1004 					switch(modifier) {
1005 						case LM_LONG_DOUBLE:
1006 							fp_num = (double) va_arg(ap, long double);
1007 							break;
1008 						case LM_STD:
1009 							fp_num = va_arg(ap, double);
1010 							break;
1011 						default:
1012 							goto fmt_error;
1013 					}
1014 
1015 					if (zend_isnan(fp_num)) {
1016 						s = "NAN";
1017 						s_len = 3;
1018 					} else if (zend_isinf(fp_num)) {
1019 						s = "INF";
1020 						s_len = 3;
1021 					} else {
1022 #ifdef ZTS
1023 						localeconv_r(&lconv);
1024 #else
1025 						if (!lconv) {
1026 							lconv = localeconv();
1027 						}
1028 #endif
1029 						s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
1030 						 (adjust_precision == NO) ? FLOAT_DIGITS : precision,
1031 						 (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
1032 									&is_negative, &num_buf[1], &s_len);
1033 						if (is_negative)
1034 							prefix_char = '-';
1035 						else if (print_sign)
1036 							prefix_char = '+';
1037 						else if (print_blank)
1038 							prefix_char = ' ';
1039 					}
1040 					break;
1041 
1042 
1043 				case 'g':
1044 				case 'k':
1045 				case 'G':
1046 				case 'H':
1047 					switch(modifier) {
1048 						case LM_LONG_DOUBLE:
1049 							fp_num = (double) va_arg(ap, long double);
1050 							break;
1051 						case LM_STD:
1052 							fp_num = va_arg(ap, double);
1053 							break;
1054 						default:
1055 							goto fmt_error;
1056 					}
1057 
1058 					if (zend_isnan(fp_num)) {
1059 						s = "NAN";
1060 						s_len = 3;
1061 						break;
1062 					} else if (zend_isinf(fp_num)) {
1063 						if (fp_num > 0) {
1064 							s = "INF";
1065 							s_len = 3;
1066 						} else {
1067 							s = "-INF";
1068 							s_len = 4;
1069 						}
1070 						break;
1071 					}
1072 
1073 					if (adjust_precision == NO) {
1074 						precision = FLOAT_DIGITS;
1075 					} else if (precision == 0) {
1076 						precision = 1;
1077 					}
1078 					/*
1079 					 * * We use &num_buf[ 1 ], so that we have room for the sign
1080 					 */
1081 #ifdef ZTS
1082 					localeconv_r(&lconv);
1083 #else
1084 					if (!lconv) {
1085 						lconv = localeconv();
1086 					}
1087 #endif
1088 					s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
1089 					if (*s == '-') {
1090 						prefix_char = *s++;
1091 					} else if (print_sign) {
1092 						prefix_char = '+';
1093 					} else if (print_blank) {
1094 						prefix_char = ' ';
1095 					}
1096 
1097 					s_len = strlen(s);
1098 
1099 					if (alternate_form && (strchr(s, '.')) == NULL) {
1100 						s[s_len++] = '.';
1101 					}
1102 					break;
1103 
1104 
1105 				case 'c':
1106 					char_buf[0] = (char) (va_arg(ap, int));
1107 					s = &char_buf[0];
1108 					s_len = 1;
1109 					pad_char = ' ';
1110 					break;
1111 
1112 
1113 				case '%':
1114 					char_buf[0] = '%';
1115 					s = &char_buf[0];
1116 					s_len = 1;
1117 					pad_char = ' ';
1118 					break;
1119 
1120 
1121 				case 'n':
1122 					*(va_arg(ap, int *)) = cc;
1123 					goto skip_output;
1124 
1125 					/*
1126 					 * Always extract the argument as a "char *" pointer. We
1127 					 * should be using "void *" but there are still machines
1128 					 * that don't understand it.
1129 					 * If the pointer size is equal to the size of an unsigned
1130 					 * integer we convert the pointer to a hex number, otherwise
1131 					 * we print "%p" to indicate that we don't handle "%p".
1132 					 */
1133 				case 'p':
1134 					if (sizeof(char *) <= sizeof(u_wide_int)) {
1135 						ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
1136 						s = ap_php_conv_p2(ui_num, 4, 'x',
1137 								&num_buf[NUM_BUF_SIZE], &s_len);
1138 						if (ui_num != 0) {
1139 							*--s = 'x';
1140 							*--s = '0';
1141 							s_len += 2;
1142 						}
1143 					} else {
1144 						s = "%p";
1145 						s_len = 2;
1146 					}
1147 					pad_char = ' ';
1148 					break;
1149 
1150 
1151 				case NUL:
1152 					/*
1153 					 * The last character of the format string was %.
1154 					 * We ignore it.
1155 					 */
1156 					continue;
1157 
1158 
1159 fmt_error:
1160 				php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
1161 					/*
1162 					 * The default case is for unrecognized %'s.
1163 					 * We print %<char> to help the user identify what
1164 					 * option is not understood.
1165 					 * This is also useful in case the user wants to pass
1166 					 * the output of format_converter to another function
1167 					 * that understands some other %<char> (like syslog).
1168 					 * Note that we can't point s inside fmt because the
1169 					 * unknown <char> could be preceded by width etc.
1170 					 */
1171 				default:
1172 					char_buf[0] = '%';
1173 					char_buf[1] = *fmt;
1174 					s = char_buf;
1175 					s_len = 2;
1176 					pad_char = ' ';
1177 					break;
1178 			}
1179 
1180 			if (prefix_char != NUL) {
1181 				*--s = prefix_char;
1182 				s_len++;
1183 			}
1184 			if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) {
1185 				if (pad_char == '0' && prefix_char != NUL) {
1186 					INS_CHAR(*s, sp, bep, cc)
1187 						s++;
1188 					s_len--;
1189 					min_width--;
1190 				}
1191 				PAD(min_width, s_len, pad_char);
1192 			}
1193 			/*
1194 			 * Print the string s.
1195 			 */
1196 			for (i = s_len; i != 0; i--) {
1197 				INS_CHAR(*s, sp, bep, cc);
1198 				s++;
1199 			}
1200 
1201 			if (adjust_width && adjust == LEFT && (size_t)min_width > s_len)
1202 				PAD(min_width, s_len, pad_char);
1203 			if (free_zcopy) {
1204 				zval_ptr_dtor_str(&zcopy);
1205 			}
1206 		}
1207 skip_output:
1208 		fmt++;
1209 	}
1210 	odp->nextb = sp;
1211 	return (cc);
1212 }
1213 /* }}} */
1214 
1215 /*
1216  * This is the general purpose conversion function.
1217  */
strx_printv(int * ccp,char * buf,size_t len,const char * format,va_list ap)1218 static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1219 {
1220 	buffy od;
1221 	int cc;
1222 
1223 	/*
1224 	 * First initialize the descriptor
1225 	 * Notice that if no length is given, we initialize buf_end to the
1226 	 * highest possible address.
1227 	 */
1228 	if (len == 0) {
1229 		od.buf_end = (char *) ~0;
1230 		od.nextb   = (char *) ~0;
1231 	} else {
1232 		od.buf_end = &buf[len-1];
1233 		od.nextb   = buf;
1234 	}
1235 
1236 	/*
1237 	 * Do the conversion
1238 	 */
1239 	cc = format_converter(&od, format, ap);
1240 	if (len != 0 && od.nextb <= od.buf_end) {
1241 		*(od.nextb) = '\0';
1242 	}
1243 	if (ccp) {
1244 		*ccp = cc;
1245 	}
1246 }
1247 /* }}} */
1248 
ap_php_slprintf(char * buf,size_t len,const char * format,...)1249 PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1250 {
1251 	int cc;
1252 	va_list ap;
1253 
1254 	va_start(ap, format);
1255 	strx_printv(&cc, buf, len, format, ap);
1256 	va_end(ap);
1257 	if ((size_t)cc >= len) {
1258 		cc = (int)len -1;
1259 		buf[cc] = '\0';
1260 	}
1261 	return cc;
1262 }
1263 /* }}} */
1264 
ap_php_vslprintf(char * buf,size_t len,const char * format,va_list ap)1265 PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1266 {
1267 	int cc;
1268 
1269 	strx_printv(&cc, buf, len, format, ap);
1270 	if ((size_t)cc >= len) {
1271 		cc = (int)len -1;
1272 		buf[cc] = '\0';
1273 	}
1274 	return cc;
1275 }
1276 /* }}} */
1277 
ap_php_snprintf(char * buf,size_t len,const char * format,...)1278 PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1279 {
1280 	int cc;
1281 	va_list ap;
1282 
1283 	va_start(ap, format);
1284 	strx_printv(&cc, buf, len, format, ap);
1285 	va_end(ap);
1286 	return (cc);
1287 }
1288 /* }}} */
1289 
ap_php_vsnprintf(char * buf,size_t len,const char * format,va_list ap)1290 PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1291 {
1292 	int cc;
1293 
1294 	strx_printv(&cc, buf, len, format, ap);
1295 	return (cc);
1296 }
1297 /* }}} */
1298 
ap_php_vasprintf(char ** buf,const char * format,va_list ap)1299 PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */
1300 {
1301 	va_list ap2;
1302 	int cc;
1303 
1304 	va_copy(ap2, ap);
1305 	cc = ap_php_vsnprintf(NULL, 0, format, ap2);
1306 	va_end(ap2);
1307 
1308 	*buf = NULL;
1309 
1310 	if (cc >= 0) {
1311 		if ((*buf = malloc(++cc)) != NULL) {
1312 			if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) {
1313 				free(*buf);
1314 				*buf = NULL;
1315 			}
1316 		}
1317 	}
1318 
1319 	return cc;
1320 }
1321 /* }}} */
1322 
ap_php_asprintf(char ** buf,const char * format,...)1323 PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */
1324 {
1325 	int cc;
1326 	va_list ap;
1327 
1328 	va_start(ap, format);
1329 	cc = vasprintf(buf, format, ap);
1330 	va_end(ap);
1331 	return cc;
1332 }
1333 /* }}} */
1334