/src/php-src/main/spprintf.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 | | | http://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: Marcus Boerger <helly@php.net> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | /* This is the spprintf implementation. |
18 | | * It has emerged from apache snprintf. See original header: |
19 | | */ |
20 | | |
21 | | /* ==================================================================== |
22 | | * Copyright (c) 1995-1998 The Apache Group. All rights reserved. |
23 | | * |
24 | | * Redistribution and use in source and binary forms, with or without |
25 | | * modification, are permitted provided that the following conditions |
26 | | * are met: |
27 | | * |
28 | | * 1. Redistributions of source code must retain the above copyright |
29 | | * notice, this list of conditions and the following disclaimer. |
30 | | * |
31 | | * 2. Redistributions in binary form must reproduce the above copyright |
32 | | * notice, this list of conditions and the following disclaimer in |
33 | | * the documentation and/or other materials provided with the |
34 | | * distribution. |
35 | | * |
36 | | * 3. All advertising materials mentioning features or use of this |
37 | | * software must display the following acknowledgment: |
38 | | * "This product includes software developed by the Apache Group |
39 | | * for use in the Apache HTTP server project (http://www.apache.org/)." |
40 | | * |
41 | | * 4. The names "Apache Server" and "Apache Group" must not be used to |
42 | | * endorse or promote products derived from this software without |
43 | | * prior written permission. |
44 | | * |
45 | | * 5. Redistributions of any form whatsoever must retain the following |
46 | | * acknowledgment: |
47 | | * "This product includes software developed by the Apache Group |
48 | | * for use in the Apache HTTP server project (http://www.apache.org/)." |
49 | | * |
50 | | * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY |
51 | | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
52 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
53 | | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR |
54 | | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
55 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
56 | | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
57 | | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
58 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
59 | | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
60 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
61 | | * OF THE POSSIBILITY OF SUCH DAMAGE. |
62 | | * ==================================================================== |
63 | | * |
64 | | * This software consists of voluntary contributions made by many |
65 | | * individuals on behalf of the Apache Group and was originally based |
66 | | * on public domain software written at the National Center for |
67 | | * Supercomputing Applications, University of Illinois, Urbana-Champaign. |
68 | | * For more information on the Apache Group and the Apache HTTP server |
69 | | * project, please see <http://www.apache.org/>. |
70 | | * |
71 | | * This code is based on, and used with the permission of, the |
72 | | * SIO stdio-replacement strx_* functions by Panos Tsirigotis |
73 | | * <panos@alumni.cs.colorado.edu> for xinetd. |
74 | | */ |
75 | | #define _GNU_SOURCE |
76 | | #include "php.h" |
77 | | |
78 | | #include <stddef.h> |
79 | | #include <stdio.h> |
80 | | #include <ctype.h> |
81 | | #include <sys/types.h> |
82 | | #include <stdarg.h> |
83 | | #include <string.h> |
84 | | #include <stdlib.h> |
85 | | #include <math.h> |
86 | | #include <inttypes.h> |
87 | | |
88 | | #include <locale.h> |
89 | | #ifdef ZTS |
90 | | #include "ext/standard/php_string.h" |
91 | | #define LCONV_DECIMAL_POINT (*lconv.decimal_point) |
92 | | #else |
93 | 105k | #define LCONV_DECIMAL_POINT (*lconv->decimal_point) |
94 | | #endif |
95 | | |
96 | | #include "snprintf.h" |
97 | | |
98 | | #define FALSE 0 |
99 | | #define TRUE 1 |
100 | 1.68M | #define NUL '\0' |
101 | | #define INT_NULL ((int *)0) |
102 | | |
103 | 0 | #define S_NULL "(null)" |
104 | 0 | #define S_NULL_LEN 6 |
105 | | |
106 | 0 | #define FLOAT_DIGITS 6 |
107 | | #define EXPONENT_LENGTH 10 |
108 | | |
109 | | #include "zend_smart_str.h" |
110 | | #include "zend_smart_string.h" |
111 | | |
112 | | /* {{{ macros */ |
113 | | |
114 | 9.04M | #define INS_CHAR(xbuf, ch, is_char) do { \ |
115 | 9.04M | if ((is_char)) { \ |
116 | 735k | smart_string_appendc((smart_string *)(xbuf), (ch)); \ |
117 | 8.30M | } else { \ |
118 | 8.30M | smart_str_appendc((smart_str *)(xbuf), (ch)); \ |
119 | 8.30M | } \ |
120 | 9.04M | } while (0); |
121 | | |
122 | 841k | #define INS_STRING(xbuf, str, len, is_char) do { \ |
123 | 841k | if ((is_char)) { \ |
124 | 23.6k | smart_string_appendl((smart_string *)(xbuf), (str), (len)); \ |
125 | 817k | } else { \ |
126 | 817k | smart_str_appendl((smart_str *)(xbuf), (str), (len)); \ |
127 | 817k | } \ |
128 | 841k | } while (0); |
129 | | |
130 | 0 | #define PAD_CHAR(xbuf, ch, count, is_char) do { \ |
131 | 0 | if ((is_char)) { \ |
132 | 0 | smart_string_alloc(((smart_string *)(xbuf)), (count), 0); \ |
133 | 0 | memset(((smart_string *)(xbuf))->c + ((smart_string *)(xbuf))->len, (ch), (count)); \ |
134 | 0 | ((smart_string *)(xbuf))->len += (count); \ |
135 | 0 | } else { \ |
136 | 0 | smart_str_alloc(((smart_str *)(xbuf)), (count), 0); \ |
137 | 0 | memset(ZSTR_VAL(((smart_str *)(xbuf))->s) + ZSTR_LEN(((smart_str *)(xbuf))->s), (ch), (count)); \ |
138 | 0 | ZSTR_LEN(((smart_str *)(xbuf))->s) += (count); \ |
139 | 0 | } \ |
140 | 0 | } while (0); |
141 | | |
142 | | /* |
143 | | * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions |
144 | | * which can be at most max length of double |
145 | | */ |
146 | 288k | #define NUM_BUF_SIZE PHP_DOUBLE_MAX_LENGTH |
147 | | |
148 | 0 | #define NUM(c) (c - '0') |
149 | | |
150 | 0 | #define STR_TO_DEC(str, num) do { \ |
151 | 0 | num = NUM(*str++); \ |
152 | 0 | while (isdigit((int)*str)) { \ |
153 | 0 | num *= 10; \ |
154 | 0 | num += NUM(*str++); \ |
155 | 0 | if (num >= INT_MAX / 10) { \ |
156 | 0 | while (isdigit((int)*str++)); \ |
157 | 0 | break; \ |
158 | 0 | } \ |
159 | 0 | } \ |
160 | 0 | } while (0) |
161 | | |
162 | | /* |
163 | | * This macro does zero padding so that the precision |
164 | | * requirement is satisfied. The padding is done by |
165 | | * adding '0's to the left of the string that is going |
166 | | * to be printed. |
167 | | */ |
168 | 288k | #define FIX_PRECISION(adjust, precision, s, s_len) do { \ |
169 | 288k | if (adjust) \ |
170 | 0 | while (s_len < (size_t)precision) { \ |
171 | 0 | *--s = '0'; \ |
172 | 0 | s_len++; \ |
173 | 0 | } \ |
174 | 288k | } while (0) |
175 | | |
176 | | /* }}} */ |
177 | | |
178 | | #if !HAVE_STRNLEN |
179 | | static size_t strnlen(const char *s, size_t maxlen) { |
180 | | char *r = memchr(s, '\0', maxlen); |
181 | | return r ? r-s : maxlen; |
182 | | } |
183 | | #endif |
184 | | |
185 | | /* |
186 | | * Do format conversion placing the output in buffer |
187 | | */ |
188 | | static void xbuf_format_converter(void *xbuf, zend_bool is_char, const char *fmt, va_list ap) /* {{{ */ |
189 | 344k | { |
190 | 344k | char *s = NULL; |
191 | 344k | size_t s_len; |
192 | 344k | int free_zcopy; |
193 | 344k | zval *zvp, zcopy; |
194 | | |
195 | 344k | int min_width = 0; |
196 | 344k | int precision = 0; |
197 | 344k | enum { |
198 | 344k | LEFT, RIGHT |
199 | 344k | } adjust; |
200 | 344k | char pad_char; |
201 | 344k | char prefix_char; |
202 | | |
203 | 344k | double fp_num; |
204 | 344k | wide_int i_num = (wide_int) 0; |
205 | 344k | u_wide_int ui_num = (u_wide_int) 0; |
206 | | |
207 | 344k | char num_buf[NUM_BUF_SIZE]; |
208 | 344k | char char_buf[2]; /* for printing %% and %<unknown> */ |
209 | | |
210 | | #ifdef ZTS |
211 | | struct lconv lconv; |
212 | | #else |
213 | 344k | struct lconv *lconv = NULL; |
214 | 344k | #endif |
215 | | |
216 | | /* |
217 | | * Flag variables |
218 | | */ |
219 | 344k | length_modifier_e modifier; |
220 | 344k | boolean_e alternate_form; |
221 | 344k | boolean_e print_sign; |
222 | 344k | boolean_e print_blank; |
223 | 344k | boolean_e adjust_precision; |
224 | 344k | boolean_e adjust_width; |
225 | 344k | bool_int is_negative; |
226 | | |
227 | 10.2M | while (*fmt) { |
228 | 9.88M | if (*fmt != '%') { |
229 | 9.04M | INS_CHAR(xbuf, *fmt, is_char); |
230 | 841k | } else { |
231 | | /* |
232 | | * Default variable settings |
233 | | */ |
234 | 841k | adjust = RIGHT; |
235 | 841k | alternate_form = print_sign = print_blank = NO; |
236 | 841k | pad_char = ' '; |
237 | 841k | prefix_char = NUL; |
238 | 841k | free_zcopy = 0; |
239 | | |
240 | 841k | fmt++; |
241 | | |
242 | | /* |
243 | | * Try to avoid checking for flags, width or precision |
244 | | */ |
245 | 841k | if (isascii((int)*fmt) && !islower((int)*fmt)) { |
246 | | /* |
247 | | * Recognize flags: -, #, BLANK, + |
248 | | */ |
249 | 80.4k | for (;; fmt++) { |
250 | 80.4k | if (*fmt == '-') |
251 | 0 | adjust = LEFT; |
252 | 80.4k | else if (*fmt == '+') |
253 | 0 | print_sign = YES; |
254 | 80.4k | else if (*fmt == '#') |
255 | 0 | alternate_form = YES; |
256 | 80.4k | else if (*fmt == ' ') |
257 | 0 | print_blank = YES; |
258 | 80.4k | else if (*fmt == '0') |
259 | 0 | pad_char = '0'; |
260 | 80.4k | else |
261 | 80.4k | break; |
262 | 80.4k | } |
263 | | |
264 | | /* |
265 | | * Check if a width was specified |
266 | | */ |
267 | 0 | if (isdigit((int)*fmt)) { |
268 | 0 | STR_TO_DEC(fmt, min_width); |
269 | 0 | adjust_width = YES; |
270 | 80.4k | } else if (*fmt == '*') { |
271 | 0 | min_width = va_arg(ap, int); |
272 | 0 | fmt++; |
273 | 0 | adjust_width = YES; |
274 | 0 | if (min_width < 0) { |
275 | 0 | adjust = LEFT; |
276 | 0 | min_width = -min_width; |
277 | 0 | } |
278 | 0 | } else |
279 | 80.4k | adjust_width = NO; |
280 | | |
281 | | /* |
282 | | * Check if a precision was specified |
283 | | */ |
284 | 80.4k | if (*fmt == '.') { |
285 | 80.4k | adjust_precision = YES; |
286 | 80.4k | fmt++; |
287 | 0 | if (isdigit((int)*fmt)) { |
288 | 0 | STR_TO_DEC(fmt, precision); |
289 | 80.4k | } else if (*fmt == '*') { |
290 | 80.4k | precision = va_arg(ap, int); |
291 | 80.4k | fmt++; |
292 | 80.4k | if (precision < -1) |
293 | 0 | precision = -1; |
294 | 80.4k | } else |
295 | 0 | precision = 0; |
296 | | |
297 | 80.4k | if (precision > FORMAT_CONV_MAX_PRECISION) { |
298 | 0 | precision = FORMAT_CONV_MAX_PRECISION; |
299 | 0 | } |
300 | 80.4k | } else |
301 | 0 | adjust_precision = NO; |
302 | 80.4k | } else |
303 | 760k | adjust_precision = adjust_width = NO; |
304 | | |
305 | | /* |
306 | | * Modifier check |
307 | | */ |
308 | 841k | switch (*fmt) { |
309 | 0 | case 'L': |
310 | 0 | fmt++; |
311 | 0 | modifier = LM_LONG_DOUBLE; |
312 | 0 | break; |
313 | 3.10k | case 'l': |
314 | 3.10k | fmt++; |
315 | 3.10k | #if SIZEOF_LONG_LONG |
316 | 3.10k | if (*fmt == 'l') { |
317 | 0 | fmt++; |
318 | 0 | modifier = LM_LONG_LONG; |
319 | 0 | } else |
320 | 3.10k | #endif |
321 | 3.10k | modifier = LM_LONG; |
322 | 3.10k | break; |
323 | 0 | case 'z': |
324 | 0 | fmt++; |
325 | 0 | modifier = LM_SIZE_T; |
326 | 0 | break; |
327 | 0 | case 'j': |
328 | 0 | fmt++; |
329 | 0 | #if SIZEOF_INTMAX_T |
330 | 0 | modifier = LM_INTMAX_T; |
331 | | #else |
332 | | modifier = LM_SIZE_T; |
333 | | #endif |
334 | 0 | break; |
335 | 0 | case 't': |
336 | 0 | fmt++; |
337 | 0 | #if SIZEOF_PTRDIFF_T |
338 | 0 | modifier = LM_PTRDIFF_T; |
339 | | #else |
340 | | modifier = LM_SIZE_T; |
341 | | #endif |
342 | 0 | break; |
343 | 0 | case 'p': { |
344 | 0 | char __next = *(fmt+1); |
345 | 0 | if ('d' == __next || 'u' == __next || 'x' == __next || 'o' == __next) { |
346 | 0 | fmt++; |
347 | 0 | modifier = LM_PHP_INT_T; |
348 | 0 | } else { |
349 | 0 | modifier = LM_STD; |
350 | 0 | } |
351 | 0 | } |
352 | 0 | break; |
353 | 0 | case 'h': |
354 | 0 | fmt++; |
355 | 0 | if (*fmt == 'h') { |
356 | 0 | fmt++; |
357 | 0 | } |
358 | | /* these are promoted to int, so no break */ |
359 | 838k | default: |
360 | 838k | modifier = LM_STD; |
361 | 838k | break; |
362 | 841k | } |
363 | | |
364 | | /* |
365 | | * Argument extraction and printing. |
366 | | * First we determine the argument type. |
367 | | * Then, we convert the argument to a string. |
368 | | * On exit from the switch, s points to the string that |
369 | | * must be printed, s_len has the length of the string |
370 | | * The precision requirements, if any, are reflected in s_len. |
371 | | * |
372 | | * NOTE: pad_char may be set to '0' because of the 0 flag. |
373 | | * It is reset to ' ' by non-numeric formats |
374 | | */ |
375 | 841k | switch (*fmt) { |
376 | 0 | case 'Z': { |
377 | 0 | zvp = (zval*) va_arg(ap, zval*); |
378 | 0 | free_zcopy = zend_make_printable_zval(zvp, &zcopy); |
379 | 0 | if (free_zcopy) { |
380 | 0 | zvp = &zcopy; |
381 | 0 | } |
382 | 0 | s_len = Z_STRLEN_P(zvp); |
383 | 0 | s = Z_STRVAL_P(zvp); |
384 | 0 | if (adjust_precision && (size_t)precision < s_len) { |
385 | 0 | s_len = precision; |
386 | 0 | } |
387 | 0 | break; |
388 | 0 | } |
389 | 133k | case 'u': |
390 | 133k | switch(modifier) { |
391 | 133k | default: |
392 | 133k | i_num = (wide_int) va_arg(ap, unsigned int); |
393 | 133k | break; |
394 | 0 | case LM_LONG_DOUBLE: |
395 | 0 | goto fmt_error; |
396 | 0 | case LM_LONG: |
397 | 0 | i_num = (wide_int) va_arg(ap, unsigned long int); |
398 | 0 | break; |
399 | 0 | case LM_SIZE_T: |
400 | 0 | i_num = (wide_int) va_arg(ap, size_t); |
401 | 0 | break; |
402 | 0 | #if SIZEOF_LONG_LONG |
403 | 0 | case LM_LONG_LONG: |
404 | 0 | i_num = (wide_int) va_arg(ap, u_wide_int); |
405 | 0 | break; |
406 | 0 | #endif |
407 | 0 | #if SIZEOF_INTMAX_T |
408 | 0 | case LM_INTMAX_T: |
409 | 0 | i_num = (wide_int) va_arg(ap, uintmax_t); |
410 | 0 | break; |
411 | 0 | #endif |
412 | 0 | #if SIZEOF_PTRDIFF_T |
413 | 0 | case LM_PTRDIFF_T: |
414 | 0 | i_num = (wide_int) va_arg(ap, ptrdiff_t); |
415 | 0 | break; |
416 | 0 | #endif |
417 | 0 | case LM_PHP_INT_T: |
418 | 0 | i_num = (wide_int) va_arg(ap, zend_ulong); |
419 | 0 | break; |
420 | 133k | } |
421 | | /* |
422 | | * The rest also applies to other integer formats, so fall |
423 | | * into that case. |
424 | | */ |
425 | 288k | case 'd': |
426 | 154k | case 'i': |
427 | | /* |
428 | | * Get the arg if we haven't already. |
429 | | */ |
430 | 154k | if ((*fmt) != 'u') { |
431 | 21.3k | switch(modifier) { |
432 | 18.2k | default: |
433 | 18.2k | i_num = (wide_int) va_arg(ap, int); |
434 | 18.2k | break; |
435 | 0 | case LM_LONG_DOUBLE: |
436 | 0 | goto fmt_error; |
437 | 3.10k | case LM_LONG: |
438 | 3.10k | i_num = (wide_int) va_arg(ap, long int); |
439 | 3.10k | break; |
440 | 0 | case LM_SIZE_T: |
441 | 0 | #if SIZEOF_SSIZE_T |
442 | 0 | i_num = (wide_int) va_arg(ap, ssize_t); |
443 | | #else |
444 | | i_num = (wide_int) va_arg(ap, size_t); |
445 | | #endif |
446 | 0 | break; |
447 | 0 | #if SIZEOF_LONG_LONG |
448 | 0 | case LM_LONG_LONG: |
449 | 0 | i_num = (wide_int) va_arg(ap, wide_int); |
450 | 0 | break; |
451 | 0 | #endif |
452 | 0 | #if SIZEOF_INTMAX_T |
453 | 0 | case LM_INTMAX_T: |
454 | 0 | i_num = (wide_int) va_arg(ap, intmax_t); |
455 | 0 | break; |
456 | 0 | #endif |
457 | 0 | #if SIZEOF_PTRDIFF_T |
458 | 0 | case LM_PTRDIFF_T: |
459 | 0 | i_num = (wide_int) va_arg(ap, ptrdiff_t); |
460 | 0 | break; |
461 | 0 | #endif |
462 | 0 | case LM_PHP_INT_T: |
463 | 0 | i_num = (wide_int) va_arg(ap, zend_long); |
464 | 0 | break; |
465 | 154k | } |
466 | 154k | } |
467 | 154k | s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative, |
468 | 154k | &num_buf[NUM_BUF_SIZE], &s_len); |
469 | 154k | FIX_PRECISION(adjust_precision, precision, s, s_len); |
470 | | |
471 | 154k | if (*fmt != 'u') { |
472 | 21.3k | if (is_negative) |
473 | 0 | prefix_char = '-'; |
474 | 21.3k | else if (print_sign) |
475 | 0 | prefix_char = '+'; |
476 | 21.3k | else if (print_blank) |
477 | 0 | prefix_char = ' '; |
478 | 21.3k | } |
479 | 154k | break; |
480 | | |
481 | | |
482 | 0 | case 'o': |
483 | 0 | switch(modifier) { |
484 | 0 | default: |
485 | 0 | ui_num = (u_wide_int) va_arg(ap, unsigned int); |
486 | 0 | break; |
487 | 0 | case LM_LONG_DOUBLE: |
488 | 0 | goto fmt_error; |
489 | 0 | case LM_LONG: |
490 | 0 | ui_num = (u_wide_int) va_arg(ap, unsigned long int); |
491 | 0 | break; |
492 | 0 | case LM_SIZE_T: |
493 | 0 | ui_num = (u_wide_int) va_arg(ap, size_t); |
494 | 0 | break; |
495 | 0 | #if SIZEOF_LONG_LONG |
496 | 0 | case LM_LONG_LONG: |
497 | 0 | ui_num = (u_wide_int) va_arg(ap, u_wide_int); |
498 | 0 | break; |
499 | 0 | #endif |
500 | 0 | #if SIZEOF_INTMAX_T |
501 | 0 | case LM_INTMAX_T: |
502 | 0 | ui_num = (u_wide_int) va_arg(ap, uintmax_t); |
503 | 0 | break; |
504 | 0 | #endif |
505 | 0 | #if SIZEOF_PTRDIFF_T |
506 | 0 | case LM_PTRDIFF_T: |
507 | 0 | ui_num = (u_wide_int) va_arg(ap, ptrdiff_t); |
508 | 0 | break; |
509 | 0 | #endif |
510 | 0 | case LM_PHP_INT_T: |
511 | 0 | ui_num = (u_wide_int) va_arg(ap, zend_ulong); |
512 | 0 | break; |
513 | 0 | } |
514 | 0 | s = ap_php_conv_p2(ui_num, 3, *fmt, |
515 | 0 | &num_buf[NUM_BUF_SIZE], &s_len); |
516 | 0 | FIX_PRECISION(adjust_precision, precision, s, s_len); |
517 | 0 | if (alternate_form && *s != '0') { |
518 | 0 | *--s = '0'; |
519 | 0 | s_len++; |
520 | 0 | } |
521 | 0 | break; |
522 | | |
523 | |
|
524 | 133k | case 'x': |
525 | 133k | case 'X': |
526 | 133k | switch(modifier) { |
527 | 133k | default: |
528 | 133k | ui_num = (u_wide_int) va_arg(ap, unsigned int); |
529 | 133k | break; |
530 | 0 | case LM_LONG_DOUBLE: |
531 | 0 | goto fmt_error; |
532 | 0 | case LM_LONG: |
533 | 0 | ui_num = (u_wide_int) va_arg(ap, unsigned long int); |
534 | 0 | break; |
535 | 0 | case LM_SIZE_T: |
536 | 0 | ui_num = (u_wide_int) va_arg(ap, size_t); |
537 | 0 | break; |
538 | 0 | #if SIZEOF_LONG_LONG |
539 | 0 | case LM_LONG_LONG: |
540 | 0 | ui_num = (u_wide_int) va_arg(ap, u_wide_int); |
541 | 0 | break; |
542 | 0 | #endif |
543 | 0 | #if SIZEOF_INTMAX_T |
544 | 0 | case LM_INTMAX_T: |
545 | 0 | ui_num = (u_wide_int) va_arg(ap, uintmax_t); |
546 | 0 | break; |
547 | 0 | #endif |
548 | 0 | #if SIZEOF_PTRDIFF_T |
549 | 0 | case LM_PTRDIFF_T: |
550 | 0 | ui_num = (u_wide_int) va_arg(ap, ptrdiff_t); |
551 | 0 | break; |
552 | 0 | #endif |
553 | 0 | case LM_PHP_INT_T: |
554 | 0 | ui_num = (u_wide_int) va_arg(ap, zend_ulong); |
555 | 0 | break; |
556 | 133k | } |
557 | 133k | s = ap_php_conv_p2(ui_num, 4, *fmt, |
558 | 133k | &num_buf[NUM_BUF_SIZE], &s_len); |
559 | 133k | FIX_PRECISION(adjust_precision, precision, s, s_len); |
560 | 133k | if (alternate_form && ui_num != 0) { |
561 | 0 | *--s = *fmt; /* 'x' or 'X' */ |
562 | 0 | *--s = '0'; |
563 | 0 | s_len += 2; |
564 | 0 | } |
565 | 133k | break; |
566 | | |
567 | | |
568 | 337k | case 's': |
569 | 337k | s = va_arg(ap, char *); |
570 | 337k | if (s != NULL) { |
571 | 337k | if (!adjust_precision) { |
572 | 337k | s_len = strlen(s); |
573 | 0 | } else { |
574 | 0 | s_len = strnlen(s, precision); |
575 | 0 | } |
576 | 0 | } else { |
577 | 0 | s = S_NULL; |
578 | 0 | s_len = S_NULL_LEN; |
579 | 0 | } |
580 | 337k | pad_char = ' '; |
581 | 337k | break; |
582 | | |
583 | | |
584 | 0 | case 'f': |
585 | 0 | case 'F': |
586 | 0 | case 'e': |
587 | 0 | case 'E': |
588 | 0 | switch(modifier) { |
589 | 0 | case LM_LONG_DOUBLE: |
590 | 0 | fp_num = (double) va_arg(ap, long double); |
591 | 0 | break; |
592 | 0 | case LM_STD: |
593 | 0 | fp_num = va_arg(ap, double); |
594 | 0 | break; |
595 | 0 | default: |
596 | 0 | goto fmt_error; |
597 | 0 | } |
598 | | |
599 | 0 | if (zend_isnan(fp_num)) { |
600 | 0 | s = "nan"; |
601 | 0 | s_len = 3; |
602 | 0 | } else if (zend_isinf(fp_num)) { |
603 | 0 | s = "inf"; |
604 | 0 | s_len = 3; |
605 | 0 | } else { |
606 | | #ifdef ZTS |
607 | | localeconv_r(&lconv); |
608 | | #else |
609 | 0 | if (!lconv) { |
610 | 0 | lconv = localeconv(); |
611 | 0 | } |
612 | 0 | #endif |
613 | 0 | s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form, |
614 | 0 | (adjust_precision == NO) ? FLOAT_DIGITS : precision, |
615 | 0 | (*fmt == 'f')?LCONV_DECIMAL_POINT:'.', |
616 | 0 | &is_negative, &num_buf[1], &s_len); |
617 | 0 | if (is_negative) |
618 | 0 | prefix_char = '-'; |
619 | 0 | else if (print_sign) |
620 | 0 | prefix_char = '+'; |
621 | 0 | else if (print_blank) |
622 | 0 | prefix_char = ' '; |
623 | 0 | } |
624 | 0 | break; |
625 | | |
626 | |
|
627 | 0 | case 'g': |
628 | 0 | case 'k': |
629 | 26.6k | case 'G': |
630 | 80.4k | case 'H': |
631 | 80.4k | switch(modifier) { |
632 | 0 | case LM_LONG_DOUBLE: |
633 | 0 | fp_num = (double) va_arg(ap, long double); |
634 | 0 | break; |
635 | 80.4k | case LM_STD: |
636 | 80.4k | fp_num = va_arg(ap, double); |
637 | 80.4k | break; |
638 | 0 | default: |
639 | 0 | goto fmt_error; |
640 | 80.4k | } |
641 | | |
642 | 80.4k | if (zend_isnan(fp_num)) { |
643 | 53 | s = "NAN"; |
644 | 53 | s_len = 3; |
645 | 53 | break; |
646 | 80.3k | } else if (zend_isinf(fp_num)) { |
647 | 1.62k | if (fp_num > 0) { |
648 | 329 | s = "INF"; |
649 | 329 | s_len = 3; |
650 | 1.29k | } else { |
651 | 1.29k | s = "-INF"; |
652 | 1.29k | s_len = 4; |
653 | 1.29k | } |
654 | 1.62k | break; |
655 | 1.62k | } |
656 | | |
657 | 78.7k | if (adjust_precision == NO) |
658 | 0 | precision = FLOAT_DIGITS; |
659 | 78.7k | 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 ZTS |
665 | | localeconv_r(&lconv); |
666 | | #else |
667 | 78.7k | if (!lconv) { |
668 | 78.7k | lconv = localeconv(); |
669 | 78.7k | } |
670 | 78.7k | #endif |
671 | 78.7k | s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]); |
672 | 78.7k | if (*s == '-') |
673 | 15.0k | prefix_char = *s++; |
674 | 63.7k | else if (print_sign) |
675 | 0 | prefix_char = '+'; |
676 | 63.7k | else if (print_blank) |
677 | 0 | prefix_char = ' '; |
678 | | |
679 | 78.7k | s_len = strlen(s); |
680 | | |
681 | 78.7k | if (alternate_form && (strchr(s, '.')) == NULL) |
682 | 0 | s[s_len++] = '.'; |
683 | 78.7k | break; |
684 | | |
685 | | |
686 | 134k | case 'c': |
687 | 134k | char_buf[0] = (char) (va_arg(ap, int)); |
688 | 134k | s = &char_buf[0]; |
689 | 134k | s_len = 1; |
690 | 134k | pad_char = ' '; |
691 | 134k | break; |
692 | | |
693 | | |
694 | 0 | case '%': |
695 | 0 | char_buf[0] = '%'; |
696 | 0 | s = &char_buf[0]; |
697 | 0 | s_len = 1; |
698 | 0 | pad_char = ' '; |
699 | 0 | break; |
700 | | |
701 | | |
702 | 0 | case 'n': |
703 | 0 | *(va_arg(ap, int *)) = is_char? (int)((smart_string *)xbuf)->len : (int)ZSTR_LEN(((smart_str *)xbuf)->s); |
704 | 0 | 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 | 0 | case 'p': |
715 | 0 | if (sizeof(char *) <= sizeof(u_wide_int)) { |
716 | 0 | ui_num = (u_wide_int)((size_t) va_arg(ap, char *)); |
717 | 0 | s = ap_php_conv_p2(ui_num, 4, 'x', |
718 | 0 | &num_buf[NUM_BUF_SIZE], &s_len); |
719 | 0 | if (ui_num != 0) { |
720 | 0 | *--s = 'x'; |
721 | 0 | *--s = '0'; |
722 | 0 | s_len += 2; |
723 | 0 | } |
724 | 0 | } else { |
725 | 0 | s = "%p"; |
726 | 0 | s_len = 2; |
727 | 0 | } |
728 | 0 | pad_char = ' '; |
729 | 0 | break; |
730 | | |
731 | | |
732 | 0 | case NUL: |
733 | | /* |
734 | | * The last character of the format string was %. |
735 | | * We ignore it. |
736 | | */ |
737 | 0 | continue; |
738 | | |
739 | | |
740 | 0 | fmt_error: |
741 | 0 | 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 | 0 | default: |
753 | 0 | char_buf[0] = '%'; |
754 | 0 | char_buf[1] = *fmt; |
755 | 0 | s = char_buf; |
756 | 0 | s_len = 2; |
757 | 0 | pad_char = ' '; |
758 | 0 | break; |
759 | 841k | } |
760 | | |
761 | 841k | if (prefix_char != NUL) { |
762 | 15.0k | *--s = prefix_char; |
763 | 15.0k | s_len++; |
764 | 15.0k | } |
765 | 841k | if (adjust_width && adjust == RIGHT && (size_t)min_width > s_len) { |
766 | 0 | if (pad_char == '0' && prefix_char != NUL) { |
767 | 0 | INS_CHAR(xbuf, *s, is_char); |
768 | 0 | s++; |
769 | 0 | s_len--; |
770 | 0 | min_width--; |
771 | 0 | } |
772 | 0 | PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char); |
773 | 0 | } |
774 | | /* |
775 | | * Print the string s. |
776 | | */ |
777 | 841k | INS_STRING(xbuf, s, s_len, is_char); |
778 | | |
779 | 841k | if (adjust_width && adjust == LEFT && (size_t)min_width > s_len) { |
780 | 0 | PAD_CHAR(xbuf, pad_char, min_width - s_len, is_char); |
781 | 0 | } |
782 | | |
783 | 841k | if (free_zcopy) { |
784 | 0 | zval_ptr_dtor_str(&zcopy); |
785 | 0 | } |
786 | 841k | } |
787 | 9.88M | skip_output: |
788 | 9.88M | fmt++; |
789 | 9.88M | } |
790 | 344k | return; |
791 | 344k | } |
792 | | /* }}} */ |
793 | | |
794 | | PHPAPI void php_printf_to_smart_string(smart_string *buf, const char *format, va_list ap) /* {{{ */ |
795 | 23.4k | { |
796 | 23.4k | xbuf_format_converter(buf, 1, format, ap); |
797 | 23.4k | } |
798 | | /* }}} */ |
799 | | |
800 | | PHPAPI void php_printf_to_smart_str(smart_str *buf, const char *format, va_list ap) /* {{{ */ |
801 | 320k | { |
802 | 320k | xbuf_format_converter(buf, 0, format, ap); |
803 | 320k | } |
804 | | /* }}} */ |