diff options
Diffstat (limited to 'ext/standard/formatted_print.c')
-rw-r--r-- | ext/standard/formatted_print.c | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/ext/standard/formatted_print.c b/ext/standard/formatted_print.c new file mode 100644 index 0000000000..d6e3f3326f --- /dev/null +++ b/ext/standard/formatted_print.c @@ -0,0 +1,602 @@ +/* + +----------------------------------------------------------------------+ + | PHP HTML Embedded Scripting Language Version 3.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997,1998 PHP Development Team (See Credits file) | + +----------------------------------------------------------------------+ + | This program is free software; you can redistribute it and/or modify | + | it under the terms of one of the following licenses: | + | | + | A) the GNU General Public License as published by the Free Software | + | Foundation; either version 2 of the License, or (at your option) | + | any later version. | + | | + | B) the PHP License as published by the PHP Development Team and | + | included in the distribution in the file: LICENSE | + | | + | This program is distributed in the hope that it will be useful, | + | but WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | + | GNU General Public License for more details. | + | | + | You should have received a copy of both licenses referred to here. | + | If you did not, or have any questions about PHP licensing, please | + | contact core@php.net. | + +----------------------------------------------------------------------+ + | Authors: Stig Sæther Bakken <ssb@guardian.no> | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include <math.h> /* modf() */ +#ifdef THREAD_SAFE +#include "tls.h" +#endif +#include "php.h" +#include "head.h" +#include "php3_string.h" +#include "zend_execute.h" +#include <stdio.h> + +#define ALIGN_LEFT 0 +#define ALIGN_RIGHT 1 +#define ADJ_WIDTH 1 +#define ADJ_PRECISION 2 +#define NUM_BUF_SIZE 500 +#define NDIG 80 +#define FLOAT_DIGITS 6 +#define FLOAT_PRECISION 6 +#define MAX_FLOAT_DIGITS 38 +#define MAX_FLOAT_PRECISION 40 + +#if 0 +/* trick to control varargs functions through cpp */ +# define PRINTF_DEBUG(arg) php3_printf arg +#else +# define PRINTF_DEBUG(arg) +#endif + +static char hexchars[] = "0123456789abcdef"; +static char HEXCHARS[] = "0123456789ABCDEF"; + + +/* + * cvt.c - IEEE floating point formatting routines for FreeBSD + * from GNU libc-4.6.27 + */ + +/* + * _php3_cvt converts to decimal + * the number of digits is specified by ndigit + * decpt is set to the position of the decimal point + * sign is set to 0 for positive, 1 for negative + */ +static char * +_php3_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag) +{ + register int r2; + double fi, fj; + register char *p, *p1; + /*THREADX*/ +#ifndef THREAD_SAFE + static char cvt_buf[NDIG]; +#endif + TLS_VARS; + + if (ndigits >= NDIG - 1) + ndigits = NDIG - 2; + r2 = 0; + *sign = 0; + p = &STATIC(cvt_buf)[0]; + if (arg < 0) { + *sign = 1; + arg = -arg; + } + arg = modf(arg, &fi); + p1 = &STATIC(cvt_buf)[NDIG]; + /* + * Do integer part + */ + if (fi != 0) { + p1 = &STATIC(cvt_buf)[NDIG]; + while (fi != 0) { + fj = modf(fi / 10, &fi); + *--p1 = (int) ((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < &STATIC(cvt_buf)[NDIG]) + *p++ = *p1++; + } else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + p1 = &STATIC(cvt_buf)[ndigits]; + if (eflag == 0) + p1 += r2; + *decpt = r2; + if (p1 < &STATIC(cvt_buf)[0]) { + STATIC(cvt_buf)[0] = '\0'; + return (STATIC(cvt_buf)); + } + while (p <= p1 && p < &STATIC(cvt_buf)[NDIG]) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int) fj + '0'; + } + if (p1 >= &STATIC(cvt_buf)[NDIG]) { + STATIC(cvt_buf)[NDIG - 1] = '\0'; + return (STATIC(cvt_buf)); + } + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > STATIC(cvt_buf)) + ++ * --p1; + else { + *p1 = '1'; + (*decpt)++; + if (eflag == 0) { + if (p > STATIC(cvt_buf)) + *p = '0'; + p++; + } + } + } + *p = '\0'; + return (STATIC(cvt_buf)); +} + + +inline static void +_php3_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(), *size)); + *buffer = erealloc(*buffer, *size); + } + PRINTF_DEBUG(("sprintf: appending '%c', pos=\n", add, *pos)); + (*buffer)[(*pos)++] = add; +} + + +inline static void +_php3_sprintf_appendstring(char **buffer, int *pos, int *size, char *add, + int min_width, int max_width, char padding, + int alignment, int len) +{ + register int npad = min_width - MIN(len,(max_width?max_width:len)); + + if (npad<0) { + npad=0; + } + + PRINTF_DEBUG(("sprintf: appendstring(%x, %d, %d, \"%s\", %d, '%c', %d)\n", + *buffer, *pos, *size, add, min_width, padding, alignment)); + if (max_width == 0) { + max_width = MAX(min_width,len); + } + if ((*pos + max_width) >= *size) { + while ((*pos + max_width) >= *size) { + *size <<= 1; + } + PRINTF_DEBUG(("sprintf ereallocing buffer to %d bytes\n", *size)); + *buffer = erealloc(*buffer, *size); + } + if (alignment == ALIGN_RIGHT) { + while (npad-- > 0) { + (*buffer)[(*pos)++] = padding; + } + } + PRINTF_DEBUG(("sprintf: appending \"%s\"\n", add)); + strncpy(&(*buffer)[*pos], add, max_width); + *pos += MIN(max_width,len); + if (alignment == ALIGN_LEFT) { + while (npad--) { + (*buffer)[(*pos)++] = padding; + } + } +} + + +inline static void +_php3_sprintf_appendint(char **buffer, int *pos, int *size, int number, + int width, char padding, int alignment) +{ + char numbuf[NUM_BUF_SIZE]; + register unsigned int magn, nmagn, 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) { + neg = 1; + magn = ((unsigned int) -(number + 1)) + 1; + } else { + magn = (unsigned int) number; + } + + numbuf[i] = '\0'; + + do { + nmagn = magn / 10; + + numbuf[--i] = (magn - (nmagn * 10)) + '0'; + magn = nmagn; + } + while (magn > 0 && i > 0); + if (neg) { + numbuf[--i] = '-'; + } + PRINTF_DEBUG(("sprintf: appending %d as \"%s\", i=%d\n", + number, &numbuf[i], i)); + _php3_sprintf_appendstring(buffer, pos, size, &numbuf[i], width, 0, + padding, alignment, (NUM_BUF_SIZE - 1) - i); +} + + +inline static void +_php3_sprintf_appenddouble(char **buffer, int *pos, + int *size, double number, + int width, char padding, + int alignment, int precision, + int adjust, char fmt) +{ + char numbuf[NUM_BUF_SIZE]; + char *cvt; + register int i = 0, j = 0; + int sign, decpt; + + PRINTF_DEBUG(("sprintf: appenddouble(%x, %x, %x, %f, %d, '%c', %d, %c)\n", + *buffer, pos, size, number, width, padding, alignment, fmt)); + if ((adjust & ADJ_PRECISION) == 0) { + precision = FLOAT_PRECISION; + } else if (precision > MAX_FLOAT_PRECISION) { + precision = MAX_FLOAT_PRECISION; + } + cvt = _php3_cvt(number, precision, &decpt, &sign, (fmt == 'e')); + + if (sign) { + numbuf[i++] = '-'; + } + + if (fmt == 'f') { + if (decpt <= 0) { + numbuf[i++] = '0'; + if (precision > 0) { + int k = precision; + numbuf[i++] = '.'; + while ((decpt++ < 0) && k--) { + numbuf[i++] = '0'; + } + } + } else { + while (decpt-- > 0) + numbuf[i++] = cvt[j++]; + if (precision > 0) + numbuf[i++] = '.'; + } + } else { + numbuf[i++] = cvt[j++]; + if (precision > 0) + numbuf[i++] = '.'; + } + + while (cvt[j]) { + numbuf[i++] = cvt[j++]; + } + + numbuf[i] = '\0'; + + if (precision > 0) { + width += (precision + 1); + } + _php3_sprintf_appendstring(buffer, pos, size, numbuf, width, 0, padding, + alignment, i); +} + + +inline static void +_php3_sprintf_append2n(char **buffer, int *pos, int *size, int number, + int width, char padding, int alignment, int n, + char *chartable) +{ + char numbuf[NUM_BUF_SIZE]; + register unsigned int num, i = NUM_BUF_SIZE - 1, neg = 0; + register int andbits = (1 << n) - 1; + + PRINTF_DEBUG(("sprintf: append2n(%x, %x, %x, %d, %d, '%c', %d, %d, %x)\n", + *buffer, pos, size, number, width, padding, alignment, n, + chartable)); + PRINTF_DEBUG(("sprintf: append2n 2^%d andbits=%x\n", n, andbits)); + + if (number < 0) { + neg = 1; + num = ((unsigned int) -(number + 1)) + 1; + } else { + num = (unsigned int) number; + } + + numbuf[i] = '\0'; + + do { + numbuf[--i] = chartable[(num & andbits)]; + num >>= n; + } + while (num > 0); + + if (neg) { + numbuf[--i] = '-'; + } + _php3_sprintf_appendstring(buffer, pos, size, &numbuf[i], width, 0, + padding, alignment, (NUM_BUF_SIZE - 1) - i); +} + + +inline static int +_php3_sprintf_getnumber(char *buffer, int *pos) +{ + char *endptr; + register int num = strtol(&buffer[*pos], &endptr, 10); + register int i = 0; + + if (endptr != NULL) { + i = (endptr - &buffer[*pos]); + } + PRINTF_DEBUG(("sprintf_getnumber: number was %d bytes long\n", i)); + *pos += i; + return num; +} + + +/* + * New sprintf implementation for PHP. + * + * Modifiers: + * + * " " pad integers with spaces + * "-" left adjusted field + * n field size + * "."n precision (floats only) + * + * 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 + * + */ +static char * +php3_formatted_print(int ht, int *len) +{ + pval **args; + int argc, size = 240, inpos = 0, outpos = 0; + int alignment, width, precision, currarg, adjusting; + char *format, *result, padding; + + argc = ARG_COUNT(ht); + + if (argc < 1) { + WRONG_PARAM_COUNT_WITH_RETVAL(NULL); + } + args = emalloc(argc * sizeof(pval *)); + + if (getParametersArray(ht, argc, args) == FAILURE) { + efree(args); + WRONG_PARAM_COUNT_WITH_RETVAL(NULL); + } + convert_to_string(args[0]); + format = args[0]->value.str.val; + result = emalloc(size); + + currarg = 1; + + while (format[inpos]) { + PRINTF_DEBUG(("sprintf: format[%d]='%c'\n", inpos, format[inpos])); + PRINTF_DEBUG(("sprintf: outpos=%d\n", outpos)); + if (format[inpos] != '%') { + _php3_sprintf_appendchar(&result, &outpos, &size, format[inpos++]); + } else if (format[inpos + 1] == '%') { + _php3_sprintf_appendchar(&result, &outpos, &size, '%'); + inpos += 2; + } else { + if (currarg >= argc && format[inpos + 1] != '%') { + efree(result); + efree(args); + php3_error(E_WARNING, "%s(): too few arguments",get_active_function_name()); + return NULL; + } + /* starting a new format specifier, reset variables */ + alignment = ALIGN_RIGHT; + adjusting = 0; + padding = ' '; + inpos++; /* skip the '%' */ + + PRINTF_DEBUG(("sprintf: first looking at '%c', inpos=%d\n", + format[inpos], inpos)); + if (isascii((int)format[inpos]) && !isalpha((int)format[inpos])) { + /* first look for modifiers */ + PRINTF_DEBUG(("sprintf: looking for modifiers\n" + "sprintf: now looking at '%c', inpos=%d\n", + format[inpos], inpos)); + for (;; inpos++) { + if (format[inpos] == ' ' || format[inpos] == '0') { + padding = format[inpos]; + } else if (format[inpos] == '-') { + alignment = ALIGN_LEFT; + /* space padding, the default */ + } else if (format[inpos] == '\'') { + padding = format[++inpos]; + } else { + PRINTF_DEBUG(("sprintf: end of modifiers\n")); + break; + } + } + PRINTF_DEBUG(("sprintf: padding='%c'\n", padding)); + PRINTF_DEBUG(("sprintf: alignment=%s\n", + (alignment == ALIGN_LEFT) ? "left" : "right")); + + + /* after modifiers comes width */ + if (isdigit((int)format[inpos])) { + PRINTF_DEBUG(("sprintf: getting width\n")); + width = _php3_sprintf_getnumber(format, &inpos); + adjusting |= ADJ_WIDTH; + } else { + width = 0; + } + PRINTF_DEBUG(("sprintf: width=%d\n", width)); + + /* after width comes precision */ + if (format[inpos] == '.') { + inpos++; + PRINTF_DEBUG(("sprintf: getting precision\n")); + if (isdigit((int)format[inpos])) { + precision = _php3_sprintf_getnumber(format, &inpos); + adjusting |= ADJ_PRECISION; + } else { + precision = 0; + } + } else { + precision = 0; + } + PRINTF_DEBUG(("sprintf: precision=%d\n", precision)); + } else { + width = precision = 0; + } + + if (format[inpos] == 'l') { + inpos++; + } + PRINTF_DEBUG(("sprintf: format character='%c'\n", format[inpos])); + /* now we expect to find a type specifier */ + switch (format[inpos]) { + case 's': + convert_to_string(args[currarg]); + _php3_sprintf_appendstring(&result, &outpos, &size, + args[currarg]->value.str.val, + width, precision, padding, + alignment, + args[currarg]->value.str.len); + break; + + case 'd': + convert_to_long(args[currarg]); + _php3_sprintf_appendint(&result, &outpos, &size, + args[currarg]->value.lval, + width, padding, alignment); + break; + + case 'e': + case 'f': + /* XXX not done */ + convert_to_double(args[currarg]); + _php3_sprintf_appenddouble(&result, &outpos, &size, + args[currarg]->value.dval, + width, padding, alignment, + precision, adjusting, + format[inpos]); + break; + + case 'c': + convert_to_long(args[currarg]); + _php3_sprintf_appendchar(&result, &outpos, &size, + (char) args[currarg]->value.lval); + break; + + case 'o': + convert_to_long(args[currarg]); + _php3_sprintf_append2n(&result, &outpos, &size, + args[currarg]->value.lval, + width, padding, alignment, 3, + hexchars); + break; + + case 'x': + convert_to_long(args[currarg]); + _php3_sprintf_append2n(&result, &outpos, &size, + args[currarg]->value.lval, + width, padding, alignment, 4, + hexchars); + break; + + case 'X': + convert_to_long(args[currarg]); + _php3_sprintf_append2n(&result, &outpos, &size, + args[currarg]->value.lval, + width, padding, alignment, 4, + HEXCHARS); + break; + + case 'b': + convert_to_long(args[currarg]); + _php3_sprintf_append2n(&result, &outpos, &size, + args[currarg]->value.lval, + width, padding, alignment, 1, + hexchars); + break; + + case '%': + _php3_sprintf_appendchar(&result, &outpos, &size, '%'); + + break; + default: + break; + } + currarg++; + inpos++; + } + } + + efree(args); + + /* possibly, we have to make sure we have room for the terminating null? */ + result[outpos]=0; + *len = outpos; + return result; +} + + +PHP_FUNCTION(user_sprintf) +{ + char *result; + int len; + TLS_VARS; + + if ((result=php3_formatted_print(ht,&len))==NULL) { + RETURN_FALSE; + } + RETVAL_STRINGL(result,len,1); + efree(result); +} + + +PHP_FUNCTION(user_printf) +{ + char *result; + int len; + TLS_VARS; + + if ((result=php3_formatted_print(ht,&len))==NULL) { + RETURN_FALSE; + } + PHPWRITE(result,len); + efree(result); +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ |