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