1 /* snprintf.c - Formatted, length-limited print to a string */
12 #include <netinet/in.h>
14 #if defined(AFS_AIX32_ENV) || defined(AFS_SUN_ENV) || defined(AFS_XBSD_ENV)
15 #include <sys/socket.h>
27 /* Generate an ASCII representation of an integer <val>, as follows:
28 * <base> indicates the base to be used (2-36)
29 * <uc> is nonzero if letter digits should be uppercase
30 * <prec> is the minimum number of digits
31 * The resulting number is stored in <buf>, which must be long enough
32 * to receive it. The minimum length is <prec> or ceil(log{base}(val)),
33 * whichever is larger, plus room for a trailing NUL.
35 static void mkint(char *buf, afs_uintmax_t val,
36 int base, int uc, unsigned prec)
42 val = (val - dig) / base;
43 if (dig < 10) dig = dig + '0';
44 else if (uc) dig = dig + 'A' - 10;
45 else dig = dig + 'a' - 10;
48 while (len < prec) buf[len++] = '0';
49 for (i = 0; i < (len + 1) / 2; i++) {
51 buf[i] = buf[len - i - 1];
52 buf[len - i - 1] = dig;
58 /* This function is a mostly-complete implementation of snprintf,
59 * with the following features:
61 * - Actually obeys the length limit, which (unfortunately) many
62 * implementations of snprintf do not.
64 * - Supports all the standard format specifiers for integers
65 * (d, i, o, u, x, X), floating-point values (f, e, E, g, G),
66 * and strings and characters (c, s, %), plus a few unusual
67 * but useful ones described below.
69 * - Supports all the standard flags (-, 0, +, space, #). These
70 * flags are ignored if used when they are not appropriate.
72 * - Supports the standard size modifiers for short (h), long (h),
73 * and double (L) arguments. These modifiers are ignored if used
74 * when they are not appropriate.
76 * - Supports minimum field width and precision, where appropriate,
77 * including the use of '*' to specify a value given as an argument
78 * instead of in the format string. There is a maximum precision
81 * - At present, the 'p' specifier for printing pointers is not
82 * implemented, because it is inherently non-portable and thus
83 * can be implemented correctly only by the compiler's run-time
86 * - Floating-point specifier (%e, %f, %g) are implemented by
87 * calling the standard sprintf, and thus may be unsafe.
89 * - The '%...$' notation is used primarily when the format string
90 * is specified by the user, who knows but cannot change the order
91 * of the arguments. Such usage is inherently dangerous and
92 * insecure; thus, it is not supported.
94 * The custom format specifier '%I' is supported. This specifier
95 * takes as its argument an unsigned long integer containing an
96 * IPv4 address in network byte order. The address is rendered
97 * either as a hostname or as a dotted quad, as follows:
99 * - If precision is nonzero or unspecified, a hostname lookup
100 * is attempted; if it is successful, the hostname is printed.
101 * If the hostname lookup fails, the address is printed in
102 * dotted-quad notation.
104 * - If precision is explicitly specified as 0, then the hostname
105 * lookup is skipped, and dotted-quad notation is always used.
107 * - If a hostname is to be printed:
108 * + The precision controls the maximum number of characters
109 * printed, as with %s.
110 * + If the '#' flag is specified, any letters in the hostname
111 * will be forced to lower case before printing.
112 * + If the '+' flag is specified, any letters in the hostname
113 * will be forced to upper case before printing. If both
114 * '#' and '+' are given, the '+' flag will be ignored.
115 * + The '0' and ' ' flags have no effect.
117 * - If a dotted quad is to be printed:
118 * + The precision has no effect; dotted quads are always
119 * 7 to 12 characters in length, depending on the value
120 * to be printed and the format flags used.
121 * + If the '0' flag is given, each field (byte) of the address
122 * will be padded with '0' on the left to three digits.
123 * + If the ' ' flag is given, each field (byte) of the address
124 * will be padded with spaces on the left to three digits. If
125 * both '0' and ' ' are given, the ' ' flag will be ignored.
126 * + The '#' and '+' flags have no effect.
128 int afs_vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
130 unsigned int width, precision, haveprec, len;
131 int ljust, plsign, spsign, altform, zfill;
132 int hflag, lflag, count, *countp, j;
133 char *x, *y, xbuf[MAXPREC + 21], fbuf[20];
138 afs_intmax_t* llcountp;
145 while (*fmt && avail) {
153 /** Found a format specifier **/
154 ljust = plsign = spsign = altform = zfill = 0;
155 width = precision = haveprec = 0;
159 /* parse format flags */
162 case '-': ljust = 1; fmt++; continue; /* left justify */
163 case '+': plsign = 1; fmt++; continue; /* use + or - */
164 case ' ': spsign = 1; fmt++; continue; /* use space or - */
165 case '#': altform = 1; fmt++; continue; /* alternate form */
166 case '0': zfill = 1; fmt++; continue; /* pad with 0 */
172 /* parse minimum width */
174 width = va_arg(ap, int);
176 } else while (isdigit(*fmt)) {
177 width = (width * 10) + (*fmt - '0');
181 /* parse precision */
186 precision = va_arg(ap, int);
188 } else while (isdigit(*fmt)) {
189 precision = (precision * 10) + (*fmt - '0');
194 /* parse size flags */
197 case 'h': hflag = 1; fmt++; continue; /* short argument */
198 case 'l': lflag += 1; fmt++; continue; /* long argument */
199 case 'L': lflag = 2; fmt++; continue; /* long long argument */
205 /* parse format specifier */
213 FVAL = va_arg(ap, double);
214 sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""),
215 altform ? "#" : "", fmt[-1]);
216 if (!haveprec) precision = 6;
217 if (precision > MAXPREC) precision = MAXPREC;
218 sprintf(xbuf, fbuf, precision, FVAL);
224 case 'd': /* signed decimal integer */
225 if (lflag > 1) SVAL = va_arg(ap, afs_intmax_t);
226 else if (lflag) SVAL = va_arg(ap, long);
227 else if (hflag) SVAL = va_arg(ap, int);
228 else SVAL = va_arg(ap, int);
229 UVAL = (SVAL < 0) ? -SVAL : SVAL;
231 if (SVAL < 0) xbuf[0] = '-';
232 else if (plsign) xbuf[0] = '+';
233 else if (spsign) xbuf[0] = ' ';
237 if (zfill && !ljust) precision = width - !!xbuf[0];
239 if (precision < 1 + !!xbuf[0]) precision = 1 + !!xbuf[0];
241 if (precision > MAXPREC) precision = MAXPREC;
243 mkint(xbuf + 1, UVAL, 10, 0, precision);
249 case 'o': /* unsigned octal integer */
250 if (lflag > 1) UVAL = va_arg(ap, afs_uintmax_t);
251 else if (lflag) UVAL = va_arg(ap, unsigned long);
252 else if (hflag) UVAL = va_arg(ap, unsigned int);
253 else UVAL = va_arg(ap, unsigned int);
258 if (zfill && !ljust) precision = width;
261 if (precision > MAXPREC) precision = MAXPREC;
263 mkint(xbuf + 1, UVAL, 8, 0, precision);
264 x = xbuf + (xbuf[1] == '0' || !altform);
268 case 'u': /* unsigned decimal integer */
269 if (lflag > 1) UVAL = va_arg(ap, afs_uintmax_t);
270 else if (lflag) UVAL = va_arg(ap, unsigned long);
271 else if (hflag) UVAL = va_arg(ap, unsigned int);
272 else UVAL = va_arg(ap, unsigned int);
275 if (zfill && !ljust) precision = width;
278 if (precision > MAXPREC) precision = MAXPREC;
280 mkint(xbuf, UVAL, 10, 0, precision);
286 case 'X': /* unsigned hexadecimal integer */
287 if (lflag > 1) UVAL = va_arg(ap, afs_uintmax_t);
288 else if (lflag) UVAL = va_arg(ap, unsigned long);
289 else if (hflag) UVAL = va_arg(ap, unsigned int);
290 else UVAL = va_arg(ap, unsigned int);
296 if (zfill && !ljust) precision = width;
299 if (precision > MAXPREC) precision = MAXPREC;
301 mkint(xbuf + 2, UVAL, 16, 0, precision);
302 x = xbuf + ((altform && UVAL) ? 0 : 2);
306 case '%': /* literal % */
313 case 'c': /* character */
314 xbuf[0] = va_arg(ap, int);
320 case 's': /* string */
321 x = va_arg(ap, char *);
322 if (!x) x = "<null>";
324 if (haveprec && precision < len) len = precision;
327 case 'I': /* IP address:
328 * value is provided as a network-order unsigned long integer
329 * precision specifies max hostname length, as for %s
330 * if precision is explicitly 0, no hostname lookup is done
331 * if 0fill specified, IPaddr fields are 0-filled to 3 digits
332 * if spsign specified, IPaddr fields are space-filled to 3 digits
334 UVAL = va_arg(ap, unsigned long);
336 if (haveprec && !precision) he = 0;
337 else he = gethostbyaddr((char *)&ia, 4, AF_INET);
341 if (haveprec && precision < len) len = precision;
343 for (y = x; *y; y++) if (isupper(*y)) *y = tolower(*y); }
345 for (y = x; *y; y++) if (islower(*y)) *y = toupper(*y);
349 if (zfill) { x = "%03u.%03u.%03u.%03u"; }
350 else if (spsign) { x = "%3u.%3u.%3u.%3u"; }
351 else { x = "%u.%u.%u.%u"; }
353 (UVAL & 0xff000000) >> 24, (UVAL & 0x00ff0000) >> 16,
354 (UVAL & 0x0000ff00) >> 8, (UVAL & 0x000000ff));
360 case 'n': /* report count so far */
362 llcountp = va_arg(ap, afs_intmax_t *);
363 *llcountp = (long long) count;
365 lcountp = va_arg(ap, long *);
366 *lcountp = (long) count;
368 hcountp = va_arg(ap, short *);
369 *hcountp = (short) count;
371 countp = va_arg(ap, int *);
376 default: /* unknown specifier */
380 /* render the results */
381 if (len > avail) len = avail;
382 if (!width) width = len;
383 if (width > avail) width = avail;
390 if (!ljust) while (j-- > 0) *p++ = ' ';
397 if (ljust) while (j-- > 0) *p++ = ' ';
403 int afs_snprintf(char *p, size_t avail, const char *fmt, ...)
409 result = afs_vsnprintf(p, avail, fmt, ap);
414 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_VSNPRINTF)
417 int vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
419 void vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap)
423 result = afs_vsnprintf(p, avail, fmt, ap);
428 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
430 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_SNPRINTF)
433 int snprintf(char *p, size_t avail, const char *fmt, ...)
435 void snprintf(char *p, unsigned int avail, char *fmt, ...)
442 result = afs_vsnprintf(p, avail, fmt, ap);
448 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */