From f918914d50a051446390e70eda732768e1151cff Mon Sep 17 00:00:00 2001 From: Jeffrey Hutzelman Date: Sun, 11 Feb 2001 15:05:08 +0000 Subject: [PATCH] include-snprintf-in-util-20010211 For platforms which need snprintf, this should be suitable for AC_REPLACE_FUNCS when we get that far --- src/util/snprintf.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 src/util/snprintf.c diff --git a/src/util/snprintf.c b/src/util/snprintf.c new file mode 100644 index 0000000..bad2699 --- /dev/null +++ b/src/util/snprintf.c @@ -0,0 +1,383 @@ +/* snprintf.c - Formatted, length-limited print to a string */ + +#include +#include +#include +#include +#include +#include + +#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, unsigned long val, int base, int uc, int prec) +{ + int len = 0, dig, i; + + while (val) { + dig = 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: + * + * - Actually obeys the length limit, which (unfortunately) many + * implementations of snprintf do not. + * + * - Supports all the standard format specifiers for integers + * (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. + * + * - Supports all the standard flags (-, 0, +, space, #). These + * flags are ignored if used when they are not appropriate. + * + * - Supports the standard size modifiers for short (h), long (h), + * and double (L) arguments. These modifiers are ignored if used + * when they are not appropriate. + * + * - Supports minimum field width and precision, where appropriate, + * including the use of '*' to specify a value given as an argument + * 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. + * + * - Floating-point specifier (%e, %f, %g) are implemented by + * calling the standard sprintf, and thus may be unsafe. + * + * - The '%...$' notation is used primarily when the format string + * 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. + * + * The custom format specifier '%I' is supported. This specifier + * takes as its argument an unsigned long integer containing an + * IPv4 address in network byte order. The address is rendered + * either as a hostname or as a dotted quad, as follows: + * + * - If precision is nonzero or unspecified, a hostname lookup + * is attempted; if it is successful, the hostname is printed. + * If the hostname lookup fails, the address is printed in + * dotted-quad notation. + * + * - If precision is explicitly specified as 0, then the hostname + * lookup is skipped, and dotted-quad notation is always used. + * + * - If a hostname is to be printed: + * + The precision controls the maximum number of characters + * printed, as with %s. + * + If the '#' flag is specified, any letters in the hostname + * will be forced to lower case before printing. + * + If the '+' flag is specified, any letters in the hostname + * will be forced to upper case before printing. If both + * '#' and '+' are given, the '+' flag will be ignored. + * + The '0' and ' ' flags have no effect. + * + * - If a dotted quad is to be printed: + * + The precision has no effect; dotted quads are always + * 7 to 12 characters in length, depending on the value + * to be printed and the format flags used. + * + If the '0' flag is given, each field (byte) of the address + * will be padded with '0' on the left to three digits. + * + If the ' ' flag is given, each field (byte) of the address + * 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. + */ +static void vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap) +{ + unsigned int width, precision, haveprec, 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; + unsigned long UVAL; + long SVAL, *lcountp; + double FVAL; + short *hcountp; + + count = 0; + avail--; + while (*fmt && avail) { + if (*fmt != '%') { + *p++ = *fmt++; + avail--; + count++; + continue; + } + + /** 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; + } + break; + } + + /* parse minimum width */ + if (*fmt == '*') { + width = va_arg(ap, int); + fmt++; + } else while (isdigit(*fmt)) { + width = (width * 10) + (*fmt - '0'); + fmt++; + } + + /* 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++; + } + } + + /* parse size flags */ + while (*fmt) { + switch (*fmt) { + case 'h': hflag = 1; fmt++; continue; /* short argument */ + case 'l': lflag = 1; fmt++; continue; /* long argument */ + default: break; + } + break; + } + + /* 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) SVAL = va_arg(ap, long); + else if (hflag) SVAL = va_arg(ap, short); + 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) UVAL = va_arg(ap, unsigned long); + else if (hflag) UVAL = va_arg(ap, unsigned short); + else UVAL = va_arg(ap, unsigned int); + + xbuf[0] = '0'; + + if (!haveprec) { + if (zfill && !ljust) precision = width; + else precision = 1; + } + 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) UVAL = va_arg(ap, unsigned long); + else if (hflag) UVAL = va_arg(ap, unsigned short); + else UVAL = va_arg(ap, unsigned int); + + if (!haveprec) { + if (zfill && !ljust) precision = width; + else precision = 1; + } + if (precision > MAXPREC) precision = MAXPREC; + + mkint(xbuf, UVAL, 10, 0, precision); + x = xbuf; + len = strlen(x); + break; + + case 'x': + case 'X': /* unsigned hexadecimal integer */ + if (lflag) UVAL = va_arg(ap, unsigned long); + else if (hflag) UVAL = va_arg(ap, unsigned short); + else UVAL = va_arg(ap, unsigned int); + + xbuf[0] = '0'; + xbuf[1] = 'x'; + + if (!haveprec) { + if (zfill && !ljust) precision = width; + else precision = 1; + } + 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 = 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(UVAL); + if (zfill) x = "%03u.%03u.%03u.%03u"; + else if (spsign) x = "%3u.%3u.%3u.%3u"; + else x = "%u.%u.%u.%u"; + sprintf(xbuf, x, + (UVAL & 0xff000000) >> 24, (UVAL & 0x00ff0000) >> 16, + (UVAL & 0x0000ff00) >> 8, (UVAL & 0x000000ff)); + x = xbuf; + len = strlen(xbuf); + } + break; + + case 'n': /* report count so far */ + if (lflag) { + lcountp = va_arg(ap, long *); + *lcountp = count; + } else if (hflag) { + hcountp = va_arg(ap, short *); + *hcountp = count; + } else { + countp = va_arg(ap, int *); + *countp = count; + } + continue; + + default: /* unknown specifier */ + continue; + } + + /* 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++ = ' '; + + strncpy(p, x, len); + avail -= len; + count += len; + p += len; + + if (ljust) while (j-- > 0) *p++ = ' '; + } + *p = 0; +} + + +void snprintf(char *p, unsigned int avail, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsnprintf(p, avail, fmt, ap); + va_end(ap); +} -- 1.9.4