Back to index

php5  5.3.10
snprintf.c
Go to the documentation of this file.
00001 /*
00002   +----------------------------------------------------------------------+
00003   | PHP Version 5                                                        |
00004   +----------------------------------------------------------------------+
00005   | Copyright (c) 1997-2012 The PHP Group                                |
00006   +----------------------------------------------------------------------+
00007   | This source file is subject to version 3.01 of the PHP license,      |
00008   | that is bundled with this package in the file LICENSE, and is        |
00009   | available through the world-wide-web at the following url:           |
00010   | http://www.php.net/license/3_01.txt                                  |
00011   | If you did not receive a copy of the PHP license and are unable to   |
00012   | obtain it through the world-wide-web, please send a note to          |
00013   | license@php.net so we can mail you a copy immediately.               |
00014   +----------------------------------------------------------------------+
00015   | Author:                                                              |
00016   +----------------------------------------------------------------------+
00017 */
00018 
00019 /* $Id: snprintf.c 321634 2012-01-01 13:15:04Z felipe $ */
00020 
00021 
00022 #include "php.h"
00023 
00024 #include <zend_strtod.h>
00025 
00026 #include <stddef.h>
00027 #include <stdio.h>
00028 #include <ctype.h>
00029 #include <sys/types.h>
00030 #include <stdarg.h>
00031 #include <string.h>
00032 #include <stdlib.h>
00033 #include <math.h>
00034 
00035 #ifdef HAVE_INTTYPES_H
00036 #include <inttypes.h>
00037 #endif
00038 
00039 #ifdef HAVE_LOCALE_H
00040 #include <locale.h>
00041 #define LCONV_DECIMAL_POINT (*lconv->decimal_point)
00042 #else
00043 #define LCONV_DECIMAL_POINT '.'
00044 #endif
00045 
00046 /*
00047  * Copyright (c) 2002, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
00048  *
00049  * Permission to use, copy, modify, and distribute this software for any
00050  * purpose with or without fee is hereby granted, provided that the above
00051  * copyright notice and this permission notice appear in all copies.
00052  *
00053  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
00054  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00055  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
00056  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00057  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00058  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
00059  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00060  *
00061  * Sponsored in part by the Defense Advanced Research Projects
00062  * Agency (DARPA) and Air Force Research Laboratory, Air Force
00063  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
00064  */
00065 
00066 static char * __cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad) /* {{{ */
00067 {
00068        register char *s = NULL;
00069        char *p, *rve, c;
00070        size_t siz;
00071 
00072        if (ndigit < 0) {
00073               siz = -ndigit + 1;
00074        } else {
00075               siz = ndigit + 1;
00076        }
00077 
00078        /* __dtoa() doesn't allocate space for 0 so we do it by hand */
00079        if (value == 0.0) {
00080               *decpt = 1 - fmode; /* 1 for 'e', 0 for 'f' */
00081               *sign = 0;
00082               if ((rve = s = (char *)malloc(ndigit?siz:2)) == NULL) {
00083                      return(NULL);
00084               }
00085               *rve++ = '0';
00086               *rve = '\0';
00087               if (!ndigit) {
00088                      return(s);
00089               }
00090        } else {
00091               p = zend_dtoa(value, fmode + 2, ndigit, decpt, sign, &rve);
00092               if (*decpt == 9999) {
00093                      /* Infinity or Nan, convert to inf or nan like printf */
00094                      *decpt = 0;
00095                      c = *p;
00096                      zend_freedtoa(p);
00097                      return(c == 'I' ? "INF" : "NAN");
00098               }
00099               /* Make a local copy and adjust rve to be in terms of s */
00100               if (pad && fmode) {
00101                      siz += *decpt;
00102               }
00103               if ((s = (char *)malloc(siz+1)) == NULL) {
00104                      zend_freedtoa(p);
00105                      return(NULL);
00106               }
00107               (void) strlcpy(s, p, siz);
00108               rve = s + (rve - p);
00109               zend_freedtoa(p);
00110        }
00111 
00112        /* Add trailing zeros */
00113        if (pad) {
00114               siz -= rve - s;
00115               while (--siz) {
00116                      *rve++ = '0';
00117               }
00118               *rve = '\0';
00119        }
00120 
00121        return(s);
00122 }
00123 /* }}} */
00124 
00125 static inline char *php_ecvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
00126 {
00127        return(__cvt(value, ndigit, decpt, sign, 0, 1));
00128 }
00129 /* }}} */
00130 
00131 static inline char *php_fcvt(double value, int ndigit, int *decpt, int *sign) /* {{{ */
00132 {
00133     return(__cvt(value, ndigit, decpt, sign, 1, 1));
00134 }
00135 /* }}} */
00136 
00137 PHPAPI char *php_gcvt(double value, int ndigit, char dec_point, char exponent, char *buf) /* {{{ */
00138 {
00139        char *digits, *dst, *src;
00140        int i, decpt, sign;
00141 
00142        digits = zend_dtoa(value, 2, ndigit, &decpt, &sign, NULL);
00143        if (decpt == 9999) {
00144               /*
00145                * Infinity or NaN, convert to inf or nan with sign.
00146                * We assume the buffer is at least ndigit long.
00147                */
00148               snprintf(buf, ndigit + 1, "%s%s", (sign && *digits == 'I') ? "-" : "", *digits == 'I' ? "INF" : "NAN");
00149               zend_freedtoa(digits);
00150               return (buf);
00151        }
00152 
00153        dst = buf;
00154        if (sign) {
00155               *dst++ = '-';
00156        }
00157 
00158        if ((decpt >= 0 && decpt > ndigit) || decpt < -3) { /* use E-style */
00159               /* exponential format (e.g. 1.2345e+13) */
00160               if (--decpt < 0) {
00161                      sign = 1;
00162                      decpt = -decpt;
00163               } else {
00164                      sign = 0;
00165               }
00166               src = digits;
00167               *dst++ = *src++;
00168               *dst++ = dec_point;
00169               if (*src == '\0') {
00170                      *dst++ = '0';
00171               } else {
00172                      do {
00173                             *dst++ = *src++;
00174                      } while (*src != '\0');
00175               }
00176               *dst++ = exponent;
00177               if (sign) {
00178                      *dst++ = '-';
00179               } else {
00180                      *dst++ = '+';
00181               }
00182               if (decpt < 10) {
00183                      *dst++ = '0' + decpt;
00184                      *dst = '\0';
00185               } else {
00186                      /* XXX - optimize */
00187                      for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
00188                             continue;
00189                      dst[i + 1] = '\0';
00190                      while (decpt != 0) {
00191                             dst[i--] = '0' + decpt % 10;
00192                             decpt /= 10;
00193                      }
00194               }
00195        } else if (decpt < 0) {
00196               /* standard format 0. */
00197               *dst++ = '0';   /* zero before decimal point */
00198               *dst++ = dec_point;
00199               do {
00200                      *dst++ = '0';
00201               } while (++decpt < 0);
00202               src = digits;
00203               while (*src != '\0') {
00204                      *dst++ = *src++;
00205               }
00206               *dst = '\0';
00207        } else {
00208               /* standard format */
00209               for (i = 0, src = digits; i < decpt; i++) {
00210                      if (*src != '\0') {
00211                             *dst++ = *src++;
00212                      } else {
00213                             *dst++ = '0';
00214                      }
00215               }
00216               if (*src != '\0') {
00217                      if (src == digits) {
00218                             *dst++ = '0';   /* zero before decimal point */
00219                      }
00220                      *dst++ = dec_point;
00221                      for (i = decpt; digits[i] != '\0'; i++) {
00222                 *dst++ = digits[i];
00223             }
00224         }
00225         *dst = '\0';
00226     }
00227     zend_freedtoa(digits);
00228     return (buf);
00229 }
00230 /* }}} */
00231 
00232 /* {{{ Apache license */
00233 /* ====================================================================
00234  * Copyright (c) 1995-1998 The Apache Group.  All rights reserved.
00235  *
00236  * Redistribution and use in source and binary forms, with or without
00237  * modification, are permitted provided that the following conditions
00238  * are met:
00239  *
00240  * 1. Redistributions of source code must retain the above copyright
00241  *    notice, this list of conditions and the following disclaimer.
00242  *
00243  * 2. Redistributions in binary form must reproduce the above copyright
00244  *    notice, this list of conditions and the following disclaimer in
00245  *    the documentation and/or other materials provided with the
00246  *    distribution.
00247  *
00248  * 3. All advertising materials mentioning features or use of this
00249  *    software must display the following acknowledgment:
00250  *    "This product includes software developed by the Apache Group
00251  *    for use in the Apache HTTP server project (http://www.apache.org/)."
00252  *
00253  * 4. The names "Apache Server" and "Apache Group" must not be used to
00254  *    endorse or promote products derived from this software without
00255  *    prior written permission.
00256  *
00257  * 5. Redistributions of any form whatsoever must retain the following
00258  *    acknowledgment:
00259  *    "This product includes software developed by the Apache Group
00260  *    for use in the Apache HTTP server project (http://www.apache.org/)."
00261  *
00262  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
00263  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00264  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00265  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
00266  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00267  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00268  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00269  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00270  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
00271  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00272  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
00273  * OF THE POSSIBILITY OF SUCH DAMAGE.
00274  * ====================================================================
00275  *
00276  * This software consists of voluntary contributions made by many
00277  * individuals on behalf of the Apache Group and was originally based
00278  * on public domain software written at the National Center for
00279  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
00280  * For more information on the Apache Group and the Apache HTTP server
00281  * project, please see <http://www.apache.org/>.
00282  *
00283  * This code is based on, and used with the permission of, the
00284  * SIO stdio-replacement strx_* functions by Panos Tsirigotis
00285  * <panos@alumni.cs.colorado.edu> for xinetd.
00286  */
00287 /* }}} */
00288 
00289 #define FALSE               0
00290 #define TRUE                1
00291 #define NUL                 '\0'
00292 #define INT_NULL            ((int *)0)
00293 
00294 #define S_NULL                     "(null)"
00295 #define S_NULL_LEN          6
00296 
00297 #define FLOAT_DIGITS        6
00298 #define EXPONENT_LENGTH            10
00299 
00300 
00301 /*
00302  * Convert num to its decimal format.
00303  * Return value:
00304  *   - a pointer to a string containing the number (no sign)
00305  *   - len contains the length of the string
00306  *   - is_negative is set to TRUE or FALSE depending on the sign
00307  *     of the number (always set to FALSE if is_unsigned is TRUE)
00308  *
00309  * The caller provides a buffer for the string: that is the buf_end argument
00310  * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
00311  * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
00312  */
00313 /* char * ap_php_conv_10() {{{ */
00314 char * ap_php_conv_10(register wide_int num, register bool_int is_unsigned,
00315           register bool_int * is_negative, char *buf_end, register int *len)
00316 {
00317        register char *p = buf_end;
00318        register u_wide_int magnitude;
00319 
00320        if (is_unsigned) {
00321               magnitude = (u_wide_int) num;
00322               *is_negative = FALSE;
00323        } else {
00324               *is_negative = (num < 0);
00325 
00326               /*
00327                * On a 2's complement machine, negating the most negative integer
00328                * results in a number that cannot be represented as a signed integer.
00329                * Here is what we do to obtain the number's magnitude:
00330                *      a. add 1 to the number
00331                *      b. negate it (becomes positive)
00332                *      c. convert it to unsigned
00333                *      d. add 1
00334                */
00335               if (*is_negative) {
00336                      wide_int t = num + 1;
00337                      magnitude = ((u_wide_int) - t) + 1;
00338               } else {
00339                      magnitude = (u_wide_int) num;
00340               }
00341        }
00342 
00343        /*
00344         * We use a do-while loop so that we write at least 1 digit
00345         */
00346        do {
00347               register u_wide_int new_magnitude = magnitude / 10;
00348 
00349               *--p = (char)(magnitude - new_magnitude * 10 + '0');
00350               magnitude = new_magnitude;
00351        }
00352        while (magnitude);
00353 
00354        *len = buf_end - p;
00355        return (p);
00356 }
00357 /* }}} */
00358 
00359 /* If you change this value then also change bug24640.phpt.
00360  * Also NDIG must be reasonable smaller than NUM_BUF_SIZE.
00361  */
00362 #define       NDIG   320
00363 
00364 
00365 /*
00366  * Convert a floating point number to a string formats 'f', 'e' or 'E'.
00367  * The result is placed in buf, and len denotes the length of the string
00368  * The sign is returned in the is_negative argument (and is not placed
00369  * in buf).
00370  */
00371 /* PHPAPI char * php_conv_fp() {{{ */
00372 PHPAPI char * php_conv_fp(register char format, register double num,
00373                boolean_e add_dp, int precision, char dec_point, bool_int * is_negative, char *buf, int *len)
00374 {
00375        register char *s = buf;
00376        register char *p, *p_orig;
00377        int decimal_point;
00378 
00379        if (precision >= NDIG - 1) {
00380               precision = NDIG - 2;
00381        }
00382 
00383        if (format == 'F') {
00384               p_orig = p = php_fcvt(num, precision, &decimal_point, is_negative);
00385        } else {                                         /* either e or E format */
00386               p_orig = p = php_ecvt(num, precision + 1, &decimal_point, is_negative);
00387        }
00388 
00389        /*
00390         * Check for Infinity and NaN
00391         */
00392        if (isalpha((int)*p)) {
00393               *len = strlen(p);
00394               memcpy(buf, p, *len + 1);
00395               *is_negative = FALSE;
00396               free(p_orig);
00397               return (buf);
00398        }
00399        if (format == 'F') {
00400               if (decimal_point <= 0) {
00401                      if (num != 0 || precision > 0) {
00402                             *s++ = '0';
00403                             if (precision > 0) {
00404                                    *s++ = dec_point;
00405                                    while (decimal_point++ < 0) {
00406                                           *s++ = '0';
00407                                    }
00408                             } else if (add_dp) {
00409                                    *s++ = dec_point;
00410                             }
00411                      }
00412               } else {
00413                      int addz = decimal_point >= NDIG ? decimal_point - NDIG + 1 : 0;
00414                      decimal_point -= addz;
00415                      while (decimal_point-- > 0) {
00416                             *s++ = *p++;
00417                      }
00418                      while (addz-- > 0) {
00419                             *s++ = '0';
00420                      }
00421                      if (precision > 0 || add_dp) {
00422                             *s++ = dec_point;
00423                      }
00424               }
00425        } else {
00426               *s++ = *p++;
00427               if (precision > 0 || add_dp) {
00428                      *s++ = '.';
00429               }
00430        }
00431 
00432        /*
00433         * copy the rest of p, the NUL is NOT copied
00434         */
00435        while (*p) {
00436               *s++ = *p++;
00437        }
00438 
00439        if (format != 'F') {
00440               char temp[EXPONENT_LENGTH];        /* for exponent conversion */
00441               int t_len;
00442               bool_int exponent_is_negative;
00443 
00444               *s++ = format;                     /* either e or E */
00445               decimal_point--;
00446               if (decimal_point != 0) {
00447                      p = ap_php_conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len);
00448                      *s++ = exponent_is_negative ? '-' : '+';
00449 
00450                      /*
00451                       * Make sure the exponent has at least 2 digits
00452                       */
00453                      while (t_len--) {
00454                             *s++ = *p++;
00455                      }
00456               } else {
00457                      *s++ = '+';
00458                      *s++ = '0';
00459               }
00460        }
00461        *len = s - buf;
00462        free(p_orig);
00463        return (buf);
00464 }
00465 /* }}} */
00466 
00467 /*
00468  * Convert num to a base X number where X is a power of 2. nbits determines X.
00469  * For example, if nbits is 3, we do base 8 conversion
00470  * Return value:
00471  *      a pointer to a string containing the number
00472  *
00473  * The caller provides a buffer for the string: that is the buf_end argument
00474  * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
00475  * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
00476  */
00477 char * ap_php_conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) /* {{{ */
00478 {
00479        register int mask = (1 << nbits) - 1;
00480        register char *p = buf_end;
00481        static char low_digits[] = "0123456789abcdef";
00482        static char upper_digits[] = "0123456789ABCDEF";
00483        register char *digits = (format == 'X') ? upper_digits : low_digits;
00484 
00485        do {
00486               *--p = digits[num & mask];
00487               num >>= nbits;
00488        }
00489        while (num);
00490 
00491        *len = buf_end - p;
00492        return (p);
00493 }
00494 /* }}} */
00495 
00496 /*
00497  * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
00498  *
00499  * XXX: this is a magic number; do not decrease it
00500  * Emax = 1023
00501  * NDIG = 320
00502  * NUM_BUF_SIZE >= strlen("-") + Emax + strlrn(".") + NDIG + strlen("E+1023") + 1;
00503  */
00504 #define NUM_BUF_SIZE        2048
00505 
00506 
00507 /*
00508  * Descriptor for buffer area
00509  */
00510 struct buf_area {
00511        char *buf_end;
00512        char *nextb;                       /* pointer to next byte to read/write   */
00513 };
00514 
00515 typedef struct buf_area buffy;
00516 
00517 /*
00518  * The INS_CHAR macro inserts a character in the buffer and writes
00519  * the buffer back to disk if necessary
00520  * It uses the char pointers sp and bep:
00521  *      sp points to the next available character in the buffer
00522  *      bep points to the end-of-buffer+1
00523  * While using this macro, note that the nextb pointer is NOT updated.
00524  *
00525  * NOTE: Evaluation of the c argument should not have any side-effects
00526  */
00527 #define INS_CHAR(c, sp, bep, cc) \
00528        {                            \
00529               if (sp < bep)            \
00530               {                        \
00531                      *sp++ = c;           \
00532               }                        \
00533               cc++;                    \
00534        }
00535 
00536 #define NUM( c )                   ( c - '0' )
00537 
00538 #define STR_TO_DEC( str, num )            \
00539     num = NUM( *str++ ) ;          \
00540     while ( isdigit((int)*str ) )         \
00541     {                              \
00542        num *= 10 ;                 \
00543        num += NUM( *str++ ) ;             \
00544     }
00545 
00546 /*
00547  * This macro does zero padding so that the precision
00548  * requirement is satisfied. The padding is done by
00549  * adding '0's to the left of the string that is going
00550  * to be printed.
00551  */
00552 #define FIX_PRECISION( adjust, precision, s, s_len )    \
00553     if ( adjust )                                \
00554        while ( s_len < precision )               \
00555        {                                         \
00556            *--s = '0' ;                          \
00557            s_len++ ;                             \
00558        }
00559 
00560 /*
00561  * Macro that does padding. The padding is done by printing
00562  * the character ch.
00563  */
00564 #define PAD( width, len, ch )      do            \
00565        {                                  \
00566            INS_CHAR( ch, sp, bep, cc ) ;  \
00567            width-- ;                      \
00568        }                                  \
00569        while ( width > len )
00570 
00571 /*
00572  * Prefix the character ch to the string str
00573  * Increase length
00574  * Set the has_prefix flag
00575  */
00576 #define PREFIX( str, length, ch )   *--str = ch ; length++ ; has_prefix = YES
00577 
00578 
00579 /*
00580  * Do format conversion placing the output in buffer
00581  */
00582 static int format_converter(register buffy * odp, const char *fmt, va_list ap) /* {{{ */
00583 {
00584        char *sp;
00585        char *bep;
00586        int cc = 0;
00587        int i;
00588 
00589        char *s = NULL;
00590        int s_len, free_zcopy;
00591        zval *zvp, zcopy;
00592 
00593        int min_width = 0;
00594        int precision = 0;
00595        enum {
00596               LEFT, RIGHT
00597        } adjust;
00598        char pad_char;
00599        char prefix_char;
00600 
00601        double fp_num;
00602        wide_int i_num = (wide_int) 0;
00603        u_wide_int ui_num;
00604 
00605        char num_buf[NUM_BUF_SIZE];
00606        char char_buf[2];                  /* for printing %% and %<unknown> */
00607 
00608 #ifdef HAVE_LOCALE_H
00609        struct lconv *lconv = NULL;
00610 #endif
00611 
00612        /*
00613         * Flag variables
00614         */
00615        length_modifier_e modifier;
00616        boolean_e alternate_form;
00617        boolean_e print_sign;
00618        boolean_e print_blank;
00619        boolean_e adjust_precision;
00620        boolean_e adjust_width;
00621        bool_int is_negative;
00622 
00623        sp = odp->nextb;
00624        bep = odp->buf_end;
00625 
00626        while (*fmt) {
00627               if (*fmt != '%') {
00628                      INS_CHAR(*fmt, sp, bep, cc);
00629               } else {
00630                      /*
00631                       * Default variable settings
00632                       */
00633                      adjust = RIGHT;
00634                      alternate_form = print_sign = print_blank = NO;
00635                      pad_char = ' ';
00636                      prefix_char = NUL;
00637                      free_zcopy = 0;
00638 
00639                      fmt++;
00640 
00641                      /*
00642                       * Try to avoid checking for flags, width or precision
00643                       */
00644                      if (isascii((int)*fmt) && !islower((int)*fmt)) {
00645                             /*
00646                              * Recognize flags: -, #, BLANK, +
00647                              */
00648                             for (;; fmt++) {
00649                                    if (*fmt == '-')
00650                                           adjust = LEFT;
00651                                    else if (*fmt == '+')
00652                                           print_sign = YES;
00653                                    else if (*fmt == '#')
00654                                           alternate_form = YES;
00655                                    else if (*fmt == ' ')
00656                                           print_blank = YES;
00657                                    else if (*fmt == '0')
00658                                           pad_char = '0';
00659                                    else
00660                                           break;
00661                             }
00662 
00663                             /*
00664                              * Check if a width was specified
00665                              */
00666                             if (isdigit((int)*fmt)) {
00667                                    STR_TO_DEC(fmt, min_width);
00668                                    adjust_width = YES;
00669                             } else if (*fmt == '*') {
00670                                    min_width = va_arg(ap, int);
00671                                    fmt++;
00672                                    adjust_width = YES;
00673                                    if (min_width < 0) {
00674                                           adjust = LEFT;
00675                                           min_width = -min_width;
00676                                    }
00677                             } else
00678                                    adjust_width = NO;
00679 
00680                             /*
00681                              * Check if a precision was specified
00682                              */
00683                             if (*fmt == '.') {
00684                                    adjust_precision = YES;
00685                                    fmt++;
00686                                    if (isdigit((int)*fmt)) {
00687                                           STR_TO_DEC(fmt, precision);
00688                                    } else if (*fmt == '*') {
00689                                           precision = va_arg(ap, int);
00690                                           fmt++;
00691                                           if (precision < 0)
00692                                                  precision = 0;
00693                                    } else
00694                                           precision = 0;
00695                                    
00696                                    if (precision > FORMAT_CONV_MAX_PRECISION) {
00697                                           precision = FORMAT_CONV_MAX_PRECISION;
00698                                    }
00699                             } else
00700                                    adjust_precision = NO;
00701                      } else
00702                             adjust_precision = adjust_width = NO;
00703 
00704                      /*
00705                       * Modifier check
00706                       */
00707                      switch (*fmt) {
00708                             case 'L':
00709                                    fmt++;
00710                                    modifier = LM_LONG_DOUBLE;
00711                                    break;
00712                             case 'I':
00713                                    fmt++;
00714 #if SIZEOF_LONG_LONG
00715                                    if (*fmt == '6' && *(fmt+1) == '4') {
00716                                           fmt += 2;
00717                                           modifier = LM_LONG_LONG;
00718                                    } else
00719 #endif
00720                                           if (*fmt == '3' && *(fmt+1) == '2') {
00721                                                  fmt += 2;
00722                                                  modifier = LM_LONG;
00723                                           } else {
00724 #ifdef _WIN64
00725                                                  modifier = LM_LONG_LONG;
00726 #else
00727                                                  modifier = LM_LONG;
00728 #endif
00729                                           }
00730                                    break;
00731                             case 'l':
00732                                    fmt++;
00733 #if SIZEOF_LONG_LONG
00734                                    if (*fmt == 'l') {
00735                                           fmt++;
00736                                           modifier = LM_LONG_LONG;
00737                                    } else
00738 #endif
00739                                           modifier = LM_LONG;
00740                                    break;
00741                             case 'z':
00742                                    fmt++;
00743                                    modifier = LM_SIZE_T;
00744                                    break;
00745                             case 'j':
00746                                    fmt++;
00747 #if SIZEOF_INTMAX_T
00748                                    modifier = LM_INTMAX_T;
00749 #else
00750                                    modifier = LM_SIZE_T;
00751 #endif
00752                                    break;
00753                             case 't':
00754                                    fmt++;
00755 #if SIZEOF_PTRDIFF_T
00756                                    modifier = LM_PTRDIFF_T;
00757 #else
00758                                    modifier = LM_SIZE_T;
00759 #endif
00760                                    break;
00761                             case 'h':
00762                                    fmt++;
00763                                    if (*fmt == 'h') {
00764                                           fmt++;
00765                                    }
00766                                    /* these are promoted to int, so no break */
00767                             default:
00768                                    modifier = LM_STD;
00769                                    break;
00770                      }
00771 
00772                      /*
00773                       * Argument extraction and printing.
00774                       * First we determine the argument type.
00775                       * Then, we convert the argument to a string.
00776                       * On exit from the switch, s points to the string that
00777                       * must be printed, s_len has the length of the string
00778                       * The precision requirements, if any, are reflected in s_len.
00779                       *
00780                       * NOTE: pad_char may be set to '0' because of the 0 flag.
00781                       *   It is reset to ' ' by non-numeric formats
00782                       */
00783                      switch (*fmt) {
00784                             case 'Z':
00785                                    zvp = (zval*) va_arg(ap, zval*);
00786                                    zend_make_printable_zval(zvp, &zcopy, &free_zcopy);
00787                                    if (free_zcopy) {
00788                                           zvp = &zcopy;
00789                                    }
00790                                    s_len = Z_STRLEN_P(zvp);
00791                                    s = Z_STRVAL_P(zvp);
00792                                    if (adjust_precision && precision < s_len) {
00793                                           s_len = precision;
00794                                    }
00795                                    break;
00796                             case 'u':
00797                                    switch(modifier) {
00798                                           default:
00799                                                  i_num = (wide_int) va_arg(ap, unsigned int);
00800                                                  break;
00801                                           case LM_LONG_DOUBLE:
00802                                                  goto fmt_error;
00803                                           case LM_LONG:
00804                                                  i_num = (wide_int) va_arg(ap, unsigned long int);
00805                                                  break;
00806                                           case LM_SIZE_T:
00807                                                  i_num = (wide_int) va_arg(ap, size_t);
00808                                                  break;
00809 #if SIZEOF_LONG_LONG
00810                                           case LM_LONG_LONG:
00811                                                  i_num = (wide_int) va_arg(ap, u_wide_int);
00812                                                  break;
00813 #endif
00814 #if SIZEOF_INTMAX_T
00815                                           case LM_INTMAX_T:
00816                                                  i_num = (wide_int) va_arg(ap, uintmax_t);
00817                                                  break;
00818 #endif
00819 #if SIZEOF_PTRDIFF_T
00820                                           case LM_PTRDIFF_T:
00821                                                  i_num = (wide_int) va_arg(ap, ptrdiff_t);
00822                                                  break;
00823 #endif
00824                                    }
00825                                    /*
00826                                     * The rest also applies to other integer formats, so fall
00827                                     * into that case.
00828                                     */
00829                             case 'd':
00830                             case 'i':
00831                                    /*
00832                                     * Get the arg if we haven't already.
00833                                     */
00834                                    if ((*fmt) != 'u') {
00835                                           switch(modifier) {
00836                                                  default:
00837                                                         i_num = (wide_int) va_arg(ap, int);
00838                                                         break;
00839                                                  case LM_LONG_DOUBLE:
00840                                                         goto fmt_error;
00841                                                  case LM_LONG:
00842                                                         i_num = (wide_int) va_arg(ap, long int);
00843                                                         break;
00844                                                  case LM_SIZE_T:
00845 #if SIZEOF_SSIZE_T
00846                                                         i_num = (wide_int) va_arg(ap, ssize_t);
00847 #else
00848                                                         i_num = (wide_int) va_arg(ap, size_t);
00849 #endif
00850                                                         break;
00851 #if SIZEOF_LONG_LONG
00852                                                  case LM_LONG_LONG:
00853                                                         i_num = (wide_int) va_arg(ap, wide_int);
00854                                                         break;
00855 #endif
00856 #if SIZEOF_INTMAX_T
00857                                                  case LM_INTMAX_T:
00858                                                         i_num = (wide_int) va_arg(ap, intmax_t);
00859                                                         break;
00860 #endif
00861 #if SIZEOF_PTRDIFF_T
00862                                                  case LM_PTRDIFF_T:
00863                                                         i_num = (wide_int) va_arg(ap, ptrdiff_t);
00864                                                         break;
00865 #endif
00866                                           }
00867                                    }
00868                                    s = ap_php_conv_10(i_num, (*fmt) == 'u', &is_negative,
00869                                                         &num_buf[NUM_BUF_SIZE], &s_len);
00870                                    FIX_PRECISION(adjust_precision, precision, s, s_len);
00871 
00872                                    if (*fmt != 'u') {
00873                                           if (is_negative) {
00874                                                  prefix_char = '-';
00875                                           } else if (print_sign) {
00876                                                  prefix_char = '+';
00877                                           } else if (print_blank) {
00878                                                  prefix_char = ' ';
00879                                           }
00880                                    }
00881                                    break;
00882 
00883 
00884                             case 'o':
00885                                    switch(modifier) {
00886                                           default:
00887                                                  ui_num = (u_wide_int) va_arg(ap, unsigned int);
00888                                                  break;
00889                                           case LM_LONG_DOUBLE:
00890                                                  goto fmt_error;
00891                                           case LM_LONG:
00892                                                  ui_num = (u_wide_int) va_arg(ap, unsigned long int);
00893                                                  break;
00894                                           case LM_SIZE_T:
00895                                                  ui_num = (u_wide_int) va_arg(ap, size_t);
00896                                                  break;
00897 #if SIZEOF_LONG_LONG
00898                                           case LM_LONG_LONG:
00899                                                  ui_num = (u_wide_int) va_arg(ap, u_wide_int);
00900                                                  break;
00901 #endif
00902 #if SIZEOF_INTMAX_T
00903                                           case LM_INTMAX_T:
00904                                                  ui_num = (u_wide_int) va_arg(ap, uintmax_t);
00905                                                  break;
00906 #endif
00907 #if SIZEOF_PTRDIFF_T
00908                                           case LM_PTRDIFF_T:
00909                                                  ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
00910                                                  break;
00911 #endif
00912                                    }
00913                                    s = ap_php_conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
00914                                    FIX_PRECISION(adjust_precision, precision, s, s_len);
00915                                    if (alternate_form && *s != '0') {
00916                                           *--s = '0';
00917                                           s_len++;
00918                                    }
00919                                    break;
00920 
00921 
00922                             case 'x':
00923                             case 'X':
00924                                    switch(modifier) {
00925                                           default:
00926                                                  ui_num = (u_wide_int) va_arg(ap, unsigned int);
00927                                                  break;
00928                                           case LM_LONG_DOUBLE:
00929                                                  goto fmt_error;
00930                                           case LM_LONG:
00931                                                  ui_num = (u_wide_int) va_arg(ap, unsigned long int);
00932                                                  break;
00933                                           case LM_SIZE_T:
00934                                                  ui_num = (u_wide_int) va_arg(ap, size_t);
00935                                                  break;
00936 #if SIZEOF_LONG_LONG
00937                                           case LM_LONG_LONG:
00938                                                  ui_num = (u_wide_int) va_arg(ap, u_wide_int);
00939                                                  break;
00940 #endif
00941 #if SIZEOF_INTMAX_T
00942                                           case LM_INTMAX_T:
00943                                                  ui_num = (u_wide_int) va_arg(ap, uintmax_t);
00944                                                  break;
00945 #endif
00946 #if SIZEOF_PTRDIFF_T
00947                                           case LM_PTRDIFF_T:
00948                                                  ui_num = (u_wide_int) va_arg(ap, ptrdiff_t);
00949                                                  break;
00950 #endif
00951                                    }
00952                                    s = ap_php_conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len);
00953                                    FIX_PRECISION(adjust_precision, precision, s, s_len);
00954                                    if (alternate_form && i_num != 0) {
00955                                           *--s = *fmt;  /* 'x' or 'X' */
00956                                           *--s = '0';
00957                                           s_len += 2;
00958                                    }
00959                                    break;
00960 
00961 
00962                             case 's':
00963                             case 'v':
00964                                    s = va_arg(ap, char *);
00965                                    if (s != NULL) {
00966                                           s_len = strlen(s);
00967                                           if (adjust_precision && precision < s_len) {
00968                                                  s_len = precision;
00969                                           }
00970                                    } else {
00971                                           s = S_NULL;
00972                                           s_len = S_NULL_LEN;
00973                                    }
00974                                    pad_char = ' ';
00975                                    break;
00976 
00977 
00978                             case 'f':
00979                             case 'F':
00980                             case 'e':
00981                             case 'E':
00982                                    switch(modifier) {
00983                                           case LM_LONG_DOUBLE:
00984                                                  fp_num = (double) va_arg(ap, long double);
00985                                                  break;
00986                                           case LM_STD:
00987                                                  fp_num = va_arg(ap, double);
00988                                                  break;
00989                                           default:
00990                                                  goto fmt_error;
00991                                    }
00992 
00993                                    if (zend_isnan(fp_num)) {
00994                                           s = "NAN";
00995                                           s_len = 3;
00996                                    } else if (zend_isinf(fp_num)) {
00997                                           s = "INF";
00998                                           s_len = 3;
00999                                    } else {
01000 #ifdef HAVE_LOCALE_H
01001                                           if (!lconv) {
01002                                                  lconv = localeconv();
01003                                           }
01004 #endif
01005                                           s = php_conv_fp((*fmt == 'f')?'F':*fmt, fp_num, alternate_form,
01006                                            (adjust_precision == NO) ? FLOAT_DIGITS : precision,
01007                                            (*fmt == 'f')?LCONV_DECIMAL_POINT:'.',
01008                                                                &is_negative, &num_buf[1], &s_len);
01009                                           if (is_negative)
01010                                                  prefix_char = '-';
01011                                           else if (print_sign)
01012                                                  prefix_char = '+';
01013                                           else if (print_blank)
01014                                                  prefix_char = ' ';
01015                                    }
01016                                    break;
01017 
01018 
01019                             case 'g':
01020                             case 'k':
01021                             case 'G':
01022                             case 'H':
01023                                    switch(modifier) {
01024                                           case LM_LONG_DOUBLE:
01025                                                  fp_num = (double) va_arg(ap, long double);
01026                                                  break;
01027                                           case LM_STD:
01028                                                  fp_num = va_arg(ap, double);
01029                                                  break;
01030                                           default:
01031                                                  goto fmt_error;
01032                                    }
01033 
01034                                    if (zend_isnan(fp_num)) {
01035                                           s = "NAN";
01036                                           s_len = 3;
01037                                           break;
01038                                    } else if (zend_isinf(fp_num)) {
01039                                           if (fp_num > 0) {
01040                                                  s = "INF";
01041                                                  s_len = 3;
01042                                           } else {
01043                                                  s = "-INF";
01044                                                  s_len = 4;
01045                                           }
01046                                           break;
01047                                    }
01048 
01049                                    if (adjust_precision == NO) {
01050                                           precision = FLOAT_DIGITS;
01051                                    } else if (precision == 0) {
01052                                           precision = 1;
01053                                    }
01054                                    /*
01055                                     * * We use &num_buf[ 1 ], so that we have room for the sign
01056                                     */
01057 #ifdef HAVE_LOCALE_H
01058                                    if (!lconv) {
01059                                           lconv = localeconv();
01060                                    }
01061 #endif
01062                                    s = php_gcvt(fp_num, precision, (*fmt=='H' || *fmt == 'k') ? '.' : LCONV_DECIMAL_POINT, (*fmt == 'G' || *fmt == 'H')?'E':'e', &num_buf[1]);
01063                                    if (*s == '-') {
01064                                           prefix_char = *s++;
01065                                    } else if (print_sign) {
01066                                           prefix_char = '+';
01067                                    } else if (print_blank) {
01068                                           prefix_char = ' ';
01069                                    }
01070 
01071                                    s_len = strlen(s);
01072 
01073                                    if (alternate_form && (strchr(s, '.')) == NULL) {
01074                                           s[s_len++] = '.';
01075                                    }
01076                                    break;
01077 
01078 
01079                             case 'c':
01080                                    char_buf[0] = (char) (va_arg(ap, int));
01081                                    s = &char_buf[0];
01082                                    s_len = 1;
01083                                    pad_char = ' ';
01084                                    break;
01085 
01086 
01087                             case '%':
01088                                    char_buf[0] = '%';
01089                                    s = &char_buf[0];
01090                                    s_len = 1;
01091                                    pad_char = ' ';
01092                                    break;
01093 
01094 
01095                             case 'n':
01096                                    *(va_arg(ap, int *)) = cc;
01097                                    goto skip_output;
01098 
01099                                    /*
01100                                     * Always extract the argument as a "char *" pointer. We
01101                                     * should be using "void *" but there are still machines
01102                                     * that don't understand it.
01103                                     * If the pointer size is equal to the size of an unsigned
01104                                     * integer we convert the pointer to a hex number, otherwise
01105                                     * we print "%p" to indicate that we don't handle "%p".
01106                                     */
01107                             case 'p':
01108                                    if (sizeof(char *) <= sizeof(u_wide_int)) {
01109                                           ui_num = (u_wide_int)((size_t) va_arg(ap, char *));
01110                                           s = ap_php_conv_p2(ui_num, 4, 'x',
01111                                                         &num_buf[NUM_BUF_SIZE], &s_len);
01112                                           if (ui_num != 0) {
01113                                                  *--s = 'x';
01114                                                  *--s = '0';
01115                                                  s_len += 2;
01116                                           }
01117                                    } else {
01118                                           s = "%p";
01119                                           s_len = 2;
01120                                    }
01121                                    pad_char = ' ';
01122                                    break;
01123 
01124 
01125                             case NUL:
01126                                    /*
01127                                     * The last character of the format string was %.
01128                                     * We ignore it.
01129                                     */
01130                                    continue;
01131 
01132 
01133 fmt_error:
01134                             php_error(E_ERROR, "Illegal length modifier specified '%c' in s[np]printf call", *fmt);
01135                                    /*
01136                                     * The default case is for unrecognized %'s.
01137                                     * We print %<char> to help the user identify what
01138                                     * option is not understood.
01139                                     * This is also useful in case the user wants to pass
01140                                     * the output of format_converter to another function
01141                                     * that understands some other %<char> (like syslog).
01142                                     * Note that we can't point s inside fmt because the
01143                                     * unknown <char> could be preceded by width etc.
01144                                     */
01145                             default:
01146                                    char_buf[0] = '%';
01147                                    char_buf[1] = *fmt;
01148                                    s = char_buf;
01149                                    s_len = 2;
01150                                    pad_char = ' ';
01151                                    break;
01152                      }
01153 
01154                      if (prefix_char != NUL) {
01155                             *--s = prefix_char;
01156                             s_len++;
01157                      }
01158                      if (adjust_width && adjust == RIGHT && min_width > s_len) {
01159                             if (pad_char == '0' && prefix_char != NUL) {
01160                                    INS_CHAR(*s, sp, bep, cc)
01161                                           s++;
01162                                    s_len--;
01163                                    min_width--;
01164                             }
01165                             PAD(min_width, s_len, pad_char);
01166                      }
01167                      /*
01168                       * Print the string s.
01169                       */
01170                      for (i = s_len; i != 0; i--) {
01171                             INS_CHAR(*s, sp, bep, cc);
01172                             s++;
01173                      }
01174 
01175                      if (adjust_width && adjust == LEFT && min_width > s_len)
01176                             PAD(min_width, s_len, pad_char);
01177                      if (free_zcopy) {
01178                             zval_dtor(&zcopy);
01179                      }
01180               }
01181 skip_output:
01182               fmt++;
01183        }
01184        odp->nextb = sp;
01185        return (cc);
01186 }
01187 /* }}} */
01188 
01189 /*
01190  * This is the general purpose conversion function.
01191  */
01192 static void strx_printv(int *ccp, char *buf, size_t len, const char *format, va_list ap) /* {{{ */
01193 {
01194        buffy od;
01195        int cc;
01196 
01197        /*
01198         * First initialize the descriptor
01199         * Notice that if no length is given, we initialize buf_end to the
01200         * highest possible address.
01201         */
01202        if (len == 0) {
01203               od.buf_end = (char *) ~0;
01204               od.nextb   = (char *) ~0;
01205        } else {
01206               od.buf_end = &buf[len-1];
01207               od.nextb   = buf;
01208        }
01209 
01210        /*
01211         * Do the conversion
01212         */
01213        cc = format_converter(&od, format, ap);
01214        if (len != 0 && od.nextb <= od.buf_end) {
01215               *(od.nextb) = '\0';
01216        }
01217        if (ccp) {
01218               *ccp = cc;
01219        }
01220 }
01221 /* }}} */
01222 
01223 PHPAPI int ap_php_slprintf(char *buf, size_t len, const char *format,...) /* {{{ */
01224 {
01225        int cc;
01226        va_list ap;
01227 
01228        va_start(ap, format);
01229        strx_printv(&cc, buf, len, format, ap);
01230        va_end(ap);
01231        if (cc >= len) {
01232               cc = len -1;
01233               buf[cc] = '\0';
01234        }
01235        return cc;
01236 }
01237 /* }}} */
01238 
01239 PHPAPI int ap_php_vslprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
01240 {
01241        int cc;
01242 
01243        strx_printv(&cc, buf, len, format, ap);
01244        if (cc >= len) {
01245               cc = len -1;
01246               buf[cc] = '\0';
01247        }
01248        return cc;
01249 }
01250 /* }}} */
01251 
01252 PHPAPI int ap_php_snprintf(char *buf, size_t len, const char *format,...) /* {{{ */
01253 {
01254        int cc;
01255        va_list ap;
01256 
01257        va_start(ap, format);
01258        strx_printv(&cc, buf, len, format, ap);
01259        va_end(ap);
01260        return (cc);
01261 }
01262 /* }}} */
01263 
01264 PHPAPI int ap_php_vsnprintf(char *buf, size_t len, const char *format, va_list ap) /* {{{ */
01265 {
01266        int cc;
01267 
01268        strx_printv(&cc, buf, len, format, ap);
01269        return (cc);
01270 }
01271 /* }}} */
01272 
01273 PHPAPI int ap_php_vasprintf(char **buf, const char *format, va_list ap) /* {{{ */
01274 {
01275        va_list ap2;
01276        int cc;
01277 
01278        va_copy(ap2, ap);
01279        cc = ap_php_vsnprintf(NULL, 0, format, ap2);
01280        va_end(ap2);
01281 
01282        *buf = NULL;
01283 
01284        if (cc >= 0) {
01285               if ((*buf = malloc(++cc)) != NULL) {
01286                      if ((cc = ap_php_vsnprintf(*buf, cc, format, ap)) < 0) {
01287                             free(*buf);
01288                             *buf = NULL;
01289                      }
01290               }
01291        }
01292 
01293        return cc;
01294 }
01295 /* }}} */
01296 
01297 PHPAPI int ap_php_asprintf(char **buf, const char *format, ...) /* {{{ */
01298 {
01299        int cc;
01300        va_list ap;
01301 
01302        va_start(ap, format);
01303        cc = vasprintf(buf, format, ap);
01304        va_end(ap);
01305        return cc;
01306 }
01307 /* }}} */
01308 
01309 /*
01310  * Local variables:
01311  * tab-width: 4
01312  * c-basic-offset: 4
01313  * End:
01314  * vim600: sw=4 ts=4 fdm=marker
01315  * vim<600: sw=4 ts=4
01316  */