From: Marcus Boerger Date: Wed, 10 Apr 2002 01:09:22 +0000 (+0000) Subject: introducing spprintf and vspprintf X-Git-Tag: php-4.3.0dev-ZendEngine2-Preview1~755 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c664a0a202bcbc580dc7d6e5741421e409783087;p=php introducing spprintf and vspprintf #mail follows --- diff --git a/main/spprintf.c b/main/spprintf.c new file mode 100644 index 0000000000..6bc1330d3f --- /dev/null +++ b/main/spprintf.c @@ -0,0 +1,653 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.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: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* This is the spprintf implementation. + * It has emerged from apache snprintf. See original header: + */ + +/* ==================================================================== + * 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 . + * + * This code is based on, and used with the permission of, the + * SIO stdio-replacement strx_* functions by Panos Tsirigotis + * for xinetd. + */ + +#include "php.h" +#include "snprintf.h" + +#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 + +/* + * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions + * + * XXX: this is a magic number; do not decrease it + */ +#define NUM_BUF_SIZE 512 + + +/* + * Size for realloc operations + */ +#define SPPRINTF_BLOCK_SIZE 10 + +/* + * Descriptor for buffer area + */ +struct xbuf_area { + char *buf; /* pointer to buffer */ + size_t size; + size_t max_len; + char *buf_end; /* pointer to buffer end or ~0 */ + char *nextb; /* pointer to next byte to read/write */ +}; + +typedef struct xbuf_area xbuffy; + +/* Resize xbuf so that add bytes can be added. Reallocation is done + * in defined block size to minimize calls to realloc. + */ +static int xbuf_resize(xbuffy *xbuf, size_t add) +{ + char *buf; + int ret; + size_t size, offset; + + if (xbuf->buf) { + offset = xbuf->nextb - xbuf->buf; + if (offset+add < xbuf->size) { + return 0; /* do not change size if not necessary */ + } + } else { + offset = 0; + } + if (addsize + SPPRINTF_BLOCK_SIZE; + } else { + size = xbuf->size + add; + } + if (xbuf->max_len && size > xbuf->max_len) { + size = xbuf->max_len; + ret = 1; + } else { + ret = 0; + } + + buf = erealloc(xbuf->buf, size+1); /* alloc space for NUL */ + + if (!buf) { + return 1; + } else { + xbuf->buf = buf; + xbuf->buf_end = xbuf->max_len ? &buf[size] : (char *) ~0; + xbuf->nextb = buf+offset; + xbuf->size = size; + return ret; + } +} + +/* Initialise xbuffy with size spprintf_BLOCK_SIZE + */ +static int xbuf_init(xbuffy *xbuf, size_t max_len) +{ + xbuf->buf = NULL; + xbuf->size = 0; + xbuf->max_len = max_len; + return xbuf_resize(xbuf, 0); /* NOT max_len */ +} + +/* + * 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_NR(xbuf, ch, cc) \ + if (xbuf->nextb < xbuf->buf_end) { \ + *(xbuf->nextb++) = ch; \ + cc++; \ + } + +#define INS_CHAR(xbuf, ch, cc) \ + xbuf_resize(xbuf, 1); \ + INS_CHAR_NR(xbuf, ch, cc) + +/* + * Macro that does padding. The padding is done by printing + * the character ch. + */ +#define PAD(xbuf, width, len, ch) \ + if (width > len) { \ + xbuf_resize(xbuf, width-len); \ + do { \ + INS_CHAR_NR(xbuf, ch, cc); \ + width--; \ + } \ + while (width > len);\ + } + +#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++; \ + } + +/* + * 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 xbuf_format_converter(register xbuffy * xbuf, const char *fmt, va_list ap) +{ + register int cc = 0; + register int i; + + register char *s = NULL; + char *q; + int s_len; + + register 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 % */ + + /* + * Flag variables + */ + boolean_e is_long; + boolean_e alternate_form; + boolean_e print_sign; + boolean_e print_blank; + boolean_e adjust_precision; + boolean_e adjust_width; + bool_int is_negative; + + while (*fmt) { + if (*fmt != '%') { + INS_CHAR(xbuf, *fmt, cc); + } else { + /* + * Default variable settings + */ + adjust = RIGHT; + alternate_form = print_sign = print_blank = NO; + pad_char = ' '; + prefix_char = NUL; + + 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 + * + * XXX: an unreasonable amount of precision may be specified + * resulting in overflow of num_buf. Currently we + * ignore this possibility. + */ + 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; + } else + adjust_precision = NO; + } else + adjust_precision = adjust_width = NO; + + /* + * Modifier check + */ + if (*fmt == 'l') { + is_long = YES; + fmt++; + } else + is_long = NO; + + /* + * 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 'u': + if (is_long) + i_num = va_arg(ap, u_wide_int); + else + i_num = (wide_int) va_arg(ap, unsigned int); + /* + * 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') { + if (is_long) + i_num = va_arg(ap, wide_int); + else + i_num = (wide_int) va_arg(ap, int); + }; + 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': + if (is_long) + ui_num = va_arg(ap, u_wide_int); + else + ui_num = (u_wide_int) va_arg(ap, unsigned int); + 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': + if (is_long) + ui_num = (u_wide_int) va_arg(ap, u_wide_int); + else + ui_num = (u_wide_int) va_arg(ap, unsigned int); + 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': + 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 'e': + case 'E': + fp_num = va_arg(ap, double); + + if (zend_isnan(fp_num)) { + s = "nan"; + s_len = 3; + } else if (zend_isinf(fp_num)) { + s = "inf"; + s_len = 3; + } else { + s = ap_php_conv_fp(*fmt, fp_num, alternate_form, + (adjust_precision == NO) ? FLOAT_DIGITS : precision, + &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 'G': + 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 + */ + s = ap_php_gcvt(va_arg(ap, double), precision, &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 && (q = strchr(s, '.')) == NULL) + s[s_len++] = '.'; + if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) + *q = 'E'; + 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; + break; + + /* + * 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': + ui_num = (u_wide_int) va_arg(ap, char *); + + if (sizeof(char *) <= sizeof(u_wide_int)) + s = ap_php_conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); + else { + s = "%p"; + s_len = 2; + } + pad_char = ' '; + break; + + + case NUL: + /* + * The last character of the format string was %. + * We ignore it. + */ + continue; + + + /* + * The default case is for unrecognized %'s. + * We print % 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 % (like syslog). + * Note that we can't point s inside fmt because the + * unknown 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(xbuf, *s, cc) + s++; + s_len--; + min_width--; + } + PAD(xbuf, min_width, s_len, pad_char); + } + /* + * Print the string s. + */ + xbuf_resize(xbuf, s_len); + for (i = s_len; i != 0; i--) { + INS_CHAR_NR(xbuf, *s, cc); + s++; + } + + if (adjust_width && adjust == LEFT && min_width > s_len) + PAD(xbuf, min_width, s_len, pad_char); + } + fmt++; + } + return (cc); +} + + +/* + * This is the general purpose conversion function. + */ +int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap) +{ + xbuffy xbuf; + int cc; + + assert(pbuf != NULL); + /* + * First initialize the descriptor + * Notice that if no length is given, we initialize buf_end to the + * highest possible address. + */ + if (xbuf_init(&xbuf, max_len)) { + *pbuf = NULL; + return 0; + } else { + /* + * Do the conversion + */ + cc = xbuf_format_converter(&xbuf, format, ap); + if (xbuf.nextb <= xbuf.buf_end) + *(xbuf.nextb) = '\0'; + *pbuf = xbuf.buf; + return cc; + } +} + + +int spprintf(char **pbuf, size_t max_len, const char *format, ...) +{ + int cc; + va_list ap; + + va_start(ap, format); + vspprintf(pbuf, max_len, 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 + */ diff --git a/main/spprintf.h b/main/spprintf.h new file mode 100644 index 0000000000..3c0b0ab5b9 --- /dev/null +++ b/main/spprintf.h @@ -0,0 +1,35 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 4 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2002 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.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: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +#ifndef SPPRINTF_H +#define SPPRINTF_H + +#include "snprintf.h" + +extern int spprintf( char **pbuf, size_t max_len, const char *format, ...); + +extern int vspprintf(char **pbuf, size_t max_len, const char *format, va_list ap); + +#endif /* SNPRINTF_H */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */