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