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