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