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