1 /* snprintf.c - Formatted, length-limited print to a string */
15 #include <netinet/in.h>
23 #if defined(AFS_AIX32_ENV) || defined(AFS_SUN_ENV) || defined(AFS_XBSD_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SGI65_ENV)
24 #include <sys/socket.h>
29 /* Generate an ASCII representation of an integer <val>, as follows:
30 * <base> indicates the base to be used (2-36)
31 * <uc> is nonzero if letter digits should be uppercase
32 * <prec> is the minimum number of digits
33 * The resulting number is stored in <buf>, which must be long enough
34 * to receive it. The minimum length is <prec> or ceil(log{base}(val)),
35 * whichever is larger, plus room for a trailing NUL.
38 mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec)
43 dig = (int) (val % base);
44 val = (val - dig) / base;
55 for (i = 0; i < (len + 1) / 2; i++) {
57 buf[i] = buf[len - i - 1];
58 buf[len - i - 1] = dig;
64 /* This function is a mostly-complete implementation of snprintf,
65 * with the following features:
67 * - Actually obeys the length limit, which (unfortunately) many
68 * implementations of snprintf do not.
70 * - Supports all the standard format specifiers for integers
71 * (d, i, o, u, x, X), floating-point values (f, e, E, g, G),
72 * and strings and characters (c, s, %), plus a few unusual
73 * but useful ones described below.
75 * - Supports all the standard flags (-, 0, +, space, #). These
76 * flags are ignored if used when they are not appropriate.
78 * - Supports the standard size modifiers for short (h), long (h),
79 * and double (L) arguments. These modifiers are ignored if used
80 * when they are not appropriate.
82 * - Supports minimum field width and precision, where appropriate,
83 * including the use of '*' to specify a value given as an argument
84 * instead of in the format string. There is a maximum precision
87 * - At present, the 'p' specifier for printing pointers is not
88 * implemented, because it is inherently non-portable and thus
89 * can be implemented correctly only by the compiler's run-time
92 * - Floating-point specifier (%e, %f, %g) are implemented by
93 * calling the standard sprintf, and thus may be unsafe.
95 * - The '%...$' notation is used primarily when the format string
96 * is specified by the user, who knows but cannot change the order
97 * of the arguments. Such usage is inherently dangerous and
98 * insecure; thus, it is not supported.
100 * The custom format specifier '%I' is supported. This specifier
101 * takes as its argument an unsigned long integer containing an
102 * IPv4 address in network byte order. The address is rendered
103 * either as a hostname or as a dotted quad, as follows:
105 * - If precision is nonzero or unspecified, a hostname lookup
106 * is attempted; if it is successful, the hostname is printed.
107 * If the hostname lookup fails, the address is printed in
108 * dotted-quad notation.
110 * - If precision is explicitly specified as 0, then the hostname
111 * lookup is skipped, and dotted-quad notation is always used.
113 * - If a hostname is to be printed:
114 * + The precision controls the maximum number of characters
115 * printed, as with %s.
116 * + If the '#' flag is specified, any letters in the hostname
117 * will be forced to lower case before printing.
118 * + If the '+' flag is specified, any letters in the hostname
119 * will be forced to upper case before printing. If both
120 * '#' and '+' are given, the '+' flag will be ignored.
121 * + The '0' and ' ' flags have no effect.
123 * - If a dotted quad is to be printed:
124 * + The precision has no effect; dotted quads are always
125 * 7 to 12 characters in length, depending on the value
126 * to be printed and the format flags used.
127 * + If the '0' flag is given, each field (byte) of the address
128 * will be padded with '0' on the left to three digits.
129 * + If the ' ' flag is given, each field (byte) of the address
130 * will be padded with spaces on the left to three digits. If
131 * both '0' and ' ' are given, the ' ' flag will be ignored.
132 * + The '#' and '+' flags have no effect.
135 afs_vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
137 unsigned int width, precision, haveprec;
139 int ljust, plsign, spsign, altform, zfill;
140 int hflag, lflag, count, *countp, j;
141 char *x, *y, xbuf[MAXPREC + 21], fbuf[20];
146 afs_intmax_t *llcountp;
153 while (*fmt && avail) {
161 /** Found a format specifier **/
162 ljust = plsign = spsign = altform = zfill = 0;
163 width = precision = haveprec = 0;
167 /* parse format flags */
173 continue; /* left justify */
177 continue; /* use + or - */
181 continue; /* use space or - */
185 continue; /* alternate form */
189 continue; /* pad with 0 */
196 /* parse minimum width */
198 width = va_arg(ap, int);
201 while (isdigit(*fmt)) {
202 width = (width * 10) + (*fmt - '0');
206 /* parse precision */
211 precision = va_arg(ap, int);
214 while (isdigit(*fmt)) {
215 precision = (precision * 10) + (*fmt - '0');
220 /* parse size flags */
226 continue; /* short argument */
230 continue; /* long argument */
234 continue; /* long long argument */
241 /* parse format specifier */
250 FVAL = va_arg(ap, double);
251 sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""),
252 altform ? "#" : "", fmt[-1]);
255 if (precision > MAXPREC)
257 sprintf(xbuf, fbuf, precision, FVAL);
263 case 'd': /* signed decimal integer */
265 SVAL = va_arg(ap, afs_intmax_t);
267 SVAL = va_arg(ap, long);
269 SVAL = va_arg(ap, int);
271 SVAL = va_arg(ap, int);
272 UVAL = (SVAL < 0) ? -SVAL : SVAL;
285 precision = width - !!xbuf[0];
288 if (precision < 1 + !!xbuf[0])
289 precision = 1 + !!xbuf[0];
291 if (precision > MAXPREC)
294 mkint(xbuf + 1, UVAL, 10, 0, precision);
300 case 'o': /* unsigned octal integer */
302 UVAL = va_arg(ap, afs_uintmax_t);
304 UVAL = va_arg(ap, unsigned long);
306 UVAL = va_arg(ap, unsigned int);
308 UVAL = va_arg(ap, unsigned int);
318 if (precision > MAXPREC)
321 mkint(xbuf + 1, UVAL, 8, 0, precision);
322 x = xbuf + (xbuf[1] == '0' || !altform);
326 case 'u': /* unsigned decimal integer */
328 UVAL = va_arg(ap, afs_uintmax_t);
330 UVAL = va_arg(ap, unsigned long);
332 UVAL = va_arg(ap, unsigned int);
334 UVAL = va_arg(ap, unsigned int);
342 if (precision > MAXPREC)
345 mkint(xbuf, UVAL, 10, 0, precision);
350 case 'p': /* unsigned decimal integer */
351 UVAL = va_arg(ap, void *);
362 if (precision > MAXPREC)
365 mkint(xbuf + 2, UVAL, 16, 0, precision);
366 x = xbuf + ((altform && UVAL) ? 0 : 2);
371 case 'X': /* unsigned hexadecimal integer */
373 UVAL = va_arg(ap, afs_uintmax_t);
375 UVAL = va_arg(ap, unsigned long);
377 UVAL = va_arg(ap, unsigned int);
379 UVAL = va_arg(ap, unsigned int);
390 if (precision > MAXPREC)
393 mkint(xbuf + 2, UVAL, 16, 0, precision);
394 x = xbuf + ((altform && UVAL) ? 0 : 2);
398 case '%': /* literal % */
405 case 'c': /* character */
406 xbuf[0] = va_arg(ap, int);
412 case 's': /* string */
413 x = va_arg(ap, char *);
417 if (haveprec && precision < len)
421 case 'I': /* IP address:
422 * value is provided as a network-order unsigned long integer
423 * precision specifies max hostname length, as for %s
424 * if precision is explicitly 0, no hostname lookup is done
425 * if 0fill specified, IPaddr fields are 0-filled to 3 digits
426 * if spsign specified, IPaddr fields are space-filled to 3 digits
428 UVAL = va_arg(ap, unsigned long);
429 ia.s_addr = (unsigned long)UVAL;
430 if (haveprec && !precision)
433 he = gethostbyaddr((char *)&ia, 4, AF_INET);
437 if (haveprec && precision < len)
449 UVAL = ntohl((unsigned long)UVAL);
451 x = "%03u.%03u.%03u.%03u";
453 x = "%3u.%3u.%3u.%3u";
457 /* typecast to whatever '%u' is! */
458 sprintf(xbuf, x, (unsigned int)((UVAL & 0xff000000) >> 24),
459 (unsigned int)((UVAL & 0x00ff0000) >> 16),
460 (unsigned int)((UVAL & 0x0000ff00) >> 8),
461 (unsigned int)(UVAL & 0x000000ff));
467 case 'n': /* report count so far */
469 llcountp = va_arg(ap, afs_intmax_t *);
470 *llcountp = (afs_intmax_t)count;
472 lcountp = va_arg(ap, long *);
473 *lcountp = (long)count;
475 hcountp = va_arg(ap, short *);
476 *hcountp = (short)count;
478 countp = va_arg(ap, int *);
483 default: /* unknown specifier */
487 /* render the results */
518 afs_snprintf(char *p, size_t avail, const char *fmt, ...)
524 result = afs_vsnprintf(p, avail, fmt, ap);
529 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_VSNPRINTF)
531 #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV)
533 vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
536 vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap)
540 result = afs_vsnprintf(p, avail, fmt, ap);
541 #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV)
545 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
550 vsyslog(int priority, const char *format, va_list args)
553 vsnprintf(buf, sizeof(buf), format, args);
554 syslog(priority, "%s", buf);
558 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_SNPRINTF)
562 snprintf(char *p, size_t avail, const char *fmt, ...)
565 snprintf(char *p, unsigned int avail, char *fmt, ...)
572 result = afs_vsnprintf(p, avail, fmt, ap);
578 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
579 #endif /* AFS_NT40_ENV */