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 272374 2008-12-31 11:17: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 102751 : {
68 102751 : register char *s = NULL;
69 : char *p, *rve, c;
70 : size_t siz;
71 :
72 102751 : if (ndigit < 0) {
73 0 : siz = -ndigit + 1;
74 : } else {
75 102751 : siz = ndigit + 1;
76 : }
77 :
78 : /* __dtoa() doesn't allocate space for 0 so we do it by hand */
79 102751 : if (value == 0.0) {
80 491 : *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
81 491 : *sign = 0;
82 491 : if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
83 0 : return(NULL);
84 : }
85 491 : *rve++ = '0';
86 491 : *rve = '\0';
87 491 : if (!ndigit) {
88 3 : return(s);
89 : }
90 : } else {
91 102260 : p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
92 102260 : 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 102260 : if (pad && fmode) {
101 101520 : siz += *decpt;
102 : }
103 102260 : if ((s = (char *)malloc(siz+1)) == NULL) {
104 0 : zend_freedtoa(p);
105 0 : return(NULL);
106 : }
107 102260 : (void) strlcpy(s, p, siz);
108 102260 : rve = s + (rve - p);
109 102260 : zend_freedtoa(p);
110 : }
111 :
112 : /* Add trailing zeros */
113 102748 : if (pad) {
114 102748 : siz -= rve - s;
115 428508 : while (--siz) {
116 223012 : *rve++ = '0';
117 : }
118 102748 : *rve = '\0';
119 : }
120 :
121 102748 : 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 101781 : {
133 101781 : 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 21751 : {
139 : char *digits, *dst, *src;
140 : int i, decpt, sign;
141 :
142 21751 : digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, NULL);
143 21751 : 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 21739 : dst = buf;
154 21739 : if (sign) {
155 2358 : *dst++ = '-';
156 : }
157 :
158 22416 : if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */
159 : /* exponential format (e.g. 1.2345e+13) */
160 677 : if (--decpt < 0) {
161 568 : sign = 1;
162 568 : decpt = -decpt;
163 : } else {
164 109 : sign = 0;
165 : }
166 677 : src = digits;
167 677 : *dst++ = *src++;
168 677 : *dst++ = dec_point;
169 677 : if (*src == '\0') {
170 165 : *dst++ = '0';
171 : } else {
172 : do {
173 4309 : *dst++ = *src++;
174 4309 : } while (*src != '\0');
175 : }
176 677 : *dst++ = exponent;
177 677 : if (sign) {
178 568 : *dst++ = '-';
179 : } else {
180 109 : *dst++ = '+';
181 : }
182 677 : if (decpt < 10) {
183 470 : *dst++ = '0' + decpt;
184 470 : *dst = '\0';
185 : } else {
186 : /* XXX - optimize */
187 207 : for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
188 : continue;
189 207 : dst[i + 1] = '\0';
190 848 : while (decpt != 0) {
191 434 : dst[i--] = '0' + decpt % 10;
192 434 : decpt /= 10;
193 : }
194 : }
195 21062 : } else if (decpt < 0) {
196 : /* standard format 0. */
197 297 : *dst++ = '0'; /* zero before decimal point */
198 297 : *dst++ = dec_point;
199 : do {
200 504 : *dst++ = '0';
201 504 : } while (++decpt < 0);
202 297 : src = digits;
203 2144 : while (*src != '\0') {
204 1550 : *dst++ = *src++;
205 : }
206 297 : *dst = '\0';
207 : } else {
208 : /* standard format */
209 62147 : for (i = 0, src = digits; i < decpt; i++) {
210 41382 : if (*src != '\0') {
211 36751 : *dst++ = *src++;
212 : } else {
213 4631 : *dst++ = '0';
214 : }
215 : }
216 20765 : if (*src != '\0') {
217 16520 : if (src == digits) {
218 2715 : *dst++ = '0'; /* zero before decimal point */
219 : }
220 16520 : *dst++ = dec_point;
221 153844 : for (i = decpt; digits[i] != '\0'; i++) {
222 137324 : *dst++ = digits[i];
223 : }
224 : }
225 20765 : *dst = '\0';
226 : }
227 21739 : zend_freedtoa(digits);
228 21739 : 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 4547166 : {
317 4547166 : register char *p = buf_end;
318 : register u_wide_int magnitude;
319 :
320 4547166 : if (is_unsigned) {
321 901 : magnitude = (u_wide_int) num;
322 901 : *is_negative = FALSE;
323 : } else {
324 4546265 : *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 4546265 : if (*is_negative) {
336 4536 : wide_int t = num + 1;
337 4536 : magnitude = ((u_wide_int) - t) + 1;
338 : } else {
339 4541729 : 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 17653864 : register u_wide_int new_magnitude = magnitude / 10;
348 :
349 17653864 : *--p = (char)(magnitude - new_magnitude * 10 + '0');
350 17653864 : magnitude = new_magnitude;
351 : }
352 17653864 : while (magnitude);
353 :
354 4547166 : *len = buf_end - p;
355 4547166 : 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 102751 : {
375 102751 : register char *s = buf;
376 : register char *p, *p_orig;
377 : int decimal_point;
378 :
379 102751 : if (precision >= NDIG - 1) {
380 1 : precision = NDIG - 2;
381 : }
382 :
383 102751 : if (format == 'F') {
384 101781 : 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 102751 : 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 102751 : if (format == 'F') {
400 101781 : if (decimal_point <= 0) {
401 100429 : if (num != 0 || precision > 0) {
402 100426 : *s++ = '0';
403 100426 : if (precision > 0) {
404 100426 : *s++ = dec_point;
405 212165 : while (decimal_point++ < 0) {
406 11313 : *s++ = '0';
407 : }
408 0 : } else if (add_dp) {
409 0 : *s++ = dec_point;
410 : }
411 : }
412 : } else {
413 1352 : int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
414 1352 : decimal_point -= addz;
415 9981 : while (decimal_point-- > 0) {
416 7277 : *s++ = *p++;
417 : }
418 2704 : while (addz-- > 0) {
419 0 : *s++ = '0';
420 : }
421 1352 : if (precision > 0 || add_dp) {
422 1206 : *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 1009547 : while (*p) {
436 804045 : *s++ = *p++;
437 : }
438 :
439 102751 : 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 102751 : *len = s - buf;
462 102751 : free(p_orig);
463 102751 : 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 17982 : {
479 17982 : register int mask = (1 << nbits) - 1;
480 17982 : register char *p = buf_end;
481 : static char low_digits[] = "0123456789abcdef";
482 : static char upper_digits[] = "0123456789ABCDEF";
483 17982 : register char *digits = (format == 'X') ? upper_digits : low_digits;
484 :
485 : do {
486 115496 : *--p = digits[num & mask];
487 115496 : num >>= nbits;
488 : }
489 115496 : while (num);
490 :
491 17982 : *len = buf_end - p;
492 17982 : 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 557740 : {
581 : register char *sp;
582 : register char *bep;
583 557740 : register int cc = 0;
584 : register int i;
585 :
586 557740 : register char *s = NULL;
587 : char *q;
588 : int s_len;
589 :
590 557740 : register int min_width = 0;
591 557740 : int precision = 0;
592 : enum {
593 : LEFT, RIGHT
594 : } adjust;
595 : char pad_char;
596 : char prefix_char;
597 :
598 : double fp_num;
599 557740 : wide_int i_num = (wide_int) 0;
600 : u_wide_int ui_num;
601 :
602 : char num_buf[NUM_BUF_SIZE];
603 : char char_buf[2]; /* for printing %% and %<unknown> */
604 :
605 : #ifdef HAVE_LOCALE_H
606 557740 : struct lconv *lconv = NULL;
607 : #endif
608 :
609 : /*
610 : * Flag variables
611 : */
612 : length_modifier_e modifier;
613 : boolean_e alternate_form;
614 : boolean_e print_sign;
615 : boolean_e print_blank;
616 : boolean_e adjust_precision;
617 : boolean_e adjust_width;
618 : bool_int is_negative;
619 :
620 557740 : sp = odp->nextb;
621 557740 : bep = odp->buf_end;
622 :
623 2056407 : while (*fmt) {
624 940927 : if (*fmt != '%') {
625 200447 : INS_CHAR(*fmt, sp, bep, cc);
626 : } else {
627 : /*
628 : * Default variable settings
629 : */
630 740480 : adjust = RIGHT;
631 740480 : alternate_form = print_sign = print_blank = NO;
632 740480 : pad_char = ' ';
633 740480 : prefix_char = NUL;
634 :
635 740480 : fmt++;
636 :
637 : /*
638 : * Try to avoid checking for flags, width or precision
639 : */
640 853424 : if (isascii((int)*fmt) && !islower((int)*fmt)) {
641 : /*
642 : * Recognize flags: -, #, BLANK, +
643 : */
644 12685 : for (;; fmt++) {
645 125629 : if (*fmt == '-')
646 33 : adjust = LEFT;
647 125596 : else if (*fmt == '+')
648 0 : print_sign = YES;
649 125596 : else if (*fmt == '#')
650 0 : alternate_form = YES;
651 125596 : else if (*fmt == ' ')
652 0 : print_blank = YES;
653 125596 : else if (*fmt == '0')
654 12652 : pad_char = '0';
655 : else
656 112944 : break;
657 12685 : }
658 :
659 : /*
660 : * Check if a width was specified
661 : */
662 112944 : if (isdigit((int)*fmt)) {
663 12678 : STR_TO_DEC(fmt, min_width);
664 12678 : adjust_width = YES;
665 100266 : } else if (*fmt == '*') {
666 0 : min_width = va_arg(ap, int);
667 0 : fmt++;
668 0 : adjust_width = YES;
669 0 : if (min_width < 0) {
670 0 : adjust = LEFT;
671 0 : min_width = -min_width;
672 : }
673 : } else
674 100266 : adjust_width = NO;
675 :
676 : /*
677 : * Check if a precision was specified
678 : *
679 : * XXX: an unreasonable amount of precision may be specified
680 : * resulting in overflow of num_buf. Currently we
681 : * ignore this possibility.
682 : */
683 112944 : if (*fmt == '.') {
684 100221 : adjust_precision = YES;
685 100221 : fmt++;
686 100221 : if (isdigit((int)*fmt)) {
687 100116 : STR_TO_DEC(fmt, precision);
688 105 : } else if (*fmt == '*') {
689 105 : precision = va_arg(ap, int);
690 105 : fmt++;
691 105 : if (precision < 0)
692 0 : precision = 0;
693 : } else
694 0 : precision = 0;
695 : } else
696 12723 : adjust_precision = NO;
697 : } else
698 627536 : adjust_precision = adjust_width = NO;
699 :
700 : /*
701 : * Modifier check
702 : */
703 740480 : switch (*fmt) {
704 : case 'L':
705 0 : fmt++;
706 0 : modifier = LM_LONG_DOUBLE;
707 0 : break;
708 : case 'I':
709 0 : fmt++;
710 : #if SIZEOF_LONG_LONG
711 0 : if (*fmt == '6' && *(fmt+1) == '4') {
712 0 : fmt += 2;
713 0 : modifier = LM_LONG_LONG;
714 : } else
715 : #endif
716 0 : if (*fmt == '3' && *(fmt+1) == '2') {
717 0 : fmt += 2;
718 0 : modifier = LM_LONG;
719 : } else {
720 : #ifdef _WIN64
721 : modifier = LM_LONG_LONG;
722 : #else
723 0 : modifier = LM_LONG;
724 : #endif
725 : }
726 0 : break;
727 : case 'l':
728 462443 : fmt++;
729 : #if SIZEOF_LONG_LONG
730 462443 : if (*fmt == 'l') {
731 1532 : fmt++;
732 1532 : modifier = LM_LONG_LONG;
733 : } else
734 : #endif
735 460911 : modifier = LM_LONG;
736 462443 : break;
737 : case 'z':
738 0 : fmt++;
739 0 : modifier = LM_SIZE_T;
740 0 : break;
741 : case 'j':
742 0 : fmt++;
743 : #if SIZEOF_INTMAX_T
744 0 : modifier = LM_INTMAX_T;
745 : #else
746 : modifier = LM_SIZE_T;
747 : #endif
748 0 : break;
749 : case 't':
750 0 : fmt++;
751 : #if SIZEOF_PTRDIFF_T
752 0 : modifier = LM_PTRDIFF_T;
753 : #else
754 : modifier = LM_SIZE_T;
755 : #endif
756 0 : break;
757 : case 'h':
758 0 : fmt++;
759 0 : if (*fmt == 'h') {
760 0 : fmt++;
761 : }
762 : /* these are promoted to int, so no break */
763 : default:
764 278037 : modifier = LM_STD;
765 : break;
766 : }
767 :
768 : /*
769 : * Argument extraction and printing.
770 : * First we determine the argument type.
771 : * Then, we convert the argument to a string.
772 : * On exit from the switch, s points to the string that
773 : * must be printed, s_len has the length of the string
774 : * The precision requirements, if any, are reflected in s_len.
775 : *
776 : * NOTE: pad_char may be set to '0' because of the 0 flag.
777 : * It is reset to ' ' by non-numeric formats
778 : */
779 740480 : switch (*fmt) {
780 : case 'u':
781 249 : switch(modifier) {
782 : default:
783 243 : i_num = (wide_int) va_arg(ap, unsigned int);
784 243 : break;
785 : case LM_LONG_DOUBLE:
786 0 : goto fmt_error;
787 : case LM_LONG:
788 0 : i_num = (wide_int) va_arg(ap, unsigned long int);
789 0 : break;
790 : case LM_SIZE_T:
791 0 : i_num = (wide_int) va_arg(ap, size_t);
792 0 : break;
793 : #if SIZEOF_LONG_LONG
794 : case LM_LONG_LONG:
795 6 : i_num = (wide_int) va_arg(ap, u_wide_int);
796 6 : break;
797 : #endif
798 : #if SIZEOF_INTMAX_T
799 : case LM_INTMAX_T:
800 0 : i_num = (wide_int) va_arg(ap, uintmax_t);
801 0 : break;
802 : #endif
803 : #if SIZEOF_PTRDIFF_T
804 : case LM_PTRDIFF_T:
805 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
806 : break;
807 : #endif
808 : }
809 : /*
810 : * The rest also applies to other integer formats, so fall
811 : * into that case.
812 : */
813 : case 'd':
814 : case 'i':
815 : /*
816 : * Get the arg if we haven't already.
817 : */
818 476638 : if ((*fmt) != 'u') {
819 476389 : switch(modifier) {
820 : default:
821 13956 : i_num = (wide_int) va_arg(ap, int);
822 13956 : break;
823 : case LM_LONG_DOUBLE:
824 0 : goto fmt_error;
825 : case LM_LONG:
826 460907 : i_num = (wide_int) va_arg(ap, long int);
827 460907 : break;
828 : case LM_SIZE_T:
829 : #if SIZEOF_SSIZE_T
830 0 : i_num = (wide_int) va_arg(ap, ssize_t);
831 : #else
832 : i_num = (wide_int) va_arg(ap, size_t);
833 : #endif
834 0 : break;
835 : #if SIZEOF_LONG_LONG
836 : case LM_LONG_LONG:
837 1526 : i_num = (wide_int) va_arg(ap, wide_int);
838 1526 : break;
839 : #endif
840 : #if SIZEOF_INTMAX_T
841 : case LM_INTMAX_T:
842 0 : i_num = (wide_int) va_arg(ap, intmax_t);
843 0 : break;
844 : #endif
845 : #if SIZEOF_PTRDIFF_T
846 : case LM_PTRDIFF_T:
847 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
848 : break;
849 : #endif
850 : }
851 : }
852 476638 : s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
853 : &num_buf[NUM_BUF_SIZE], &s_len);
854 476638 : FIX_PRECISION(adjust_precision, precision, s, s_len);
855 :
856 476638 : if (*fmt != 'u') {
857 476389 : if (is_negative) {
858 24 : prefix_char = '-';
859 476365 : } else if (print_sign) {
860 0 : prefix_char = '+';
861 476365 : } else if (print_blank) {
862 0 : prefix_char = ' ';
863 : }
864 : }
865 476638 : break;
866 :
867 :
868 : case 'o':
869 0 : switch(modifier) {
870 : default:
871 0 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
872 0 : break;
873 : case LM_LONG_DOUBLE:
874 0 : goto fmt_error;
875 : case LM_LONG:
876 0 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
877 0 : break;
878 : case LM_SIZE_T:
879 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
880 0 : break;
881 : #if SIZEOF_LONG_LONG
882 : case LM_LONG_LONG:
883 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
884 0 : break;
885 : #endif
886 : #if SIZEOF_INTMAX_T
887 : case LM_INTMAX_T:
888 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
889 0 : break;
890 : #endif
891 : #if SIZEOF_PTRDIFF_T
892 : case LM_PTRDIFF_T:
893 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
894 : break;
895 : #endif
896 : }
897 0 : s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
898 0 : FIX_PRECISION(adjust_precision, precision, s, s_len);
899 0 : if (alternate_form && *s != '0') {
900 0 : *--s = '0';
901 0 : s_len++;
902 : }
903 0 : break;
904 :
905 :
906 : case 'x':
907 : case 'X':
908 4 : switch(modifier) {
909 : default:
910 0 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
911 0 : break;
912 : case LM_LONG_DOUBLE:
913 0 : goto fmt_error;
914 : case LM_LONG:
915 4 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
916 4 : break;
917 : case LM_SIZE_T:
918 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
919 0 : break;
920 : #if SIZEOF_LONG_LONG
921 : case LM_LONG_LONG:
922 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
923 0 : break;
924 : #endif
925 : #if SIZEOF_INTMAX_T
926 : case LM_INTMAX_T:
927 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
928 0 : break;
929 : #endif
930 : #if SIZEOF_PTRDIFF_T
931 : case LM_PTRDIFF_T:
932 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
933 : break;
934 : #endif
935 : }
936 4 : s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
937 4 : FIX_PRECISION(adjust_precision, precision, s, s_len);
938 4 : if (alternate_form && i_num != 0) {
939 0 : *--s = *fmt; /* 'x' or 'X' */
940 0 : *--s = '0';
941 0 : s_len += 2;
942 : }
943 4 : break;
944 :
945 :
946 : case 's':
947 : case 'v':
948 162542 : s = va_arg(ap, char *);
949 162542 : if (s != NULL) {
950 162542 : s_len = strlen(s);
951 162542 : if (adjust_precision && precision < s_len) {
952 0 : s_len = precision;
953 : }
954 : } else {
955 0 : s = S_NULL;
956 0 : s_len = S_NULL_LEN;
957 : }
958 162542 : pad_char = ' ';
959 162542 : break;
960 :
961 :
962 : case 'f':
963 : case 'F':
964 : case 'e':
965 : case 'E':
966 100115 : switch(modifier) {
967 : case LM_LONG_DOUBLE:
968 0 : fp_num = (double) va_arg(ap, long double);
969 0 : break;
970 : case LM_STD:
971 100115 : fp_num = va_arg(ap, double);
972 100115 : break;
973 : default:
974 0 : goto fmt_error;
975 : }
976 :
977 100115 : if (zend_isnan(fp_num)) {
978 0 : s = "NAN";
979 0 : s_len = 3;
980 100115 : } else if (zend_isinf(fp_num)) {
981 0 : s = "INF";
982 0 : s_len = 3;
983 : } else {
984 : #ifdef HAVE_LOCALE_H
985 100115 : if (!lconv) {
986 100115 : lconv = localeconv();
987 : }
988 : #endif
989 100115 : s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
990 : (adjust_precision == NO) ? FLOAT_DIGITS : precision,
991 : (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
992 : &is_negative, &num_buf[1], &s_len);
993 100115 : if (is_negative)
994 0 : prefix_char = '-';
995 100115 : else if (print_sign)
996 0 : prefix_char = '+';
997 100115 : else if (print_blank)
998 0 : prefix_char = ' ';
999 : }
1000 100115 : break;
1001 :
1002 :
1003 : case 'g':
1004 : case 'k':
1005 : case 'G':
1006 : case 'H':
1007 2 : switch(modifier) {
1008 : case LM_LONG_DOUBLE:
1009 0 : fp_num = (double) va_arg(ap, long double);
1010 0 : break;
1011 : case LM_STD:
1012 2 : fp_num = va_arg(ap, double);
1013 2 : break;
1014 : default:
1015 0 : goto fmt_error;
1016 : }
1017 :
1018 2 : if (zend_isnan(fp_num)) {
1019 0 : s = "NAN";
1020 0 : s_len = 3;
1021 0 : break;
1022 2 : } else if (zend_isinf(fp_num)) {
1023 0 : if (fp_num > 0) {
1024 0 : s = "INF";
1025 0 : s_len = 3;
1026 : } else {
1027 0 : s = "-INF";
1028 0 : s_len = 4;
1029 : }
1030 0 : break;
1031 : }
1032 :
1033 2 : if (adjust_precision == NO) {
1034 0 : precision = FLOAT_DIGITS;
1035 2 : } else if (precision == 0) {
1036 0 : precision = 1;
1037 : }
1038 : /*
1039 : * * We use &num_buf[ 1 ], so that we have room for the sign
1040 : */
1041 : #ifdef HAVE_LOCALE_H
1042 2 : if (!lconv) {
1043 2 : lconv = localeconv();
1044 : }
1045 : #endif
1046 2 : s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
1047 2 : if (*s == '-') {
1048 0 : prefix_char = *s++;
1049 2 : } else if (print_sign) {
1050 0 : prefix_char = '+';
1051 2 : } else if (print_blank) {
1052 0 : prefix_char = ' ';
1053 : }
1054 :
1055 2 : s_len = strlen(s);
1056 :
1057 2 : if (alternate_form && (q = strchr(s, '.')) == NULL) {
1058 0 : s[s_len++] = '.';
1059 : }
1060 2 : break;
1061 :
1062 :
1063 : case 'c':
1064 1134 : char_buf[0] = (char) (va_arg(ap, int));
1065 1134 : s = &char_buf[0];
1066 1134 : s_len = 1;
1067 1134 : pad_char = ' ';
1068 1134 : break;
1069 :
1070 :
1071 : case '%':
1072 45 : char_buf[0] = '%';
1073 45 : s = &char_buf[0];
1074 45 : s_len = 1;
1075 45 : pad_char = ' ';
1076 45 : break;
1077 :
1078 :
1079 : case 'n':
1080 0 : *(va_arg(ap, int *)) = cc;
1081 0 : goto skip_output;
1082 :
1083 : /*
1084 : * Always extract the argument as a "char *" pointer. We
1085 : * should be using "void *" but there are still machines
1086 : * that don't understand it.
1087 : * If the pointer size is equal to the size of an unsigned
1088 : * integer we convert the pointer to a hex number, otherwise
1089 : * we print "%p" to indicate that we don't handle "%p".
1090 : */
1091 : case 'p':
1092 : if (sizeof(char *) <= sizeof(u_wide_int)) {
1093 0 : ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
1094 0 : s = ap_php_conv_p2(ui_num, 4, 'x',
1095 : &num_buf[NUM_BUF_SIZE], &s_len);
1096 0 : if (ui_num != 0) {
1097 0 : *--s = 'x';
1098 0 : *--s = '0';
1099 0 : s_len += 2;
1100 : }
1101 : } else {
1102 : s = "%p";
1103 : s_len = 2;
1104 : }
1105 0 : pad_char = ' ';
1106 0 : break;
1107 :
1108 :
1109 : case NUL:
1110 : /*
1111 : * The last character of the format string was %.
1112 : * We ignore it.
1113 : */
1114 0 : continue;
1115 :
1116 :
1117 0 : fmt_error:
1118 0 : php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
1119 : /*
1120 : * The default case is for unrecognized %'s.
1121 : * We print %<char> to help the user identify what
1122 : * option is not understood.
1123 : * This is also useful in case the user wants to pass
1124 : * the output of format_converter to another function
1125 : * that understands some other %<char> (like syslog).
1126 : * Note that we can't point s inside fmt because the
1127 : * unknown <char> could be preceded by width etc.
1128 : */
1129 : default:
1130 0 : char_buf[0] = '%';
1131 0 : char_buf[1] = *fmt;
1132 0 : s = char_buf;
1133 0 : s_len = 2;
1134 0 : pad_char = ' ';
1135 : break;
1136 : }
1137 :
1138 740480 : if (prefix_char != NUL) {
1139 24 : *--s = prefix_char;
1140 24 : s_len++;
1141 : }
1142 740480 : if (adjust_width && adjust == RIGHT && min_width > s_len) {
1143 7516 : if (pad_char == '0' && prefix_char != NUL) {
1144 0 : INS_CHAR(*s, sp, bep, cc)
1145 0 : s++;
1146 0 : s_len--;
1147 0 : min_width--;
1148 : }
1149 7548 : PAD(min_width, s_len, pad_char);
1150 : }
1151 : /*
1152 : * Print the string s.
1153 : */
1154 5465521 : for (i = s_len; i != 0; i--) {
1155 4725041 : INS_CHAR(*s, sp, bep, cc);
1156 4725041 : s++;
1157 : }
1158 :
1159 740480 : if (adjust_width && adjust == LEFT && min_width > s_len)
1160 1304 : PAD(min_width, s_len, pad_char);
1161 : }
1162 940927 : skip_output:
1163 940927 : fmt++;
1164 : }
1165 557740 : odp->nextb = sp;
1166 557740 : return (cc);
1167 : }
1168 : /* }}} */
1169 :
1170 : /*
1171 : * This is the general purpose conversion function.
1172 : */
1173 : static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1174 557740 : {
1175 : buffy od;
1176 : int cc;
1177 :
1178 : /*
1179 : * First initialize the descriptor
1180 : * Notice that if no length is given, we initialize buf_end to the
1181 : * highest possible address.
1182 : */
1183 557740 : if (len == 0) {
1184 0 : od.buf_end = (char *) ~0;
1185 0 : od.nextb = (char *) ~0;
1186 : } else {
1187 557740 : od.buf_end = &buf[len-1];
1188 557740 : od.nextb = buf;
1189 : }
1190 :
1191 : /*
1192 : * Do the conversion
1193 : */
1194 557740 : cc = format_converter(&od, format, ap);
1195 557740 : if (len != 0 && od.nextb <= od.buf_end) {
1196 557740 : *(od.nextb) = '\0';
1197 : }
1198 557740 : if (ccp) {
1199 557740 : *ccp = cc;
1200 : }
1201 557740 : }
1202 : /* }}} */
1203 :
1204 : PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1205 379219 : {
1206 : int cc;
1207 : va_list ap;
1208 :
1209 379219 : va_start(ap, format);
1210 379219 : strx_printv(&cc, buf, len, format, ap);
1211 379219 : va_end(ap);
1212 379219 : if (cc >= len) {
1213 0 : cc = len -1;
1214 0 : buf[cc] = '\0';
1215 : }
1216 379219 : return cc;
1217 : }
1218 : /* }}} */
1219 :
1220 : PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1221 11 : {
1222 : int cc;
1223 :
1224 11 : strx_printv(&cc, buf, len, format, ap);
1225 11 : if (cc >= len) {
1226 0 : cc = len -1;
1227 0 : buf[cc] = '\0';
1228 : }
1229 11 : return cc;
1230 : }
1231 : /* }}} */
1232 :
1233 : PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
1234 178510 : {
1235 : int cc;
1236 : va_list ap;
1237 :
1238 178510 : va_start(ap, format);
1239 178510 : strx_printv(&cc, buf, len, format, ap);
1240 178510 : va_end(ap);
1241 178510 : return (cc);
1242 : }
1243 : /* }}} */
1244 :
1245 : PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
1246 0 : {
1247 : int cc;
1248 :
1249 0 : strx_printv(&cc, buf, len, format, ap);
1250 0 : return (cc);
1251 : }
1252 : /* }}} */
1253 :
1254 : /*
1255 : * Local variables:
1256 : * tab-width: 4
1257 : * c-basic-offset: 4
1258 : * End:
1259 : * vim600: sw=4 ts=4 fdm=marker
1260 : * vim<600: sw=4 ts=4
1261 : */
|