From: Jeffrey Altman Date: Wed, 24 Jun 2009 16:15:39 +0000 (+0000) Subject: util-snprintf-replacement-20090624 X-Git-Tag: openafs-devel-1_5_61~207 X-Git-Url: https://git.openafs.org/?p=openafs.git;a=commitdiff_plain;h=b45838ca82a085360491b14fdb16e88612a43165 util-snprintf-replacement-20090624 LICENSE BSD 1. Add a test program for *printf functionality. util/tests/snprintf_test.c 2. Replace OpenAFS implementation of afs_*printf() with Heimdal's version. 3. Add support to Heimdal's version to support: - floating point - OpenAFS %I ipv4 address formatting (dotted notation and hostname lookup) - Microsoft's I32 and I64 integer size modifiers With these changes OpenAFS gains: - output that is compliant with standard *printf implementations. the previous implementation had justification, padding and case errors. - support for a NULL buffer which computes the required size based upon the input format and arguments. the previous implementation would crash. - support for additional format types. - OpenAFS specific implementations of vasnprintf(), vasprintf(), asprintf(), and asnprintf(). --- diff --git a/src/util/afsutil.h b/src/util/afsutil.h index 473abd4..4b8832e 100644 --- a/src/util/afsutil.h +++ b/src/util/afsutil.h @@ -56,17 +56,28 @@ afs_vsnprintf( /*@out@ */ char *p, size_t avail, const char *fmt, /*@requires maxSet(p) >= (avail-1)@ */ /*@modifies p@ */ ; - extern /*@printflike@ */ int - afs_snprintf( /*@out@ */ char *p, size_t avail, +extern /*@printflike@ */ int +afs_snprintf( /*@out@ */ char *p, size_t avail, const char *fmt, ...) /*@requires maxSet(p) >= (avail-1)@ */ /*@modifies p@ */ ; +extern int +afs_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args); + +extern int +afs_vasprintf (char **ret, const char *format, va_list args); + +extern int +afs_asprintf (char **ret, const char *format, ...); + +extern int +afs_asnprintf (char **ret, size_t max_sz, const char *format, ...); /* special version of ctime that clobbers a *different static variable, so * that ViceLog can call ctime and not cause buffer confusion. */ - extern char *vctime(const time_t * atime); +extern char *vctime(const time_t * atime); /* Need a thead safe ctime for pthread builds. Use std ctime for LWP */ #if defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV) diff --git a/src/util/snprintf.c b/src/util/snprintf.c index 5a5004a..1bbe36e 100644 --- a/src/util/snprintf.c +++ b/src/util/snprintf.c @@ -1,3 +1,36 @@ +/* + * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * 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. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS 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 INSTITUTE OR 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. + */ + /* snprintf.c - Formatted, length-limited print to a string */ #include @@ -7,6 +40,7 @@ RCSID ("$Header$"); #include +#include #include #include #include @@ -24,45 +58,12 @@ RCSID #include #endif -#define MAXPREC 100 - -/* Generate an ASCII representation of an integer , as follows: - * indicates the base to be used (2-36) - * is nonzero if letter digits should be uppercase - * is the minimum number of digits - * The resulting number is stored in , which must be long enough - * to receive it. The minimum length is or ceil(log{base}(val)), - * whichever is larger, plus room for a trailing NUL. - */ -static void -mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec) -{ - int len = 0, dig, i; - - while (val) { - dig = (int) (val % base); - val = (val - dig) / base; - if (dig < 10) - dig = dig + '0'; - else if (uc) - dig = dig + 'A' - 10; - else - dig = dig + 'a' - 10; - buf[len++] = dig; - } - while (len < prec) - buf[len++] = '0'; - for (i = 0; i < (len + 1) / 2; i++) { - dig = buf[i]; - buf[i] = buf[len - i - 1]; - buf[len - i - 1] = dig; - } - buf[len] = 0; -} - - -/* This function is a mostly-complete implementation of snprintf, - * with the following features: +/* This is an enhanced version of the *printf functionality shipped + * with Heimdal. In addition to the standard Unix formatting types + * this version also supports Microsoft's I32 and I64 type modifiers + * and the OpenAFS I type which is used to generate output from + * network byte order IPv4 addresses (either dotted notation or + * hostname lookups. Implementation details follow: * * - Actually obeys the length limit, which (unfortunately) many * implementations of snprintf do not. @@ -71,6 +72,10 @@ mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec) * (d, i, o, u, x, X), floating-point values (f, e, E, g, G), * and strings and characters (c, s, %), plus a few unusual * but useful ones described below. + * + * - The Microsoft integral size modifiers I32 and I64 are + * supported. I32 is equivalent to 'l'. + * I64 is equivalent to 'll'. * * - Supports all the standard flags (-, 0, +, space, #). These * flags are ignored if used when they are not appropriate. @@ -84,10 +89,8 @@ mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec) * instead of in the format string. There is a maximum precision * of 100 digits. * - * - At present, the 'p' specifier for printing pointers is not - * implemented, because it is inherently non-portable and thus - * can be implemented correctly only by the compiler's run-time - * library. + * - The 'p' specifier for printing pointers is implemented using + * compile time knowledge. (AFS_64BITPOINTER_ENV) * * - Floating-point specifier (%e, %f, %g) are implemented by * calling the standard sprintf, and thus may be unsafe. @@ -96,6 +99,10 @@ mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec) * is specified by the user, who knows but cannot change the order * of the arguments. Such usage is inherently dangerous and * insecure; thus, it is not supported. + * + * - Passing in a format and an NULL buffer is supported. This + * will compute the size of the buffer required by the format + * and the provided input parameters. * * The custom format specifier '%I' is supported. This specifier * takes as its argument an unsigned long integer containing an @@ -130,403 +137,809 @@ mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec) * will be padded with spaces on the left to three digits. If * both '0' and ' ' are given, the ' ' flag will be ignored. * + The '#' and '+' flags have no effect. + * + * A test program exists in src/util/tests/snprintf_tests.c. */ -int -afs_vsnprintf(char *p, size_t avail, const char *fmt, va_list ap) + +#define MAXPREC 100 + +enum format_flags { + minus_flag = 1, + plus_flag = 2, + space_flag = 4, + alternate_flag = 8, + zero_flag = 16 +}; + +/* + * Common state + */ + +struct snprintf_state { + unsigned char *str; + unsigned char *s; + unsigned char *theend; + size_t sz; + size_t max_sz; + void (*append_char)(struct snprintf_state *, unsigned char); + /* XXX - methods */ +}; + +static int +afs_sn_reserve (struct snprintf_state *state, size_t n) { - unsigned int width, precision, haveprec; - size_t len; - int ljust, plsign, spsign, altform, zfill; - int hflag, lflag, count, *countp, j; - char *x, *y, xbuf[MAXPREC + 21], fbuf[20]; - struct hostent *he; - struct in_addr ia; - afs_uintmax_t UVAL; - afs_intmax_t SVAL; - afs_intmax_t *llcountp; - long *lcountp; - double FVAL; - short *hcountp; - - count = 0; - avail--; - while (*fmt && avail) { - if (*fmt != '%') { - *p++ = *fmt++; - avail--; - count++; - continue; + return state->s + n > state->theend; +} + +static void +afs_sn_append_char (struct snprintf_state *state, unsigned char c) +{ + if (!afs_sn_reserve (state, 1)) + *state->s++ = c; +} + +static int +as_reserve (struct snprintf_state *state, size_t n) +{ + if (state->s + n > state->theend) { + size_t off = state->s - state->str; + unsigned char *tmp; + + if (state->max_sz && state->sz >= state->max_sz) + return 1; + + state->sz = max(state->sz * 2, state->sz + n); + if (state->max_sz) + state->sz = min(state->sz, state->max_sz); + tmp = (unsigned char *)realloc (state->str, state->sz); + if (tmp == NULL) + return 1; + state->str = tmp; + state->s = state->str + off; + state->theend = state->str + state->sz - 1; + } + return 0; +} + +static void +as_append_char (struct snprintf_state *state, unsigned char c) +{ + if(!as_reserve (state, 1)) + *state->s++ = c; +} + +/* longest integer types */ + +#ifdef AFS_64BIT_ENV +typedef afs_uint64 u_longest; +typedef afs_int64 longest; +#else +typedef afs_uint32 u_longest; +typedef afs_int32 longest; +#endif + +static int +pad(struct snprintf_state *state, int width, char c) +{ + int len = 0; + while(width-- > 0){ + (*state->append_char)(state, c); + ++len; + } + return len; +} + +/* return true if we should use alternatve hex form */ +static int +use_alternative (int flags, u_longest num, unsigned base) +{ + return (flags & alternate_flag) && base == 16 && num != 0; +} + +static int +append_number(struct snprintf_state *state, + u_longest num, unsigned base, const char *rep, + int width, int prec, int flags, int minusp) +{ + int len = 0; + u_longest n = num; + char nstr[MAXPREC]; /* enough for <192 bit octal integers */ + int nstart, nlen; + char signchar; + + /* given precision, ignore zero flag */ + if(prec != -1) + flags &= ~zero_flag; + else + prec = 1; + + /* format number as string */ + nstart = sizeof(nstr); + nlen = 0; + nstr[--nstart] = '\0'; + + do { + nstr[--nstart] = rep[n % base]; + ++nlen; + n /= base; + } while(n); + + /* zero value with zero precision should produce no digits */ + if(prec == 0 && num == 0) { + nlen--; + nstart++; + } + + /* figure out what char to use for sign */ + if(minusp) + signchar = '-'; + else if((flags & plus_flag)) + signchar = '+'; + else if((flags & space_flag)) + signchar = ' '; + else + signchar = '\0'; + + if((flags & alternate_flag) && base == 8) { + /* if necessary, increase the precision to + make first digit a zero */ + + /* XXX C99 claims (regarding # and %o) that "if the value and + precision are both 0, a single 0 is printed", but there is + no such wording for %x. This would mean that %#.o would + output "0", but %#.x "". This does not make sense, and is + also not what other printf implementations are doing. */ + + if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0') + prec = nlen + 1; + } + + /* possible formats: + pad | sign | alt | zero | digits + sign | alt | zero | digits | pad minus_flag + sign | alt | zero | digits zero_flag */ + + /* if not right justifying or padding with zeros, we need to + compute the length of the rest of the string, and then pad with + spaces */ + if(!(flags & (minus_flag | zero_flag))) { + if(prec > nlen) + width -= prec; + else + width -= nlen; + + if(use_alternative(flags, num, base)) + width -= 2; + + if(signchar != '\0') + width--; + + /* pad to width */ + len += pad(state, width, ' '); + } + if(signchar != '\0') { + (*state->append_char)(state, signchar); + ++len; + } + if(use_alternative(flags, num, base)) { + (*state->append_char)(state, '0'); + (*state->append_char)(state, rep[10] + 23); /* XXX */ + len += 2; + } + if(flags & zero_flag) { + /* pad to width with zeros */ + if(prec - nlen > width - len - nlen) + len += pad(state, prec - nlen, '0'); + else + len += pad(state, width - len - nlen, '0'); + } else + /* pad to prec with zeros */ + len += pad(state, prec - nlen, '0'); + + while(nstr[nstart] != '\0') { + (*state->append_char)(state, nstr[nstart++]); + ++len; + } + + if(flags & minus_flag) + len += pad(state, width - len, ' '); + + return len; +} + +/* + * return length + */ + +static int +append_string (struct snprintf_state *state, + const unsigned char *arg, + int width, + int prec, + int flags) +{ + int len = 0; + + if(arg == NULL) + arg = (const unsigned char*)"(null)"; + + if(prec != -1) + width -= prec; + else + width -= (int)strlen((const char *)arg); + if(!(flags & minus_flag)) + len += pad(state, width, ' '); + + if (prec != -1) { + while (*arg && prec--) { + (*state->append_char) (state, *arg++); + ++len; + } + } else { + while (*arg) { + (*state->append_char) (state, *arg++); + ++len; } + } + if(flags & minus_flag) + len += pad(state, width, ' '); + return len; +} - /** Found a format specifier **/ - ljust = plsign = spsign = altform = zfill = 0; - width = precision = haveprec = 0; - hflag = lflag = 0; - fmt++; - - /* parse format flags */ - while (*fmt) { - switch (*fmt) { - case '-': - ljust = 1; - fmt++; - continue; /* left justify */ - case '+': - plsign = 1; - fmt++; - continue; /* use + or - */ - case ' ': - spsign = 1; - fmt++; - continue; /* use space or - */ - case '#': - altform = 1; - fmt++; - continue; /* alternate form */ - case '0': - zfill = 1; - fmt++; - continue; /* pad with 0 */ - default: - break; +static int +append_char(struct snprintf_state *state, + unsigned char arg, + int width, + int flags) +{ + int len = 0; + + while(!(flags & minus_flag) && --width > 0) { + (*state->append_char) (state, ' ') ; + ++len; + } + (*state->append_char) (state, arg); + ++len; + while((flags & minus_flag) && --width > 0) { + (*state->append_char) (state, ' '); + ++len; + } + return 0; +} + +#define MAXPREC 100 +static int +append_float(struct snprintf_state *state, + char type, + double arg, + int width, + int prec, + int flags) +{ + int len = 0; + char fbuf[20], xbuf[MAXPREC + 21]; + + sprintf(fbuf, "%%%s%s.*L%c", + (flags & plus_flag) ? "+" : ((flags & space_flag) ? " " : ((flags & minus_flag) ? "-" : "")), + (flags & alternate_flag) ? "#" : "", type); + if (prec == -1) + prec = 6; + if (prec > MAXPREC) + prec = MAXPREC; + sprintf(xbuf, fbuf, prec, arg); + + len = append_string(state, (unsigned char *)xbuf, width, -1, 0); + return len; +} + +static int +append_address(struct snprintf_state *state, + afs_uint32 arg, + int width, + int prec, + int flags) +{ + int len = 0; + struct hostent * he; + struct in_addr ia; + char * x, *y; + + /* IP address: + * value is provided as a network-order unsigned long integer + * precision specifies max hostname length, as for %s + * if precision is explicitly 0, no hostname lookup is done + * if 0fill specified, IPaddr fields are 0-filled to 3 digits + * if spsign specified, IPaddr fields are space-filled to 3 digits + */ + ia.s_addr = arg; + if (prec == 0) + he = 0; + else + he = gethostbyaddr((char *)&ia, 4, AF_INET); + if (he) { + x = he->h_name; + len = (int)strlen(x); + if (prec != -1 && prec < len) + width = prec; + else + width = len; + if (flags & alternate_flag) { + for (y = x; *y; y++) + if (isupper(*y)) + *y = tolower(*y); + } else if (flags & plus_flag) { + for (y = x; *y; y++) + if (islower(*y)) + *y = toupper(*y); + } + len = append_string(state, (unsigned char *)x, width, prec, 0); + } else { + char xbuf[16]; + arg = ntohl(arg); + if (flags & zero_flag) { + x = "%03u.%03u.%03u.%03u"; + } else if (flags & space_flag) { + x = "%3u.%3u.%3u.%3u"; + } else { + x = "%u.%u.%u.%u"; + } + /* typecast to whatever '%u' is! */ + sprintf(xbuf, x, (unsigned int)((arg & 0xff000000) >> 24), + (unsigned int)((arg & 0x00ff0000) >> 16), + (unsigned int)((arg & 0x0000ff00) >> 8), + (unsigned int)(arg & 0x000000ff)); + len = append_string(state, (unsigned char *)xbuf, 0, -1, 0); + } + + return len; +} + +/* + * This can't be made into a function... + */ + +#if defined(AFS_64BIT_ENV) +#if defined(AFS_NT40_ENV) + +#define PARSE_INT_FORMAT(res, arg, unsig) \ +if (long_long_flag) \ + res = (unsig __int64)va_arg(arg, unsig __int64); \ +else if (long_flag || addr_flag) \ + res = (unsig long)va_arg(arg, unsig long); \ +else if (size_t_flag) \ + res = (size_t)va_arg(arg, size_t); \ +else if (short_flag) \ + res = (unsig short)va_arg(arg, unsig int); \ +else \ + res = (unsig int)va_arg(arg, unsig int) + +#else /* AFS_NT40_ENV */ + +#define PARSE_INT_FORMAT(res, arg, unsig) \ +if (long_long_flag) \ + res = (unsig long long)va_arg(arg, unsig long long); \ +else if (long_flag || addr_flag) \ + res = (unsig long)va_arg(arg, unsig long); \ +else if (size_t_flag) \ + res = (size_t)va_arg(arg, size_t); \ +else if (short_flag) \ + res = (unsig short)va_arg(arg, unsig int); \ +else \ + res = (unsig int)va_arg(arg, unsig int) +#endif + +#else + +#define PARSE_INT_FORMAT(res, arg, unsig) \ +if (long_flag || addr_flag) \ + res = (afs_uint32)va_arg(arg, afs_uint32); \ +else if (size_t_flag) \ + res = (size_t)va_arg(arg, size_t); \ +else if (short_flag) \ + res = (unsig short)va_arg(arg, unsig int); \ +else \ + res = (unsig int)va_arg(arg, unsig int) + +#endif + +/* + * zyxprintf - return length, as snprintf + */ + +static int +xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap) +{ + const unsigned char *format = (const unsigned char *)char_format; + unsigned char c; + int len = 0; + + while((c = *format++)) { + if (c == '%') { + int flags = 0; + int width = 0; + int prec = -1; + int size_t_flag = 0; + int long_long_flag = 0; + int long_flag = 0; + int short_flag = 0; + int addr_flag = 0; + + /* flags */ + while((c = *format++)){ + if(c == '-') + flags |= minus_flag; + else if(c == '+') + flags |= plus_flag; + else if(c == ' ') + flags |= space_flag; + else if(c == '#') + flags |= alternate_flag; + else if(c == '0') + flags |= zero_flag; + else if(c == '\'') + ; /* just ignore */ + else + break; } - break; - } - /* parse minimum width */ - if (*fmt == '*') { - width = va_arg(ap, int); - fmt++; - } else - while (isdigit(*fmt)) { - width = (width * 10) + (*fmt - '0'); - fmt++; + if((flags & space_flag) && (flags & plus_flag)) + flags ^= space_flag; + + if((flags & minus_flag) && (flags & zero_flag)) + flags ^= zero_flag; + + /* width */ + if (isdigit(c)) + do { + width = width * 10 + c - '0'; + c = *format++; + } while(isdigit(c)); + else if(c == '*') { + width = va_arg(ap, int); + c = *format++; } - /* parse precision */ - if (*fmt == '.') { - fmt++; - haveprec = 1; - if (*fmt == '*') { - precision = va_arg(ap, int); - fmt++; - } else - while (isdigit(*fmt)) { - precision = (precision * 10) + (*fmt - '0'); - fmt++; + /* precision */ + if (c == '.') { + prec = 0; + c = *format++; + if (isdigit(c)) + do { + prec = prec * 10 + c - '0'; + c = *format++; + } while(isdigit(c)); + else if (c == '*') { + prec = va_arg(ap, int); + c = *format++; } - } + } - /* parse size flags */ - while (*fmt) { - switch (*fmt) { - case 'h': - hflag = 1; - fmt++; - continue; /* short argument */ - case 'l': - lflag += 1; - fmt++; - continue; /* long argument */ - case 'L': - lflag = 2; - fmt++; - continue; /* long long argument */ - default: + /* size */ + + if (c == 'h') { + short_flag = 1; + c = *format++; + } else if (c == 'z') { + size_t_flag = 1; + c = *format++; + } else if (c == 'l') { + long_flag = 1; + c = *format++; + if (c == 'l') { + long_long_flag = 1; + c = *format++; + } + } else if (c == 'I') { + /* This could be either Microsoft I{32,64} notation */ + c = *format++; + if (c == '3') { + long_flag = 1; + c = *format++; + if (c == '2') { + c = *format++; + } + } else if (c == '6') { + long_flag = 1; + c = *format++; + if (c == '4') { + long_long_flag = 1; + c = *format++; + } + } else { + /* Or the OpenAFS special %I meaning network address */ + addr_flag = 1; + --format; + c = 'I'; + } + } else if (c == 'p') { + flags |= zero_flag; + if (prec == -1) + prec = 2 * sizeof(void *); + if (sizeof(void *) == sizeof(afs_uint64)) + long_long_flag = 1; + else if (sizeof(void *) == sizeof(afs_uint32)) + long_flag = 1; + else + long_flag = 1; + } + + if(c != 'd' && c != 'i' && c != 'I') + flags &= ~(plus_flag | space_flag); + + switch (c) { + case 'c' : + append_char(state, va_arg(ap, int), width, flags); + ++len; + break; + case 's' : + len += append_string(state, + va_arg(ap, unsigned char*), + width, + prec, + flags); + break; + case 'd' : + case 'i' : { + longest arg; + u_longest num; + int minusp = 0; + + PARSE_INT_FORMAT(arg, ap, signed); + + if (arg < 0) { + minusp = 1; + num = -arg; + } else + num = arg; + + len += append_number (state, num, 10, "0123456789", + width, prec, flags, minusp); break; } - break; - } + case 'u' : { + u_longest arg; - /* parse format specifier */ - if (!*fmt) - break; - switch (*fmt++) { - case 'e': - case 'E': - case 'f': - case 'g': - case 'G': - FVAL = va_arg(ap, double); - sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""), - altform ? "#" : "", fmt[-1]); - if (!haveprec) - precision = 6; - if (precision > MAXPREC) - precision = MAXPREC; - sprintf(xbuf, fbuf, precision, FVAL); - x = xbuf; - len = strlen(x); - break; - - case 'i': - case 'd': /* signed decimal integer */ - if (lflag > 1) - SVAL = va_arg(ap, afs_intmax_t); - else if (lflag) - SVAL = va_arg(ap, long); - else if (hflag) - SVAL = va_arg(ap, int); - else - SVAL = va_arg(ap, int); - UVAL = (SVAL < 0) ? -SVAL : SVAL; - - if (SVAL < 0) - xbuf[0] = '-'; - else if (plsign) - xbuf[0] = '+'; - else if (spsign) - xbuf[0] = ' '; - else - xbuf[0] = 0; - - if (!haveprec) { - if (zfill && !ljust) - precision = width - !!xbuf[0]; - else - precision = 1; - if (precision < 1 + !!xbuf[0]) - precision = 1 + !!xbuf[0]; - } - if (precision > MAXPREC) - precision = MAXPREC; - - mkint(xbuf + 1, UVAL, 10, 0, precision); - x = xbuf + !xbuf[0]; - len = strlen(x); - break; - - - case 'o': /* unsigned octal integer */ - if (lflag > 1) - UVAL = va_arg(ap, afs_uintmax_t); - else if (lflag) - UVAL = va_arg(ap, unsigned long); - else if (hflag) - UVAL = va_arg(ap, unsigned int); - else - UVAL = va_arg(ap, unsigned int); - - xbuf[0] = '0'; - - if (!haveprec) { - if (zfill && !ljust) - precision = width; - else - precision = 1; + PARSE_INT_FORMAT(arg, ap, unsigned); + + len += append_number (state, arg, 10, "0123456789", + width, prec, flags, 0); + break; } - if (precision > MAXPREC) - precision = MAXPREC; - - mkint(xbuf + 1, UVAL, 8, 0, precision); - x = xbuf + (xbuf[1] == '0' || !altform); - len = strlen(x); - break; - - case 'u': /* unsigned decimal integer */ - if (lflag > 1) - UVAL = va_arg(ap, afs_uintmax_t); - else if (lflag) - UVAL = va_arg(ap, unsigned long); - else if (hflag) - UVAL = va_arg(ap, unsigned int); - else - UVAL = va_arg(ap, unsigned int); - - if (!haveprec) { - if (zfill && !ljust) - precision = width; - else - precision = 1; + case 'o' : { + u_longest arg; + + PARSE_INT_FORMAT(arg, ap, unsigned); + + len += append_number (state, arg, 010, "01234567", + width, prec, flags, 0); + break; } - if (precision > MAXPREC) - precision = MAXPREC; + case 'x' : { + u_longest arg; - mkint(xbuf, UVAL, 10, 0, precision); - x = xbuf; - len = strlen(x); - break; + PARSE_INT_FORMAT(arg, ap, unsigned); - case 'p': /* unsigned decimal integer */ - UVAL = va_arg(ap, void *); + len += append_number (state, arg, 0x10, "0123456789abcdef", + width, prec, flags, 0); + break; + } + case 'X' :{ + u_longest arg; - xbuf[0] = '0'; - xbuf[1] = 'x'; + PARSE_INT_FORMAT(arg, ap, unsigned); - if (!haveprec) { - if (zfill && !ljust) - precision = width; - else - precision = 1; + len += append_number (state, arg, 0x10, "0123456789ABCDEF", + width, prec, flags, 0); + break; } - if (precision > MAXPREC) - precision = MAXPREC; - - mkint(xbuf + 2, UVAL, 16, 0, precision); - x = xbuf + ((altform && UVAL) ? 0 : 2); - len = strlen(x); - break; - - case 'x': - case 'X': /* unsigned hexadecimal integer */ - if (lflag > 1) - UVAL = va_arg(ap, afs_uintmax_t); - else if (lflag) - UVAL = va_arg(ap, unsigned long); - else if (hflag) - UVAL = va_arg(ap, unsigned int); - else - UVAL = va_arg(ap, unsigned int); - - xbuf[0] = '0'; - xbuf[1] = 'x'; - - if (!haveprec) { - if (zfill && !ljust) - precision = width; - else - precision = 1; + case 'p' : { +#ifdef AFS_64BITPOINTER_ENV + u_longest arg = (u_longest)va_arg(ap, void*); +#else + u_longest arg = (unsigned long)va_arg(ap, void*); +#endif + len += append_number (state, arg, 0x10, "0123456789ABCDEF", + width, prec, flags, 0); + break; } - if (precision > MAXPREC) - precision = MAXPREC; - - mkint(xbuf + 2, UVAL, 16, 0, precision); - x = xbuf + ((altform && UVAL) ? 0 : 2); - len = strlen(x); - break; - - case '%': /* literal % */ - xbuf[0] = '%'; - xbuf[1] = 0; - x = xbuf; - len = 1; - break; - - case 'c': /* character */ - xbuf[0] = va_arg(ap, int); - xbuf[1] = 0; - x = xbuf; - len = 1; - break; - - case 's': /* string */ - x = va_arg(ap, char *); - if (!x) - x = ""; - len = strlen(x); - if (haveprec && precision < len) - len = precision; - break; - - case 'I': /* IP address: - * value is provided as a network-order unsigned long integer - * precision specifies max hostname length, as for %s - * if precision is explicitly 0, no hostname lookup is done - * if 0fill specified, IPaddr fields are 0-filled to 3 digits - * if spsign specified, IPaddr fields are space-filled to 3 digits - */ - UVAL = va_arg(ap, unsigned long); - ia.s_addr = (unsigned long)UVAL; - if (haveprec && !precision) - he = 0; - else - he = gethostbyaddr((char *)&ia, 4, AF_INET); - if (he) { - x = he->h_name; - len = strlen(x); - if (haveprec && precision < len) - len = precision; - if (altform) { - for (y = x; *y; y++) - if (isupper(*y)) - *y = tolower(*y); - } else if (plsign) { - for (y = x; *y; y++) - if (islower(*y)) - *y = toupper(*y); - } - } else { - UVAL = ntohl((unsigned long)UVAL); - if (zfill) { - x = "%03u.%03u.%03u.%03u"; - } else if (spsign) { - x = "%3u.%3u.%3u.%3u"; - } else { - x = "%u.%u.%u.%u"; - } - /* typecast to whatever '%u' is! */ - sprintf(xbuf, x, (unsigned int)((UVAL & 0xff000000) >> 24), - (unsigned int)((UVAL & 0x00ff0000) >> 16), - (unsigned int)((UVAL & 0x0000ff00) >> 8), - (unsigned int)(UVAL & 0x000000ff)); - x = xbuf; - len = strlen(xbuf); + case 'n' : { + int *arg = va_arg(ap, int*); + *arg = (int)(state->s - state->str); + break; } - break; - - case 'n': /* report count so far */ - if (lflag > 1) { - llcountp = va_arg(ap, afs_intmax_t *); - *llcountp = (afs_intmax_t)count; - } else if (lflag) { - lcountp = va_arg(ap, long *); - *lcountp = (long)count; - } else if (hflag) { - hcountp = va_arg(ap, short *); - *hcountp = (short)count; - } else { - countp = va_arg(ap, int *); - *countp = count; + case 'I' : { + u_longest arg; + + PARSE_INT_FORMAT(arg, ap, unsigned); + + len += append_address (state, (unsigned long)arg, width, prec, flags); + break; + } + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { + double arg = (double)va_arg(ap, double); + + len += append_float (state, c, arg, width, prec, flags); + break; + } + case '\0' : + --format; + /* FALLTHROUGH */ + case '%' : + (*state->append_char)(state, c); + ++len; + break; + default : + (*state->append_char)(state, '%'); + (*state->append_char)(state, c); + len += 2; + break; } - continue; - - default: /* unknown specifier */ - continue; + } else { + (*state->append_char) (state, c); + ++len; } + } + return len; +} - /* render the results */ - if (len > avail) - len = avail; - if (!width) - width = len; - if (width > avail) - width = avail; - j = width - len; - if (j > 0) { - avail -= j; - count += j; - } - if (!ljust) - while (j-- > 0) - *p++ = ' '; +int +afs_vsnprintf (char *str, size_t sz, const char *format, va_list args) +{ + struct snprintf_state state; + int ret; + unsigned char *ustr = (unsigned char *)str; + + state.max_sz = 0; + state.sz = sz; + state.str = ustr; + state.s = ustr; + state.theend = ustr + sz - (sz > 0); + state.append_char = afs_sn_append_char; + + ret = xyzprintf (&state, format, args); + if (state.s != NULL && sz != 0) + *state.s = '\0'; + return ret; +} + +int +afs_snprintf (char *str, size_t sz, const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = afs_vsnprintf (str, sz, format, args); + va_end(args); + +#ifdef PARANOIA + { + int ret2; + unsigned char *tmp; + + tmp = (unsigned char *)malloc (sz); + if (tmp == NULL) + abort (); + + va_start(args, format); + ret2 = afs_vsprintf (tmp, format, args); + va_end(args); + if (ret != ret2 || strcmp(str, tmp)) + abort (); + free (tmp); + } +#endif - strncpy(p, x, len); - avail -= len; - count += len; - p += len; + return ret; +} - if (ljust) - while (j-- > 0) - *p++ = ' '; +int +afs_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args) +{ + int st; + struct snprintf_state state; + + state.max_sz = max_sz; + state.sz = 1; + state.str = (unsigned char *)malloc(state.sz); + if (state.str == NULL) { + *ret = NULL; + return -1; + } + state.s = state.str; + state.theend = state.s + state.sz - 1; + state.append_char = as_append_char; + + st = xyzprintf (&state, format, args); + if (st > state.sz) { + free (state.str); + *ret = NULL; + return -1; + } else { + char *tmp; + + *state.s = '\0'; + tmp = (char *)realloc (state.str, st+1); + if (tmp == NULL) { + free (state.str); + *ret = NULL; + return -1; + } + *ret = tmp; + return st; } - *p = 0; - return count; } int -afs_snprintf(char *p, size_t avail, const char *fmt, ...) +afs_vasprintf (char **ret, const char *format, va_list args) { - va_list ap; - int result; + return afs_vasnprintf (ret, 0, format, args); +} - va_start(ap, fmt); - result = afs_vsnprintf(p, avail, fmt, ap); - va_end(ap); - return result; +int +afs_asprintf (char **ret, const char *format, ...) +{ + va_list args; + int val; + + va_start(args, format); + val = afs_vasprintf (ret, format, args); + va_end(args); + +#ifdef PARANOIA + { + int ret2; + unsigned char *tmp; + tmp = (unsigned char *)malloc (val + 1); + if (tmp == NULL) + abort (); + + va_start(args, format); + ret2 = afs_vsprintf (tmp, format, args); + va_end(args); + if (val != ret2 || strcmp(*ret, tmp)) + abort (); + free (tmp); + } +#endif + + return val; +} + +int +afs_asnprintf (char **ret, size_t max_sz, const char *format, ...) +{ + va_list args; + int val; + + va_start(args, format); + val = afs_vasnprintf (ret, max_sz, format, args); + +#ifdef PARANOIA + { + int ret2; + unsigned char *tmp; + tmp = (unsigned char *)malloc (val + 1); + if (tmp == NULL) + abort (); + + ret2 = afs_vsprintf (tmp, format, args); + if (val != ret2 || strcmp(*ret, tmp)) + abort (); + free (tmp); + } +#endif + + va_end(args); + return val; } -#if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_VSNPRINTF) +#if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF) #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV) int @@ -550,7 +963,7 @@ void vsyslog(int priority, const char *format, va_list args) { char buf[1024]; - vsnprintf(buf, sizeof(buf), format, args); + afs_vsnprintf(buf, sizeof(buf), format, args); syslog(priority, "%s", buf); } #endif diff --git a/src/util/test/NTMakefile b/src/util/test/NTMakefile index 2c0d0eb..c393c95 100644 --- a/src/util/test/NTMakefile +++ b/src/util/test/NTMakefile @@ -14,14 +14,20 @@ LIBS =\ $(DESTDIR)\lib\afs\afsreg.lib $(OUT)\dirpath_test.exe: $(OUT)\dirpath_test.obj $(LIBS) - $(EXECONLINK) + $(EXECONLINK) shell32.lib $(_VC_MANIFEST_EMBED_EXE) $(CODESIGN_USERLAND) -test tests: $(OUT)\dirpath_test.exe +$(OUT)\snprintf_test.exe: $(OUT)\snprintf_test.obj $(LIBS) + $(EXECONLINK) shell32.lib + $(_VC_MANIFEST_EMBED_EXE) + $(CODESIGN_USERLAND) + +test tests: $(OUT)\dirpath_test.exe $(OUT)\snprintf_test.exe clean:: - $(DEL) $(OUT)\dirpath_test.exe + $(DEL) $(OUT)\dirpath_test.* + $(DEL) $(OUT)\snprintf_test.* mkdir: diff --git a/src/util/test/snprintf_test.c b/src/util/test/snprintf_test.c new file mode 100644 index 0000000..0b82e55 --- /dev/null +++ b/src/util/test/snprintf_test.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2000 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * 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. Neither the name of KTH nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS 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 KTH 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 + +static int +try (const char *format, ...) +{ + int ret; + va_list ap; + char buf1[256], buf2[256]; + + va_start (ap, format); + ret = afs_vsnprintf (buf1, sizeof(buf1), format, ap); + if (ret >= sizeof(buf1)) { + fprintf(stderr, "increase buf and try again\n"); + exit(1); + } + va_end (ap); + va_start (ap, format); + vsprintf (buf2, format, ap); + ret = strcmp (buf1, buf2); + if (ret) + printf ("failed: format = \"%s\", \"%s\" != \"%s\"\n", + format, buf1, buf2); + va_end (ap); + return ret; +} + +static int +cmp_with_sprintf_int (void) +{ + int tot = 0; + int int_values[] = {INT_MIN, -17, -1, 0, 1, 17, 4711, 65535, INT_MAX}; + int i; + + for (i = 0; i < sizeof(int_values) / sizeof(int_values[0]); ++i) { + tot += try ("%d", int_values[i]); + tot += try ("%x", int_values[i]); + tot += try ("%X", int_values[i]); + tot += try ("%o", int_values[i]); + tot += try ("%#x", int_values[i]); + tot += try ("%#X", int_values[i]); + tot += try ("%#o", int_values[i]); + tot += try ("%10d", int_values[i]); + tot += try ("%10x", int_values[i]); + tot += try ("%10X", int_values[i]); + tot += try ("%10o", int_values[i]); + tot += try ("%#10x", int_values[i]); + tot += try ("%#10X", int_values[i]); + tot += try ("%#10o", int_values[i]); + tot += try ("%-10d", int_values[i]); + tot += try ("%-10x", int_values[i]); + tot += try ("%-10X", int_values[i]); + tot += try ("%-10o", int_values[i]); + tot += try ("%-#10x", int_values[i]); + tot += try ("%-#10X", int_values[i]); + tot += try ("%-#10o", int_values[i]); + } + return tot; +} + +static int +cmp_with_sprintf_long (void) +{ + int tot = 0; + long long_values[] = {LONG_MIN, -17, -1, 0, 1, 17, 4711, 65535, LONG_MAX}; + int i; + + for (i = 0; i < sizeof(long_values) / sizeof(long_values[0]); ++i) { + tot += try ("%ld", long_values[i]); + tot += try ("%lx", long_values[i]); + tot += try ("%lX", long_values[i]); + tot += try ("%lo", long_values[i]); + tot += try ("%#lx", long_values[i]); + tot += try ("%#lX", long_values[i]); + tot += try ("%#lo", long_values[i]); + tot += try ("%10ld", long_values[i]); + tot += try ("%10lx", long_values[i]); + tot += try ("%10lX", long_values[i]); + tot += try ("%10lo", long_values[i]); + tot += try ("%#10lx", long_values[i]); + tot += try ("%#10lX", long_values[i]); + tot += try ("%#10lo", long_values[i]); + tot += try ("%-10ld", long_values[i]); + tot += try ("%-10lx", long_values[i]); + tot += try ("%-10lX", long_values[i]); + tot += try ("%-10lo", long_values[i]); + tot += try ("%-#10lx", long_values[i]); + tot += try ("%-#10lX", long_values[i]); + tot += try ("%-#10lo", long_values[i]); + } + return tot; +} + +#ifdef HAVE_LONG_LONG + +/* XXX doesn't work as expected on lp64 platforms with sizeof(long + * long) == sizeof(long) */ + +static int +cmp_with_sprintf_long_long (void) +{ + int tot = 0; + long long long_long_values[] = { + ((long long)LONG_MIN) -1, LONG_MIN, -17, -1, + 0, + 1, 17, 4711, 65535, LONG_MAX, ((long long)LONG_MAX) + 1}; + int i; + + for (i = 0; i < sizeof(long_long_values) / sizeof(long_long_values[0]); ++i) { + tot += try ("%lld", long_long_values[i]); + tot += try ("%llx", long_long_values[i]); + tot += try ("%llX", long_long_values[i]); + tot += try ("%llo", long_long_values[i]); + tot += try ("%#llx", long_long_values[i]); + tot += try ("%#llX", long_long_values[i]); + tot += try ("%#llo", long_long_values[i]); + tot += try ("%10lld", long_long_values[i]); + tot += try ("%10llx", long_long_values[i]); + tot += try ("%10llX", long_long_values[i]); + tot += try ("%10llo", long_long_values[i]); + tot += try ("%#10llx", long_long_values[i]); + tot += try ("%#10llX", long_long_values[i]); + tot += try ("%#10llo", long_long_values[i]); + tot += try ("%-10lld", long_long_values[i]); + tot += try ("%-10llx", long_long_values[i]); + tot += try ("%-10llX", long_long_values[i]); + tot += try ("%-10llo", long_long_values[i]); + tot += try ("%-#10llx", long_long_values[i]); + tot += try ("%-#10llX", long_long_values[i]); + tot += try ("%-#10llo", long_long_values[i]); + } + return tot; +} + +#endif + +#if defined(AFS_64BIT_ENV) && defined(AFS_NT40_ENV) + +static int +cmp_with_sprintf_I64 (void) +{ + int tot = 0; + __int64 int64_values[] = { + ((__int64)LONG_MIN) -1, LONG_MIN, -17, -1, + 0, + 1, 17, 4711, 65535, LONG_MAX, ((__int64)LONG_MAX) + 1}; + int i; + + for (i = 0; i < sizeof(int64_values) / sizeof(int64_values[0]); ++i) { + tot += try ("%I64d", int64_values[i]); + tot += try ("%I64x", int64_values[i]); + tot += try ("%I64X", int64_values[i]); + tot += try ("%I64o", int64_values[i]); + tot += try ("%#I64x", int64_values[i]); + tot += try ("%#I64X", int64_values[i]); + tot += try ("%#I64o", int64_values[i]); + tot += try ("%10I64d", int64_values[i]); + tot += try ("%10I64x", int64_values[i]); + tot += try ("%10I64X", int64_values[i]); + tot += try ("%10I64o", int64_values[i]); + tot += try ("%#10I64x", int64_values[i]); + tot += try ("%#10I64X", int64_values[i]); + tot += try ("%#10I64o", int64_values[i]); + tot += try ("%-10I64d", int64_values[i]); + tot += try ("%-10I64x", int64_values[i]); + tot += try ("%-10I64X", int64_values[i]); + tot += try ("%-10I64o", int64_values[i]); + tot += try ("%-#10I64x", int64_values[i]); + tot += try ("%-#10I64X", int64_values[i]); + tot += try ("%-#10I64o", int64_values[i]); + } + return tot; +} + +#endif + +static int +cmp_with_sprintf_float (void) +{ + int tot = 0; + double double_values[] = {-99999, -999, -17.4, -4.3, -3.0, -1.5, -1, + 0, 0.1, 0.2342374852, 0.2340007, + 3.1415926, 14.7845, 34.24758, 9999, 9999999}; + int i; + + for (i = 0; i < sizeof(double_values) / sizeof(double_values[0]); ++i) { + tot += try ("%f", double_values[i]); + tot += try ("%10f", double_values[i]); + tot += try ("%.2f", double_values[i]); + tot += try ("%7.0f", double_values[i]); + tot += try ("%5.2f", double_values[i]); + tot += try ("%0f", double_values[i]); + tot += try ("%#f", double_values[i]); + tot += try ("%e", double_values[i]); + tot += try ("%10e", double_values[i]); + tot += try ("%.2e", double_values[i]); + tot += try ("%7.0e", double_values[i]); + tot += try ("%5.2e", double_values[i]); + tot += try ("%0e", double_values[i]); + tot += try ("%#e", double_values[i]); + tot += try ("%E", double_values[i]); + tot += try ("%10E", double_values[i]); + tot += try ("%.2E", double_values[i]); + tot += try ("%7.0E", double_values[i]); + tot += try ("%5.2E", double_values[i]); + tot += try ("%0E", double_values[i]); + tot += try ("%#E", double_values[i]); + tot += try ("%g", double_values[i]); + tot += try ("%10g", double_values[i]); + tot += try ("%.2g", double_values[i]); + tot += try ("%7.0g", double_values[i]); + tot += try ("%5.2g", double_values[i]); + tot += try ("%0g", double_values[i]); + tot += try ("%#g", double_values[i]); + tot += try ("%G", double_values[i]); + tot += try ("%10G", double_values[i]); + tot += try ("%.2G", double_values[i]); + tot += try ("%7.0G", double_values[i]); + tot += try ("%5.2G", double_values[i]); + tot += try ("%0G", double_values[i]); + tot += try ("%#G", double_values[i]); + } + return tot; +} + +static int +test_null (void) +{ + return afs_snprintf (NULL, 0, "foo") != 3; +} + +static int +test_sizet (void) +{ + int tot = 0; + size_t sizet_values[] = { 0, 1, 2, 200, 4294967295u +#ifdef _WIN64 + ,0xffffffffffffffffui64 +#endif + }; /* SIZE_MAX */ + char *result[] = { "0", "1", "2", "200", "4294967295" +#ifdef _WIN64 + ,"18446744073709551615" +#endif + }; + int i; + + for (i = 0; i < sizeof(sizet_values) / sizeof(sizet_values[0]); ++i) { +#if 0 + tot += try("%zu", sizet_values[i]); + tot += try("%zx", sizet_values[i]); + tot += try("%zX", sizet_values[i]); +#else + char buf[256]; + afs_snprintf(buf, sizeof(buf), "%zu", sizet_values[i]); + if (strcmp(buf, result[i]) != 0) { + printf("%s != %s", buf, result[i]); + tot++; + } +#endif + } + return tot; +} + +static int +test_ptr (void) +{ + int tot = 0; + void * ptr; + + if (sizeof(ptr) == 4) { + ptr = (void *)0x12345678; + tot += try ("%p", ptr); + tot += try ("%#p", ptr); + tot += try ("%4p", ptr); + tot += try ("%#4p", ptr); + tot += try ("%-4p", ptr); + tot += try ("%-#4p", ptr); + } else if (sizeof(ptr) == 8) { + ptr = (void *)0x0102030405060708; + tot += try ("%p", ptr); + tot += try ("%#p", ptr); + tot += try ("%8p", ptr); + tot += try ("%#8p", ptr); + tot += try ("%-8p", ptr); + tot += try ("%-#8p", ptr); + } + + + return tot; +} + +static int +test_ipaddr (void) +{ + int tot = 0; + struct hostent * he = gethostbyname("www.openafs.org"); + char buf[256]; + unsigned long addr; + + if (!he) { + fprintf(stderr, "gethostbyname failure\n"); + tot = 1; + } + addr = *((unsigned long *)he->h_addr); + afs_snprintf(buf, sizeof(buf), "%I", addr); + if (strcmp(buf, "OPENAFS.ORG")) { + fprintf(stderr, "%s != %s\n", buf, "OPENAFS.ORG"); + tot++; + } + afs_snprintf(buf, sizeof(buf), "%+I", addr); + if (strcmp(buf, "OPENAFS.ORG")) { + fprintf(stderr, "%s != %s\n", buf, "OPENAFS.ORG"); + tot++; + } + afs_snprintf(buf, sizeof(buf), "%#I", addr); + if (strcmp(buf, "openafs.org")) { + fprintf(stderr, "%s != %s\n", buf, "openafs.org"); + tot++; + } + afs_snprintf(buf, sizeof(buf), "%.7I", addr); + if (strcmp(buf, "OPENAFS")) { + fprintf(stderr, "%s != %s\n", buf, "OPENAFS"); + tot++; + } + afs_snprintf(buf, sizeof(buf), "%.0I", addr); + if (strcmp(buf, "128.2.200.90")) { + fprintf(stderr, "%s != %s\n", buf, "128.2.200.90"); + tot++; + } + afs_snprintf(buf, sizeof(buf), "%0.0I", addr); + if (strcmp(buf, "128.002.200.090")) { + fprintf(stderr, "%s != %s\n", buf, "128.002.200.090"); + tot++; + } + afs_snprintf(buf, sizeof(buf), "% .0I", addr); + if (strcmp(buf, "128. 2.200. 90")) { + fprintf(stderr, "%s != %s\n", buf, "128. 2.200. 90"); + tot++; + } + + return tot; +} + + +int +main (int argc, char **argv) +{ + int ret = 0; + +#ifdef AFS_NT40_ENV + afs_winsockInit(); +#endif + + ret += cmp_with_sprintf_int (); + ret += cmp_with_sprintf_long (); +#ifdef HAVE_LONG_LONG + ret += cmp_with_sprintf_long_long (); +#endif +#if defined(AFS_64BIT_ENV) && defined(AFS_NT40_ENV) + ret += cmp_with_sprintf_I64 (); +#endif + ret += cmp_with_sprintf_float (); + ret += test_null (); + ret += test_sizet (); + ret += test_ptr (); + ret += test_ipaddr (); + +#ifdef AFS_NT40_ENV + afs_winsockCleanup(); +#endif + + return ret; +}