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