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