Index: ext/standard/formatted_print.c =================================================================== RCS file: /repository/php-src/ext/standard/formatted_print.c,v retrieving revision 1.82.2.1.2.1 diff -u -r1.82.2.1.2.1 formatted_print.c --- ext/standard/formatted_print.c 26 Jun 2006 18:48:56 -0000 1.82.2.1.2.1 +++ ext/standard/formatted_print.c 10 Aug 2006 08:39:15 -0000 @@ -33,11 +33,11 @@ #define ALIGN_RIGHT 1 #define ADJ_WIDTH 1 #define ADJ_PRECISION 2 +#define ADJ_ALT_FORM 4 +#define ADJ_ADD_DP 8 #define NUM_BUF_SIZE 500 -#define NDIG 80 -#define FLOAT_DIGITS 6 +#define NDIG 80 #define FLOAT_PRECISION 6 -#define MAX_FLOAT_DIGITS 38 #define MAX_FLOAT_PRECISION 40 #if 0 @@ -126,7 +126,7 @@ *decpt = ++r2; } while (p <= p1 && p < &cvt_buf[NDIG]) { - *p++ = (int) fj + '0'; + *p++ = (int) fj + '0'; arg = modf(arg * 10, &fj); } } @@ -156,11 +156,11 @@ inline static void -php_sprintf_appendchar(char **buffer, int *pos, int *size, char add TSRMLS_DC) +php_sprintf_appendchar(char **buffer, int *pos, int *size, char add) { if ((*pos + 1) >= *size) { *size <<= 1; - PRINTF_DEBUG(("%s(): ereallocing buffer to %d bytes\n", get_active_function_name(TSRMLS_C), *size)); + PRINTF_DEBUG(("sprintf: ereallocing buffer to %d bytes\n", *size)); *buffer = erealloc(*buffer, *size); } PRINTF_DEBUG(("sprintf: appending '%c', pos=\n", add, *pos)); @@ -171,13 +171,13 @@ inline static void php_sprintf_appendstring(char **buffer, int *pos, int *size, char *add, int min_width, int max_width, char padding, - int alignment, int len, int neg, int expprec, int always_sign) + int alignment, int len, int neg, int always_sign) { register int npad; int req_size; int copy_len; - copy_len = (expprec ? MIN(max_width, len) : len); + copy_len = max_width < 0 ? len : MIN(max_width, len); npad = min_width - copy_len; if (npad < 0) { @@ -193,7 +193,7 @@ while (req_size > *size) { *size <<= 1; } - PRINTF_DEBUG(("sprintf ereallocing buffer to %d bytes\n", *size)); + PRINTF_DEBUG(("sprintf: ereallocing buffer to %d bytes\n", *size)); *buffer = erealloc(*buffer, *size); } if (alignment == ALIGN_RIGHT) { @@ -219,17 +219,18 @@ inline static void -php_sprintf_appendint(char **buffer, int *pos, int *size, long number, +php_sprintf_appendint(char **buffer, int *pos, int *size, + long number, int is_unsigned, int width, char padding, int alignment, - int always_sign) + int precision, int always_sign) { char numbuf[NUM_BUF_SIZE]; register unsigned long magn, nmagn; register unsigned int i = NUM_BUF_SIZE - 1, neg = 0; - PRINTF_DEBUG(("sprintf: appendint(%x, %x, %x, %d, %d, '%c', %d)\n", - *buffer, pos, size, number, width, padding, alignment)); - if (number < 0) { + PRINTF_DEBUG(("sprintf: appendint(%x, %x, %x, %d (%ssigned), %d, '%c', %d)\n", + *buffer, pos, size, number, (is_unsigned ? "un" : ""), width, padding, alignment)); + if (number < 0 && !is_unsigned) { neg = 1; magn = ((unsigned long) -(number + 1)) + 1; } else { @@ -237,7 +238,7 @@ } /* Can't right-pad 0's on integers */ - if(alignment==0 && padding=='0') padding=' '; + if (alignment == ALIGN_LEFT && padding == '0') padding = ' '; numbuf[i] = '\0'; @@ -247,7 +248,16 @@ numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0'; magn = nmagn; } - while (magn > 0 && i > 0); + while (magn > 0 && i > 1); /* i > 1 to allow for a sign */ + + if (precision > (signed) (NUM_BUF_SIZE - 1 - i)) { + precision -= NUM_BUF_SIZE - 1 - i; + + while (precision-- && i > 1) { + numbuf[--i] = '0'; + } + } + if (neg) { numbuf[--i] = '-'; } else if (always_sign) { @@ -255,40 +265,11 @@ } PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n", number, &numbuf[i], i)); - php_sprintf_appendstring(buffer, pos, size, &numbuf[i], width, 0, + php_sprintf_appendstring(buffer, pos, size, &numbuf[i], width, -1, padding, alignment, (NUM_BUF_SIZE - 1) - i, - neg, 0, always_sign); + neg, always_sign); } -inline static void -php_sprintf_appenduint(char **buffer, int *pos, int *size, - unsigned long number, - int width, char padding, int alignment) -{ - char numbuf[NUM_BUF_SIZE]; - register unsigned long magn, nmagn; - register unsigned int i = NUM_BUF_SIZE - 1; - - PRINTF_DEBUG(("sprintf: appenduint(%x, %x, %x, %d, %d, '%c', %d)\n", - *buffer, pos, size, number, width, padding, alignment)); - magn = (unsigned int) number; - - /* Can't right-pad 0's on integers */ - if (alignment == 0 && padding == '0') padding = ' '; - - numbuf[i] = '\0'; - - do { - nmagn = magn / 10; - - numbuf[--i] = (unsigned char)(magn - (nmagn * 10)) + '0'; - magn = nmagn; - } while (magn > 0 && i > 0); - - PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n", number, &numbuf[i], i)); - php_sprintf_appendstring(buffer, pos, size, &numbuf[i], width, 0, - padding, alignment, (NUM_BUF_SIZE - 1) - i, 0, 0, 0); -} inline static void php_sprintf_appenddouble(char **buffer, int *pos, @@ -296,8 +277,7 @@ int width, char padding, int alignment, int precision, int adjust, char fmt, - int always_sign - TSRMLS_DC) + int always_sign) { char numbuf[NUM_BUF_SIZE]; char *cvt; @@ -323,19 +303,38 @@ if (zend_isnan(number)) { sign = (number<0); - php_sprintf_appendstring(buffer, pos, size, "NaN", 3, 0, padding, - alignment, precision, sign, 0, always_sign); + php_sprintf_appendstring(buffer, pos, size, "NaN", 3, -1, padding, + alignment, precision, sign, always_sign); return; } if (zend_isinf(number)) { sign = (number<0); - php_sprintf_appendstring(buffer, pos, size, "INF", 3, 0, padding, - alignment, precision, sign, 0, always_sign); + php_sprintf_appendstring(buffer, pos, size, "INF", 3, -1, padding, + alignment, precision, sign, always_sign); return; } - cvt = php_convert_to_decimal(number, precision, &decpt, &sign, (fmt == 'e')); + if (fmt == 'f' || fmt == 'F') { + cvt = php_convert_to_decimal(number, precision, &decpt, &sign, 0); + } else if (fmt == 'g' || fmt == 'G') { + precision = precision > 0 ? precision : 1; + cvt = php_convert_to_decimal(number, precision, &decpt, &sign, 1); + + if (decpt - precision > 0 || decpt < -3) { + fmt -= 2; /* Switch g or G to e or E */ + precision--; + } else { + fmt = 'F'; + precision -= decpt > 0 ? decpt : 1; + } + + /* Flip alt form bit since we're faking e/E/F, and g/G's default is the opposite */ + adjust ^= ADJ_ALT_FORM; + } else { /* e or E */ + cvt = php_convert_to_decimal(number, precision + 1, &decpt, &sign, 1); + } + cvt_len = strlen(cvt); if (sign) { @@ -346,19 +345,41 @@ if (fmt == 'f' || fmt == 'F') { if (decpt <= 0) { + if ((adjust & ADJ_ALT_FORM) && precision > 0) { + int end = cvt_len - 1; + + while (end >= 0 && cvt[end] == '0') { + end--; + } + precision = (end >= 0) ? end - decpt + 1 : 0; + } + numbuf[i++] = '0'; - if (precision > 0) { + if (precision > 0 || (adjust & ADJ_ADD_DP)) { int k = precision; numbuf[i++] = fmt == 'F' ? decimal_point : locale_decimal_point; while ((decpt++ < 0) && k--) { numbuf[i++] = '0'; } + + while (k-- && cvt[j]) { + numbuf[i++] = cvt[j++]; + } } } else { + if ((adjust & ADJ_ALT_FORM) && precision > 0) { + int end = cvt_len - 1; + + while (end >= decpt && cvt[end] == '0') { + end--; + } + precision = (end >= decpt) ? end - decpt + 1 : 0; + } + while (decpt-- > 0) { numbuf[i++] = j < cvt_len ? cvt[j++] : '0'; } - if (precision > 0) { + if (precision > 0 || (adjust & ADJ_ADD_DP)) { numbuf[i++] = fmt == 'F' ? decimal_point : locale_decimal_point; while (precision-- > 0) { numbuf[i++] = j < cvt_len ? cvt[j++] : '0'; @@ -368,22 +389,26 @@ } else if (fmt == 'e' || fmt == 'E') { char *exp_p; int dec2; - + + if ((adjust & ADJ_ALT_FORM) && precision > 0) { + int end = cvt_len - 1; + + while (end > 0 && cvt[end--] == '0') { + precision--; + } + } + decpt--; - numbuf[i++] = cvt[j++]; - numbuf[i++] = decimal_point; - if (precision > 0) { + if (precision > 0 || (adjust & ADJ_ADD_DP)) { int k = precision; - + numbuf[i++] = decimal_point; while (k-- && cvt[j]) { numbuf[i++] = cvt[j++]; } - } else { - numbuf[i++] = '0'; } - + numbuf[i++] = fmt; exp_p = php_convert_to_decimal(decpt, 0, &dec2, &sign, 0); numbuf[i++] = sign ? '-' : '+'; @@ -394,32 +419,26 @@ } else { numbuf[i++] = '0'; } - } else { - numbuf[i++] = cvt[j++]; - if (precision > 0) - numbuf[i++] = decimal_point; - } - - while (cvt[j]) { - numbuf[i++] = cvt[j++]; } numbuf[i] = '\0'; - php_sprintf_appendstring(buffer, pos, size, numbuf, width, 0, padding, - alignment, i, sign, 0, always_sign); + php_sprintf_appendstring(buffer, pos, size, numbuf, width, -1, padding, + alignment, i, sign, always_sign); } inline static void php_sprintf_append2n(char **buffer, int *pos, int *size, long number, - int width, char padding, int alignment, int n, - char *chartable, int expprec) + int width, char padding, + int alignment, int precision, + int n, char fmt, int alt_form) { char numbuf[NUM_BUF_SIZE]; register unsigned long num; register unsigned int i = NUM_BUF_SIZE - 1; register int andbits = (1 << n) - 1; + char *chartable = (fmt == 'X') ? HEXCHARS : hexchars; PRINTF_DEBUG(("sprintf: append2n(%x, %x, %x, %d, %d, '%c', %d, %d, %x)\n", *buffer, pos, size, number, width, padding, alignment, n, @@ -435,9 +454,31 @@ } while (num > 0); - php_sprintf_appendstring(buffer, pos, size, &numbuf[i], width, 0, - padding, alignment, (NUM_BUF_SIZE - 1) - i, - 0, expprec, 0); + if (precision > (signed) (NUM_BUF_SIZE - 1 - i)) { + precision -= NUM_BUF_SIZE - 1 - i; + + while (precision-- && i > 0) { + numbuf[--i] = '0'; + } + } + + if (alt_form) { + if (fmt == 'x' || fmt == 'X') { + if ((unsigned long) number != 0) { + /* If precision added too many 0's */ + if (i < 2) { + i = 2; + } + numbuf[--i] = fmt; + numbuf[--i] = '0'; + } + } else if (numbuf[i + 1] != '0' && i > 0) { + numbuf[--i] = '0'; + } + } + + php_sprintf_appendstring(buffer, pos, size, &numbuf[i], width, -1, + padding, alignment, (NUM_BUF_SIZE - 1) - i, 0, 0); } @@ -461,36 +502,51 @@ * * Modifiers: * - * " " pad integers with spaces + * " " pad with spaces + * "0" pad with zeroes + * "'"c pad with c character * "-" left adjusted field - * n field size - * "."n precision (floats only) * "+" Always place a sign (+ or -) in front of a number + * "#" alternate output form (see below) + * "!" output will always have a decimal point (float types) + * n minimum field width + * "."n precision (decimal digits [eEfF]; significant digits [gG]; min digits for an integer; max chars for a string) + * + * Alternate output form: + * + * [eEfF] trailing zeros and decimal point are removed + * [gG] trailing zeros are not removed + * [o] first digit will be 0 + * [xX] 0x or 0X will be prefixed to a non-zero result * * Type specifiers: * - * "%" literal "%", modifiers are ignored. - * "b" integer argument is printed as binary - * "c" integer argument is printed as a single character - * "d" argument is an integer - * "f" the argument is a float - * "o" integer argument is printed as octal - * "s" argument is a string - * "x" integer argument is printed as lowercase hexadecimal - * "X" integer argument is printed as uppercase hexadecimal + * "%" literal "%", modifiers are ignored + * "b" integer, printed as binary + * "c" integer, printed as a single character + * "d" integer, printed as a signed decimal + * "e" float, printed in scientific notation (lowercase e) + * "E" float, printed in scientific notation (uppercase E) + * "f" float, printed as floating-point (locale aware) + * "F" float, printed as floating-point (non-locale aware) + * "g" float, \__ %e or %E if exponent < -4 or >= precision, otherwise %F + * "G" float, / trailing zeros and decimal point are removed + * "o" integer, printed as octal + * "s" string + * "u" integer, printed as an unsigned decimal + * "x" integer, printed as hexadecimal (lowercase) + * "X" integer, printed as hexadecimal (uppercase) * */ static char * -php_formatted_print(int ht, int *len, int use_array, int format_offset TSRMLS_DC) +php_formatted_print(int argc, int *len, int use_array, int format_offset TSRMLS_DC) { zval ***args, **z_format; - int argc, size = 240, inpos = 0, outpos = 0, temppos; + int size = 240, inpos = 0, outpos = 0, temppos; int alignment, width, precision, currarg, adjusting, argnum; char *format, *result, padding; int always_sign; - argc = ZEND_NUM_ARGS(); - /* verify the number of args */ if ((use_array && argc != (2 + format_offset)) || (!use_array && argc < (1 + format_offset))) { @@ -534,15 +590,15 @@ currarg = 1; while (inpos