/* ** L2 - OSSP Logging Library ** Copyright (c) 2001 The OSSP Project (http://www.ossp.org/) ** Copyright (c) 2001 Cable & Wireless Deutschland (http://www.cw.com/de/) ** ** This file is part of OSSP L2, a flexible logging library which ** can be found at http://www.ossp.org/pkg/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 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-2001 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 #include #include #include #include #include #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)) 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 = modf(arg, &fi); p1 = &buf[NDIG]; /* Do integer part */ if (fi != 0) { p1 = &buf[NDIG]; while (fi != 0 && p1 > &buf[0]) { fj = 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 = 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 % */ 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 * % 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: 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) 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; }