1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Marcus Boerger <helly@php.net> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: spprintf.c 277841 2009-03-26 20:02:53Z felipe $ */
20 :
21 : /* This is the spprintf implementation.
22 : * It has emerged from apache snprintf. See original header:
23 : */
24 :
25 : /* ====================================================================
26 : * Copyright (c) 1995-1998 The Apache Group. All rights reserved.
27 : *
28 : * Redistribution and use in source and binary forms, with or without
29 : * modification, are permitted provided that the following conditions
30 : * are met:
31 : *
32 : * 1. Redistributions of source code must retain the above copyright
33 : * notice, this list of conditions and the following disclaimer.
34 : *
35 : * 2. Redistributions in binary form must reproduce the above copyright
36 : * notice, this list of conditions and the following disclaimer in
37 : * the documentation and/or other materials provided with the
38 : * distribution.
39 : *
40 : * 3. All advertising materials mentioning features or use of this
41 : * software must display the following acknowledgment:
42 : * "This product includes software developed by the Apache Group
43 : * for use in the Apache HTTP server project (http://www.apache.org/)."
44 : *
45 : * 4. The names "Apache Server" and "Apache Group" must not be used to
46 : * endorse or promote products derived from this software without
47 : * prior written permission.
48 : *
49 : * 5. Redistributions of any form whatsoever must retain the following
50 : * acknowledgment:
51 : * "This product includes software developed by the Apache Group
52 : * for use in the Apache HTTP server project (http://www.apache.org/)."
53 : *
54 : * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
55 : * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
58 : * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
60 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
61 : * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
63 : * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
64 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
65 : * OF THE POSSIBILITY OF SUCH DAMAGE.
66 : * ====================================================================
67 : *
68 : * This software consists of voluntary contributions made by many
69 : * individuals on behalf of the Apache Group and was originally based
70 : * on public domain software written at the National Center for
71 : * Supercomputing Applications, University of Illinois, Urbana-Champaign.
72 : * For more information on the Apache Group and the Apache HTTP server
73 : * project, please see <http://www.apache.org/>.
74 : *
75 : * This code is based on, and used with the permission of, the
76 : * SIO stdio-replacement strx_* functions by Panos Tsirigotis
77 : * <panos@alumni.cs.colorado.edu> for xinetd.
78 : */
79 :
80 : #define _GNU_SOURCE
81 : #include "php.h"
82 :
83 : #include <stddef.h>
84 : #include <stdio.h>
85 : #include <ctype.h>
86 : #include <sys/types.h>
87 : #include <stdarg.h>
88 : #include <string.h>
89 : #include <stdlib.h>
90 : #include <math.h>
91 : #ifdef HAVE_INTTYPES_H
92 : #include <inttypes.h>
93 : #endif
94 :
95 : #ifdef HAVE_LOCALE_H
96 : #include <locale.h>
97 : #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
98 : #else
99 : #define LCONV_DECIMAL_POINT '.'
100 : #endif
101 :
102 : #include "snprintf.h"
103 :
104 : #define FALSE 0
105 : #define TRUE 1
106 : #define NUL '\0'
107 : #define INT_NULL ((int *)0)
108 :
109 : #define S_NULL "(null)"
110 : #define S_NULL_LEN 6
111 :
112 : #define FLOAT_DIGITS 6
113 : #define EXPONENT_LENGTH 10
114 :
115 : #include "ext/standard/php_smart_str.h"
116 :
117 : /* {{{ macros */
118 :
119 : /*
120 : * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
121 : *
122 : * XXX: this is a magic number; do not decrease it
123 : */
124 : #define NUM_BUF_SIZE 512
125 :
126 : /*
127 : * The INS_CHAR macro inserts a character in the buffer.
128 : *
129 : * NOTE: Evaluation of the ch argument should not have any side-effects
130 : */
131 : #define INS_CHAR_NR(unicode, xbuf, ch) do { \
132 : if (unicode) { \
133 : smart_str_append2c(xbuf, ch); \
134 : } else { \
135 : smart_str_appendc(xbuf, ch); \
136 : } \
137 : } while (0)
138 :
139 : #define INS_STRING(unicode, s_unicode, xbuf, s, s_len) \
140 : do { \
141 : if (unicode) { \
142 : size_t newlen, p, sz = 2*(s_len); \
143 : smart_str_alloc(xbuf, (sz), 0); \
144 : if (s_unicode) { \
145 : memcpy(xbuf->c + xbuf->len, s, (sz)); \
146 : xbuf->len += sz; \
147 : } else { \
148 : p = (s_len); \
149 : while(p--) { \
150 : smart_str_append2c(xbuf, *s); \
151 : *s++; \
152 : } \
153 : } \
154 : } else { \
155 : smart_str_appendl(xbuf, s, s_len); \
156 : } \
157 : } while (0)
158 :
159 : #define INS_CHAR(unicode, xbuf, ch) \
160 : INS_CHAR_NR(unicode, xbuf, ch)
161 :
162 : /*
163 : * Macro that does padding. The padding is done by printing
164 : * the character ch.
165 : */
166 : #define PAD(unicode, xbuf, count, ch) do { \
167 : if ((count) > 0) { \
168 : size_t newlen, p, sz = (count); \
169 : if (unicode) { \
170 : p = sz; \
171 : sz <<= 1; \
172 : smart_str_alloc(xbuf, sz, 0); \
173 : while(p--) smart_str_append2c(xbuf, ch);\
174 : } else { \
175 : smart_str_alloc(xbuf, sz, 0); \
176 : memset(xbuf->c + xbuf->len, ch, sz); \
177 : xbuf->len += sz; \
178 : } \
179 : } \
180 : } while (0)
181 :
182 : #define NUM(c) (c - '0')
183 :
184 : #define STR_TO_DEC(str, num) do { \
185 : num = NUM(*str++); \
186 : while (isdigit((int)*str)) { \
187 : num *= 10; \
188 : num += NUM(*str++); \
189 : if (num >= INT_MAX / 10) { \
190 : while (isdigit((int)*str++)); \
191 : break; \
192 : } \
193 : } \
194 : } while (0)
195 :
196 : /*
197 : * This macro does zero padding so that the precision
198 : * requirement is satisfied. The padding is done by
199 : * adding '0's to the left of the string that is going
200 : * to be printed.
201 : */
202 : #define FIX_PRECISION(adjust, precision, s, s_len) \
203 : do { \
204 : if (adjust) \
205 : while (s_len < precision) { \
206 : *--s = '0'; \
207 : s_len++; \
208 : } \
209 : } while (0)
210 :
211 : /* }}} */
212 :
213 :
214 : #if !HAVE_STRNLEN
215 : static size_t strnlen(const char *s, size_t maxlen) {
216 : char *r = memchr(s, '\0', maxlen);
217 : return r ? r-s : maxlen;
218 : }
219 : #endif
220 :
221 : /*
222 : * Do format conversion placing the output in buffer
223 : */
224 : static void xbuf_format_converter(int unicode, smart_str *xbuf, const char *fmt, va_list ap) /* {{{ */
225 6377948 : {
226 6377948 : char *s = NULL;
227 6377948 : UChar *u = NULL;
228 : char *q;
229 : int s_len, s_unicode, u_len, free_zcopy;
230 : zval *zvp, zcopy;
231 6377948 : int min_width = 0;
232 6377948 : int precision = 0;
233 : enum {
234 : LEFT, RIGHT
235 : } adjust;
236 : char pad_char;
237 : char prefix_char;
238 :
239 : double fp_num;
240 6377948 : wide_int i_num = (wide_int) 0;
241 : u_wide_int ui_num;
242 :
243 : char num_buf[NUM_BUF_SIZE];
244 : char char_buf[2]; /* for printing %% and %<unknown> */
245 : zend_bool free_s; /* free string if allocated here */
246 :
247 : #ifdef HAVE_LOCALE_H
248 6377948 : struct lconv *lconv = NULL;
249 : #endif
250 :
251 : /*
252 : * Flag variables
253 : */
254 : length_modifier_e modifier;
255 : boolean_e alternate_form;
256 : boolean_e print_sign;
257 : boolean_e print_blank;
258 : boolean_e adjust_precision;
259 : boolean_e adjust_width;
260 : bool_int is_negative;
261 :
262 : TSRMLS_FETCH();
263 :
264 111952782 : while (*fmt) {
265 99196886 : if (*fmt != '%') {
266 80643677 : INS_CHAR(unicode, xbuf, *fmt);
267 : } else {
268 : /*
269 : * Default variable settings
270 : */
271 18553209 : adjust = RIGHT;
272 18553209 : alternate_form = print_sign = print_blank = NO;
273 18553209 : pad_char = ' ';
274 18553209 : prefix_char = NUL;
275 18553209 : free_s = 0;
276 18553209 : free_zcopy = 0;
277 18553209 : s_unicode = 0;
278 :
279 18553209 : fmt++;
280 :
281 : /*
282 : * Try to avoid checking for flags, width or precision
283 : */
284 18845722 : if (isascii((int)*fmt) && !islower((int)*fmt)) {
285 : /*
286 : * Recognize flags: -, #, BLANK, +
287 : */
288 19708 : for (;; fmt++) {
289 312221 : if (*fmt == '-')
290 20 : adjust = LEFT;
291 312201 : else if (*fmt == '+')
292 0 : print_sign = YES;
293 312201 : else if (*fmt == '#')
294 0 : alternate_form = YES;
295 312201 : else if (*fmt == ' ')
296 0 : print_blank = YES;
297 312201 : else if (*fmt == '0')
298 19688 : pad_char = '0';
299 : else
300 292513 : break;
301 19708 : }
302 :
303 : /*
304 : * Check if a width was specified
305 : */
306 292513 : if (isdigit((int)*fmt)) {
307 19598 : STR_TO_DEC(fmt, min_width);
308 19598 : adjust_width = YES;
309 272915 : } else if (*fmt == '*') {
310 144339 : min_width = va_arg(ap, int);
311 144339 : fmt++;
312 144339 : adjust_width = YES;
313 144339 : if (min_width < 0) {
314 0 : adjust = LEFT;
315 0 : min_width = -min_width;
316 : }
317 : } else
318 128576 : adjust_width = NO;
319 :
320 : /*
321 : * Check if a precision was specified
322 : *
323 : * XXX: an unreasonable amount of precision may be specified
324 : * resulting in overflow of num_buf. Currently we
325 : * ignore this possibility.
326 : */
327 292513 : if (*fmt == '.') {
328 8761 : adjust_precision = YES;
329 8761 : fmt++;
330 8761 : if (isdigit((int)*fmt)) {
331 390 : STR_TO_DEC(fmt, precision);
332 8371 : } else if (*fmt == '*') {
333 8371 : precision = va_arg(ap, int);
334 8371 : fmt++;
335 8371 : if (precision < 0)
336 0 : precision = 0;
337 : } else
338 0 : precision = 0;
339 : } else
340 283752 : adjust_precision = NO;
341 : } else
342 18260696 : adjust_precision = adjust_width = NO;
343 :
344 : /*
345 : * Modifier check
346 : */
347 18553209 : switch (*fmt) {
348 : case 'L':
349 0 : fmt++;
350 0 : modifier = LM_LONG_DOUBLE;
351 0 : break;
352 : case 'I':
353 0 : fmt++;
354 : #if SIZEOF_LONG_LONG
355 0 : if (*fmt == '6' && *(fmt+1) == '4') {
356 0 : fmt += 2;
357 0 : modifier = LM_LONG_LONG;
358 : } else
359 : #endif
360 0 : if (*fmt == '3' && *(fmt+1) == '2') {
361 0 : fmt += 2;
362 0 : modifier = LM_LONG;
363 : } else {
364 : #ifdef _WIN64
365 : modifier = LM_LONG_LONG;
366 : #else
367 0 : modifier = LM_LONG;
368 : #endif
369 : }
370 0 : break;
371 : case 'l':
372 1297941 : fmt++;
373 : #if SIZEOF_LONG_LONG
374 1297941 : if (*fmt == 'l') {
375 2389 : fmt++;
376 2389 : modifier = LM_LONG_LONG;
377 : } else
378 : #endif
379 1295552 : modifier = LM_LONG;
380 1297941 : break;
381 : case 'z':
382 3 : fmt++;
383 3 : modifier = LM_SIZE_T;
384 3 : break;
385 : case 'j':
386 0 : fmt++;
387 : #if SIZEOF_INTMAX_T
388 0 : modifier = LM_INTMAX_T;
389 : #else
390 : modifier = LM_SIZE_T;
391 : #endif
392 0 : break;
393 : case 't':
394 0 : fmt++;
395 : #if SIZEOF_PTRDIFF_T
396 0 : modifier = LM_PTRDIFF_T;
397 : #else
398 : modifier = LM_SIZE_T;
399 : #endif
400 0 : break;
401 : case 'h':
402 0 : fmt++;
403 0 : if (*fmt == 'h') {
404 0 : fmt++;
405 : }
406 : /* these are promoted to int, so no break */
407 : default:
408 17255265 : modifier = LM_STD;
409 : break;
410 : }
411 :
412 : /*
413 : * Argument extraction and printing.
414 : * First we determine the argument type.
415 : * Then, we convert the argument to a string.
416 : * On exit from the switch, s points to the string that
417 : * must be printed, s_len has the length of the string
418 : * The precision requirements, if any, are reflected in s_len.
419 : *
420 : * NOTE: pad_char may be set to '0' because of the 0 flag.
421 : * It is reset to ' ' by non-numeric formats
422 : */
423 18553209 : switch (*fmt) {
424 : case 'Z':
425 184 : zvp = (zval*) va_arg(ap, zval*);
426 184 : if (unicode) {
427 0 : zend_make_unicode_zval(zvp, &zcopy, &free_zcopy);
428 0 : s_unicode = 1;
429 : } else {
430 184 : zend_make_string_zval(zvp, &zcopy, &free_zcopy);
431 : }
432 184 : if (free_zcopy) {
433 184 : zvp = &zcopy;
434 : }
435 184 : s_len = Z_UNILEN_P(zvp);
436 184 : s = Z_STRVAL_P(zvp);
437 184 : if (adjust_precision && precision < s_len) {
438 0 : s_len = precision;
439 : }
440 184 : break;
441 : case 'u':
442 1027 : switch(modifier) {
443 : default:
444 1025 : i_num = (wide_int) va_arg(ap, unsigned int);
445 1025 : break;
446 : case LM_LONG_DOUBLE:
447 0 : goto fmt_error;
448 : case LM_LONG:
449 1 : i_num = (wide_int) va_arg(ap, unsigned long int);
450 1 : break;
451 : case LM_SIZE_T:
452 0 : i_num = (wide_int) va_arg(ap, size_t);
453 0 : break;
454 : #if SIZEOF_LONG_LONG
455 : case LM_LONG_LONG:
456 1 : i_num = (wide_int) va_arg(ap, u_wide_int);
457 1 : break;
458 : #endif
459 : #if SIZEOF_INTMAX_T
460 : case LM_INTMAX_T:
461 0 : i_num = (wide_int) va_arg(ap, uintmax_t);
462 0 : break;
463 : #endif
464 : #if SIZEOF_PTRDIFF_T
465 : case LM_PTRDIFF_T:
466 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
467 : break;
468 : #endif
469 : }
470 : /*
471 : * The rest also applies to other integer formats, so fall
472 : * into that case.
473 : */
474 : case 'd':
475 : case 'i':
476 : /*
477 : * Get the arg if we haven't already.
478 : */
479 3910383 : if ((*fmt) != 'u') {
480 3909356 : switch(modifier) {
481 : default:
482 2611414 : i_num = (wide_int) va_arg(ap, int);
483 2611414 : break;
484 : case LM_LONG_DOUBLE:
485 0 : goto fmt_error;
486 : case LM_LONG:
487 1295551 : i_num = (wide_int) va_arg(ap, long int);
488 1295551 : break;
489 : case LM_SIZE_T:
490 : #if SIZEOF_SSIZE_T
491 3 : i_num = (wide_int) va_arg(ap, ssize_t);
492 : #else
493 : i_num = (wide_int) va_arg(ap, size_t);
494 : #endif
495 3 : break;
496 : #if SIZEOF_LONG_LONG
497 : case LM_LONG_LONG:
498 2388 : i_num = (wide_int) va_arg(ap, wide_int);
499 2388 : break;
500 : #endif
501 : #if SIZEOF_INTMAX_T
502 : case LM_INTMAX_T:
503 0 : i_num = (wide_int) va_arg(ap, intmax_t);
504 0 : break;
505 : #endif
506 : #if SIZEOF_PTRDIFF_T
507 : case LM_PTRDIFF_T:
508 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
509 : break;
510 : #endif
511 : }
512 : }
513 3910383 : s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
514 : &num_buf[NUM_BUF_SIZE], &s_len);
515 3910383 : FIX_PRECISION(adjust_precision, precision, s, s_len);
516 :
517 3910383 : if (*fmt != 'u') {
518 3909356 : if (is_negative)
519 2731 : prefix_char = '-';
520 3906625 : else if (print_sign)
521 0 : prefix_char = '+';
522 3906625 : else if (print_blank)
523 0 : prefix_char = ' ';
524 : }
525 3910383 : break;
526 :
527 :
528 : case 'o':
529 2 : switch(modifier) {
530 : default:
531 2 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
532 2 : break;
533 : case LM_LONG_DOUBLE:
534 0 : goto fmt_error;
535 : case LM_LONG:
536 0 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
537 0 : break;
538 : case LM_SIZE_T:
539 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
540 0 : break;
541 : #if SIZEOF_LONG_LONG
542 : case LM_LONG_LONG:
543 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
544 0 : break;
545 : #endif
546 : #if SIZEOF_INTMAX_T
547 : case LM_INTMAX_T:
548 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
549 0 : break;
550 : #endif
551 : #if SIZEOF_PTRDIFF_T
552 : case LM_PTRDIFF_T:
553 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
554 : break;
555 : #endif
556 : }
557 2 : s = ap_php_conv_p2(ui_num, 3, *fmt,
558 : &num_buf[NUM_BUF_SIZE], &s_len);
559 2 : FIX_PRECISION(adjust_precision, precision, s, s_len);
560 2 : if (alternate_form && *s != '0') {
561 0 : *--s = '0';
562 0 : s_len++;
563 : }
564 2 : break;
565 :
566 :
567 : case 'x':
568 : case 'X':
569 854 : switch(modifier) {
570 : default:
571 854 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
572 854 : break;
573 : case LM_LONG_DOUBLE:
574 0 : goto fmt_error;
575 : case LM_LONG:
576 0 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
577 0 : break;
578 : case LM_SIZE_T:
579 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
580 0 : break;
581 : #if SIZEOF_LONG_LONG
582 : case LM_LONG_LONG:
583 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
584 0 : break;
585 : #endif
586 : #if SIZEOF_INTMAX_T
587 : case LM_INTMAX_T:
588 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
589 0 : break;
590 : #endif
591 : #if SIZEOF_PTRDIFF_T
592 : case LM_PTRDIFF_T:
593 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
594 : break;
595 : #endif
596 : }
597 854 : s = ap_php_conv_p2(ui_num, 4, *fmt,
598 : &num_buf[NUM_BUF_SIZE], &s_len);
599 854 : FIX_PRECISION(adjust_precision, precision, s, s_len);
600 854 : if (alternate_form && i_num != 0) {
601 0 : *--s = *fmt; /* 'x' or 'X' */
602 0 : *--s = '0';
603 0 : s_len += 2;
604 : }
605 854 : break;
606 :
607 : case 'v':
608 5423182 : goto fmt_unicode;
609 : break;
610 :
611 : case 'R':
612 : {
613 119626 : int type = va_arg(ap, int);
614 119626 : if (type != IS_UNICODE) {
615 12962 : if (alternate_form) {
616 0 : va_arg(ap, UConverter *);
617 : }
618 12962 : goto fmt_string;
619 : }
620 : }
621 : /* break omitted */
622 :
623 : case 'r':
624 5529902 : fmt_unicode:
625 : {
626 5529902 : UConverter *conv = ZEND_U_CONVERTER(UG(output_encoding_conv));
627 5529902 : UErrorCode status = U_ZERO_ERROR;
628 5529902 : char *res = NULL;
629 :
630 5529902 : if (alternate_form) {
631 0 : conv = va_arg(ap, UConverter *);
632 : }
633 :
634 5529902 : u = va_arg(ap, UChar *);
635 5529902 : if (u == NULL) {
636 0 : s = S_NULL;
637 0 : s_len = S_NULL_LEN;
638 0 : break;
639 : }
640 :
641 5529902 : u_len = u_strlen(u);
642 5529902 : if (unicode) {
643 3894 : s_len = u_len; /* UTODO actually we think of 2bytes each atm */
644 3894 : s = (char*)u;
645 3894 : s_unicode = 1;
646 : } else {
647 5526008 : zend_unicode_to_string_ex(conv, &res, &s_len, u, u_len, &status);
648 5526008 : if (U_FAILURE(status)) {
649 0 : php_error(E_WARNING, "Could not convert Unicode to printable form in s[np]printf call");
650 0 : return;
651 : }
652 5526008 : s = res;
653 5526008 : free_s = 1;
654 : }
655 :
656 5529902 : pad_char = ' ';
657 5529902 : break;
658 : }
659 :
660 : case 's':
661 8937174 : fmt_string:
662 8937174 : s = va_arg(ap, char *);
663 8937174 : if (s != NULL) {
664 8937166 : if (!adjust_precision) {
665 8936970 : s_len = strlen(s);
666 : } else {
667 196 : s_len = strnlen(s, precision);
668 : }
669 : } else {
670 8 : s = S_NULL;
671 8 : s_len = S_NULL_LEN;
672 : }
673 8937174 : pad_char = ' ';
674 8937174 : break;
675 :
676 : case 'f':
677 : case 'F':
678 : case 'e':
679 : case 'E':
680 1513 : switch(modifier) {
681 : case LM_LONG_DOUBLE:
682 0 : fp_num = (double) va_arg(ap, long double);
683 0 : break;
684 : case LM_STD:
685 1513 : fp_num = va_arg(ap, double);
686 1513 : break;
687 : default:
688 0 : goto fmt_error;
689 : }
690 :
691 1513 : if (zend_isnan(fp_num)) {
692 0 : s = "nan";
693 0 : s_len = 3;
694 1513 : } else if (zend_isinf(fp_num)) {
695 2 : s = "inf";
696 2 : s_len = 3;
697 : } else {
698 : #ifdef HAVE_LOCALE_H
699 1511 : if (!lconv) {
700 1511 : lconv = localeconv();
701 : }
702 : #endif
703 1511 : s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
704 : (adjust_precision == NO) ? FLOAT_DIGITS : precision,
705 : (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
706 : &is_negative, &num_buf[1], &s_len);
707 1511 : if (is_negative)
708 4 : prefix_char = '-';
709 1507 : else if (print_sign)
710 0 : prefix_char = '+';
711 1507 : else if (print_blank)
712 0 : prefix_char = ' ';
713 : }
714 1513 : break;
715 :
716 :
717 : case 'g':
718 : case 'k':
719 : case 'G':
720 : case 'H':
721 7052 : switch(modifier) {
722 : case LM_LONG_DOUBLE:
723 0 : fp_num = (double) va_arg(ap, long double);
724 0 : break;
725 : case LM_STD:
726 7052 : fp_num = va_arg(ap, double);
727 7052 : break;
728 : default:
729 0 : goto fmt_error;
730 : }
731 :
732 7052 : if (zend_isnan(fp_num)) {
733 99 : s = "NAN";
734 99 : s_len = 3;
735 99 : break;
736 6953 : } else if (zend_isinf(fp_num)) {
737 99 : if (fp_num > 0) {
738 76 : s = "INF";
739 76 : s_len = 3;
740 : } else {
741 23 : s = "-INF";
742 23 : s_len = 4;
743 : }
744 99 : break;
745 : }
746 :
747 6854 : if (adjust_precision == NO)
748 0 : precision = FLOAT_DIGITS;
749 6854 : else if (precision == 0)
750 0 : precision = 1;
751 : /*
752 : * * We use &num_buf[ 1 ], so that we have room for the sign
753 : */
754 : #ifdef HAVE_LOCALE_H
755 6854 : if (!lconv) {
756 6854 : lconv = localeconv();
757 : }
758 : #endif
759 6854 : s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
760 6854 : if (*s == '-')
761 1632 : prefix_char = *s++;
762 5222 : else if (print_sign)
763 0 : prefix_char = '+';
764 5222 : else if (print_blank)
765 0 : prefix_char = ' ';
766 :
767 6854 : s_len = strlen(s);
768 :
769 6854 : if (alternate_form && (q = strchr(s, '.')) == NULL)
770 0 : s[s_len++] = '.';
771 6854 : break;
772 :
773 :
774 : case 'c':
775 165167 : char_buf[0] = (char) (va_arg(ap, int));
776 165167 : s = &char_buf[0];
777 165167 : s_len = 1;
778 165167 : pad_char = ' ';
779 165167 : break;
780 :
781 :
782 : case '%':
783 4 : char_buf[0] = '%';
784 4 : s = &char_buf[0];
785 4 : s_len = 1;
786 4 : pad_char = ' ';
787 4 : break;
788 :
789 :
790 : case 'n':
791 0 : *(va_arg(ap, int *)) = xbuf->len;
792 0 : goto skip_output;
793 :
794 : /*
795 : * Always extract the argument as a "char *" pointer. We
796 : * should be using "void *" but there are still machines
797 : * that don't understand it.
798 : * If the pointer size is equal to the size of an unsigned
799 : * integer we convert the pointer to a hex number, otherwise
800 : * we print "%p" to indicate that we don't handle "%p".
801 : */
802 : case 'p':
803 : if (sizeof(char *) <= sizeof(u_wide_int)) {
804 974 : ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
805 974 : s = ap_php_conv_p2(ui_num, 4, 'x',
806 : &num_buf[NUM_BUF_SIZE], &s_len);
807 974 : if (ui_num != 0) {
808 974 : *--s = 'x';
809 974 : *--s = '0';
810 974 : s_len += 2;
811 : }
812 : } else {
813 : s = "%p";
814 : s_len = 2;
815 : }
816 974 : pad_char = ' ';
817 974 : break;
818 :
819 :
820 : case NUL:
821 : /*
822 : * The last character of the format string was %.
823 : * We ignore it.
824 : */
825 0 : continue;
826 :
827 :
828 0 : fmt_error:
829 0 : php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
830 : /*
831 : * The default case is for unrecognized %'s.
832 : * We print %<char> to help the user identify what
833 : * option is not understood.
834 : * This is also useful in case the user wants to pass
835 : * the output of format_converter to another function
836 : * that understands some other %<char> (like syslog).
837 : * Note that we can't point s inside fmt because the
838 : * unknown <char> could be preceded by width etc.
839 : */
840 : default:
841 0 : char_buf[0] = '%';
842 0 : char_buf[1] = *fmt;
843 0 : s = char_buf;
844 0 : s_len = 2;
845 0 : pad_char = ' ';
846 : break;
847 : }
848 :
849 18553209 : if (prefix_char != NUL) {
850 4367 : *--s = prefix_char;
851 4367 : s_len++;
852 : }
853 18553209 : if (adjust_width && adjust == RIGHT && min_width > s_len) {
854 155932 : if (pad_char == '0' && prefix_char != NUL) {
855 0 : INS_CHAR(unicode, xbuf, *s);
856 0 : s++;
857 0 : s_len--;
858 0 : min_width--;
859 : }
860 155932 : PAD(unicode, xbuf, min_width - s_len, pad_char);
861 : }
862 : /*
863 : * Print the string s.
864 : */
865 18553209 : INS_STRING(unicode, s_unicode, xbuf, s, s_len);
866 18553209 : if (free_s) {
867 5526008 : efree(s);
868 : }
869 18553209 : if (free_zcopy) {
870 184 : zval_dtor(&zcopy);
871 : }
872 :
873 18553209 : if (adjust_width && adjust == LEFT && min_width > s_len) {
874 20 : PAD(unicode, xbuf, min_width - s_len, pad_char);
875 : }
876 : }
877 99196886 : skip_output:
878 99196886 : fmt++;
879 : }
880 6377948 : return;
881 : }
882 : /* }}} */
883 :
884 : /*
885 : * This is the general purpose conversion function.
886 : */
887 : PHPAPI int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) /* {{{ */
888 6341474 : {
889 6341474 : smart_str xbuf = {0};
890 :
891 6341474 : xbuf_format_converter(0, &xbuf, format, ap);
892 :
893 6341474 : if (max_len && xbuf.len > max_len) {
894 1 : xbuf.len = max_len;
895 : }
896 6341474 : smart_str_0(&xbuf);
897 :
898 6341474 : *pbuf = xbuf.c;
899 :
900 6341474 : return xbuf.len;
901 : }
902 : /* }}} */
903 :
904 : PHPAPI int spprintf(char **pbuf, size_t max_len, const char *format, ...) /* {{{ */
905 1810174 : {
906 : int cc;
907 : va_list ap;
908 :
909 1810174 : va_start(ap, format);
910 1810174 : cc = vspprintf(pbuf, max_len, format, ap);
911 1810174 : va_end(ap);
912 1810174 : return (cc);
913 : }
914 : /* }}} */
915 :
916 : PHPAPI int vuspprintf(UChar **pbuf, size_t max_len, const char *format, va_list ap) /* {{{ */
917 36474 : {
918 36474 : smart_str xbuf = {0};
919 :
920 36474 : xbuf_format_converter(1, &xbuf, format, ap);
921 :
922 36474 : if (max_len && xbuf.len > max_len) {
923 0 : xbuf.len = max_len;
924 : }
925 36474 : smart_str_0(&xbuf);
926 :
927 36474 : *pbuf = (UChar*)xbuf.c;
928 :
929 36474 : return xbuf.len / sizeof(UChar);
930 : }
931 : /* }}} */
932 :
933 : PHPAPI int uspprintf(UChar **pbuf, size_t max_len, const char *format, ...) /* {{{ */
934 0 : {
935 : int cc;
936 : va_list ap;
937 :
938 0 : va_start(ap, format);
939 0 : cc = vuspprintf(pbuf, max_len, format, ap);
940 0 : va_end(ap);
941 0 : return (cc);
942 : }
943 : /* }}} */
944 :
945 : PHPAPI int vzspprintf(zend_uchar type, zstr *pbuf, size_t max_len, const char *format, va_list ap) /* {{{ */
946 4926 : {
947 4926 : if (type == IS_UNICODE) {
948 3894 : return vuspprintf(&pbuf->u, max_len, format, ap);
949 : } else {
950 1032 : return vspprintf(&pbuf->s, max_len, format, ap);
951 : }
952 : }
953 : /* }}} */
954 :
955 : PHPAPI int zspprintf( zend_uchar type, zstr *pbuf, size_t max_len, const char *format, ...) /* {{{ */
956 4926 : {
957 : int cc;
958 : va_list ap;
959 :
960 4926 : va_start(ap, format);
961 4926 : cc = vzspprintf(type, pbuf, max_len, format, ap);
962 4926 : va_end(ap);
963 4926 : return (cc);
964 : }
965 : /* }}} */
966 :
967 : /*
968 : * Local variables:
969 : * tab-width: 4
970 : * c-basic-offset: 4
971 : * End:
972 : * vim600: sw=4 ts=4 fdm=marker
973 : * vim<600: sw=4 ts=4
974 : */
|