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