summaryrefslogtreecommitdiff
path: root/main/snprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/snprintf.c')
-rw-r--r--main/snprintf.c1316
1 files changed, 1316 insertions, 0 deletions
diff --git a/main/snprintf.c b/main/snprintf.c
new file mode 100644
index 0000000..4514bd6
--- /dev/null
+++ b/main/snprintf.c
@@ -0,0 +1,1316 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#define _GNU_SOURCE
+#include "php.h"
+
+#include <zend_strtod.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#define LCONV_DECIMAL_POINT (*lconv->decimal_point)
+#else
+#define LCONV_DECIMAL_POINT '.'
+#endif
+
+/*
+ * Copyright (c) 2002, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) /* {{{ */
+{
+ register char *s = NULL;
+ char *p, *rve, c;
+ size_t siz;
+
+ if (ndigit < 0) {
+ siz = -ndigit + 1;
+ } else {
+ siz = ndigit + 1;
+ }
+
+ /* __dtoa() doesn't allocate space for 0 so we do it by hand */
+ if (value == 0.0) {
+ *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
+ *sign = 0;
+ if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
+ return(NULL);
+ }
+ *rve++ = '0';
+ *rve = '\0';
+ if (!ndigit) {
+ return(s);
+ }
+ } else {
+ p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
+ if (*decpt == 9999) {
+ /* Infinity or Nan, convert to inf or nan like printf */
+ *decpt = 0;
+ c = *p;
+ zend_freedtoa(p);
+ return strdup((c == 'I' ? "INF" : "NAN"));
+ }
+ /* Make a local copy and adjust rve to be in terms of s */
+ if (pad && fmode) {
+ siz += *decpt;
+ }
+ if ((s = (char *)malloc(siz+1)) == NULL) {
+ zend_freedtoa(p);
+ return(NULL);
+ }
+ (void) strlcpy(s, p, siz);
+ rve = s + (rve - p);
+ zend_freedtoa(p);
+ }
+
+ /* Add trailing zeros */
+ if (pad) {
+ siz -= rve - s;
+ while (--siz) {
+ *rve++ = '0';
+ }
+ *rve = '\0';
+ }
+
+ return(s);
+}
+/* }}} */
+
+static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
+{
+ return(__cvt(value, ndigit, decpt, sign, 0, 1));
+}
+/* }}} */
+
+static inline char *php_fcvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
+{
+ return(__cvt(value, ndigit, decpt, sign, 1, 1));
+}
+/* }}} */
+
+PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) /* {{{ */
+{
+ char *digits, *dst, *src;
+ int i, decpt, sign;
+
+ digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, NULL);
+ if (decpt == 9999) {
+ /*
+ * Infinity or NaN, convert to inf or nan with sign.
+ * We assume the buffer is at least ndigit long.
+ */
+ snprintf(buf, ndigit + 1, "%s%s", (sign && *digits == 'I') ? "-" : "", *digits == 'I' ? "INF" : "NAN");
+ zend_freedtoa(digits);
+ return (buf);
+ }
+
+ dst = buf;
+ if (sign) {
+ *dst++ = '-';
+ }
+
+ if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */
+ /* exponential format (e.g. 1.2345e+13) */
+ if (--decpt < 0) {
+ sign = 1;
+ decpt = -decpt;
+ } else {
+ sign = 0;
+ }
+ src = digits;
+ *dst++ = *src++;
+ *dst++ = dec_point;
+ if (*src == '\0') {
+ *dst++ = '0';
+ } else {
+ do {
+ *dst++ = *src++;
+ } while (*src != '\0');
+ }
+ *dst++ = exponent;
+ if (sign) {
+ *dst++ = '-';
+ } else {
+ *dst++ = '+';
+ }
+ if (decpt < 10) {
+ *dst++ = '0' + decpt;
+ *dst = '\0';
+ } else {
+ /* XXX - optimize */
+ for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
+ continue;
+ dst[i + 1] = '\0';
+ while (decpt != 0) {
+ dst[i--] = '0' + decpt % 10;
+ decpt /= 10;
+ }
+ }
+ } else if (decpt < 0) {
+ /* standard format 0. */
+ *dst++ = '0'; /* zero before decimal point */
+ *dst++ = dec_point;
+ do {
+ *dst++ = '0';
+ } while (++decpt < 0);
+ src = digits;
+ while (*src != '\0') {
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+ } else {
+ /* standard format */
+ for (i = 0, src = digits; i < decpt; i++) {
+ if (*src != '\0') {
+ *dst++ = *src++;
+ } else {
+ *dst++ = '0';
+ }
+ }
+ if (*src != '\0') {
+ if (src == digits) {
+ *dst++ = '0'; /* zero before decimal point */
+ }
+ *dst++ = dec_point;
+ for (i = decpt; digits[i] != '\0'; i++) {
+ *dst++ = digits[i];
+ }
+ }
+ *dst = '\0';
+ }
+ zend_freedtoa(digits);
+ return (buf);
+}
+/* }}} */
+
+/* {{{ Apache license */
+/* ====================================================================
+ * Copyright (c) 1995-1998 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ * This code is based on, and used with the permission of, the
+ * SIO stdio-replacement strx_* functions by Panos Tsirigotis
+ * <panos@alumni.cs.colorado.edu> for xinetd.
+ */
+/* }}} */
+
+#define FALSE 0
+#define TRUE 1
+#define NUL '\0'
+#define INT_NULL ((int *)0)
+
+#define S_NULL "(null)"
+#define S_NULL_LEN 6
+
+#define FLOAT_DIGITS 6
+#define EXPONENT_LENGTH 10
+
+
+/*
+ * Convert num to its decimal format.
+ * Return value:
+ * - a pointer to a string containing the number (no sign)
+ * - len contains the length of the string
+ * - is_negative is set to TRUE or FALSE depending on the sign
+ * of the number (always set to FALSE if is_unsigned is TRUE)
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ */
+/* char * ap_php_conv_10() {{{ */
+char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,
+ register bool_int * is_negative, char *buf_end, register int *len)
+{
+ register char *p = buf_end;
+ register u_wide_int magnitude;
+
+ if (is_unsigned) {
+ magnitude = (u_wide_int) num;
+ *is_negative = FALSE;
+ } else {
+ *is_negative = (num < 0);
+
+ /*
+ * On a 2's complement machine, negating the most negative integer
+ * results in a number that cannot be represented as a signed integer.
+ * Here is what we do to obtain the number's magnitude:
+ * a. add 1 to the number
+ * b. negate it (becomes positive)
+ * c. convert it to unsigned
+ * d. add 1
+ */
+ if (*is_negative) {
+ wide_int t = num + 1;
+ magnitude = ((u_wide_int) - t) + 1;
+ } else {
+ magnitude = (u_wide_int) num;
+ }
+ }
+
+ /*
+ * We use a do-while loop so that we write at least 1 digit
+ */
+ do {
+ register u_wide_int new_magnitude = magnitude / 10;
+
+ *--p = (char)(magnitude - new_magnitude * 10 + '0');
+ magnitude = new_magnitude;
+ }
+ while (magnitude);
+
+ *len = buf_end - p;
+ return (p);
+}
+/* }}} */
+
+/* If you change this value then also change bug24640.phpt.
+ * Also NDIG must be reasonable smaller than NUM_BUF_SIZE.
+ */
+#define NDIG 320
+
+
+/*
+ * Convert a floating point number to a string formats 'f', 'e' or 'E'.
+ * The result is placed in buf, and len denotes the length of the string
+ * The sign is returned in the is_negative argument (and is not placed
+ * in buf).
+ */
+/* PHPAPI char * php_conv_fp() {{{ */
+PHPAPI char * php_conv_fp(register char format, register double num,
+ boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len)
+{
+ register char *s = buf;
+ register char *p, *p_orig;
+ int decimal_point;
+
+ if (precision >= NDIG - 1) {
+ precision = NDIG - 2;
+ }
+
+ if (format == 'F') {
+ p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
+ } else { /* either e or E format */
+ p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
+ }
+
+ /*
+ * Check for Infinity and NaN
+ */
+ if (isalpha((int)*p)) {
+ *len = strlen(p);
+ memcpy(buf, p, *len + 1);
+ *is_negative = FALSE;
+ free(p_orig);
+ return (buf);
+ }
+ if (format == 'F') {
+ if (decimal_point <= 0) {
+ if (num != 0 || precision > 0) {
+ *s++ = '0';
+ if (precision > 0) {
+ *s++ = dec_point;
+ while (decimal_point++ < 0) {
+ *s++ = '0';
+ }
+ } else if (add_dp) {
+ *s++ = dec_point;
+ }
+ }
+ } else {
+ int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
+ decimal_point -= addz;
+ while (decimal_point-- > 0) {
+ *s++ = *p++;
+ }
+ while (addz-- > 0) {
+ *s++ = '0';
+ }
+ if (precision > 0 || add_dp) {
+ *s++ = dec_point;
+ }
+ }
+ } else {
+ *s++ = *p++;
+ if (precision > 0 || add_dp) {
+ *s++ = '.';
+ }
+ }
+
+ /*
+ * copy the rest of p, the NUL is NOT copied
+ */
+ while (*p) {
+ *s++ = *p++;
+ }
+
+ if (format != 'F') {
+ char temp[EXPONENT_LENGTH]; /* for exponent conversion */
+ int t_len;
+ bool_int exponent_is_negative;
+
+ *s++ = format; /* either e or E */
+ decimal_point--;
+ if (decimal_point != 0) {
+ p = ap_php_conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len);
+ *s++ = exponent_is_negative ? '-' : '+';
+
+ /*
+ * Make sure the exponent has at least 2 digits
+ */
+ while (t_len--) {
+ *s++ = *p++;
+ }
+ } else {
+ *s++ = '+';
+ *s++ = '0';
+ }
+ }
+ *len = s - buf;
+ free(p_orig);
+ return (buf);
+}
+/* }}} */
+
+/*
+ * Convert num to a base X number where X is a power of 2. nbits determines X.
+ * For example, if nbits is 3, we do base 8 conversion
+ * Return value:
+ * a pointer to a string containing the number
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ */
+char * ap_php_conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) /* {{{ */
+{
+ register int mask = (1 << nbits) - 1;
+ register char *p = buf_end;
+ static char low_digits[] = "0123456789abcdef";
+ static char upper_digits[] = "0123456789ABCDEF";
+ register char *digits = (format == 'X') ? upper_digits : low_digits;
+
+ do {
+ *--p = digits[num & mask];
+ num >>= nbits;
+ }
+ while (num);
+
+ *len = buf_end - p;
+ return (p);
+}
+/* }}} */
+
+/*
+ * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
+ *
+ * XXX: this is a magic number; do not decrease it
+ * Emax = 1023
+ * NDIG = 320
+ * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1;
+ */
+#define NUM_BUF_SIZE 2048
+
+
+/*
+ * Descriptor for buffer area
+ */
+struct buf_area {
+ char *buf_end;
+ char *nextb; /* pointer to next byte to read/write */
+};
+
+typedef struct buf_area buffy;
+
+/*
+ * The INS_CHAR macro inserts a character in the buffer and writes
+ * the buffer back to disk if necessary
+ * It uses the char pointers sp and bep:
+ * sp points to the next available character in the buffer
+ * bep points to the end-of-buffer+1
+ * While using this macro, note that the nextb pointer is NOT updated.
+ *
+ * NOTE: Evaluation of the c argument should not have any side-effects
+ */
+#define INS_CHAR(c, sp, bep, cc) \
+ { \
+ if (sp < bep) \
+ { \
+ *sp++ = c; \
+ } \
+ cc++; \
+ }
+
+#define NUM( c ) ( c - '0' )
+
+#define STR_TO_DEC( str, num ) \
+ num = NUM( *str++ ) ; \
+ while ( isdigit((int)*str ) ) \
+ { \
+ num *= 10 ; \
+ num += NUM( *str++ ) ; \
+ }
+
+/*
+ * This macro does zero padding so that the precision
+ * requirement is satisfied. The padding is done by
+ * adding '0's to the left of the string that is going
+ * to be printed.
+ */
+#define FIX_PRECISION( adjust, precision, s, s_len ) \
+ if ( adjust ) \
+ while ( s_len < precision ) \
+ { \
+ *--s = '0' ; \
+ s_len++ ; \
+ }
+
+/*
+ * Macro that does padding. The padding is done by printing
+ * the character ch.
+ */
+#define PAD( width, len, ch ) do \
+ { \
+ INS_CHAR( ch, sp, bep, cc ) ; \
+ width-- ; \
+ } \
+ while ( width > len )
+
+/*
+ * Prefix the character ch to the string str
+ * Increase length
+ * Set the has_prefix flag
+ */
+#define PREFIX( str, length, ch ) *--str = ch ; length++ ; has_prefix = YES
+
+
+/*
+ * Do format conversion placing the output in buffer
+ */
+static int format_converter(register buffy * odp, const char *fmt, va_list ap) /* {{{ */
+{
+ char *sp;
+ char *bep;
+ int cc = 0;
+ int i;
+
+ char *s = NULL;
+ int s_len, free_zcopy;
+ zval *zvp, zcopy;
+
+ int min_width = 0;
+ int precision = 0;
+ enum {
+ LEFT, RIGHT
+ } adjust;
+ char pad_char;
+ char prefix_char;
+
+ double fp_num;
+ wide_int i_num = (wide_int) 0;
+ u_wide_int ui_num;
+
+ char num_buf[NUM_BUF_SIZE];
+ char char_buf[2]; /* for printing %% and %<unknown> */
+
+#ifdef HAVE_LOCALE_H
+ struct lconv *lconv = NULL;
+#endif
+
+ /*
+ * Flag variables
+ */
+ length_modifier_e modifier;
+ boolean_e alternate_form;
+ boolean_e print_sign;
+ boolean_e print_blank;
+ boolean_e adjust_precision;
+ boolean_e adjust_width;
+ bool_int is_negative;
+
+ sp = odp->nextb;
+ bep = odp->buf_end;
+
+ while (*fmt) {
+ if (*fmt != '%') {
+ INS_CHAR(*fmt, sp, bep, cc);
+ } else {
+ /*
+ * Default variable settings
+ */
+ adjust = RIGHT;
+ alternate_form = print_sign = print_blank = NO;
+ pad_char = ' ';
+ prefix_char = NUL;
+ free_zcopy = 0;
+
+ fmt++;
+
+ /*
+ * Try to avoid checking for flags, width or precision
+ */
+ if (isascii((int)*fmt) && !islower((int)*fmt)) {
+ /*
+ * Recognize flags: -, #, BLANK, +
+ */
+ for (;; fmt++) {
+ if (*fmt == '-')
+ adjust = LEFT;
+ else if (*fmt == '+')
+ print_sign = YES;
+ else if (*fmt == '#')
+ alternate_form = YES;
+ else if (*fmt == ' ')
+ print_blank = YES;
+ else if (*fmt == '0')
+ pad_char = '0';
+ else
+ break;
+ }
+
+ /*
+ * Check if a width was specified
+ */
+ if (isdigit((int)*fmt)) {
+ STR_TO_DEC(fmt, min_width);
+ adjust_width = YES;
+ } else if (*fmt == '*') {
+ min_width = va_arg(ap, int);
+ fmt++;
+ adjust_width = YES;
+ if (min_width < 0) {
+ adjust = LEFT;
+ min_width = -min_width;
+ }
+ } else
+ adjust_width = NO;
+
+ /*
+ * Check if a precision was specified
+ */
+ if (*fmt == '.') {
+ adjust_precision = YES;
+ fmt++;
+ if (isdigit((int)*fmt)) {
+ STR_TO_DEC(fmt, precision);
+ } else if (*fmt == '*') {
+ precision = va_arg(ap, int);
+ fmt++;
+ if (precision < 0)
+ precision = 0;
+ } else
+ precision = 0;
+
+ if (precision > FORMAT_CONV_MAX_PRECISION) {
+ precision = FORMAT_CONV_MAX_PRECISION;
+ }
+ } else
+ adjust_precision = NO;
+ } else
+ adjust_precision = adjust_width = NO;
+
+ /*
+ * Modifier check
+ */
+ switch (*fmt) {
+ case 'L':
+ fmt++;
+ modifier = LM_LONG_DOUBLE;
+ break;
+ case 'I':
+ fmt++;
+#if SIZEOF_LONG_LONG
+ if (*fmt == '6' && *(fmt+1) == '4') {
+ fmt += 2;
+ modifier = LM_LONG_LONG;
+ } else
+#endif
+ if (*fmt == '3' && *(fmt+1) == '2') {
+ fmt += 2;
+ modifier = LM_LONG;
+ } else {
+#ifdef _WIN64
+ modifier = LM_LONG_LONG;
+#else
+ modifier = LM_LONG;
+#endif
+ }
+ break;
+ case 'l':
+ fmt++;
+#if SIZEOF_LONG_LONG
+ if (*fmt == 'l') {
+ fmt++;
+ modifier = LM_LONG_LONG;
+ } else
+#endif
+ modifier = LM_LONG;
+ break;
+ case 'z':
+ fmt++;
+ modifier = LM_SIZE_T;
+ break;
+ case 'j':
+ fmt++;
+#if SIZEOF_INTMAX_T
+ modifier = LM_INTMAX_T;
+#else
+ modifier = LM_SIZE_T;
+#endif
+ break;
+ case 't':
+ fmt++;
+#if SIZEOF_PTRDIFF_T
+ modifier = LM_PTRDIFF_T;
+#else
+ modifier = LM_SIZE_T;
+#endif
+ break;
+ case 'h':
+ fmt++;
+ if (*fmt == 'h') {
+ fmt++;
+ }
+ /* these are promoted to int, so no break */
+ default:
+ modifier = LM_STD;
+ break;
+ }
+
+ /*
+ * Argument extraction and printing.
+ * First we determine the argument type.
+ * Then, we convert the argument to a string.
+ * On exit from the switch, s points to the string that
+ * must be printed, s_len has the length of the string
+ * The precision requirements, if any, are reflected in s_len.
+ *
+ * NOTE: pad_char may be set to '0' because of the 0 flag.
+ * It is reset to ' ' by non-numeric formats
+ */
+ switch (*fmt) {
+ case 'Z':
+ zvp = (zval*) va_arg(ap, zval*);
+ zend_make_printable_zval(zvp, &zcopy, &free_zcopy);
+ if (free_zcopy) {
+ zvp = &zcopy;
+ }
+ s_len = Z_STRLEN_P(zvp);
+ s = Z_STRVAL_P(zvp);
+ if (adjust_precision && precision < s_len) {
+ s_len = precision;
+ }
+ break;
+ case 'u':
+ switch(modifier) {
+ default:
+ i_num = (wide_int) va_arg(ap, unsigned int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ i_num = (wide_int) va_arg(ap, unsigned long int);
+ break;
+ case LM_SIZE_T:
+ i_num = (wide_int) va_arg(ap, size_t);
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ i_num = (wide_int) va_arg(ap, u_wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ i_num = (wide_int) va_arg(ap, uintmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ i_num = (wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ /*
+ * The rest also applies to other integer formats, so fall
+ * into that case.
+ */
+ case 'd':
+ case 'i':
+ /*
+ * Get the arg if we haven't already.
+ */
+ if ((*fmt) != 'u') {
+ switch(modifier) {
+ default:
+ i_num = (wide_int) va_arg(ap, int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ i_num = (wide_int) va_arg(ap, long int);
+ break;
+ case LM_SIZE_T:
+#if SIZEOF_SSIZE_T
+ i_num = (wide_int) va_arg(ap, ssize_t);
+#else
+ i_num = (wide_int) va_arg(ap, size_t);
+#endif
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ i_num = (wide_int) va_arg(ap, wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ i_num = (wide_int) va_arg(ap, intmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ i_num = (wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ }
+ s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+ if (*fmt != 'u') {
+ if (is_negative) {
+ prefix_char = '-';
+ } else if (print_sign) {
+ prefix_char = '+';
+ } else if (print_blank) {
+ prefix_char = ' ';
+ }
+ }
+ break;
+
+
+ case 'o':
+ switch(modifier) {
+ default:
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ ui_num = (u_wide_int) va_arg(ap, unsigned long int);
+ break;
+ case LM_SIZE_T:
+ ui_num = (u_wide_int) va_arg(ap, size_t);
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ ui_num = (u_wide_int) va_arg(ap, uintmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && *s != '0') {
+ *--s = '0';
+ s_len++;
+ }
+ break;
+
+
+ case 'x':
+ case 'X':
+ switch(modifier) {
+ default:
+ ui_num = (u_wide_int) va_arg(ap, unsigned int);
+ break;
+ case LM_LONG_DOUBLE:
+ goto fmt_error;
+ case LM_LONG:
+ ui_num = (u_wide_int) va_arg(ap, unsigned long int);
+ break;
+ case LM_SIZE_T:
+ ui_num = (u_wide_int) va_arg(ap, size_t);
+ break;
+#if SIZEOF_LONG_LONG
+ case LM_LONG_LONG:
+ ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+ break;
+#endif
+#if SIZEOF_INTMAX_T
+ case LM_INTMAX_T:
+ ui_num = (u_wide_int) va_arg(ap, uintmax_t);
+ break;
+#endif
+#if SIZEOF_PTRDIFF_T
+ case LM_PTRDIFF_T:
+ ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
+ break;
+#endif
+ }
+ s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ if (alternate_form && i_num != 0) {
+ *--s = *fmt; /* 'x' or 'X' */
+ *--s = '0';
+ s_len += 2;
+ }
+ break;
+
+
+ case 's':
+ case 'v':
+ s = va_arg(ap, char *);
+ if (s != NULL) {
+ s_len = strlen(s);
+ if (adjust_precision && precision < s_len) {
+ s_len = precision;
+ }
+ } else {
+ s = S_NULL;
+ s_len = S_NULL_LEN;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case 'f':
+ case 'F':
+ case 'e':
+ case 'E':
+ switch(modifier) {
+ case LM_LONG_DOUBLE:
+ fp_num = (double) va_arg(ap, long double);
+ break;
+ case LM_STD:
+ fp_num = va_arg(ap, double);
+ break;
+ default:
+ goto fmt_error;
+ }
+
+ if (zend_isnan(fp_num)) {
+ s = "NAN";
+ s_len = 3;
+ } else if (zend_isinf(fp_num)) {
+ s = "INF";
+ s_len = 3;
+ } else {
+#ifdef HAVE_LOCALE_H
+ if (!lconv) {
+ lconv = localeconv();
+ }
+#endif
+ s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
+ (adjust_precision == NO) ? FLOAT_DIGITS : precision,
+ (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
+ &is_negative, &num_buf[1], &s_len);
+ if (is_negative)
+ prefix_char = '-';
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+ }
+ break;
+
+
+ case 'g':
+ case 'k':
+ case 'G':
+ case 'H':
+ switch(modifier) {
+ case LM_LONG_DOUBLE:
+ fp_num = (double) va_arg(ap, long double);
+ break;
+ case LM_STD:
+ fp_num = va_arg(ap, double);
+ break;
+ default:
+ goto fmt_error;
+ }
+
+ if (zend_isnan(fp_num)) {
+ s = "NAN";
+ s_len = 3;
+ break;
+ } else if (zend_isinf(fp_num)) {
+ if (fp_num > 0) {
+ s = "INF";
+ s_len = 3;
+ } else {
+ s = "-INF";
+ s_len = 4;
+ }
+ break;
+ }
+
+ if (adjust_precision == NO) {
+ precision = FLOAT_DIGITS;
+ } else if (precision == 0) {
+ precision = 1;
+ }
+ /*
+ * * We use &num_buf[ 1 ], so that we have room for the sign
+ */
+#ifdef HAVE_LOCALE_H
+ if (!lconv) {
+ lconv = localeconv();
+ }
+#endif
+ s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
+ if (*s == '-') {
+ prefix_char = *s++;
+ } else if (print_sign) {
+ prefix_char = '+';
+ } else if (print_blank) {
+ prefix_char = ' ';
+ }
+
+ s_len = strlen(s);
+
+ if (alternate_form && (strchr(s, '.')) == NULL) {
+ s[s_len++] = '.';
+ }
+ break;
+
+
+ case 'c':
+ char_buf[0] = (char) (va_arg(ap, int));
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case '%':
+ char_buf[0] = '%';
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+
+ case 'n':
+ *(va_arg(ap, int *)) = cc;
+ goto skip_output;
+
+ /*
+ * Always extract the argument as a "char *" pointer. We
+ * should be using "void *" but there are still machines
+ * that don't understand it.
+ * If the pointer size is equal to the size of an unsigned
+ * integer we convert the pointer to a hex number, otherwise
+ * we print "%p" to indicate that we don't handle "%p".
+ */
+ case 'p':
+ if (sizeof(char *) <= sizeof(u_wide_int)) {
+ ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
+ s = ap_php_conv_p2(ui_num, 4, 'x',
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ if (ui_num != 0) {
+ *--s = 'x';
+ *--s = '0';
+ s_len += 2;
+ }
+ } else {
+ s = "%p";
+ s_len = 2;
+ }
+ pad_char = ' ';
+ break;
+
+
+ case NUL:
+ /*
+ * The last character of the format string was %.
+ * We ignore it.
+ */
+ continue;
+
+
+fmt_error:
+ php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
+ /*
+ * The default case is for unrecognized %'s.
+ * We print %<char> to help the user identify what
+ * option is not understood.
+ * This is also useful in case the user wants to pass
+ * the output of format_converter to another function
+ * that understands some other %<char> (like syslog).
+ * Note that we can't point s inside fmt because the
+ * unknown <char> could be preceded by width etc.
+ */
+ default:
+ char_buf[0] = '%';
+ char_buf[1] = *fmt;
+ s = char_buf;
+ s_len = 2;
+ pad_char = ' ';
+ break;
+ }
+
+ if (prefix_char != NUL) {
+ *--s = prefix_char;
+ s_len++;
+ }
+ if (adjust_width && adjust == RIGHT && min_width > s_len) {
+ if (pad_char == '0' && prefix_char != NUL) {
+ INS_CHAR(*s, sp, bep, cc)
+ s++;
+ s_len--;
+ min_width--;
+ }
+ PAD(min_width, s_len, pad_char);
+ }
+ /*
+ * Print the string s.
+ */
+ for (i = s_len; i != 0; i--) {
+ INS_CHAR(*s, sp, bep, cc);
+ s++;
+ }
+
+ if (adjust_width && adjust == LEFT && min_width > s_len)
+ PAD(min_width, s_len, pad_char);
+ if (free_zcopy) {
+ zval_dtor(&zcopy);
+ }
+ }
+skip_output:
+ fmt++;
+ }
+ odp->nextb = sp;
+ return (cc);
+}
+/* }}} */
+
+/*
+ * This is the general purpose conversion function.
+ */
+static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) /* {{{ */
+{
+ buffy od;
+ int cc;
+
+ /*
+ * First initialize the descriptor
+ * Notice that if no length is given, we initialize buf_end to the
+ * highest possible address.
+ */
+ if (len == 0) {
+ od.buf_end = (char *) ~0;
+ od.nextb = (char *) ~0;
+ } else {
+ od.buf_end = &buf[len-1];
+ od.nextb = buf;
+ }
+
+ /*
+ * Do the conversion
+ */
+ cc = format_converter(&od, format, ap);
+ if (len != 0 && od.nextb <= od.buf_end) {
+ *(od.nextb) = '\0';
+ }
+ if (ccp) {
+ *ccp = cc;
+ }
+}
+/* }}} */
+
+PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
+{
+ unsigned int cc;
+ va_list ap;
+
+ va_start(ap, format);
+ strx_printv(&cc, buf, len, format, ap);
+ va_end(ap);
+ if (cc >= len) {
+ cc = len -1;
+ buf[cc] = '\0';
+ }
+ return cc;
+}
+/* }}} */
+
+PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
+{
+ unsigned int cc;
+
+ strx_printv(&cc, buf, len, format, ap);
+ if (cc >= len) {
+ cc = len -1;
+ buf[cc] = '\0';
+ }
+ return cc;
+}
+/* }}} */
+
+PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
+{
+ int cc;
+ va_list ap;
+
+ va_start(ap, format);
+ strx_printv(&cc, buf, len, format, ap);
+ va_end(ap);
+ return (cc);
+}
+/* }}} */
+
+PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
+{
+ int cc;
+
+ strx_printv(&cc, buf, len, format, ap);
+ return (cc);
+}
+/* }}} */
+
+PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */
+{
+ va_list ap2;
+ int cc;
+
+ va_copy(ap2, ap);
+ cc = ap_php_vsnprintf(NULL, 0, format, ap2);
+ va_end(ap2);
+
+ *buf = NULL;
+
+ if (cc >= 0) {
+ if ((*buf = malloc(++cc)) != NULL) {
+ if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) {
+ free(*buf);
+ *buf = NULL;
+ }
+ }
+ }
+
+ return cc;
+}
+/* }}} */
+
+PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */
+{
+ int cc;
+ va_list ap;
+
+ va_start(ap, format);
+ cc = vasprintf(buf, format, ap);
+ va_end(ap);
+ return cc;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */