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