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