Annotation of embedaddon/php/main/snprintf.c, revision 1.1
1.1 ! misho 1: /*
! 2: +----------------------------------------------------------------------+
! 3: | PHP Version 5 |
! 4: +----------------------------------------------------------------------+
! 5: | Copyright (c) 1997-2012 The PHP Group |
! 6: +----------------------------------------------------------------------+
! 7: | This source file is subject to version 3.01 of the PHP license, |
! 8: | that is bundled with this package in the file LICENSE, and is |
! 9: | available through the world-wide-web at the following url: |
! 10: | http://www.php.net/license/3_01.txt |
! 11: | If you did not receive a copy of the PHP license and are unable to |
! 12: | obtain it through the world-wide-web, please send a note to |
! 13: | license@php.net so we can mail you a copy immediately. |
! 14: +----------------------------------------------------------------------+
! 15: | Author: |
! 16: +----------------------------------------------------------------------+
! 17: */
! 18:
! 19: /* $Id: snprintf.c 321634 2012-01-01 13:15:04Z felipe $ */
! 20:
! 21:
! 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);
! 97: return(c == 'I' ? "INF" : "NAN");
! 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: #if SUHOSIN_PATCH
! 786: zend_suhosin_log(S_MISC, "'Z' specifier within format string");
! 787: goto skip_output;
! 788: #else
! 789: zvp = (zval*) va_arg(ap, zval*);
! 790: zend_make_printable_zval(zvp, &zcopy, &free_zcopy);
! 791: if (free_zcopy) {
! 792: zvp = &zcopy;
! 793: }
! 794: s_len = Z_STRLEN_P(zvp);
! 795: s = Z_STRVAL_P(zvp);
! 796: if (adjust_precision && precision < s_len) {
! 797: s_len = precision;
! 798: }
! 799: #endif
! 800: break;
! 801: case 'u':
! 802: switch(modifier) {
! 803: default:
! 804: i_num = (wide_int) va_arg(ap, unsigned int);
! 805: break;
! 806: case LM_LONG_DOUBLE:
! 807: goto fmt_error;
! 808: case LM_LONG:
! 809: i_num = (wide_int) va_arg(ap, unsigned long int);
! 810: break;
! 811: case LM_SIZE_T:
! 812: i_num = (wide_int) va_arg(ap, size_t);
! 813: break;
! 814: #if SIZEOF_LONG_LONG
! 815: case LM_LONG_LONG:
! 816: i_num = (wide_int) va_arg(ap, u_wide_int);
! 817: break;
! 818: #endif
! 819: #if SIZEOF_INTMAX_T
! 820: case LM_INTMAX_T:
! 821: i_num = (wide_int) va_arg(ap, uintmax_t);
! 822: break;
! 823: #endif
! 824: #if SIZEOF_PTRDIFF_T
! 825: case LM_PTRDIFF_T:
! 826: i_num = (wide_int) va_arg(ap, ptrdiff_t);
! 827: break;
! 828: #endif
! 829: }
! 830: /*
! 831: * The rest also applies to other integer formats, so fall
! 832: * into that case.
! 833: */
! 834: case 'd':
! 835: case 'i':
! 836: /*
! 837: * Get the arg if we haven't already.
! 838: */
! 839: if ((*fmt) != 'u') {
! 840: switch(modifier) {
! 841: default:
! 842: i_num = (wide_int) va_arg(ap, int);
! 843: break;
! 844: case LM_LONG_DOUBLE:
! 845: goto fmt_error;
! 846: case LM_LONG:
! 847: i_num = (wide_int) va_arg(ap, long int);
! 848: break;
! 849: case LM_SIZE_T:
! 850: #if SIZEOF_SSIZE_T
! 851: i_num = (wide_int) va_arg(ap, ssize_t);
! 852: #else
! 853: i_num = (wide_int) va_arg(ap, size_t);
! 854: #endif
! 855: break;
! 856: #if SIZEOF_LONG_LONG
! 857: case LM_LONG_LONG:
! 858: i_num = (wide_int) va_arg(ap, wide_int);
! 859: break;
! 860: #endif
! 861: #if SIZEOF_INTMAX_T
! 862: case LM_INTMAX_T:
! 863: i_num = (wide_int) va_arg(ap, intmax_t);
! 864: break;
! 865: #endif
! 866: #if SIZEOF_PTRDIFF_T
! 867: case LM_PTRDIFF_T:
! 868: i_num = (wide_int) va_arg(ap, ptrdiff_t);
! 869: break;
! 870: #endif
! 871: }
! 872: }
! 873: s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
! 874: &num_buf[NUM_BUF_SIZE], &s_len);
! 875: FIX_PRECISION(adjust_precision, precision, s, s_len);
! 876:
! 877: if (*fmt != 'u') {
! 878: if (is_negative) {
! 879: prefix_char = '-';
! 880: } else if (print_sign) {
! 881: prefix_char = '+';
! 882: } else if (print_blank) {
! 883: prefix_char = ' ';
! 884: }
! 885: }
! 886: break;
! 887:
! 888:
! 889: case 'o':
! 890: switch(modifier) {
! 891: default:
! 892: ui_num = (u_wide_int) va_arg(ap, unsigned int);
! 893: break;
! 894: case LM_LONG_DOUBLE:
! 895: goto fmt_error;
! 896: case LM_LONG:
! 897: ui_num = (u_wide_int) va_arg(ap, unsigned long int);
! 898: break;
! 899: case LM_SIZE_T:
! 900: ui_num = (u_wide_int) va_arg(ap, size_t);
! 901: break;
! 902: #if SIZEOF_LONG_LONG
! 903: case LM_LONG_LONG:
! 904: ui_num = (u_wide_int) va_arg(ap, u_wide_int);
! 905: break;
! 906: #endif
! 907: #if SIZEOF_INTMAX_T
! 908: case LM_INTMAX_T:
! 909: ui_num = (u_wide_int) va_arg(ap, uintmax_t);
! 910: break;
! 911: #endif
! 912: #if SIZEOF_PTRDIFF_T
! 913: case LM_PTRDIFF_T:
! 914: ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
! 915: break;
! 916: #endif
! 917: }
! 918: s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
! 919: FIX_PRECISION(adjust_precision, precision, s, s_len);
! 920: if (alternate_form && *s != '0') {
! 921: *--s = '0';
! 922: s_len++;
! 923: }
! 924: break;
! 925:
! 926:
! 927: case 'x':
! 928: case 'X':
! 929: switch(modifier) {
! 930: default:
! 931: ui_num = (u_wide_int) va_arg(ap, unsigned int);
! 932: break;
! 933: case LM_LONG_DOUBLE:
! 934: goto fmt_error;
! 935: case LM_LONG:
! 936: ui_num = (u_wide_int) va_arg(ap, unsigned long int);
! 937: break;
! 938: case LM_SIZE_T:
! 939: ui_num = (u_wide_int) va_arg(ap, size_t);
! 940: break;
! 941: #if SIZEOF_LONG_LONG
! 942: case LM_LONG_LONG:
! 943: ui_num = (u_wide_int) va_arg(ap, u_wide_int);
! 944: break;
! 945: #endif
! 946: #if SIZEOF_INTMAX_T
! 947: case LM_INTMAX_T:
! 948: ui_num = (u_wide_int) va_arg(ap, uintmax_t);
! 949: break;
! 950: #endif
! 951: #if SIZEOF_PTRDIFF_T
! 952: case LM_PTRDIFF_T:
! 953: ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
! 954: break;
! 955: #endif
! 956: }
! 957: s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
! 958: FIX_PRECISION(adjust_precision, precision, s, s_len);
! 959: if (alternate_form && i_num != 0) {
! 960: *--s = *fmt; /* 'x' or 'X' */
! 961: *--s = '0';
! 962: s_len += 2;
! 963: }
! 964: break;
! 965:
! 966:
! 967: case 's':
! 968: case 'v':
! 969: s = va_arg(ap, char *);
! 970: if (s != NULL) {
! 971: s_len = strlen(s);
! 972: if (adjust_precision && precision < s_len) {
! 973: s_len = precision;
! 974: }
! 975: } else {
! 976: s = S_NULL;
! 977: s_len = S_NULL_LEN;
! 978: }
! 979: pad_char = ' ';
! 980: break;
! 981:
! 982:
! 983: case 'f':
! 984: case 'F':
! 985: case 'e':
! 986: case 'E':
! 987: switch(modifier) {
! 988: case LM_LONG_DOUBLE:
! 989: fp_num = (double) va_arg(ap, long double);
! 990: break;
! 991: case LM_STD:
! 992: fp_num = va_arg(ap, double);
! 993: break;
! 994: default:
! 995: goto fmt_error;
! 996: }
! 997:
! 998: if (zend_isnan(fp_num)) {
! 999: s = "NAN";
! 1000: s_len = 3;
! 1001: } else if (zend_isinf(fp_num)) {
! 1002: s = "INF";
! 1003: s_len = 3;
! 1004: } else {
! 1005: #ifdef HAVE_LOCALE_H
! 1006: if (!lconv) {
! 1007: lconv = localeconv();
! 1008: }
! 1009: #endif
! 1010: s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
! 1011: (adjust_precision == NO) ? FLOAT_DIGITS : precision,
! 1012: (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
! 1013: &is_negative, &num_buf[1], &s_len);
! 1014: if (is_negative)
! 1015: prefix_char = '-';
! 1016: else if (print_sign)
! 1017: prefix_char = '+';
! 1018: else if (print_blank)
! 1019: prefix_char = ' ';
! 1020: }
! 1021: break;
! 1022:
! 1023:
! 1024: case 'g':
! 1025: case 'k':
! 1026: case 'G':
! 1027: case 'H':
! 1028: switch(modifier) {
! 1029: case LM_LONG_DOUBLE:
! 1030: fp_num = (double) va_arg(ap, long double);
! 1031: break;
! 1032: case LM_STD:
! 1033: fp_num = va_arg(ap, double);
! 1034: break;
! 1035: default:
! 1036: goto fmt_error;
! 1037: }
! 1038:
! 1039: if (zend_isnan(fp_num)) {
! 1040: s = "NAN";
! 1041: s_len = 3;
! 1042: break;
! 1043: } else if (zend_isinf(fp_num)) {
! 1044: if (fp_num > 0) {
! 1045: s = "INF";
! 1046: s_len = 3;
! 1047: } else {
! 1048: s = "-INF";
! 1049: s_len = 4;
! 1050: }
! 1051: break;
! 1052: }
! 1053:
! 1054: if (adjust_precision == NO) {
! 1055: precision = FLOAT_DIGITS;
! 1056: } else if (precision == 0) {
! 1057: precision = 1;
! 1058: }
! 1059: /*
! 1060: * * We use &num_buf[ 1 ], so that we have room for the sign
! 1061: */
! 1062: #ifdef HAVE_LOCALE_H
! 1063: if (!lconv) {
! 1064: lconv = localeconv();
! 1065: }
! 1066: #endif
! 1067: s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
! 1068: if (*s == '-') {
! 1069: prefix_char = *s++;
! 1070: } else if (print_sign) {
! 1071: prefix_char = '+';
! 1072: } else if (print_blank) {
! 1073: prefix_char = ' ';
! 1074: }
! 1075:
! 1076: s_len = strlen(s);
! 1077:
! 1078: if (alternate_form && (strchr(s, '.')) == NULL) {
! 1079: s[s_len++] = '.';
! 1080: }
! 1081: break;
! 1082:
! 1083:
! 1084: case 'c':
! 1085: char_buf[0] = (char) (va_arg(ap, int));
! 1086: s = &char_buf[0];
! 1087: s_len = 1;
! 1088: pad_char = ' ';
! 1089: break;
! 1090:
! 1091:
! 1092: case '%':
! 1093: char_buf[0] = '%';
! 1094: s = &char_buf[0];
! 1095: s_len = 1;
! 1096: pad_char = ' ';
! 1097: break;
! 1098:
! 1099:
! 1100: case 'n':
! 1101: #if SUHOSIN_PATCH
! 1102: zend_suhosin_log(S_MISC, "'n' specifier within format string");
! 1103: #else
! 1104: *(va_arg(ap, int *)) = cc;
! 1105: #endif
! 1106: goto skip_output;
! 1107:
! 1108: /*
! 1109: * Always extract the argument as a "char *" pointer. We
! 1110: * should be using "void *" but there are still machines
! 1111: * that don't understand it.
! 1112: * If the pointer size is equal to the size of an unsigned
! 1113: * integer we convert the pointer to a hex number, otherwise
! 1114: * we print "%p" to indicate that we don't handle "%p".
! 1115: */
! 1116: case 'p':
! 1117: if (sizeof(char *) <= sizeof(u_wide_int)) {
! 1118: ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
! 1119: s = ap_php_conv_p2(ui_num, 4, 'x',
! 1120: &num_buf[NUM_BUF_SIZE], &s_len);
! 1121: if (ui_num != 0) {
! 1122: *--s = 'x';
! 1123: *--s = '0';
! 1124: s_len += 2;
! 1125: }
! 1126: } else {
! 1127: s = "%p";
! 1128: s_len = 2;
! 1129: }
! 1130: pad_char = ' ';
! 1131: break;
! 1132:
! 1133:
! 1134: case NUL:
! 1135: /*
! 1136: * The last character of the format string was %.
! 1137: * We ignore it.
! 1138: */
! 1139: continue;
! 1140:
! 1141:
! 1142: fmt_error:
! 1143: php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
! 1144: /*
! 1145: * The default case is for unrecognized %'s.
! 1146: * We print %<char> to help the user identify what
! 1147: * option is not understood.
! 1148: * This is also useful in case the user wants to pass
! 1149: * the output of format_converter to another function
! 1150: * that understands some other %<char> (like syslog).
! 1151: * Note that we can't point s inside fmt because the
! 1152: * unknown <char> could be preceded by width etc.
! 1153: */
! 1154: default:
! 1155: char_buf[0] = '%';
! 1156: char_buf[1] = *fmt;
! 1157: s = char_buf;
! 1158: s_len = 2;
! 1159: pad_char = ' ';
! 1160: break;
! 1161: }
! 1162:
! 1163: if (prefix_char != NUL) {
! 1164: *--s = prefix_char;
! 1165: s_len++;
! 1166: }
! 1167: if (adjust_width && adjust == RIGHT && min_width > s_len) {
! 1168: if (pad_char == '0' && prefix_char != NUL) {
! 1169: INS_CHAR(*s, sp, bep, cc)
! 1170: s++;
! 1171: s_len--;
! 1172: min_width--;
! 1173: }
! 1174: PAD(min_width, s_len, pad_char);
! 1175: }
! 1176: /*
! 1177: * Print the string s.
! 1178: */
! 1179: for (i = s_len; i != 0; i--) {
! 1180: INS_CHAR(*s, sp, bep, cc);
! 1181: s++;
! 1182: }
! 1183:
! 1184: if (adjust_width && adjust == LEFT && min_width > s_len)
! 1185: PAD(min_width, s_len, pad_char);
! 1186: if (free_zcopy) {
! 1187: zval_dtor(&zcopy);
! 1188: }
! 1189: }
! 1190: skip_output:
! 1191: fmt++;
! 1192: }
! 1193: odp->nextb = sp;
! 1194: return (cc);
! 1195: }
! 1196: /* }}} */
! 1197:
! 1198: /*
! 1199: * This is the general purpose conversion function.
! 1200: */
! 1201: static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) /* {{{ */
! 1202: {
! 1203: buffy od;
! 1204: int cc;
! 1205:
! 1206: /*
! 1207: * First initialize the descriptor
! 1208: * Notice that if no length is given, we initialize buf_end to the
! 1209: * highest possible address.
! 1210: */
! 1211: if (len == 0) {
! 1212: od.buf_end = (char *) ~0;
! 1213: od.nextb = (char *) ~0;
! 1214: } else {
! 1215: od.buf_end = &buf[len-1];
! 1216: od.nextb = buf;
! 1217: }
! 1218:
! 1219: /*
! 1220: * Do the conversion
! 1221: */
! 1222: cc = format_converter(&od, format, ap);
! 1223: if (len != 0 && od.nextb <= od.buf_end) {
! 1224: *(od.nextb) = '\0';
! 1225: }
! 1226: if (ccp) {
! 1227: *ccp = cc;
! 1228: }
! 1229: }
! 1230: /* }}} */
! 1231:
! 1232: PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
! 1233: {
! 1234: int cc;
! 1235: va_list ap;
! 1236:
! 1237: va_start(ap, format);
! 1238: strx_printv(&cc, buf, len, format, ap);
! 1239: va_end(ap);
! 1240: if (cc >= len) {
! 1241: cc = len -1;
! 1242: buf[cc] = '\0';
! 1243: }
! 1244: return cc;
! 1245: }
! 1246: /* }}} */
! 1247:
! 1248: PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
! 1249: {
! 1250: int cc;
! 1251:
! 1252: strx_printv(&cc, buf, len, format, ap);
! 1253: if (cc >= len) {
! 1254: cc = len -1;
! 1255: buf[cc] = '\0';
! 1256: }
! 1257: return cc;
! 1258: }
! 1259: /* }}} */
! 1260:
! 1261: PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
! 1262: {
! 1263: int cc;
! 1264: va_list ap;
! 1265:
! 1266: va_start(ap, format);
! 1267: strx_printv(&cc, buf, len, format, ap);
! 1268: va_end(ap);
! 1269: return (cc);
! 1270: }
! 1271: /* }}} */
! 1272:
! 1273: PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
! 1274: {
! 1275: int cc;
! 1276:
! 1277: strx_printv(&cc, buf, len, format, ap);
! 1278: return (cc);
! 1279: }
! 1280: /* }}} */
! 1281:
! 1282: PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */
! 1283: {
! 1284: va_list ap2;
! 1285: int cc;
! 1286:
! 1287: va_copy(ap2, ap);
! 1288: cc = ap_php_vsnprintf(NULL, 0, format, ap2);
! 1289: va_end(ap2);
! 1290:
! 1291: *buf = NULL;
! 1292:
! 1293: if (cc >= 0) {
! 1294: if ((*buf = malloc(++cc)) != NULL) {
! 1295: if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) {
! 1296: free(*buf);
! 1297: *buf = NULL;
! 1298: }
! 1299: }
! 1300: }
! 1301:
! 1302: return cc;
! 1303: }
! 1304: /* }}} */
! 1305:
! 1306: PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */
! 1307: {
! 1308: int cc;
! 1309: va_list ap;
! 1310:
! 1311: va_start(ap, format);
! 1312: cc = vasprintf(buf, format, ap);
! 1313: va_end(ap);
! 1314: return cc;
! 1315: }
! 1316: /* }}} */
! 1317:
! 1318: /*
! 1319: * Local variables:
! 1320: * tab-width: 4
! 1321: * c-basic-offset: 4
! 1322: * End:
! 1323: * vim600: sw=4 ts=4 fdm=marker
! 1324: * vim<600: sw=4 ts=4
! 1325: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>