/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * This code is based on, and used with the permission of, the * SIO stdio-replacement strx_* functions by Panos Tsirigotis * for xinetd. */ #include "config.h" #include #include #include #include #include #include #include #include #ifdef HAVE_LIMITS_H #include #endif typedef struct { char *curpos; char *endpos; } ap_vformatter_buff; #define API_EXPORT(type) type #define API_EXPORT_NONSTD(type) type #define ap_isalnum(c) (isalnum(((unsigned char)(c)))) #define ap_isalpha(c) (isalpha(((unsigned char)(c)))) #define ap_iscntrl(c) (iscntrl(((unsigned char)(c)))) #define ap_isdigit(c) (isdigit(((unsigned char)(c)))) #define ap_isgraph(c) (isgraph(((unsigned char)(c)))) #define ap_islower(c) (islower(((unsigned char)(c)))) #define ap_isprint(c) (isprint(((unsigned char)(c)))) #define ap_ispunct(c) (ispunct(((unsigned char)(c)))) #define ap_isspace(c) (isspace(((unsigned char)(c)))) #define ap_isupper(c) (isupper(((unsigned char)(c)))) #define ap_isxdigit(c) (isxdigit(((unsigned char)(c)))) #define ap_tolower(c) (tolower(((unsigned char)(c)))) #define ap_toupper(c) (toupper(((unsigned char)(c)))) typedef enum { NO = 0, YES = 1 } boolean_e; #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #ifndef AP_LONGEST_LONG #define AP_LONGEST_LONG long #endif #define NUL '\0' #define WIDE_INT long #define WIDEST_INT AP_LONGEST_LONG typedef WIDE_INT wide_int; typedef unsigned WIDE_INT u_wide_int; typedef WIDEST_INT widest_int; #ifdef __TANDEM /* Although Tandem supports "long long" there is no unsigned variant. */ typedef unsigned long u_widest_int; #else typedef unsigned WIDEST_INT u_widest_int; #endif typedef int bool_int; #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 /* * cvt.c - IEEE floating point formatting routines for FreeBSD * from GNU libc-4.6.27. Modified to be thread safe. */ /* * ap_ecvt 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 */ #define NDIG 80 /* buf must have at least NDIG bytes */ static char *ap_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 = 0; p = &buf[0]; if (arg < 0) { *sign = 1; arg = -arg; } arg = modf(arg, &fi); p1 = &buf[NDIG]; /* * Do integer part */ if (fi != 0) { p1 = &buf[NDIG]; while (p1 > &buf[0] && fi != 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] = '\0'; 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] = '\0'; 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 = '\0'; return (buf); } static char *ap_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf) { return (ap_cvt(arg, ndigits, decpt, sign, 1, buf)); } static char *ap_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf) { return (ap_cvt(arg, ndigits, decpt, sign, 0, buf)); } /* * ap_gcvt - Floating output conversion to * minimal length string */ static char *ap_gcvt(double number, int ndigit, char *buf, boolean_e altform) { int sign, decpt; register char *p1, *p2; register int i; char buf1[NDIG]; p1 = ap_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 = '\0'; return (buf); } /* * 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(c, sp, bep, cc) \ { \ if (sp >= bep) { \ vbuff->curpos = sp; \ if (flush_func(vbuff)) \ return -1; \ sp = vbuff->curpos; \ bep = vbuff->endpos; \ } \ *sp++ = (c); \ cc++; \ } #define NUM( c ) ( c - '0' ) #define STR_TO_DEC( str, num ) \ num = NUM( *str++ ) ; \ while ( ap_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. We don't allow precision to be large * enough that we continue past the start of s. * * NOTE: this makes use of the magic info that s is * always based on num_buf with a size of NUM_BUF_SIZE. */ #define FIX_PRECISION( adjust, precision, s, s_len ) \ if ( adjust ) { \ int p = precision < NUM_BUF_SIZE - 1 ? precision : NUM_BUF_SIZE - 1; \ while ( s_len < p ) \ { \ *--s = '0' ; \ s_len++ ; \ } \ } /* * Macro that 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 = YES /* * 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 wide_int num, register bool_int is_unsigned, register bool_int *is_negative, char *buf_end, register int *len) { register char *p = buf_end; register u_wide_int magnitude; if (is_unsigned) { magnitude = (u_wide_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) { wide_int t = num + 1; magnitude = ((u_wide_int) -t) + 1; } else magnitude = (u_wide_int) num; } /* * We use a do-while loop so that we write at least 1 digit */ do { register u_wide_int new_magnitude = magnitude / 10; *--p = (char) (magnitude - new_magnitude * 10 + '0'); magnitude = new_magnitude; } while (magnitude); *len = buf_end - p; return (p); } static char *conv_10_quad(widest_int num, register bool_int is_unsigned, register bool_int *is_negative, char *buf_end, register int *len) { register char *p = buf_end; u_widest_int magnitude; /* * We see if we can use the faster non-quad version by checking the * number against the largest long value it can be. If <=, we * punt to the quicker version. */ if ((num <= ULONG_MAX && is_unsigned) || (num <= LONG_MAX && !is_unsigned)) return(conv_10( (wide_int)num, is_unsigned, is_negative, buf_end, len)); if (is_unsigned) { magnitude = (u_widest_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) { widest_int t = num + 1; magnitude = ((u_widest_int) -t) + 1; } else magnitude = (u_widest_int) num; } /* * We use a do-while loop so that we write at least 1 digit */ do { u_widest_int new_magnitude = magnitude / 10; *--p = (char) (magnitude - new_magnitude * 10 + '0'); magnitude = new_magnitude; } while (magnitude); *len = buf_end - p; return (p); } static char *conv_in_addr(struct in_addr *ia, char *buf_end, int *len) { unsigned addr = ntohl(ia->s_addr); char *p = buf_end; bool_int is_negative; int sub_len; p = conv_10((addr & 0x000000FF) , TRUE, &is_negative, p, &sub_len); *--p = '.'; p = conv_10((addr & 0x0000FF00) >> 8, TRUE, &is_negative, p, &sub_len); *--p = '.'; p = conv_10((addr & 0x00FF0000) >> 16, TRUE, &is_negative, p, &sub_len); *--p = '.'; p = conv_10((addr & 0xFF000000) >> 24, TRUE, &is_negative, p, &sub_len); *len = buf_end - p; return (p); } static char *conv_sockaddr_in(struct sockaddr_in *si, char *buf_end, int *len) { char *p = buf_end; bool_int is_negative; int sub_len; p = conv_10(ntohs(si->sin_port), TRUE, &is_negative, p, &sub_len); *--p = ':'; p = conv_in_addr(&si->sin_addr, p, &sub_len); *len = 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, boolean_e add_dp, int precision, bool_int *is_negative, char *buf, int *len) { register char *s = buf; register char *p; int decimal_point; char buf1[NDIG]; if (format == 'f') p = ap_fcvt(num, precision, &decimal_point, is_negative, buf1); else /* either e or E format */ p = ap_ecvt(num, precision + 1, &decimal_point, is_negative, buf1); /* * Check for Infinity and NaN */ if (ap_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 */ int t_len; bool_int exponent_is_negative; *s++ = format; /* either e or E */ decimal_point--; if (decimal_point != 0) { p = conv_10((wide_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 = 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 char *conv_p2(register u_wide_int num, register int nbits, char format, char *buf_end, register int *len) { register int mask = (1 << nbits) - 1; register char *p = buf_end; static const char low_digits[] = "0123456789abcdef"; static const char upper_digits[] = "0123456789ABCDEF"; register const char *digits = (format == 'X') ? upper_digits : low_digits; do { *--p = digits[num & mask]; num >>= nbits; } while (num); *len = buf_end - p; return (p); } static char *conv_p2_quad(u_widest_int num, register int nbits, char format, char *buf_end, register int *len) { register int mask = (1 << nbits) - 1; register char *p = buf_end; static const char low_digits[] = "0123456789abcdef"; static const char upper_digits[] = "0123456789ABCDEF"; register const char *digits = (format == 'X') ? upper_digits : low_digits; if (num <= ULONG_MAX) return(conv_p2( (u_wide_int)num, nbits, format, buf_end, len)); do { *--p = digits[num & mask]; num >>= nbits; } while (num); *len = buf_end - p; return (p); } /* * Do format conversion placing the output in buffer */ API_EXPORT(int) ap_vformatter(int (*flush_func)(ap_vformatter_buff *), ap_vformatter_buff *vbuff, const char *fmt, va_list ap) { register char *sp; register char *bep; 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; widest_int i_quad = (widest_int) 0; u_widest_int ui_quad; 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 % */ enum var_type_enum { IS_QUAD, IS_LONG, IS_SHORT, IS_INT }; enum var_type_enum var_type = IS_INT; /* * Flag variables */ boolean_e alternate_form; boolean_e print_sign; boolean_e print_blank; boolean_e adjust_precision; boolean_e adjust_width; bool_int is_negative; sp = vbuff->curpos; bep = vbuff->endpos; while (*fmt) { if (*fmt != '%') { INS_CHAR(*fmt, sp, bep, 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 (!ap_islower(*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 (ap_isdigit(*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 */ if (*fmt == '.') { adjust_precision = YES; fmt++; if (ap_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 = NO; } else adjust_precision = adjust_width = NO; /* * 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) { case 'u': if (var_type == IS_QUAD) { i_quad = va_arg(ap, u_widest_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 = (wide_int) va_arg(ap, u_wide_int); else if (var_type == IS_SHORT) i_num = (wide_int) (unsigned short) va_arg(ap, unsigned int); else i_num = (wide_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; case 'd': case 'i': if (var_type == IS_QUAD) { i_quad = va_arg(ap, widest_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 = (wide_int) va_arg(ap, wide_int); else if (var_type == IS_SHORT) i_num = (wide_int) (short) va_arg(ap, int); else i_num = (wide_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; case 'o': if (var_type == IS_QUAD) { ui_quad = va_arg(ap, u_widest_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_wide_int) va_arg(ap, u_wide_int); else if (var_type == IS_SHORT) ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int); else ui_num = (u_wide_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; case 'x': case 'X': if (var_type == IS_QUAD) { ui_quad = va_arg(ap, u_widest_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_wide_int) va_arg(ap, u_wide_int); else if (var_type == IS_SHORT) ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int); else ui_num = (u_wide_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; 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); /* * * We use &num_buf[ 1 ], so that we have room for the sign */ #ifdef HAVE_ISNAN if (isnan(fp_num)) { s = "nan"; s_len = 3; } else #endif #ifdef HAVE_ISINF if (isinf(fp_num)) { s = "inf"; s_len = 3; } else #endif { s = 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_gcvt(va_arg(ap, double), 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] = '\0'; /* delimit for following strchr() */ } 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': if (var_type == IS_QUAD) *(va_arg(ap, widest_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; /* * This is where we extend the printf format, with a second * type specifier */ case 'p': switch(*++fmt) { /* * If the pointer size is equal to or smaller than the size * of the largest unsigned int, we convert the pointer to a * hex number, otherwise we print "%p" to indicate that we * don't handle "%p". */ case 'p': #ifdef AP_VOID_P_IS_QUAD if (sizeof(void *) <= sizeof(u_widest_int)) { ui_quad = (u_widest_int) va_arg(ap, void *); s = conv_p2_quad(ui_quad, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); } #else if (sizeof(void *) <= sizeof(u_wide_int)) { ui_num = (u_wide_int) va_arg(ap, void *); s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); } #endif else { s = "%p"; s_len = 2; prefix_char = NUL; } pad_char = ' '; break; /* print a struct sockaddr_in as a.b.c.d:port */ case 'I': { struct sockaddr_in *si; si = va_arg(ap, struct sockaddr_in *); if (si != NULL) { s = conv_sockaddr_in(si, &num_buf[NUM_BUF_SIZE], &s_len); if (adjust_precision && precision < s_len) s_len = precision; } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ' '; } break; /* print a struct in_addr as a.b.c.d */ case 'A': { struct in_addr *ia; ia = va_arg(ap, struct in_addr *); if (ia != NULL) { s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len); if (adjust_precision && precision < s_len) s_len = precision; } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ' '; } break; case NUL: /* if %p ends the string, oh well ignore it */ continue; default: s = "bogus %p"; s_len = 8; prefix_char = NUL; break; } 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 != 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; } static int snprintf_flush(ap_vformatter_buff *vbuff) { /* if the buffer fills we have to abort immediately, there is no way * to "flush" an ap_snprintf... there's nowhere to flush it to. */ return -1; } API_EXPORT_NONSTD(int) ap_snprintf(char *buf, size_t len, const char *format,...) { int cc; va_list ap; ap_vformatter_buff vbuff; if (len == 0) return 0; /* save one byte for nul terminator */ vbuff.curpos = buf; vbuff.endpos = buf + len - 1; va_start(ap, format); cc = ap_vformatter(snprintf_flush, &vbuff, format, ap); va_end(ap); *vbuff.curpos = '\0'; return (cc == -1) ? len : cc; } API_EXPORT(int) ap_vsnprintf(char *buf, size_t len, const char *format, va_list ap) { int cc; ap_vformatter_buff vbuff; if (len == 0) return 0; /* save one byte for nul terminator */ vbuff.curpos = buf; vbuff.endpos = buf + len - 1; cc = ap_vformatter(snprintf_flush, &vbuff, format, ap); *vbuff.curpos = '\0'; return (cc == -1) ? len : cc; }