1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 5 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Marcus Boerger <helly@php.net> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: spprintf.c 272370 2008-12-31 11:15:49Z sebastian $ */
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 : #define _GNU_SOURCE
80 : #include "php.h"
81 :
82 : #include <stddef.h>
83 : #include <stdio.h>
84 : #include <ctype.h>
85 : #include <sys/types.h>
86 : #include <stdarg.h>
87 : #include <string.h>
88 : #include <stdlib.h>
89 : #include <math.h>
90 : #ifdef HAVE_INTTYPES_H
91 : #include <inttypes.h>
92 : #endif
93 :
94 : #ifdef HAVE_LOCALE_H
95 : #include <locale.h>
96 : #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
97 : #else
98 : #define LCONV_DECIMAL_POINT '.'
99 : #endif
100 :
101 : #include "snprintf.h"
102 :
103 : #define FALSE 0
104 : #define TRUE 1
105 : #define NUL '\0'
106 : #define INT_NULL ((int *)0)
107 :
108 : #define S_NULL "(null)"
109 : #define S_NULL_LEN 6
110 :
111 : #define FLOAT_DIGITS 6
112 : #define EXPONENT_LENGTH 10
113 :
114 : #include "ext/standard/php_smart_str.h"
115 :
116 : /* {{{ macros */
117 :
118 : /*
119 : * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
120 : *
121 : * XXX: this is a magic number; do not decrease it
122 : */
123 : #define NUM_BUF_SIZE 512
124 :
125 : /*
126 : * The INS_CHAR macro inserts a character in the buffer.
127 : *
128 : * NOTE: Evaluation of the ch argument should not have any side-effects
129 : */
130 : #define INS_CHAR_NR(xbuf, ch) do { \
131 : smart_str_appendc(xbuf, ch); \
132 : } while (0)
133 :
134 : #define INS_STRING(xbuf, s, slen) do { \
135 : smart_str_appendl(xbuf, s, slen); \
136 : } while (0)
137 :
138 : #define INS_CHAR(xbuf, ch) \
139 : INS_CHAR_NR(xbuf, ch)
140 :
141 : /*
142 : * Macro that does padding. The padding is done by printing
143 : * the character ch.
144 : */
145 : #define PAD(xbuf, count, ch) do { \
146 : if ((count) > 0) { \
147 : size_t newlen; \
148 : smart_str_alloc(xbuf, (count), 0); \
149 : memset(xbuf->c + xbuf->len, ch, (count)); \
150 : xbuf->len += (count); \
151 : } \
152 : } while (0)
153 :
154 : #define NUM(c) (c - '0')
155 :
156 : #define STR_TO_DEC(str, num) do { \
157 : num = NUM(*str++); \
158 : while (isdigit((int)*str)) { \
159 : num *= 10; \
160 : num += NUM(*str++); \
161 : if (num >= INT_MAX / 10) { \
162 : while (isdigit((int)*str++)); \
163 : break; \
164 : } \
165 : } \
166 : } while (0)
167 :
168 : /*
169 : * This macro does zero padding so that the precision
170 : * requirement is satisfied. The padding is done by
171 : * adding '0's to the left of the string that is going
172 : * to be printed.
173 : */
174 : #define FIX_PRECISION(adjust, precision, s, s_len) do { \
175 : if (adjust) \
176 : while (s_len < precision) { \
177 : *--s = '0'; \
178 : s_len++; \
179 : } \
180 : } while (0)
181 :
182 : /* }}} */
183 :
184 :
185 : #if !HAVE_STRNLEN
186 : static size_t strnlen(const char *s, size_t maxlen) {
187 : char *r = memchr(s, '\0', maxlen);
188 : return r ? r-s : maxlen;
189 : }
190 : #endif
191 :
192 : /*
193 : * Do format conversion placing the output in buffer
194 : */
195 : static void xbuf_format_converter(smart_str *xbuf, const char *fmt, va_list ap) /* {{{ */
196 7350884 : {
197 7350884 : char *s = NULL;
198 : char *q;
199 : int s_len, free_zcopy;
200 : zval *zvp, zcopy;
201 :
202 7350884 : int min_width = 0;
203 7350884 : int precision = 0;
204 : enum {
205 : LEFT, RIGHT
206 : } adjust;
207 : char pad_char;
208 : char prefix_char;
209 :
210 : double fp_num;
211 7350884 : wide_int i_num = (wide_int) 0;
212 : u_wide_int ui_num;
213 :
214 : char num_buf[NUM_BUF_SIZE];
215 : char char_buf[2]; /* for printing %% and %<unknown> */
216 :
217 : #ifdef HAVE_LOCALE_H
218 7350884 : struct lconv *lconv = NULL;
219 : #endif
220 :
221 : /*
222 : * Flag variables
223 : */
224 : length_modifier_e modifier;
225 : boolean_e alternate_form;
226 : boolean_e print_sign;
227 : boolean_e print_blank;
228 : boolean_e adjust_precision;
229 : boolean_e adjust_width;
230 : bool_int is_negative;
231 :
232 103890072 : while (*fmt) {
233 89188304 : if (*fmt != '%') {
234 70854710 : INS_CHAR(xbuf, *fmt);
235 : } else {
236 : /*
237 : * Default variable settings
238 : */
239 18333594 : adjust = RIGHT;
240 18333594 : alternate_form = print_sign = print_blank = NO;
241 18333594 : pad_char = ' ';
242 18333594 : prefix_char = NUL;
243 18333594 : free_zcopy = 0;
244 :
245 18333594 : fmt++;
246 :
247 : /*
248 : * Try to avoid checking for flags, width or precision
249 : */
250 18536945 : if (isascii((int)*fmt) && !islower((int)*fmt)) {
251 : /*
252 : * Recognize flags: -, #, BLANK, +
253 : */
254 23651 : for (;; fmt++) {
255 227002 : if (*fmt == '-')
256 20 : adjust = LEFT;
257 226982 : else if (*fmt == '+')
258 0 : print_sign = YES;
259 226982 : else if (*fmt == '#')
260 0 : alternate_form = YES;
261 226982 : else if (*fmt == ' ')
262 0 : print_blank = YES;
263 226982 : else if (*fmt == '0')
264 23631 : pad_char = '0';
265 : else
266 203351 : break;
267 23651 : }
268 :
269 : /*
270 : * Check if a width was specified
271 : */
272 203351 : if (isdigit((int)*fmt)) {
273 23412 : STR_TO_DEC(fmt, min_width);
274 23412 : adjust_width = YES;
275 179939 : } else if (*fmt == '*') {
276 151212 : min_width = va_arg(ap, int);
277 151212 : fmt++;
278 151212 : adjust_width = YES;
279 151212 : if (min_width < 0) {
280 0 : adjust = LEFT;
281 0 : min_width = -min_width;
282 : }
283 : } else
284 28727 : adjust_width = NO;
285 :
286 : /*
287 : * Check if a precision was specified
288 : *
289 : * XXX: an unreasonable amount of precision may be specified
290 : * resulting in overflow of num_buf. Currently we
291 : * ignore this possibility.
292 : */
293 203351 : if (*fmt == '.') {
294 28725 : adjust_precision = YES;
295 28725 : fmt++;
296 28725 : if (isdigit((int)*fmt)) {
297 512 : STR_TO_DEC(fmt, precision);
298 28213 : } else if (*fmt == '*') {
299 28213 : precision = va_arg(ap, int);
300 28213 : fmt++;
301 28213 : if (precision < 0)
302 0 : precision = 0;
303 : } else
304 0 : precision = 0;
305 : } else
306 174626 : adjust_precision = NO;
307 : } else
308 18130243 : adjust_precision = adjust_width = NO;
309 :
310 : /*
311 : * Modifier check
312 : */
313 18333594 : switch (*fmt) {
314 : case 'L':
315 0 : fmt++;
316 0 : modifier = LM_LONG_DOUBLE;
317 0 : break;
318 : case 'I':
319 0 : fmt++;
320 : #if SIZEOF_LONG_LONG
321 0 : if (*fmt == '6' && *(fmt+1) == '4') {
322 0 : fmt += 2;
323 0 : modifier = LM_LONG_LONG;
324 : } else
325 : #endif
326 0 : if (*fmt == '3' && *(fmt+1) == '2') {
327 0 : fmt += 2;
328 0 : modifier = LM_LONG;
329 : } else {
330 : #ifdef _WIN64
331 : modifier = LM_LONG_LONG;
332 : #else
333 0 : modifier = LM_LONG;
334 : #endif
335 : }
336 0 : break;
337 : case 'l':
338 2572656 : fmt++;
339 : #if SIZEOF_LONG_LONG
340 2572656 : if (*fmt == 'l') {
341 1 : fmt++;
342 1 : modifier = LM_LONG_LONG;
343 : } else
344 : #endif
345 2572655 : modifier = LM_LONG;
346 2572656 : break;
347 : case 'z':
348 2 : fmt++;
349 2 : modifier = LM_SIZE_T;
350 2 : break;
351 : case 'j':
352 0 : fmt++;
353 : #if SIZEOF_INTMAX_T
354 0 : modifier = LM_INTMAX_T;
355 : #else
356 : modifier = LM_SIZE_T;
357 : #endif
358 0 : break;
359 : case 't':
360 0 : fmt++;
361 : #if SIZEOF_PTRDIFF_T
362 0 : modifier = LM_PTRDIFF_T;
363 : #else
364 : modifier = LM_SIZE_T;
365 : #endif
366 0 : break;
367 : case 'h':
368 0 : fmt++;
369 0 : if (*fmt == 'h') {
370 0 : fmt++;
371 : }
372 : /* these are promoted to int, so no break */
373 : default:
374 15760936 : modifier = LM_STD;
375 : break;
376 : }
377 :
378 : /*
379 : * Argument extraction and printing.
380 : * First we determine the argument type.
381 : * Then, we convert the argument to a string.
382 : * On exit from the switch, s points to the string that
383 : * must be printed, s_len has the length of the string
384 : * The precision requirements, if any, are reflected in s_len.
385 : *
386 : * NOTE: pad_char may be set to '0' because of the 0 flag.
387 : * It is reset to ' ' by non-numeric formats
388 : */
389 18333594 : switch (*fmt) {
390 : case 'Z':
391 0 : zvp = (zval*) va_arg(ap, zval*);
392 0 : zend_make_printable_zval(zvp, &zcopy, &free_zcopy);
393 0 : if (free_zcopy) {
394 0 : zvp = &zcopy;
395 : }
396 0 : s_len = Z_STRLEN_P(zvp);
397 0 : s = Z_STRVAL_P(zvp);
398 0 : if (adjust_precision && precision < s_len) {
399 0 : s_len = precision;
400 : }
401 0 : break;
402 : case 'u':
403 1044 : switch(modifier) {
404 : default:
405 1041 : i_num = (wide_int) va_arg(ap, unsigned int);
406 1041 : break;
407 : case LM_LONG_DOUBLE:
408 0 : goto fmt_error;
409 : case LM_LONG:
410 2 : i_num = (wide_int) va_arg(ap, unsigned long int);
411 2 : break;
412 : case LM_SIZE_T:
413 0 : i_num = (wide_int) va_arg(ap, size_t);
414 0 : break;
415 : #if SIZEOF_LONG_LONG
416 : case LM_LONG_LONG:
417 1 : i_num = (wide_int) va_arg(ap, u_wide_int);
418 1 : break;
419 : #endif
420 : #if SIZEOF_INTMAX_T
421 : case LM_INTMAX_T:
422 0 : i_num = (wide_int) va_arg(ap, uintmax_t);
423 0 : break;
424 : #endif
425 : #if SIZEOF_PTRDIFF_T
426 : case LM_PTRDIFF_T:
427 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
428 : break;
429 : #endif
430 : }
431 : /*
432 : * The rest also applies to other integer formats, so fall
433 : * into that case.
434 : */
435 : case 'd':
436 : case 'i':
437 : /*
438 : * Get the arg if we haven't already.
439 : */
440 4845010 : if ((*fmt) != 'u') {
441 4843966 : switch(modifier) {
442 : default:
443 2271311 : i_num = (wide_int) va_arg(ap, int);
444 2271311 : break;
445 : case LM_LONG_DOUBLE:
446 0 : goto fmt_error;
447 : case LM_LONG:
448 2572653 : i_num = (wide_int) va_arg(ap, long int);
449 2572653 : break;
450 : case LM_SIZE_T:
451 : #if SIZEOF_SSIZE_T
452 2 : i_num = (wide_int) va_arg(ap, ssize_t);
453 : #else
454 : i_num = (wide_int) va_arg(ap, size_t);
455 : #endif
456 2 : break;
457 : #if SIZEOF_LONG_LONG
458 : case LM_LONG_LONG:
459 0 : i_num = (wide_int) va_arg(ap, wide_int);
460 0 : break;
461 : #endif
462 : #if SIZEOF_INTMAX_T
463 : case LM_INTMAX_T:
464 0 : i_num = (wide_int) va_arg(ap, intmax_t);
465 0 : break;
466 : #endif
467 : #if SIZEOF_PTRDIFF_T
468 : case LM_PTRDIFF_T:
469 0 : i_num = (wide_int) va_arg(ap, ptrdiff_t);
470 : break;
471 : #endif
472 : }
473 : }
474 4845010 : s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
475 : &num_buf[NUM_BUF_SIZE], &s_len);
476 4845010 : FIX_PRECISION(adjust_precision, precision, s, s_len);
477 :
478 4845010 : if (*fmt != 'u') {
479 4843966 : if (is_negative)
480 6053 : prefix_char = '-';
481 4837913 : else if (print_sign)
482 0 : prefix_char = '+';
483 4837913 : else if (print_blank)
484 0 : prefix_char = ' ';
485 : }
486 4845010 : break;
487 :
488 :
489 : case 'o':
490 2 : switch(modifier) {
491 : default:
492 2 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
493 2 : break;
494 : case LM_LONG_DOUBLE:
495 0 : goto fmt_error;
496 : case LM_LONG:
497 0 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
498 0 : break;
499 : case LM_SIZE_T:
500 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
501 0 : break;
502 : #if SIZEOF_LONG_LONG
503 : case LM_LONG_LONG:
504 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
505 0 : break;
506 : #endif
507 : #if SIZEOF_INTMAX_T
508 : case LM_INTMAX_T:
509 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
510 0 : break;
511 : #endif
512 : #if SIZEOF_PTRDIFF_T
513 : case LM_PTRDIFF_T:
514 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
515 : break;
516 : #endif
517 : }
518 2 : s = ap_php_conv_p2(ui_num, 3, *fmt,
519 : &num_buf[NUM_BUF_SIZE], &s_len);
520 2 : FIX_PRECISION(adjust_precision, precision, s, s_len);
521 2 : if (alternate_form && *s != '0') {
522 0 : *--s = '0';
523 0 : s_len++;
524 : }
525 2 : break;
526 :
527 :
528 : case 'x':
529 : case 'X':
530 22736 : switch(modifier) {
531 : default:
532 22736 : ui_num = (u_wide_int) va_arg(ap, unsigned int);
533 22736 : break;
534 : case LM_LONG_DOUBLE:
535 0 : goto fmt_error;
536 : case LM_LONG:
537 0 : ui_num = (u_wide_int) va_arg(ap, unsigned long int);
538 0 : break;
539 : case LM_SIZE_T:
540 0 : ui_num = (u_wide_int) va_arg(ap, size_t);
541 0 : break;
542 : #if SIZEOF_LONG_LONG
543 : case LM_LONG_LONG:
544 0 : ui_num = (u_wide_int) va_arg(ap, u_wide_int);
545 0 : break;
546 : #endif
547 : #if SIZEOF_INTMAX_T
548 : case LM_INTMAX_T:
549 0 : ui_num = (u_wide_int) va_arg(ap, uintmax_t);
550 0 : break;
551 : #endif
552 : #if SIZEOF_PTRDIFF_T
553 : case LM_PTRDIFF_T:
554 0 : ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
555 : break;
556 : #endif
557 : }
558 22736 : s = ap_php_conv_p2(ui_num, 4, *fmt,
559 : &num_buf[NUM_BUF_SIZE], &s_len);
560 22736 : FIX_PRECISION(adjust_precision, precision, s, s_len);
561 22736 : if (alternate_form && i_num != 0) {
562 0 : *--s = *fmt; /* 'x' or 'X' */
563 0 : *--s = '0';
564 0 : s_len += 2;
565 : }
566 22736 : break;
567 :
568 :
569 : case 's':
570 : case 'v':
571 13259534 : s = va_arg(ap, char *);
572 13259534 : if (s != NULL) {
573 13259526 : if (!adjust_precision) {
574 13259267 : s_len = strlen(s);
575 : } else {
576 259 : s_len = strnlen(s, precision);
577 : }
578 : } else {
579 8 : s = S_NULL;
580 8 : s_len = S_NULL_LEN;
581 : }
582 13259534 : pad_char = ' ';
583 13259534 : break;
584 :
585 :
586 : case 'f':
587 : case 'F':
588 : case 'e':
589 : case 'E':
590 1576 : switch(modifier) {
591 : case LM_LONG_DOUBLE:
592 0 : fp_num = (double) va_arg(ap, long double);
593 0 : break;
594 : case LM_STD:
595 1576 : fp_num = va_arg(ap, double);
596 1576 : break;
597 : default:
598 0 : goto fmt_error;
599 : }
600 :
601 1576 : if (zend_isnan(fp_num)) {
602 0 : s = "nan";
603 0 : s_len = 3;
604 1576 : } else if (zend_isinf(fp_num)) {
605 2 : s = "inf";
606 2 : s_len = 3;
607 : } else {
608 : #ifdef HAVE_LOCALE_H
609 1574 : if (!lconv) {
610 1574 : lconv = localeconv();
611 : }
612 : #endif
613 1574 : s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
614 : (adjust_precision == NO) ? FLOAT_DIGITS : precision,
615 : (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
616 : &is_negative, &num_buf[1], &s_len);
617 1574 : if (is_negative)
618 2 : prefix_char = '-';
619 1572 : else if (print_sign)
620 0 : prefix_char = '+';
621 1572 : else if (print_blank)
622 0 : prefix_char = ' ';
623 : }
624 1576 : break;
625 :
626 :
627 : case 'g':
628 : case 'k':
629 : case 'G':
630 : case 'H':
631 26890 : switch(modifier) {
632 : case LM_LONG_DOUBLE:
633 0 : fp_num = (double) va_arg(ap, long double);
634 0 : break;
635 : case LM_STD:
636 26890 : fp_num = va_arg(ap, double);
637 26890 : break;
638 : default:
639 0 : goto fmt_error;
640 : }
641 :
642 26890 : if (zend_isnan(fp_num)) {
643 1649 : s = "NAN";
644 1649 : s_len = 3;
645 1649 : break;
646 25241 : } else if (zend_isinf(fp_num)) {
647 1624 : if (fp_num > 0) {
648 1601 : s = "INF";
649 1601 : s_len = 3;
650 : } else {
651 23 : s = "-INF";
652 23 : s_len = 4;
653 : }
654 1624 : break;
655 : }
656 :
657 23617 : if (adjust_precision == NO)
658 0 : precision = FLOAT_DIGITS;
659 23617 : else if (precision == 0)
660 0 : precision = 1;
661 : /*
662 : * * We use &num_buf[ 1 ], so that we have room for the sign
663 : */
664 : #ifdef HAVE_LOCALE_H
665 23617 : if (!lconv) {
666 23617 : lconv = localeconv();
667 : }
668 : #endif
669 23617 : s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
670 23617 : if (*s == '-')
671 2431 : prefix_char = *s++;
672 21186 : else if (print_sign)
673 0 : prefix_char = '+';
674 21186 : else if (print_blank)
675 0 : prefix_char = ' ';
676 :
677 23617 : s_len = strlen(s);
678 :
679 23617 : if (alternate_form && (q = strchr(s, '.')) == NULL)
680 0 : s[s_len++] = '.';
681 23617 : break;
682 :
683 :
684 : case 'c':
685 176851 : char_buf[0] = (char) (va_arg(ap, int));
686 176851 : s = &char_buf[0];
687 176851 : s_len = 1;
688 176851 : pad_char = ' ';
689 176851 : break;
690 :
691 :
692 : case '%':
693 2 : char_buf[0] = '%';
694 2 : s = &char_buf[0];
695 2 : s_len = 1;
696 2 : pad_char = ' ';
697 2 : break;
698 :
699 :
700 : case 'n':
701 0 : *(va_arg(ap, int *)) = xbuf->len;
702 0 : goto skip_output;
703 :
704 : /*
705 : * Always extract the argument as a "char *" pointer. We
706 : * should be using "void *" but there are still machines
707 : * that don't understand it.
708 : * If the pointer size is equal to the size of an unsigned
709 : * integer we convert the pointer to a hex number, otherwise
710 : * we print "%p" to indicate that we don't handle "%p".
711 : */
712 : case 'p':
713 : if (sizeof(char *) <= sizeof(u_wide_int)) {
714 993 : ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
715 993 : s = ap_php_conv_p2(ui_num, 4, 'x',
716 : &num_buf[NUM_BUF_SIZE], &s_len);
717 993 : if (ui_num != 0) {
718 993 : *--s = 'x';
719 993 : *--s = '0';
720 993 : s_len += 2;
721 : }
722 : } else {
723 : s = "%p";
724 : s_len = 2;
725 : }
726 993 : pad_char = ' ';
727 993 : break;
728 :
729 :
730 : case NUL:
731 : /*
732 : * The last character of the format string was %.
733 : * We ignore it.
734 : */
735 0 : continue;
736 :
737 :
738 0 : fmt_error:
739 0 : php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
740 : /*
741 : * The default case is for unrecognized %'s.
742 : * We print %<char> to help the user identify what
743 : * option is not understood.
744 : * This is also useful in case the user wants to pass
745 : * the output of format_converter to another function
746 : * that understands some other %<char> (like syslog).
747 : * Note that we can't point s inside fmt because the
748 : * unknown <char> could be preceded by width etc.
749 : */
750 : default:
751 0 : char_buf[0] = '%';
752 0 : char_buf[1] = *fmt;
753 0 : s = char_buf;
754 0 : s_len = 2;
755 0 : pad_char = ' ';
756 : break;
757 : }
758 :
759 18333594 : if (prefix_char != NUL) {
760 8486 : *--s = prefix_char;
761 8486 : s_len++;
762 : }
763 18333594 : if (adjust_width && adjust == RIGHT && min_width > s_len) {
764 152329 : if (pad_char == '0' && prefix_char != NUL) {
765 0 : INS_CHAR(xbuf, *s);
766 0 : s++;
767 0 : s_len--;
768 0 : min_width--;
769 : }
770 152329 : PAD(xbuf, min_width - s_len, pad_char);
771 : }
772 : /*
773 : * Print the string s.
774 : */
775 18333594 : INS_STRING(xbuf, s, s_len);
776 :
777 18333594 : if (adjust_width && adjust == LEFT && min_width > s_len)
778 20 : PAD(xbuf, min_width - s_len, pad_char);
779 18333594 : if (free_zcopy) {
780 0 : zval_dtor(&zcopy);
781 : }
782 : }
783 89188304 : skip_output:
784 89188304 : fmt++;
785 : }
786 : return;
787 : }
788 : /* }}} */
789 :
790 : /*
791 : * This is the general purpose conversion function.
792 : */
793 : PHPAPI int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) /* {{{ */
794 7350884 : {
795 7350884 : smart_str xbuf = {0};
796 :
797 7350884 : xbuf_format_converter(&xbuf, format, ap);
798 :
799 7350884 : if (max_len && xbuf.len > max_len) {
800 1 : xbuf.len = max_len;
801 : }
802 7350884 : smart_str_0(&xbuf);
803 :
804 7350884 : *pbuf = xbuf.c;
805 :
806 7350884 : return xbuf.len;
807 : }
808 : /* }}} */
809 :
810 : PHPAPI int spprintf(char **pbuf, size_t max_len, const char *format, ...) /* {{{ */
811 1832145 : {
812 : int cc;
813 : va_list ap;
814 :
815 1832145 : va_start(ap, format);
816 1832145 : cc = vspprintf(pbuf, max_len, format, ap);
817 1832145 : va_end(ap);
818 1832145 : return (cc);
819 : }
820 : /* }}} */
821 :
822 : /*
823 : * Local variables:
824 : * tab-width: 4
825 : * c-basic-offset: 4
826 : * End:
827 : * vim600: sw=4 ts=4 fdm=marker
828 : * vim<600: sw=4 ts=4
829 : */
|