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