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