*** /dev/null Sat Nov 23 01:27:29 2024
--- - Sat Nov 23 01:27:33 2024
***************
*** 0 ****
--- 1,1190 ----
+ /*
+ ** OSSP l2 - Flexible Logging
+ ** Copyright (c) 2001-2004 Cable & Wireless <http://www.cw.com/>
+ ** Copyright (c) 2001-2004 The OSSP Project <http://www.ossp.org/>
+ ** Copyright (c) 2001-2004 Ralf S. Engelschall <rse@engelschall.com>
+ **
+ ** This file is part of OSSP l2, a flexible logging library which
+ ** can be found at http://www.ossp.org/pkg/lib/l2/.
+ **
+ ** Permission to use, copy, modify, and distribute this software for
+ ** any purpose with or without fee is hereby granted, provided that
+ ** the above copyright notice and this permission notice appear in all
+ ** copies.
+ **
+ ** THIS SOFTWARE IS PROVIDED ``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 AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+ ** 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.
+ **
+ ** l2_ut_format.c: printf(3)-style formatting
+ */
+
+ /*
+ * This is a generic printf-style formatting code which is based on
+ * Apache's ap_snprintf which it turn is based on and used with the
+ * permission of, the SIO stdio-replacement strx_* functions by Panos
+ * Tsirigotis <panos@alumni.cs.colorado.edu> for xinetd. The IEEE
+ * floating point formatting routines are derived from an anchient
+ * FreeBSD version which took it from GNU libc-4.6.27 and modified it
+ * to be thread safe. The whole code was finally cleaned up, stripped
+ * and extended by Ralf S. Engelschall for use inside the Str library.
+ * Especially any Apache and network specific kludges were removed again
+ * and instead the formatting engine now can be extended by the caller
+ * on-the-fly. It was then finally adjusted to be stand-alone for use
+ * inside OSSP l2.
+ */
+
+ /*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2002 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" must
+ * not be used to endorse or promote products derived from this
+ * software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * nor may "Apache" appear in their name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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.
+ */
+
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <stdarg.h>
+ #include <string.h>
+ #include <ctype.h>
+ #include <math.h>
+
+ #include "l2_config.h"
+ #include "l2_ut_format.h"
+
+ /* types which are locally use */
+ typedef long long_int;
+ typedef unsigned long u_long_int;
+ #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG > 0)
+ typedef long long quad_int;
+ typedef unsigned long long u_quad_int;
+ #else
+ typedef long quad_int;
+ typedef unsigned long u_quad_int;
+ #endif
+
+ /* a few handy defines */
+ #ifndef NUL
+ #define NUL '\0'
+ #endif
+ #ifndef NULL
+ #define NULL ((void *)0)
+ #endif
+ #ifndef FALSE
+ #define FALSE (0)
+ #endif
+ #ifndef TRUE
+ #define TRUE (!FALSE)
+ #endif
+ #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. This is a magic number; do NOT decrease it! */
+ #define NUM_BUF_SIZE 512
+ #define NDIG 80
+
+ /* compatibility */
+ #if !defined(HAVE_ISNAN)
+ #define isnan(d) (0)
+ #endif
+ #if !defined(HAVE_ISINF)
+ #define isinf(d) (0)
+ #endif
+
+ /* explicit support for unsigned char based ctype stuff */
+ #define l2_util_isalpha(c) (isalpha(((unsigned char)(c))))
+ #define l2_util_isdigit(c) (isdigit(((unsigned char)(c))))
+ #define l2_util_isxdigit(c) (isxdigit(((unsigned char)(c))))
+ #define l2_util_islower(c) (islower(((unsigned char)(c))))
+ #define l2_util_tolower(c) (tolower((unsigned char)(c)))
+
+ /*
+ * Convert decimal number to its string representation. 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. buf must
+ * have at least NDIG bytes.
+ */
+
+ #define l2_util_ecvt(arg,ndigits,decpt,sign,buf) \
+ l2_util_cvt((arg), (ndigits), (decpt), (sign), 1, (buf))
+
+ #define l2_util_fcvt(arg,ndigits,decpt,sign,buf) \
+ l2_util_cvt((arg), (ndigits), (decpt), (sign), 0, (buf))
+
+ /* inlined modf(3) to avoid dependency to external libm on systems
+ (like Tru64, QNX, etc) where modf(3) is not part of libc.
+ We use trnc to avoid conflicts with global definitions of trunc and truncate */
+ static double
+ l2_util_modf(
+ double arg,
+ double *iptr)
+ {
+ double fraction;
+ double integral;
+ long trnc;
+
+ trnc = (long)arg;
+ integral = (double)trnc;
+ fraction = arg - integral;
+ if (iptr != NULL)
+ *iptr = integral;
+ return fraction;
+ }
+
+ static char *
+ l2_util_cvt(
+ double arg,
+ int ndigits,
+ int *decpt,
+ int *sign,
+ int eflag,
+ char *buf)
+ {
+ register int r2;
+ double fi, fj;
+ register char *p, *p1;
+
+ if (ndigits >= NDIG - 1)
+ ndigits = NDIG - 2;
+ r2 = 0;
+ *sign = FALSE;
+ p = &buf[0];
+ if (arg < 0) {
+ *sign = TRUE;
+ arg = -arg;
+ }
+ arg = l2_util_modf(arg, &fi);
+ p1 = &buf[NDIG];
+
+ /* Do integer part */
+ if (fi != 0) {
+ p1 = &buf[NDIG];
+ while (fi != 0 && p1 > &buf[0]) {
+ fj = l2_util_modf(fi / 10, &fi);
+ *--p1 = (int)((fj + .03) * 10) + '0';
+ r2++;
+ }
+ while (p1 < &buf[NDIG])
+ *p++ = *p1++;
+ }
+ else if (arg > 0) {
+ while ((fj = arg * 10) < 1) {
+ arg = fj;
+ r2--;
+ }
+ }
+ p1 = &buf[ndigits];
+ if (eflag == 0)
+ p1 += r2;
+ *decpt = r2;
+ if (p1 < &buf[0]) {
+ buf[0] = NUL;
+ return (buf);
+ }
+ while (p <= p1 && p < &buf[NDIG]) {
+ arg *= 10;
+ arg = l2_util_modf(arg, &fj);
+ *p++ = (int) fj + '0';
+ }
+ if (p1 >= &buf[NDIG]) {
+ buf[NDIG - 1] = NUL;
+ return (buf);
+ }
+ p = p1;
+ *p1 += 5;
+ while (*p1 > '9') {
+ *p1 = '0';
+ if (p1 > buf)
+ ++ * --p1;
+ else {
+ *p1 = '1';
+ (*decpt)++;
+ if (eflag == 0) {
+ if (p > buf)
+ *p = '0';
+ p++;
+ }
+ }
+ }
+ *p = NUL;
+ return buf;
+ }
+
+ static char *
+ l2_util_gcvt(
+ double number,
+ int ndigit,
+ char *buf,
+ int altform)
+ {
+ int sign;
+ int decpt;
+ register char *p1, *p2;
+ register int i;
+ char buf1[NDIG];
+
+ p1 = l2_util_ecvt(number, ndigit, &decpt, &sign, buf1);
+ p2 = buf;
+ if (sign)
+ *p2++ = '-';
+ for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
+ ndigit--;
+ if ((decpt >= 0 && decpt - ndigit > 4)
+ || (decpt < 0 && decpt < -3)) { /* use E-style */
+ decpt--;
+ *p2++ = *p1++;
+ *p2++ = '.';
+ for (i = 1; i < ndigit; i++)
+ *p2++ = *p1++;
+ *p2++ = 'e';
+ if (decpt < 0) {
+ decpt = -decpt;
+ *p2++ = '-';
+ }
+ else
+ *p2++ = '+';
+ if (decpt / 100 > 0)
+ *p2++ = decpt / 100 + '0';
+ if (decpt / 10 > 0)
+ *p2++ = (decpt % 100) / 10 + '0';
+ *p2++ = decpt % 10 + '0';
+ }
+ else {
+ if (decpt <= 0) {
+ if (*p1 != '0')
+ *p2++ = '.';
+ while (decpt < 0) {
+ decpt++;
+ *p2++ = '0';
+ }
+ }
+ for (i = 1; i <= ndigit; i++) {
+ *p2++ = *p1++;
+ if (i == decpt)
+ *p2++ = '.';
+ }
+ if (ndigit < decpt) {
+ while (ndigit++ < decpt)
+ *p2++ = '0';
+ *p2++ = '.';
+ }
+ }
+ if (p2[-1] == '.' && !altform)
+ p2--;
+ *p2 = NUL;
+ return buf;
+ }
+
+ /*
+ * The INS_CHAR macro inserts a character in the buffer and flushes the
+ * buffer if necessary. It uses the char pointers sp and bep: sp points
+ * to the next available character in the buffer, bep points to the
+ * end-of-buffer+1. While using this macro, note that the nextb pointer
+ * is NOT updated. NOTE: Evaluation of the c argument should not have
+ * any side-effects
+ */
+ #define INS_CHAR(c, sp, bep, cc) { \
+ if (sp >= bep) { \
+ vbuff->curpos = sp; \
+ if (vbuff->flush(vbuff)) \
+ return -1; \
+ sp = vbuff->curpos; \
+ bep = vbuff->endpos; \
+ } \
+ *sp++ = (c); \
+ cc++; \
+ }
+
+ /*
+ * Convert a string to decimal value
+ */
+ #define NUM(c) ((c) - '0')
+ #define STR_TO_DEC(str, num) { \
+ num = NUM(*str++); \
+ while (l2_util_isdigit(*(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++; \
+ } \
+ }
+
+ /*
+ * This macro does padding.
+ * The padding is done by printing the character ch.
+ */
+ #define PAD(width, len, ch) \
+ do { \
+ INS_CHAR(ch, sp, bep, cc); \
+ width-- ; \
+ } while (width > len)
+
+ /*
+ * Prefix the character ch to the string str
+ * Increase length. Set the has_prefix flag.
+ */
+ #define PREFIX(str, length, ch) \
+ *--str = ch; \
+ length++; \
+ has_prefix = TRUE
+
+ /*
+ * Convert num to its decimal format.
+ * Return value:
+ * - a pointer to a string containing the number (no sign)
+ * - len contains the length of the string
+ * - is_negative is set to TRUE or FALSE depending on the sign
+ * of the number (always set to FALSE if is_unsigned is TRUE)
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ * Note: we have 2 versions. One is used when we need to use quads
+ * (conv_10_quad), the other when we don't (conv_10). We're assuming the
+ * latter is faster.
+ */
+ static char *
+ conv_10(
+ register long_int num,
+ register int is_unsigned,
+ register int *is_negative,
+ char *buf_end,
+ register size_t *len)
+ {
+ register char *p = buf_end;
+ register u_long_int magnitude;
+
+ if (is_unsigned) {
+ magnitude = (u_long_int)num;
+ *is_negative = FALSE;
+ }
+ else {
+ *is_negative = (num < 0);
+ /* On a 2's complement machine, negating the most negative integer
+ results in a number that cannot be represented as a signed integer.
+ Here is what we do to obtain the number's magnitude:
+ a. add 1 to the number
+ b. negate it (becomes positive)
+ c. convert it to unsigned
+ d. add 1 */
+ if (*is_negative) {
+ long_int t = num + 1;
+
+ magnitude = ((u_long_int) - t) + 1;
+ }
+ else
+ magnitude = (u_long_int)num;
+ }
+ /* We use a do-while loop so that we write at least 1 digit */
+ do {
+ register u_long_int new_magnitude = magnitude / 10;
+ *--p = (char) (magnitude - new_magnitude * 10 + '0');
+ magnitude = new_magnitude;
+ } while (magnitude);
+ *len = (int)(buf_end - p);
+ return p;
+ }
+
+ static char *
+ conv_10_quad(
+ quad_int num,
+ register int is_unsigned,
+ register int *is_negative,
+ char *buf_end,
+ register size_t *len)
+ {
+ register char *p = buf_end;
+ u_quad_int magnitude;
+
+ if (is_unsigned) {
+ magnitude = (u_quad_int)num;
+ *is_negative = FALSE;
+ }
+ else {
+ *is_negative = (num < 0);
+ /* On a 2's complement machine, negating the most negative integer
+ result in a number that cannot be represented as a signed integer.
+ Here is what we do to obtain the number's magnitude:
+ a. add 1 to the number
+ b. negate it (becomes positive)
+ c. convert it to unsigned
+ d. add 1 */
+ if (*is_negative) {
+ quad_int t = num + 1;
+ magnitude = ((u_quad_int) - t) + 1;
+ }
+ else
+ magnitude = (u_quad_int)num;
+ }
+ /* We use a do-while loop so that we write at least 1 digit */
+ do {
+ u_quad_int new_magnitude = magnitude / 10;
+ *--p = (char)(magnitude - new_magnitude * 10 + '0');
+ magnitude = new_magnitude;
+ } while (magnitude);
+ *len = (int)(buf_end - p);
+ return p;
+ }
+
+ /*
+ * Convert a floating point number to a string formats 'f', 'e' or 'E'.
+ * The result is placed in buf, and len denotes the length of the string
+ * The sign is returned in the is_negative argument (and is not placed
+ * in buf).
+ */
+ static char *
+ conv_fp(
+ register char format,
+ register double num,
+ int add_dp,
+ int precision,
+ int *is_negative,
+ char *buf,
+ size_t *len)
+ {
+ register char *s = buf;
+ register char *p;
+ int decimal_point;
+ char buf1[NDIG];
+
+ if (format == 'f')
+ p = l2_util_fcvt(num, precision, &decimal_point, is_negative, buf1);
+ else /* either e or E format */
+ p = l2_util_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
+
+ /* Check for Infinity and NaN */
+ if (l2_util_isalpha(*p)) {
+ *len = strlen(strcpy(buf, p));
+ *is_negative = FALSE;
+ return (buf);
+ }
+
+ if (format == 'f') {
+ if (decimal_point <= 0) {
+ *s++ = '0';
+ if (precision > 0) {
+ *s++ = '.';
+ while (decimal_point++ < 0)
+ *s++ = '0';
+ }
+ else if (add_dp)
+ *s++ = '.';
+ }
+ else {
+ while (decimal_point-- > 0)
+ *s++ = *p++;
+ if (precision > 0 || add_dp)
+ *s++ = '.';
+ }
+ }
+ else {
+ *s++ = *p++;
+ if (precision > 0 || add_dp)
+ *s++ = '.';
+ }
+
+ /* copy the rest of p, the NUL is NOT copied */
+ while (*p)
+ *s++ = *p++;
+
+ if (format != 'f') {
+ char temp[EXPONENT_LENGTH]; /* for exponent conversion */
+ size_t t_len;
+ int exponent_is_negative;
+
+ *s++ = format; /* either e or E */
+ decimal_point--;
+ if (decimal_point != 0) {
+ p = conv_10((long_int) decimal_point, FALSE, &exponent_is_negative,
+ &temp[EXPONENT_LENGTH], &t_len);
+ *s++ = exponent_is_negative ? '-' : '+';
+ /* Make sure the exponent has at least 2 digits */
+ if (t_len == 1)
+ *s++ = '0';
+ while (t_len--)
+ *s++ = *p++;
+ }
+ else {
+ *s++ = '+';
+ *s++ = '0';
+ *s++ = '0';
+ }
+ }
+
+ *len = (int)(s - buf);
+ return buf;
+ }
+
+ /*
+ * Convert num to a base X number where X is a power of 2. nbits determines X.
+ * For example, if nbits is 3, we do base 8 conversion
+ * Return value:
+ * a pointer to a string containing the number
+ * The caller provides a buffer for the string: that is the buf_end
+ * argument which is a pointer to the END of the buffer + 1 (i.e. if the
+ * buffer is declared as buf[100], buf_end should be &buf[100]) As with
+ * conv_10, we have a faster version which is used when the number isn't
+ * quad size.
+ */
+
+ static const char low_digits[] = "0123456789abcdef";
+ static const char upper_digits[] = "0123456789ABCDEF";
+
+ static char *
+ conv_p2(
+ register u_long_int num,
+ register int nbits,
+ char format,
+ char *buf_end,
+ register size_t *len)
+ {
+ register int mask = (1 << nbits) - 1;
+ register char *p = buf_end;
+ register const char *digits = (format == 'X') ? upper_digits : low_digits;
+
+ do {
+ *--p = digits[num & mask];
+ num >>= nbits;
+ } while (num);
+ *len = (int)(buf_end - p);
+ return p;
+ }
+
+ static char *
+ conv_p2_quad(
+ u_quad_int num,
+ register int nbits,
+ char format,
+ char *buf_end,
+ register size_t *len)
+ {
+ register int mask = (1 << nbits) - 1;
+ register char *p = buf_end;
+ register const char *digits = (format == 'X') ? upper_digits : low_digits;
+
+ do {
+ *--p = digits[num & mask];
+ num >>= nbits;
+ } while (num);
+ *len = (size_t)(buf_end - p);
+ return p;
+ }
+
+ /*
+ * l2_util_format(), the generic printf-style formatting routine
+ * and heart of this piece of source.
+ */
+ int
+ l2_util_format(
+ l2_util_format_t *vbuff,
+ const char *fmt,
+ va_list ap)
+ {
+ register char *sp;
+ register char *bep;
+ register int cc = 0;
+ register int i;
+
+ char *s = NULL;
+ char *q;
+ size_t s_len;
+
+ register int min_width = 0;
+ int precision = 0;
+ enum { LEFT, RIGHT } adjust;
+ char pad_char;
+ char prefix_char;
+
+ double fp_num;
+ quad_int i_quad = (quad_int)0;
+ u_quad_int ui_quad;
+ long_int i_num = (long_int)0;
+ u_long_int ui_num;
+
+ char num_buf[NUM_BUF_SIZE];
+ char char_buf[2]; /* for printing %% and %<unknown> */
+
+ enum var_type_enum { IS_QUAD, IS_LONG, IS_SHORT, IS_INT };
+ enum var_type_enum var_type = IS_INT;
+
+ int alternate_form;
+ int print_sign;
+ int print_blank;
+ int adjust_precision;
+ int adjust_width;
+ int is_negative;
+
+ char extinfo[20];
+
+ sp = vbuff->curpos;
+ bep = vbuff->endpos;
+
+ while (*fmt != NUL) {
+ if (*fmt != '%') {
+ INS_CHAR(*fmt, sp, bep, cc);
+ }
+ else {
+ /*
+ * Default variable settings
+ */
+ adjust = RIGHT;
+ alternate_form = print_sign = print_blank = FALSE;
+ pad_char = ' ';
+ prefix_char = NUL;
+ extinfo[0] = NUL;
+
+ fmt++;
+
+ /*
+ * Try to avoid checking for flags, width or precision
+ */
+ if (!l2_util_islower(*fmt)) {
+ /*
+ * Recognize flags: -, #, BLANK, +
+ */
+ for (;; fmt++) {
+ if (*fmt == '{') {
+ i = 0;
+ for (fmt++; *fmt != '}' && *fmt != NUL; fmt++) {
+ if (i < sizeof(extinfo)-1)
+ extinfo[i++] = *fmt;
+ }
+ extinfo[i] = NUL;
+ }
+ else if (*fmt == '-')
+ adjust = LEFT;
+ else if (*fmt == '+')
+ print_sign = TRUE;
+ else if (*fmt == '#')
+ alternate_form = TRUE;
+ else if (*fmt == ' ')
+ print_blank = TRUE;
+ else if (*fmt == '0')
+ pad_char = '0';
+ else
+ break;
+ }
+
+ /*
+ * Check if a width was specified
+ */
+ if (l2_util_isdigit(*fmt)) {
+ STR_TO_DEC(fmt, min_width);
+ adjust_width = TRUE;
+ }
+ else if (*fmt == '*') {
+ min_width = va_arg(ap, int);
+ fmt++;
+ adjust_width = TRUE;
+ if (min_width < 0) {
+ adjust = LEFT;
+ min_width = -min_width;
+ }
+ }
+ else
+ adjust_width = FALSE;
+
+ /*
+ * 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 = TRUE;
+ fmt++;
+ if (l2_util_isdigit(*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 = FALSE;
+ }
+ else
+ adjust_precision = adjust_width = FALSE;
+
+ /*
+ * Modifier check
+ */
+ if (*fmt == 'q') {
+ var_type = IS_QUAD;
+ fmt++;
+ }
+ else if (*fmt == 'l') {
+ var_type = IS_LONG;
+ fmt++;
+ }
+ else if (*fmt == 'h') {
+ var_type = IS_SHORT;
+ fmt++;
+ }
+ else {
+ var_type = IS_INT;
+ }
+
+ /*
+ * 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) {
+
+ /* Unsigned Decimal Integer */
+ case 'u':
+ if (var_type == IS_QUAD) {
+ i_quad = va_arg(ap, u_quad_int);
+ s = conv_10_quad(i_quad, 1, &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ }
+ else {
+ if (var_type == IS_LONG)
+ i_num = (long_int)va_arg(ap, u_long_int);
+ else if (var_type == IS_SHORT)
+ i_num = (long_int)(unsigned short)va_arg(ap, unsigned int);
+ else
+ i_num = (long_int)va_arg(ap, unsigned int);
+ s = conv_10(i_num, 1, &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ }
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+ break;
+
+ /* Signed Decimal Integer */
+ case 'd':
+ case 'i':
+ if (var_type == IS_QUAD) {
+ i_quad = va_arg(ap, quad_int);
+ s = conv_10_quad(i_quad, 0, &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ }
+ else {
+ if (var_type == IS_LONG)
+ i_num = (long_int)va_arg(ap, long_int);
+ else if (var_type == IS_SHORT)
+ i_num = (long_int)(short)va_arg(ap, int);
+ else
+ i_num = (long_int)va_arg(ap, int);
+ s = conv_10(i_num, 0, &is_negative,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ }
+ FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+ if (is_negative)
+ prefix_char = '-';
+ else if (print_sign)
+ prefix_char = '+';
+ else if (print_blank)
+ prefix_char = ' ';
+ break;
+
+ /* Unsigned Octal Integer */
+ case 'o':
+ if (var_type == IS_QUAD) {
+ ui_quad = va_arg(ap, u_quad_int);
+ s = conv_p2_quad(ui_quad, 3, *fmt,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ }
+ else {
+ if (var_type == IS_LONG)
+ ui_num = (u_long_int) va_arg(ap, u_long_int);
+ else if (var_type == IS_SHORT)
+ ui_num = (u_long_int)(unsigned short)va_arg(ap, unsigned int);
+ else
+ ui_num = (u_long_int)va_arg(ap, unsigned int);
+ s = 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;
+
+ /* Unsigned Hexadecimal Integer */
+ case 'x':
+ case 'X':
+ if (var_type == IS_QUAD) {
+ ui_quad = va_arg(ap, u_quad_int);
+ s = conv_p2_quad(ui_quad, 4, *fmt,
+ &num_buf[NUM_BUF_SIZE], &s_len);
+ }
+ else {
+ if (var_type == IS_LONG)
+ ui_num = (u_long_int)va_arg(ap, u_long_int);
+ else if (var_type == IS_SHORT)
+ ui_num = (u_long_int)(unsigned short)va_arg(ap, unsigned int);
+ else
+ ui_num = (u_long_int)va_arg(ap, unsigned int);
+ s = 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;
+
+ /* String */
+ 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;
+
+ /* Double Floating Point (style 1) */
+ case 'f':
+ case 'e':
+ case 'E':
+ fp_num = va_arg(ap, double);
+ if (isnan(fp_num)) {
+ s = "NaN";
+ s_len = 3;
+ }
+ else if (isinf(fp_num)) {
+ s = "Inf";
+ s_len = 3;
+ }
+ else {
+ /* use &num_buf[1], so that we have room for the sign */
+ s = conv_fp(*fmt, fp_num, alternate_form,
+ (adjust_precision == FALSE) ? 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;
+
+ /* Double Floating Point (style 2) */
+ case 'g':
+ case 'G':
+ fp_num = va_arg(ap, double);
+ if (isnan(fp_num)) {
+ s = "NaN";
+ s_len = 3;
+ }
+ else if (isinf(fp_num)) {
+ s = "Inf";
+ s_len = 3;
+ }
+ else {
+ if (adjust_precision == FALSE)
+ precision = FLOAT_DIGITS;
+ else if (precision == 0)
+ precision = 1;
+ /* use &num_buf[1], so that we have room for the sign */
+ s = l2_util_gcvt(fp_num, precision, &num_buf[1], alternate_form);
+ 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++] = '.';
+ s[s_len] = NUL; /* delimit for following strchr() */
+ }
+ if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
+ *q = 'E';
+ }
+ break;
+
+ /* Single Character */
+ case 'c':
+ char_buf[0] = (char) (va_arg(ap, int));
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+ /* The '%' Character */
+ case '%':
+ char_buf[0] = '%';
+ s = &char_buf[0];
+ s_len = 1;
+ pad_char = ' ';
+ break;
+
+ /* Special: Number of already written characters */
+ case 'n':
+ if (var_type == IS_QUAD)
+ *(va_arg(ap, quad_int *)) = cc;
+ else if (var_type == IS_LONG)
+ *(va_arg(ap, long *)) = cc;
+ else if (var_type == IS_SHORT)
+ *(va_arg(ap, short *)) = cc;
+ else
+ *(va_arg(ap, int *)) = cc;
+ break;
+
+ /*
+ * Pointer argument type.
+ */
+ case 'p':
+ #if defined(SIZEOF_LONG_LONG) && (SIZEOF_LONG_LONG == SIZEOF_VOID_P)
+ ui_quad = (u_quad_int) va_arg(ap, void *);
+ s = conv_p2_quad(ui_quad, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len);
+ #else
+ ui_num = (u_long_int) va_arg(ap, void *);
+ s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len);
+ #endif
+ 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
+ * %<char> to help the user identify what option is not
+ * understood. This is also useful in case the user
+ * wants to pass the output of format_converter to
+ * another function that understands some other %<char>
+ * (like syslog). Note that we can't point s inside fmt
+ * because the unknown <char> could be preceded by width
+ * etc.
+ */
+ default:
+ s = NULL;
+ if (vbuff->format != NULL) {
+ vbuff->format(vbuff,
+ &prefix_char, &pad_char, &s, &s_len,
+ num_buf, NUM_BUF_SIZE, extinfo, *fmt, &ap);
+ if (s == NULL)
+ return -1;
+ }
+ if (s == NULL) {
+ char_buf[0] = '%';
+ char_buf[1] = *fmt;
+ s = char_buf;
+ s_len = 2;
+ pad_char = ' ';
+ }
+ break;
+ }
+
+ if (prefix_char != NUL && s != S_NULL && s != char_buf) {
+ *--s = prefix_char;
+ s_len++;
+ }
+
+ if (adjust_width && adjust == RIGHT && min_width > s_len) {
+ if (pad_char == '0' && prefix_char != NUL) {
+ INS_CHAR(*s, sp, bep, cc);
+ s++;
+ s_len--;
+ min_width--;
+ }
+ PAD(min_width, s_len, pad_char);
+ }
+
+ /*
+ * Print the string s.
+ */
+ for (i = s_len; i != 0; i--) {
+ INS_CHAR(*s, sp, bep, cc);
+ s++;
+ }
+
+ if (adjust_width && adjust == LEFT && min_width > s_len)
+ PAD(min_width, s_len, pad_char);
+ }
+ fmt++;
+ }
+ vbuff->curpos = sp;
+ return cc;
+ }
+
+ /*
+ * l2_util_format -- format a new string.
+ * This is inspired by POSIX sprintf(3), but mainly provides the
+ * following differences: first it is actually a snprintf(3) style, i.e.
+ * it allows one to specify the maximum number of characters which are
+ * allowed to write. Second, it allows one to just count the number of
+ * characters which have to be written.
+ */
+
+ #define STR_FORMAT_BUFLEN 20
+
+ static int l2_util_flush_fake(l2_util_format_t *out_handle)
+ {
+ out_handle->data[1].i += out_handle->data[2].i;
+ out_handle->curpos = (char *)out_handle->data[0].vp;
+ return 0;
+ }
+
+ static int l2_util_flush_real(l2_util_format_t *out_handle)
+ {
+ return -1;
+ }
+
+ int l2_util_vsprintf(char *s, size_t n, const char *fmt, va_list ap)
+ {
+ l2_util_format_t handle;
+ char buf[STR_FORMAT_BUFLEN];
+ int rv;
+
+ if (n == 0)
+ return 0;
+ if (s == NULL) {
+ /* fake formatting, i.e., calculate output length only */
+ handle.curpos = buf;
+ handle.endpos = buf + sizeof(buf) - 1;
+ handle.flush = l2_util_flush_fake;
+ handle.format = NULL;
+ handle.data[0].vp = buf;
+ handle.data[1].i = 0;
+ handle.data[2].i = sizeof(buf);
+ rv = l2_util_format(&handle, fmt, ap);
+ if (rv == -1)
+ rv = (int)n;
+ }
+ else {
+ /* real formatting, i.e., create output */
+ handle.curpos = s;
+ handle.endpos = s + n - 1;
+ handle.flush = l2_util_flush_real;
+ handle.format = NULL;
+ rv = l2_util_format(&handle, fmt, ap);
+ *(handle.curpos) = NUL;
+ if (rv == -1)
+ rv = (int)n;
+ }
+ return rv;
+ }
+
+ char *l2_util_vasprintf(const char *fmt, va_list ap)
+ {
+ va_list apbak;
+ char *s;
+ int rv;
+
+ apbak = ap;
+ if ((rv = l2_util_vsprintf(NULL, -1, fmt, ap)) == -1)
+ return NULL;
+ if ((s = malloc(rv+1)) == NULL)
+ return NULL;
+ ap = apbak;
+ if ((rv = l2_util_vsprintf(s, rv+1, fmt, ap)) == -1) {
+ free(s);
+ return NULL;
+ }
+ return s;
+ }
+
+ int l2_util_sprintf(char *s, size_t n, const char *fmt, ...)
+ {
+ va_list ap;
+ int rv;
+
+ va_start(ap, fmt);
+ rv = l2_util_vsprintf(s, n, fmt, ap);
+ va_end(ap);
+ return rv;
+ }
+
+ char *l2_util_asprintf(const char *fmt, ...)
+ {
+ va_list ap;
+ char *rv;
+
+ va_start(ap, fmt);
+ rv = l2_util_vasprintf(fmt, ap);
+ va_end(ap);
+ return rv;
+ }
+
|