snprintf-string-includes-20030619
[openafs.git] / src / util / snprintf.c
1 /* snprintf.c - Formatted, length-limited print to a string */
2
3 #include <afsconfig.h>
4 #include <afs/param.h>
5
6 RCSID("$Header$");
7
8 #include <sys/types.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <ctype.h>
12 #include <netinet/in.h>
13 #include <netdb.h>
14 #if defined(AFS_AIX32_ENV) || defined(AFS_SUN_ENV) || defined(AFS_XBSD_ENV)
15 #include <sys/socket.h>
16 #endif
17 #ifdef HAVE_STRING_H
18 #include <string.h>
19 #else
20 #ifdef HAVE_STRINGS_H
21 #include <strings.h>
22 #endif
23 #endif
24
25 #define MAXPREC 100
26
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.
34  */
35 static void mkint(char *buf, afs_uintmax_t val,
36                   int base, int uc, unsigned prec)
37 {
38   int len = 0, dig, i;
39
40   while (val) {
41     dig = val % base;
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;
46     buf[len++] = dig;
47   }
48   while (len < prec) buf[len++] = '0';
49   for (i = 0; i < (len + 1) / 2; i++) {
50     dig = buf[i];
51     buf[i] = buf[len - i - 1];
52     buf[len - i - 1] = dig;
53   }
54   buf[len] = 0;
55 }
56
57
58 /* This function is a mostly-complete implementation of snprintf,
59  * with the following features:
60  *
61  *   - Actually obeys the length limit, which (unfortunately) many
62  *     implementations of snprintf do not.
63  *  
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.
68  *  
69  *   - Supports all the standard flags (-, 0, +, space, #).  These
70  *     flags are ignored if used when they are not appropriate.
71  *  
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.
75  *  
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
79  *     of 100 digits.
80  *  
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
84  *     library.
85  *
86  *   - Floating-point specifier (%e, %f, %g) are implemented by
87  *     calling the standard sprintf, and thus may be unsafe.
88  *  
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.
93  *  
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:
98  *  
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.
103  *  
104  *   - If precision is explicitly specified as 0, then the hostname
105  *     lookup is skipped, and dotted-quad notation is always used.
106  *  
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.
116  *  
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.
127  */
128 int afs_vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
129 {
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];
134   struct hostent *he;
135   struct in_addr ia;
136   afs_uintmax_t UVAL;
137   afs_intmax_t SVAL;
138   afs_intmax_t* llcountp;
139   long *lcountp;
140   double FVAL;
141   short *hcountp;
142
143   count = 0;
144   avail--;
145   while (*fmt && avail) {
146     if (*fmt != '%') {
147       *p++ = *fmt++;
148       avail--;
149       count++;
150       continue;
151     }
152
153     /** Found a format specifier **/
154     ljust = plsign = spsign = altform = zfill = 0;
155     width = precision = haveprec = 0;
156     hflag = lflag = 0;
157     fmt++;
158
159     /* parse format flags */
160     while (*fmt) {
161       switch (*fmt) {
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 */
167         default: break;
168       }
169       break;
170     }
171
172     /* parse minimum width */
173     if (*fmt == '*') {
174       width = va_arg(ap, int);
175       fmt++;
176     } else while (isdigit(*fmt)) {
177       width = (width * 10) + (*fmt - '0');
178       fmt++;
179     }
180
181     /* parse precision */
182     if (*fmt == '.') {
183       fmt++;
184       haveprec = 1;
185       if (*fmt == '*') {
186         precision = va_arg(ap, int);
187         fmt++;
188       } else while (isdigit(*fmt)) {
189         precision = (precision * 10) + (*fmt - '0');
190         fmt++;
191       }
192     }
193
194     /* parse size flags */
195     while (*fmt) {
196       switch (*fmt) {
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 */
200         default: break;
201       }
202       break;
203     }
204
205     /* parse format specifier */
206     if (!*fmt) break;
207     switch (*fmt++) {
208       case 'e':
209       case 'E':
210       case 'f':
211       case 'g':
212       case 'G':
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);
219         x = xbuf;
220         len = strlen(x);
221         break;
222
223       case 'i': 
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, short);
228         else            SVAL = va_arg(ap, int);
229         UVAL = (SVAL < 0) ? -SVAL : SVAL;
230
231         if (SVAL < 0)    xbuf[0] = '-';
232         else if (plsign) xbuf[0] = '+';
233         else if (spsign) xbuf[0] = ' ';
234         else             xbuf[0] = 0;
235
236         if (!haveprec) {
237           if (zfill && !ljust) precision = width - !!xbuf[0];
238           else precision = 1;
239           if (precision < 1 + !!xbuf[0]) precision = 1 + !!xbuf[0];
240         }
241         if (precision > MAXPREC) precision = MAXPREC;
242
243         mkint(xbuf + 1, UVAL, 10, 0, precision);
244         x = xbuf + !xbuf[0];
245         len = strlen(x);
246         break;
247
248
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 short);
253         else            UVAL = va_arg(ap, unsigned int);
254
255         xbuf[0] = '0';
256
257         if (!haveprec) {
258           if (zfill && !ljust) precision = width;
259           else precision = 1;
260         }
261         if (precision > MAXPREC) precision = MAXPREC;
262
263         mkint(xbuf + 1, UVAL, 8, 0, precision);
264         x = xbuf + (xbuf[1] == '0' || !altform);
265         len = strlen(x);
266         break;
267
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 short);
272         else            UVAL = va_arg(ap, unsigned int);
273
274         if (!haveprec) {
275           if (zfill && !ljust) precision = width;
276           else precision = 1;
277         }
278         if (precision > MAXPREC) precision = MAXPREC;
279
280         mkint(xbuf, UVAL, 10, 0, precision);
281         x = xbuf;
282         len = strlen(x);
283         break;
284
285       case 'x': 
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 short);
290         else            UVAL = va_arg(ap, unsigned int);
291
292         xbuf[0] = '0';
293         xbuf[1] = 'x';
294
295         if (!haveprec) {
296           if (zfill && !ljust) precision = width;
297           else precision = 1;
298         }
299         if (precision > MAXPREC) precision = MAXPREC;
300
301         mkint(xbuf + 2, UVAL, 16, 0, precision);
302         x = xbuf + ((altform && UVAL) ? 0 : 2);
303         len = strlen(x);
304         break;
305
306       case '%': /* literal % */
307         xbuf[0] = '%';
308         xbuf[1] = 0;
309         x = xbuf;
310         len = 1;
311         break;
312
313       case 'c': /* character */
314         xbuf[0] = va_arg(ap, int);
315         xbuf[1] = 0;
316         x = xbuf;
317         len = 1;
318         break;
319
320       case 's': /* string */
321         x = va_arg(ap, char *);
322         if (!x) x = "<null>";
323         len = strlen(x);
324         if (haveprec && precision < len) len = precision;
325         break;
326
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
333          */
334         UVAL = va_arg(ap, unsigned long);
335         ia.s_addr = UVAL;
336         if (haveprec && !precision) he = 0;
337         else he = gethostbyaddr((char *)&ia, 4, AF_INET);
338         if (he) {
339           x = he->h_name;
340           len = strlen(x);
341           if (haveprec && precision < len) len = precision;
342           if (altform) {
343             for (y = x; *y; y++) if (isupper(*y)) *y = tolower(*y); }
344           else if (plsign) {
345             for (y = x; *y; y++) if (islower(*y)) *y = toupper(*y);
346           }
347         } else {
348           UVAL = ntohl(UVAL);
349           if      (zfill)  { x = "%03u.%03u.%03u.%03u"; }
350           else if (spsign) { x = "%3u.%3u.%3u.%3u"; }
351           else       {      x = "%u.%u.%u.%u"; }
352           sprintf(xbuf, x,
353                   (UVAL & 0xff000000) >> 24, (UVAL & 0x00ff0000) >> 16,
354                   (UVAL & 0x0000ff00) >> 8,  (UVAL & 0x000000ff));
355           x = xbuf;
356           len = strlen(xbuf);
357         }
358         break;
359
360       case 'n': /* report count so far */
361         if (lflag > 1) {
362           llcountp = va_arg(ap, afs_intmax_t *);
363           *llcountp = (long long) count;
364         } else if (lflag) {
365           lcountp = va_arg(ap, long *);
366           *lcountp = (long) count;
367         } else if (hflag) {
368           hcountp = va_arg(ap, short *);
369           *hcountp = (short) count;
370         } else {
371           countp = va_arg(ap, int *);
372           *countp = count;
373         }
374         continue;
375
376       default: /* unknown specifier */
377         continue;
378     }
379
380     /* render the results */
381     if (len > avail)   len = avail;
382     if (!width)        width = len;
383     if (width > avail) width = avail;
384     j = width - len;
385     if (j > 0) {
386       avail -= j;
387       count += j;
388     }
389
390     if (!ljust) while (j-- > 0) *p++ = ' ';
391
392     strncpy(p, x, len);
393     avail -= len;
394     count += len;
395     p += len;
396
397     if (ljust) while (j-- > 0) *p++ = ' ';
398   }
399   *p = 0;
400   return count;
401 }
402
403 int afs_snprintf(char *p, size_t avail, const char *fmt, ...)
404 {
405     va_list ap;
406     int result;
407
408     va_start(ap, fmt);
409     result = afs_vsnprintf(p, avail, fmt, ap);
410     va_end(ap);
411     return result;
412 }
413
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)
415
416 #ifdef AFS_AIX51_ENV
417 int  vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
418 #else
419 void vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap)
420 #endif
421 {
422     int result;
423     result = afs_vsnprintf(p, avail, fmt, ap);
424 #ifdef AFS_AIX51_ENV
425     return result;
426 #endif
427 }
428 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
429
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)
431
432 #ifdef AFS_AIX51_ENV
433 int snprintf(char *p, size_t avail, const char *fmt, ...)
434 #else
435 void snprintf(char *p, unsigned int avail, char *fmt, ...)
436 #endif
437 {
438     va_list ap;
439     int result;
440
441     va_start(ap, fmt);
442     result = afs_vsnprintf(p, avail, fmt, ap);
443     va_end(ap);
444 #ifdef AFS_AIX51_ENV
445     return result;
446 #endif
447 }
448 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */