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