1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 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: snprintf.c 272370 2008-12-31 11:15:49Z sebastian $ */
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 :
66 : static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) /* {{{ */
67 105415 : {
68 105415 : register char *s = NULL;
69 : char *p, *rve, c;
70 : size_t siz;
71 :
72 105415 : if (ndigit < 0) {
73 0 : siz = -ndigit + 1;
74 : } else {
75 105415 : siz = ndigit + 1;
76 : }
77 :
78 : /* __dtoa() doesn't allocate space for 0 so we do it by hand */
79 105415 : if (value == 0.0) {
80 500 : *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
81 500 : *sign = 0;
82 500 : if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
83 0 : return(NULL);
84 : }
85 500 : *rve++ = '0';
86 500 : *rve = '\0';
87 500 : if (!ndigit) {
88 6 : return(s);
89 : }
90 : } else {
91 104915 : p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
92 104915 : if (*decpt == 9999) {
93 : /* Infinity or Nan, convert to inf or nan like printf */
94 0 : *decpt = 0;
95 0 : c = *p;
96 0 : zend_freedtoa(p);
97 0 : return(c == 'I' ? "INF" : "NAN");
98 : }
99 : /* Make a local copy and adjust rve to be in terms of s */
100 104915 : if (pad && fmode) {
101 104175 : siz += *decpt;
102 : }
103 104915 : if ((s = (char *)malloc(siz+1)) == NULL) {
104 0 : zend_freedtoa(p);
105 0 : return(NULL);
106 : }
107 104915 : (void) strlcpy(s, p, siz);
108 104915 : rve = s + (rve - p);
109 104915 : zend_freedtoa(p);
110 : }
111 :
112 : /* Add trailing zeros */
113 105409 : if (pad) {
114 105409 : siz -= rve - s;
115 442425 : while (--siz) {
116 231607 : *rve++ = '0';
117 : }
118 105409 : *rve = '\0';
119 : }
120 :
121 105409 : return(s);
122 : }
123 : /* }}} */
124 :
125 : static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
126 970 : {
127 970 : return(__cvt(value, ndigit, decpt, sign, 0, 1));
128 : }
129 : /* }}} */
130 :
131 : static inline char *php_fcvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
132 104445 : {
133 104445 : return(__cvt(value, ndigit, decpt, sign, 1, 1));
134 : }
135 : /* }}} */
136 :
137 : PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) /* {{{ */
138 23980 : {
139 : char *digits, *dst, *src;
140 : int i, decpt, sign;
141 :
142 23980 : digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, NULL);
143 23980 : 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 12 : snprintf(buf, ndigit + 1, "%s%s", (sign && *digits == 'I') ? "-" : "", *digits == 'I' ? "INF" : "NAN");
149 12 : zend_freedtoa(digits);
150 12 : return (buf);
151 : }
152 :
153 23968 : dst = buf;
154 23968 : if (sign) {
155 2458 : *dst++ = '-';
156 : }
157 :
158 24663 : if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */
159 : /* exponential format (e.g. 1.2345e+13) */
160 695 : if (--decpt < 0) {
161 565 : sign = 1;
162 565 : decpt = -decpt;
163 : } else {
164 130 : sign = 0;
165 : }
166 695 : src = digits;
167 695 : *dst++ = *src++;
168 695 : *dst++ = dec_point;
169 695 : if (*src == '\0') {
170 167 : *dst++ = '0';
171 : } else {
172 : do {
173 4511 : *dst++ = *src++;
174 4511 : } while (*src != '\0');
175 : }
176 695 : *dst++ = exponent;
177 695 : if (sign) {
178 565 : *dst++ = '-';
179 : } else {
180 130 : *dst++ = '+';
181 : }
182 695 : if (decpt < 10) {
183 467 : *dst++ = '0' + decpt;
184 467 : *dst = '\0';
185 : } else {
186 : /* XXX - optimize */
187 228 : for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
188 : continue;
189 228 : dst[i + 1] = '\0';
190 932 : while (decpt != 0) {
191 476 : dst[i--] = '0' + decpt % 10;
192 476 : decpt /= 10;
193 : }
194 : }
195 23273 : } else if (decpt < 0) {
196 : /* standard format 0. */
197 296 : *dst++ = '0'; /* zero before decimal point */
198 296 : *dst++ = dec_point;
199 : do {
200 503 : *dst++ = '0';
201 503 : } while (++decpt < 0);
202 296 : src = digits;
203 2132 : while (*src != '\0') {
204 1540 : *dst++ = *src++;
205 : }
206 296 : *dst = '\0';
207 : } else {
208 : /* standard format */
209 73419 : for (i = 0, src = digits; i < decpt; i++) {
210 50442 : if (*src != '\0') {
211 45713 : *dst++ = *src++;
212 : } else {
213 4729 : *dst++ = '0';
214 : }
215 : }
216 22977 : if (*src != '\0') {
217 18113 : if (src == digits) {
218 2967 : *dst++ = '0'; /* zero before decimal point */
219 : }
220 18113 : *dst++ = dec_point;
221 173815 : for (i = decpt; digits[i] != '\0'; i++) {
222 155702 : *dst++ = digits[i];
223 : }
224 : }
225 22977 : *dst = '\0';
226 : }
227 23968 : zend_freedtoa(digits);
228 23968 : 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() {{{ */
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 5330759 : {
317 5330759 : register char *p = buf_end;
318 : register u_wide_int magnitude;
319 :
320 5330759 : if (is_unsigned) {
321 1916 : magnitude = (u_wide_int) num;
322 1916 : *is_negative = FALSE;
323 : } else {
324 5328843 : *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 5328843 : if (*is_negative) {
336 6113 : wide_int t = num + 1;
337 6113 : magnitude = ((u_wide_int) - t) + 1;
338 : } else {
339 5322730 : 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 21890085 : register u_wide_int new_magnitude = magnitude / 10;
348 :
349 21890085 : *--p = (char)(magnitude - new_magnitude * 10 + '0');
350 21890085 : magnitude = new_magnitude;
351 : }
352 21890085 : while (magnitude);
353 :
354 5330759 : *len = buf_end - p;
355 5330759 : 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() {{{ */
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 105415 : {
375 105415 : register char *s = buf;
376 : register char *p, *p_orig;
377 : int decimal_point;
378 :
379 105415 : if (precision >= NDIG - 1) {
380 1 : precision = NDIG - 2;
381 : }
382 :
383 105415 : if (format == 'F') {
384 104445 : p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
385 : } else { /* either e or E format */
386 970 : p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
387 : }
388 :
389 : /*
390 : * Check for Infinity and NaN
391 : */
392 105415 : if (isalpha((int)*p)) {
393 0 : *len = strlen(p);
394 0 : memcpy(buf, p, *len + 1);
395 0 : *is_negative = FALSE;
396 0 : free(p_orig);
397 0 : return (buf);
398 : }
399 105415 : if (format == 'F') {
400 104445 : if (decimal_point <= 0) {
401 100447 : if (num != 0 || precision > 0) {
402 100441 : *s++ = '0';
403 100441 : if (precision > 0) {
404 100441 : *s++ = dec_point;
405 212172 : while (decimal_point++ < 0) {
406 11290 : *s++ = '0';
407 : }
408 0 : } else if (add_dp) {
409 0 : *s++ = dec_point;
410 : }
411 : }
412 : } else {
413 3998 : int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
414 3998 : decimal_point -= addz;
415 25261 : while (decimal_point-- > 0) {
416 17265 : *s++ = *p++;
417 : }
418 7996 : while (addz-- > 0) {
419 0 : *s++ = '0';
420 : }
421 3998 : if (precision > 0 || add_dp) {
422 2644 : *s++ = dec_point;
423 : }
424 : }
425 : } else {
426 970 : *s++ = *p++;
427 970 : if (precision > 0 || add_dp) {
428 969 : *s++ = '.';
429 : }
430 : }
431 :
432 : /*
433 : * copy the rest of p, the NUL is NOT copied
434 : */
435 1023505 : while (*p) {
436 812675 : *s++ = *p++;
437 : }
438 :
439 105415 : if (format != 'F') {
440 : char temp[EXPONENT_LENGTH]; /* for exponent conversion */
441 : int t_len;
442 : bool_int exponent_is_negative;
443 :
444 970 : *s++ = format; /* either e or E */
445 970 : decimal_point--;
446 970 : if (decimal_point != 0) {
447 437 : p = ap_php_conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len);
448 437 : *s++ = exponent_is_negative ? '-' : '+';
449 :
450 : /*
451 : * Make sure the exponent has at least 2 digits
452 : */
453 1341 : while (t_len--) {
454 467 : *s++ = *p++;
455 : }
456 : } else {
457 533 : *s++ = '+';
458 533 : *s++ = '0';
459 : }
460 : }
461 105415 : *len = s - buf;
462 105415 : free(p_orig);
463 105415 : 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 : */
477 : char * ap_php_conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) /* {{{ */
478 23737 : {
479 23737 : register int mask = (1 << nbits) - 1;
480 23737 : register char *p = buf_end;
481 : static char low_digits[] = "0123456789abcdef";
482 : static char upper_digits[] = "0123456789ABCDEF";
483 23737 : register char *digits = (format == 'X') ? upper_digits : low_digits;
484 :
485 : do {
486 153343 : *--p = digits[num & mask];
487 153343 : num >>= nbits;
488 : }
489 153343 : while (num);
490 :
491 23737 : *len = buf_end - p;
492 23737 : 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 : */
501 : #define NUM_BUF_SIZE 512
502 :
503 :
504 : /*
505 : * Descriptor for buffer area
506 : */
507 : struct buf_area {
508 : char *buf_end;
509 : char *nextb; /* pointer to next byte to read/write */
510 : };
511 :
512 : typedef struct buf_area buffy;
513 :
514 : /*
515 : * The INS_CHAR macro inserts a character in the buffer and writes
516 : * the buffer back to disk if necessary
517 : * It uses the char pointers sp and bep:
518 : * sp points to the next available character in the buffer
519 : * bep points to the end-of-buffer+1
520 : * While using this macro, note that the nextb pointer is NOT updated.
521 : *
522 : * NOTE: Evaluation of the c argument should not have any side-effects
523 : */
524 : #define INS_CHAR(c, sp, bep, cc) \
525 : { \
526 : if (sp < bep) \
527 : { \
528 : *sp++ = c; \
529 : } \
530 : cc++; \
531 : }
532 :
533 : #define NUM( c ) ( c - '0' )
534 :
535 : #define STR_TO_DEC( str, num ) \
536 : num = NUM( *str++ ) ; \
537 : while ( isdigit((int)*str ) ) \
538 : { \
539 : num *= 10 ; \
540 : num += NUM( *str++ ) ; \
541 : }
542 :
543 : /*
544 : * This macro does zero padding so that the precision
545 : * requirement is satisfied. The padding is done by
546 : * adding '0's to the left of the string that is going
547 : * to be printed.
548 : */
549 : #define FIX_PRECISION( adjust, precision, s, s_len ) \
550 : if ( adjust ) \
551 : while ( s_len < precision ) \
552 : { \
553 : *--s = '0' ; \
554 : s_len++ ; \
555 : }
556 :
557 : /*
558 : * Macro that does padding. The padding is done by printing
559 : * the character ch.
560 : */
561 : #define PAD( width, len, ch ) do \
562 : { \
563 : INS_CHAR( ch, sp, bep, cc ) ; \
564 : width-- ; \
565 : } \
566 : while ( width > len )
567 :
568 : /*
569 : * Prefix the character ch to the string str
570 : * Increase length
571 : * Set the has_prefix flag
572 : */
573 : #define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES
574 :
575 :
576 : /*
577 : * Do format conversion placing the output in buffer
578 : */
579 : static int format_converter(register buffy * odp, const char *fmt, va_list ap) /* {{{ */
580 1038648 : {
581 : char *sp;
582 : char *bep;
583 1038648 : int cc = 0;
584 : int i;
585 :
586 1038648 : char *s = NULL;
587 : char *q;
588 : int s_len, free_zcopy;
589 : zval *zvp, zcopy;
590 :
591 1038648 : int min_width = 0;
592 1038648 : int precision = 0;
593 : enum {
594 : LEFT, RIGHT
595 : } adjust;
596 : char pad_char;
597 : char prefix_char;
598 :
599 : double fp_num;
600 1038648 : wide_int i_num = (wide_int) 0;
601 : u_wide_int ui_num;
602 :
603 : char num_buf[NUM_BUF_SIZE];
604 : char char_buf[2]; /* for printing %% and %<unknown> */
605 :
606 : #ifdef HAVE_LOCALE_H
607 1038648 : struct lconv *lconv = NULL;
608 : #endif
609 :
610 : /*
611 : * Flag variables
612 : */
613 : length_modifier_e modifier;
614 : boolean_e alternate_form;
615 : boolean_e print_sign;
616 : boolean_e print_blank;
617 : boolean_e adjust_precision;
618 : boolean_e adjust_width;
619 : bool_int is_negative;
620 :
621 1038648 : sp = odp->nextb;
622 1038648 : bep = odp->buf_end;
623 :
624 6284555 : while (*fmt) {
625 4207259 : if (*fmt != '%') {
626 2969062 : INS_CHAR(*fmt, sp, bep, cc);
627 : } else {
628 : /*
629 : * Default variable settings
630 : */
631 1238197 : adjust = RIGHT;
632 1238197 : alternate_form = print_sign = print_blank = NO;
633 1238197 : pad_char = ' ';
634 1238197 : prefix_char = NUL;
635 1238197 : free_zcopy = 0;
636 :
637 1238197 : fmt++;
638 :
639 : /*
640 : * Try to avoid checking for flags, width or precision
641 : */
642 1357260 : if (isascii((int)*fmt) && !islower((int)*fmt)) {
643 : /*
644 : * Recognize flags: -, #, BLANK, +
645 : */
646 18872 : for (;; fmt++) {
647 137935 : if (*fmt == '-')
648 1 : adjust = LEFT;
649 137934 : else if (*fmt == '+')
650 0 : print_sign = YES;
651 137934 : else if (*fmt == '#')
652 0 : alternate_form = YES;
653 137934 : else if (*fmt == ' ')
654 0 : print_blank = YES;
655 137934 : else if (*fmt == '0')
656 18871 : pad_char = '0';
657 : else
658 119063 : break;
659 18872 : }
660 :
661 : /*
662 : * Check if a width was specified
663 : */
664 119063 : if (isdigit((int)*fmt)) {
665 18842 : STR_TO_DEC(fmt, min_width);
666 18842 : adjust_width = YES;
667 100221 : } else if (*fmt == '*') {
668 0 : min_width = va_arg(ap, int);
669 0 : fmt++;
670 0 : adjust_width = YES;
671 0 : if (min_width < 0) {
672 0 : adjust = LEFT;
673 0 : min_width = -min_width;
674 : }
675 : } else
676 100221 : adjust_width = NO;
677 :
678 : /*
679 : * Check if a precision was specified
680 : *
681 : * XXX: an unreasonable amount of precision may be specified
682 : * resulting in overflow of num_buf. Currently we
683 : * ignore this possibility.
684 : */
685 119063 : if (*fmt == '.') {
686 100221 : adjust_precision = YES;
687 100221 : fmt++;
688 100221 : if (isdigit((int)*fmt)) {
689 100116 : STR_TO_DEC(fmt, precision);
690 105 : } else if (*fmt == '*') {
691 105 : precision = va_arg(ap, int);
692 105 : fmt++;
693 105 : if (precision < 0)
694 0 : precision = 0;
695 : } else
696 0 : precision = 0;
697 : } else
698 18842 : adjust_precision = NO;
699 : } else
700 1119134 : adjust_precision = adjust_width = NO;
701 :
702 : /*
703 : * Modifier check
704 : */
705 1238197 : switch (*fmt) {
706 : case 'L':
707 0 : fmt++;
708 0 : modifier = LM_LONG_DOUBLE;
709 0 : break;
710 : case 'I':
711 0 : fmt++;
712 : #if SIZEOF_LONG_LONG
713 0 : if (*fmt == '6' && *(fmt+1) == '4') {
714 0 : fmt += 2;
715 0 : modifier = LM_LONG_LONG;
716 : } else
717 : #endif
718 0 : if (*fmt == '3' && *(fmt+1) == '2') {
719 0 : fmt += 2;
720 0 : modifier = LM_LONG;
721 : } else {
722 : #ifdef _WIN64
723 : modifier = LM_LONG_LONG;
724 : #else
725 0 : modifier = LM_LONG;
726 : #endif
727 : }
728 0 : break;
729 : case 'l':
730 464043 : fmt++;
731 : #if SIZEOF_LONG_LONG
732 464043 : if (*fmt == 'l') {
733 2435 : fmt++;
734 2435 : modifier = LM_LONG_LONG;
735 : } else
736 : #endif
737 461608 : modifier = LM_LONG;
738 464043 : break;
739 : case 'z':
740 0 : fmt++;
741 0 : modifier = LM_SIZE_T;
742 0 : break;
743 : case 'j':
744 0 : fmt++;
745 : #if SIZEOF_INTMAX_T
746 0 : modifier = LM_INTMAX_T;
747 : #else
748 : modifier = LM_SIZE_T;
749 : #endif
750 0 : break;
751 : case 't':
752 0 : fmt++;
753 : #if SIZEOF_PTRDIFF_T
754 0 : modifier = LM_PTRDIFF_T;
755 : #else
756 : modifier = LM_SIZE_T;
757 : #endif
758 0 : break;
759 : case 'h':
760 0 : fmt++;
761 0 : if (*fmt == 'h') {
762 0 : fmt++;
763 : }
764 : /* these are promoted to int, so no break */
765 : default:
766 774154 : modifier = LM_STD;
767 : break;
768 : }
769 :
770 : /*
771 : * Argument extraction and printing.
772 : * First we determine the argument type.
773 : * Then, we convert the argument to a string.
774 : * On exit from the switch, s points to the string that
775 : * must be printed, s_len has the length of the string
776 : * The precision requirements, if any, are reflected in s_len.
777 : *
778 : * NOTE: pad_char may be set to '0' because of the 0 flag.
779 : * It is reset to ' ' by non-numeric formats
780 : */
781 1238197 : switch (*fmt) {
782 : case 'Z':
783 0 : zvp = (zval*) va_arg(ap, zval*);
784 0 : zend_make_printable_zval(zvp, &zcopy, &free_zcopy);
785 0 : if (free_zcopy) {
786 0 : zvp = &zcopy;
787 : }
788 0 : s_len = Z_STRLEN_P(zvp);
789 0 : s = Z_STRVAL_P(zvp);
790 0 : if (adjust_precision && precision < s_len) {
791 0 : s_len = precision;
792 : }
793 0 : break;
794 : case 'u':
795 872 : switch(modifier) {
796 : default:
797 872 : i_num = (wide_int) va_arg(ap, unsigned int);
798 872 : break;
799 : case LM_LONG_DOUBLE:
800 0 : goto fmt_error;
801 : case LM_LONG:
802 0 : i_num = (wide_int) va_arg(ap, unsigned long int);
803 0 : break;
804 : case LM_SIZE_T:
805 0 : i_num = (wide_int) va_arg(ap, size_t);
806 0 : break;
807 : #if SIZEOF_LONG_LONG
808 : case LM_LONG_LONG:
809 0 : i_num = (wide_int) va_arg(ap, u_wide_int);
810 0 : break;
811 : #endif
812 : #if SIZEOF_INTMAX_T
813 : case LM_INTMAX_T:
814 0 : i_num = (wide_int) va_arg(ap, uintmax_t);
815 0 : break;
816 : #endif
817 : #if SIZEOF_PTRDIFF_T
818 : case LM_PTRDIFF_T:
819 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
820 : break;
821 : #endif
822 : }
823 : /*
824 : * The rest also applies to other integer formats, so fall
825 : * into that case.
826 : */
827 : case 'd':
828 : case 'i':
829 : /*
830 : * Get the arg if we haven't already.
831 : */
832 485312 : if ((*fmt) != 'u') {
833 484440 : switch(modifier) {
834 : default:
835 20403 : i_num = (wide_int) va_arg(ap, int);
836 20403 : break;
837 : case LM_LONG_DOUBLE:
838 0 : goto fmt_error;
839 : case LM_LONG:
840 461602 : i_num = (wide_int) va_arg(ap, long int);
841 461602 : break;
842 : case LM_SIZE_T:
843 : #if SIZEOF_SSIZE_T
844 0 : i_num = (wide_int) va_arg(ap, ssize_t);
845 : #else
846 : i_num = (wide_int) va_arg(ap, size_t);
847 : #endif
848 0 : break;
849 : #if SIZEOF_LONG_LONG
850 : case LM_LONG_LONG:
851 2435 : i_num = (wide_int) va_arg(ap, wide_int);
852 2435 : break;
853 : #endif
854 : #if SIZEOF_INTMAX_T
855 : case LM_INTMAX_T:
856 0 : i_num = (wide_int) va_arg(ap, intmax_t);
857 0 : break;
858 : #endif
859 : #if SIZEOF_PTRDIFF_T
860 : case LM_PTRDIFF_T:
861 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
862 : break;
863 : #endif
864 : }
865 : }
866 485312 : s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
867 : &num_buf[NUM_BUF_SIZE], &s_len);
868 485312 : FIX_PRECISION(adjust_precision, precision, s, s_len);
869 :
870 485312 : if (*fmt != 'u') {
871 484440 : if (is_negative) {
872 31 : prefix_char = '-';
873 484409 : } else if (print_sign) {
874 0 : prefix_char = '+';
875 484409 : } else if (print_blank) {
876 0 : prefix_char = ' ';
877 : }
878 : }
879 485312 : break;
880 :
881 :
882 : case 'o':
883 0 : switch(modifier) {
884 : default:
885 0 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
886 0 : break;
887 : case LM_LONG_DOUBLE:
888 0 : goto fmt_error;
889 : case LM_LONG:
890 0 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
891 0 : break;
892 : case LM_SIZE_T:
893 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
894 0 : break;
895 : #if SIZEOF_LONG_LONG
896 : case LM_LONG_LONG:
897 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
898 0 : break;
899 : #endif
900 : #if SIZEOF_INTMAX_T
901 : case LM_INTMAX_T:
902 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
903 0 : break;
904 : #endif
905 : #if SIZEOF_PTRDIFF_T
906 : case LM_PTRDIFF_T:
907 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
908 : break;
909 : #endif
910 : }
911 0 : s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
912 0 : FIX_PRECISION(adjust_precision, precision, s, s_len);
913 0 : if (alternate_form && *s != '0') {
914 0 : *--s = '0';
915 0 : s_len++;
916 : }
917 0 : break;
918 :
919 :
920 : case 'x':
921 : case 'X':
922 6 : switch(modifier) {
923 : default:
924 0 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
925 0 : break;
926 : case LM_LONG_DOUBLE:
927 0 : goto fmt_error;
928 : case LM_LONG:
929 6 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
930 6 : break;
931 : case LM_SIZE_T:
932 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
933 0 : break;
934 : #if SIZEOF_LONG_LONG
935 : case LM_LONG_LONG:
936 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
937 0 : break;
938 : #endif
939 : #if SIZEOF_INTMAX_T
940 : case LM_INTMAX_T:
941 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
942 0 : break;
943 : #endif
944 : #if SIZEOF_PTRDIFF_T
945 : case LM_PTRDIFF_T:
946 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
947 : break;
948 : #endif
949 : }
950 6 : s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
951 6 : FIX_PRECISION(adjust_precision, precision, s, s_len);
952 6 : if (alternate_form && i_num != 0) {
953 0 : *--s = *fmt; /* 'x' or 'X' */
954 0 : *--s = '0';
955 0 : s_len += 2;
956 : }
957 6 : break;
958 :
959 :
960 : case 's':
961 : case 'v':
962 650739 : s = va_arg(ap, char *);
963 650739 : if (s != NULL) {
964 650739 : s_len = strlen(s);
965 650739 : if (adjust_precision && precision < s_len) {
966 0 : s_len = precision;
967 : }
968 : } else {
969 0 : s = S_NULL;
970 0 : s_len = S_NULL_LEN;
971 : }
972 650739 : pad_char = ' ';
973 650739 : break;
974 :
975 :
976 : case 'f':
977 : case 'F':
978 : case 'e':
979 : case 'E':
980 100124 : switch(modifier) {
981 : case LM_LONG_DOUBLE:
982 0 : fp_num = (double) va_arg(ap, long double);
983 0 : break;
984 : case LM_STD:
985 100124 : fp_num = va_arg(ap, double);
986 100124 : break;
987 : default:
988 0 : goto fmt_error;
989 : }
990 :
991 100124 : if (zend_isnan(fp_num)) {
992 0 : s = "NAN";
993 0 : s_len = 3;
994 100124 : } else if (zend_isinf(fp_num)) {
995 0 : s = "INF";
996 0 : s_len = 3;
997 : } else {
998 : #ifdef HAVE_LOCALE_H
999 100124 : if (!lconv) {
1000 100124 : lconv = localeconv();
1001 : }
1002 : #endif
1003 100124 : s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
1004 : (adjust_precision == NO) ? FLOAT_DIGITS : precision,
1005 : (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
1006 : &is_negative, &num_buf[1], &s_len);
1007 100124 : if (is_negative)
1008 0 : prefix_char = '-';
1009 100124 : else if (print_sign)
1010 0 : prefix_char = '+';
1011 100124 : else if (print_blank)
1012 0 : prefix_char = ' ';
1013 : }
1014 100124 : break;
1015 :
1016 :
1017 : case 'g':
1018 : case 'k':
1019 : case 'G':
1020 : case 'H':
1021 2 : switch(modifier) {
1022 : case LM_LONG_DOUBLE:
1023 0 : fp_num = (double) va_arg(ap, long double);
1024 0 : break;
1025 : case LM_STD:
1026 2 : fp_num = va_arg(ap, double);
1027 2 : break;
1028 : default:
1029 0 : goto fmt_error;
1030 : }
1031 :
1032 2 : if (zend_isnan(fp_num)) {
1033 0 : s = "NAN";
1034 0 : s_len = 3;
1035 0 : break;
1036 2 : } else if (zend_isinf(fp_num)) {
1037 0 : if (fp_num > 0) {
1038 0 : s = "INF";
1039 0 : s_len = 3;
1040 : } else {
1041 0 : s = "-INF";
1042 0 : s_len = 4;
1043 : }
1044 0 : break;
1045 : }
1046 :
1047 2 : if (adjust_precision == NO) {
1048 0 : precision = FLOAT_DIGITS;
1049 2 : } else if (precision == 0) {
1050 0 : precision = 1;
1051 : }
1052 : /*
1053 : * * We use &num_buf[ 1 ], so that we have room for the sign
1054 : */
1055 : #ifdef HAVE_LOCALE_H
1056 2 : if (!lconv) {
1057 2 : lconv = localeconv();
1058 : }
1059 : #endif
1060 2 : s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
1061 2 : if (*s == '-') {
1062 0 : prefix_char = *s++;
1063 2 : } else if (print_sign) {
1064 0 : prefix_char = '+';
1065 2 : } else if (print_blank) {
1066 0 : prefix_char = ' ';
1067 : }
1068 :
1069 2 : s_len = strlen(s);
1070 :
1071 2 : if (alternate_form && (q = strchr(s, '.')) == NULL) {
1072 0 : s[s_len++] = '.';
1073 : }
1074 2 : break;
1075 :
1076 :
1077 : case 'c':
1078 2014 : char_buf[0] = (char) (va_arg(ap, int));
1079 2014 : s = &char_buf[0];
1080 2014 : s_len = 1;
1081 2014 : pad_char = ' ';
1082 2014 : break;
1083 :
1084 :
1085 : case '%':
1086 0 : char_buf[0] = '%';
1087 0 : s = &char_buf[0];
1088 0 : s_len = 1;
1089 0 : pad_char = ' ';
1090 0 : break;
1091 :
1092 :
1093 : case 'n':
1094 0 : *(va_arg(ap, int *)) = cc;
1095 0 : goto skip_output;
1096 :
1097 : /*
1098 : * Always extract the argument as a "char *" pointer. We
1099 : * should be using "void *" but there are still machines
1100 : * that don't understand it.
1101 : * If the pointer size is equal to the size of an unsigned
1102 : * integer we convert the pointer to a hex number, otherwise
1103 : * we print "%p" to indicate that we don't handle "%p".
1104 : */
1105 : case 'p':
1106 : if (sizeof(char *) <= sizeof(u_wide_int)) {
1107 0 : ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
1108 0 : s = ap_php_conv_p2(ui_num, 4, 'x',
1109 : &num_buf[NUM_BUF_SIZE], &s_len);
1110 0 : if (ui_num != 0) {
1111 0 : *--s = 'x';
1112 0 : *--s = '0';
1113 0 : s_len += 2;
1114 : }
1115 : } else {
1116 : s = "%p";
1117 : s_len = 2;
1118 : }
1119 0 : pad_char = ' ';
1120 0 : break;
1121 :
1122 :
1123 : case NUL:
1124 : /*
1125 : * The last character of the format string was %.
1126 : * We ignore it.
1127 : */
1128 0 : continue;
1129 :
1130 :
1131 0 : fmt_error:
1132 0 : php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
1133 : /*
1134 : * The default case is for unrecognized %'s.
1135 : * We print %<char> to help the user identify what
1136 : * option is not understood.
1137 : * This is also useful in case the user wants to pass
1138 : * the output of format_converter to another function
1139 : * that understands some other %<char> (like syslog).
1140 : * Note that we can't point s inside fmt because the
1141 : * unknown <char> could be preceded by width etc.
1142 : */
1143 : default:
1144 0 : char_buf[0] = '%';
1145 0 : char_buf[1] = *fmt;
1146 0 : s = char_buf;
1147 0 : s_len = 2;
1148 0 : pad_char = ' ';
1149 : break;
1150 : }
1151 :
1152 1238197 : if (prefix_char != NUL) {
1153 31 : *--s = prefix_char;
1154 31 : s_len++;
1155 : }
1156 1238197 : if (adjust_width && adjust == RIGHT && min_width > s_len) {
1157 10915 : if (pad_char == '0' && prefix_char != NUL) {
1158 0 : INS_CHAR(*s, sp, bep, cc)
1159 0 : s++;
1160 0 : s_len--;
1161 0 : min_width--;
1162 : }
1163 11104 : PAD(min_width, s_len, pad_char);
1164 : }
1165 : /*
1166 : * Print the string s.
1167 : */
1168 9409321 : for (i = s_len; i != 0; i--) {
1169 8171124 : INS_CHAR(*s, sp, bep, cc);
1170 8171124 : s++;
1171 : }
1172 :
1173 1238197 : if (adjust_width && adjust == LEFT && min_width > s_len)
1174 0 : PAD(min_width, s_len, pad_char);
1175 1238197 : if (free_zcopy) {
1176 0 : zval_dtor(&zcopy);
1177 : }
1178 : }
1179 4207259 : skip_output:
1180 4207259 : fmt++;
1181 : }
1182 1038648 : odp->nextb = sp;
1183 1038648 : return (cc);
1184 : }
1185 : /* }}} */
1186 :
1187 : /*
1188 : * This is the general purpose conversion function.
1189 : */
1190 : static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1191 1038648 : {
1192 : buffy od;
1193 : int cc;
1194 :
1195 : /*
1196 : * First initialize the descriptor
1197 : * Notice that if no length is given, we initialize buf_end to the
1198 : * highest possible address.
1199 : */
1200 1038648 : if (len == 0) {
1201 0 : od.buf_end = (char *) ~0;
1202 0 : od.nextb = (char *) ~0;
1203 : } else {
1204 1038648 : od.buf_end = &buf[len-1];
1205 1038648 : od.nextb = buf;
1206 : }
1207 :
1208 : /*
1209 : * Do the conversion
1210 : */
1211 1038648 : cc = format_converter(&od, format, ap);
1212 1038648 : if (len != 0 && od.nextb <= od.buf_end) {
1213 1038648 : *(od.nextb) = '\0';
1214 : }
1215 1038648 : if (ccp) {
1216 1038648 : *ccp = cc;
1217 : }
1218 1038648 : }
1219 : /* }}} */
1220 :
1221 : PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1222 845510 : {
1223 : int cc;
1224 : va_list ap;
1225 :
1226 845510 : va_start(ap, format);
1227 845510 : strx_printv(&cc, buf, len, format, ap);
1228 845510 : va_end(ap);
1229 845510 : if (cc >= len) {
1230 0 : cc = len -1;
1231 0 : buf[cc] = '\0';
1232 : }
1233 845510 : return cc;
1234 : }
1235 : /* }}} */
1236 :
1237 : PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1238 11 : {
1239 : int cc;
1240 :
1241 11 : strx_printv(&cc, buf, len, format, ap);
1242 11 : if (cc >= len) {
1243 0 : cc = len -1;
1244 0 : buf[cc] = '\0';
1245 : }
1246 11 : return cc;
1247 : }
1248 : /* }}} */
1249 :
1250 : PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1251 193127 : {
1252 : int cc;
1253 : va_list ap;
1254 :
1255 193127 : va_start(ap, format);
1256 193127 : strx_printv(&cc, buf, len, format, ap);
1257 193127 : va_end(ap);
1258 193127 : return (cc);
1259 : }
1260 : /* }}} */
1261 :
1262 : PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1263 0 : {
1264 : int cc;
1265 :
1266 0 : strx_printv(&cc, buf, len, format, ap);
1267 0 : return (cc);
1268 : }
1269 : /* }}} */
1270 :
1271 : PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */
1272 0 : {
1273 : va_list ap2;
1274 : int cc;
1275 :
1276 0 : va_copy(ap2, ap);
1277 0 : cc = ap_php_vsnprintf(NULL, 0, format, ap2);
1278 0 : va_end(ap2);
1279 :
1280 0 : *buf = NULL;
1281 :
1282 0 : if (cc >= 0) {
1283 0 : if ((*buf = malloc(++cc)) != NULL) {
1284 0 : if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) {
1285 0 : free(*buf);
1286 0 : *buf = NULL;
1287 : }
1288 : }
1289 : }
1290 :
1291 0 : return cc;
1292 : }
1293 : /* }}} */
1294 :
1295 : PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */
1296 0 : {
1297 : int cc;
1298 : va_list ap;
1299 :
1300 0 : va_start(ap, format);
1301 0 : cc = vasprintf(buf, format, ap);
1302 0 : va_end(ap);
1303 0 : return cc;
1304 : }
1305 : /* }}} */
1306 :
1307 : /*
1308 : * Local variables:
1309 : * tab-width: 4
1310 : * c-basic-offset: 4
1311 : * End:
1312 : * vim600: sw=4 ts=4 fdm=marker
1313 : * vim<600: sw=4 ts=4
1314 : */
|