reindent-20030715
[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
7     ("$Header$");
8
9 #include <sys/types.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <ctype.h>
13 #include <netinet/in.h>
14 #include <netdb.h>
15 #if defined(AFS_AIX32_ENV) || defined(AFS_SUN_ENV) || defined(AFS_XBSD_ENV)
16 #include <sys/socket.h>
17 #endif
18 #ifdef HAVE_STRING_H
19 #include <string.h>
20 #else
21 #ifdef HAVE_STRINGS_H
22 #include <strings.h>
23 #endif
24 #endif
25
26 #define MAXPREC 100
27
28 /* Generate an ASCII representation of an integer <val>, as follows:
29  * <base> indicates the base to be used (2-36)
30  * <uc> is nonzero if letter digits should be uppercase
31  * <prec> is the minimum number of digits
32  * The resulting number is stored in <buf>, which must be long enough
33  * to receive it.  The minimum length is <prec> or ceil(log{base}(val)),
34  * whichever is larger, plus room for a trailing NUL.
35  */
36 static void
37 mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec)
38 {
39     int len = 0, dig, i;
40
41     while (val) {
42         dig = val % base;
43         val = (val - dig) / base;
44         if (dig < 10)
45             dig = dig + '0';
46         else if (uc)
47             dig = dig + 'A' - 10;
48         else
49             dig = dig + 'a' - 10;
50         buf[len++] = dig;
51     }
52     while (len < prec)
53         buf[len++] = '0';
54     for (i = 0; i < (len + 1) / 2; i++) {
55         dig = buf[i];
56         buf[i] = buf[len - i - 1];
57         buf[len - i - 1] = dig;
58     }
59     buf[len] = 0;
60 }
61
62
63 /* This function is a mostly-complete implementation of snprintf,
64  * with the following features:
65  *
66  *   - Actually obeys the length limit, which (unfortunately) many
67  *     implementations of snprintf do not.
68  *  
69  *   - Supports all the standard format specifiers for integers
70  *     (d, i, o, u, x, X), floating-point values (f, e, E, g, G),
71  *     and strings and characters (c, s, %), plus a few unusual
72  *     but useful ones described below.
73  *  
74  *   - Supports all the standard flags (-, 0, +, space, #).  These
75  *     flags are ignored if used when they are not appropriate.
76  *  
77  *   - Supports the standard size modifiers for short (h), long (h),
78  *     and double (L) arguments.  These modifiers are ignored if used
79  *     when they are not appropriate.
80  *  
81  *   - Supports minimum field width and precision, where appropriate,
82  *     including the use of '*' to specify a value given as an argument
83  *     instead of in the format string.  There is a maximum precision
84  *     of 100 digits.
85  *  
86  *   - At present, the 'p' specifier for printing pointers is not
87  *     implemented, because it is inherently non-portable and thus
88  *     can be implemented correctly only by the compiler's run-time
89  *     library.
90  *
91  *   - Floating-point specifier (%e, %f, %g) are implemented by
92  *     calling the standard sprintf, and thus may be unsafe.
93  *  
94  *   - The '%...$' notation is used primarily when the format string
95  *     is specified by the user, who knows but cannot change the order
96  *     of the arguments.  Such usage is inherently dangerous and
97  *     insecure; thus, it is not supported.
98  *  
99  * The custom format specifier '%I' is supported.  This specifier
100  * takes as its argument an unsigned long integer containing an
101  * IPv4 address in network byte order.  The address is rendered
102  * either as a hostname or as a dotted quad, as follows:
103  *  
104  *   - If precision is nonzero or unspecified, a hostname lookup
105  *     is attempted; if it is successful, the hostname is printed.
106  *     If the hostname lookup fails, the address is printed in
107  *     dotted-quad notation.
108  *  
109  *   - If precision is explicitly specified as 0, then the hostname
110  *     lookup is skipped, and dotted-quad notation is always used.
111  *  
112  *   - If a hostname is to be printed:
113  *     + The precision controls the maximum number of characters
114  *       printed, as with %s.
115  *     + If the '#' flag is specified, any letters in the hostname
116  *       will be forced to lower case before printing.
117  *     + If the '+' flag is specified, any letters in the hostname
118  *       will be forced to upper case before printing.  If both
119  *       '#' and '+' are given, the '+' flag will be ignored.
120  *     + The '0' and ' ' flags have no effect.
121  *  
122  *   - If a dotted quad is to be printed:
123  *     + The precision has no effect; dotted quads are always
124  *       7 to 12 characters in length, depending on the value
125  *       to be printed and the format flags used.
126  *     + If the '0' flag is given, each field (byte) of the address
127  *       will be padded with '0' on the left to three digits.
128  *     + If the ' ' flag is given, each field (byte) of the address
129  *       will be padded with spaces on the left to three digits.  If
130  *       both '0' and ' ' are given, the ' ' flag will be ignored.
131  *     + The '#' and '+' flags have no effect.
132  */
133 int
134 afs_vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
135 {
136     unsigned int width, precision, haveprec, len;
137     int ljust, plsign, spsign, altform, zfill;
138     int hflag, lflag, count, *countp, j;
139     char *x, *y, xbuf[MAXPREC + 21], fbuf[20];
140     struct hostent *he;
141     struct in_addr ia;
142     afs_uintmax_t UVAL;
143     afs_intmax_t SVAL;
144     afs_intmax_t *llcountp;
145     long *lcountp;
146     double FVAL;
147     short *hcountp;
148
149     count = 0;
150     avail--;
151     while (*fmt && avail) {
152         if (*fmt != '%') {
153             *p++ = *fmt++;
154             avail--;
155             count++;
156             continue;
157         }
158
159     /** Found a format specifier **/
160         ljust = plsign = spsign = altform = zfill = 0;
161         width = precision = haveprec = 0;
162         hflag = lflag = 0;
163         fmt++;
164
165         /* parse format flags */
166         while (*fmt) {
167             switch (*fmt) {
168             case '-':
169                 ljust = 1;
170                 fmt++;
171                 continue;       /* left justify */
172             case '+':
173                 plsign = 1;
174                 fmt++;
175                 continue;       /* use + or - */
176             case ' ':
177                 spsign = 1;
178                 fmt++;
179                 continue;       /* use space or - */
180             case '#':
181                 altform = 1;
182                 fmt++;
183                 continue;       /* alternate form */
184             case '0':
185                 zfill = 1;
186                 fmt++;
187                 continue;       /* pad with 0 */
188             default:
189                 break;
190             }
191             break;
192         }
193
194         /* parse minimum width */
195         if (*fmt == '*') {
196             width = va_arg(ap, int);
197             fmt++;
198         } else
199             while (isdigit(*fmt)) {
200                 width = (width * 10) + (*fmt - '0');
201                 fmt++;
202             }
203
204         /* parse precision */
205         if (*fmt == '.') {
206             fmt++;
207             haveprec = 1;
208             if (*fmt == '*') {
209                 precision = va_arg(ap, int);
210                 fmt++;
211             } else
212                 while (isdigit(*fmt)) {
213                     precision = (precision * 10) + (*fmt - '0');
214                     fmt++;
215                 }
216         }
217
218         /* parse size flags */
219         while (*fmt) {
220             switch (*fmt) {
221             case 'h':
222                 hflag = 1;
223                 fmt++;
224                 continue;       /* short argument */
225             case 'l':
226                 lflag += 1;
227                 fmt++;
228                 continue;       /* long argument */
229             case 'L':
230                 lflag = 2;
231                 fmt++;
232                 continue;       /* long long argument */
233             default:
234                 break;
235             }
236             break;
237         }
238
239         /* parse format specifier */
240         if (!*fmt)
241             break;
242         switch (*fmt++) {
243         case 'e':
244         case 'E':
245         case 'f':
246         case 'g':
247         case 'G':
248             FVAL = va_arg(ap, double);
249             sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""),
250                     altform ? "#" : "", fmt[-1]);
251             if (!haveprec)
252                 precision = 6;
253             if (precision > MAXPREC)
254                 precision = MAXPREC;
255             sprintf(xbuf, fbuf, precision, FVAL);
256             x = xbuf;
257             len = strlen(x);
258             break;
259
260         case 'i':
261         case 'd':               /* signed decimal integer */
262             if (lflag > 1)
263                 SVAL = va_arg(ap, afs_intmax_t);
264             else if (lflag)
265                 SVAL = va_arg(ap, long);
266             else if (hflag)
267                 SVAL = va_arg(ap, int);
268             else
269                 SVAL = va_arg(ap, int);
270             UVAL = (SVAL < 0) ? -SVAL : SVAL;
271
272             if (SVAL < 0)
273                 xbuf[0] = '-';
274             else if (plsign)
275                 xbuf[0] = '+';
276             else if (spsign)
277                 xbuf[0] = ' ';
278             else
279                 xbuf[0] = 0;
280
281             if (!haveprec) {
282                 if (zfill && !ljust)
283                     precision = width - !!xbuf[0];
284                 else
285                     precision = 1;
286                 if (precision < 1 + !!xbuf[0])
287                     precision = 1 + !!xbuf[0];
288             }
289             if (precision > MAXPREC)
290                 precision = MAXPREC;
291
292             mkint(xbuf + 1, UVAL, 10, 0, precision);
293             x = xbuf + !xbuf[0];
294             len = strlen(x);
295             break;
296
297
298         case 'o':               /* unsigned octal integer */
299             if (lflag > 1)
300                 UVAL = va_arg(ap, afs_uintmax_t);
301             else if (lflag)
302                 UVAL = va_arg(ap, unsigned long);
303             else if (hflag)
304                 UVAL = va_arg(ap, unsigned int);
305             else
306                 UVAL = va_arg(ap, unsigned int);
307
308             xbuf[0] = '0';
309
310             if (!haveprec) {
311                 if (zfill && !ljust)
312                     precision = width;
313                 else
314                     precision = 1;
315             }
316             if (precision > MAXPREC)
317                 precision = MAXPREC;
318
319             mkint(xbuf + 1, UVAL, 8, 0, precision);
320             x = xbuf + (xbuf[1] == '0' || !altform);
321             len = strlen(x);
322             break;
323
324         case 'u':               /* unsigned decimal integer */
325             if (lflag > 1)
326                 UVAL = va_arg(ap, afs_uintmax_t);
327             else if (lflag)
328                 UVAL = va_arg(ap, unsigned long);
329             else if (hflag)
330                 UVAL = va_arg(ap, unsigned int);
331             else
332                 UVAL = va_arg(ap, unsigned int);
333
334             if (!haveprec) {
335                 if (zfill && !ljust)
336                     precision = width;
337                 else
338                     precision = 1;
339             }
340             if (precision > MAXPREC)
341                 precision = MAXPREC;
342
343             mkint(xbuf, UVAL, 10, 0, precision);
344             x = xbuf;
345             len = strlen(x);
346             break;
347
348         case 'x':
349         case 'X':               /* unsigned hexadecimal integer */
350             if (lflag > 1)
351                 UVAL = va_arg(ap, afs_uintmax_t);
352             else if (lflag)
353                 UVAL = va_arg(ap, unsigned long);
354             else if (hflag)
355                 UVAL = va_arg(ap, unsigned int);
356             else
357                 UVAL = va_arg(ap, unsigned int);
358
359             xbuf[0] = '0';
360             xbuf[1] = 'x';
361
362             if (!haveprec) {
363                 if (zfill && !ljust)
364                     precision = width;
365                 else
366                     precision = 1;
367             }
368             if (precision > MAXPREC)
369                 precision = MAXPREC;
370
371             mkint(xbuf + 2, UVAL, 16, 0, precision);
372             x = xbuf + ((altform && UVAL) ? 0 : 2);
373             len = strlen(x);
374             break;
375
376         case '%':               /* literal % */
377             xbuf[0] = '%';
378             xbuf[1] = 0;
379             x = xbuf;
380             len = 1;
381             break;
382
383         case 'c':               /* character */
384             xbuf[0] = va_arg(ap, int);
385             xbuf[1] = 0;
386             x = xbuf;
387             len = 1;
388             break;
389
390         case 's':               /* string */
391             x = va_arg(ap, char *);
392             if (!x)
393                 x = "<null>";
394             len = strlen(x);
395             if (haveprec && precision < len)
396                 len = precision;
397             break;
398
399         case 'I':               /* IP address:
400                                  * value is provided as a network-order unsigned long integer
401                                  * precision specifies max hostname length, as for %s
402                                  * if precision is explicitly 0, no hostname lookup is done
403                                  * if 0fill specified, IPaddr fields are 0-filled to 3 digits
404                                  * if spsign specified, IPaddr fields are space-filled to 3 digits
405                                  */
406             UVAL = va_arg(ap, unsigned long);
407             ia.s_addr = UVAL;
408             if (haveprec && !precision)
409                 he = 0;
410             else
411                 he = gethostbyaddr((char *)&ia, 4, AF_INET);
412             if (he) {
413                 x = he->h_name;
414                 len = strlen(x);
415                 if (haveprec && precision < len)
416                     len = precision;
417                 if (altform) {
418                     for (y = x; *y; y++)
419                         if (isupper(*y))
420                             *y = tolower(*y);
421                 } else if (plsign) {
422                     for (y = x; *y; y++)
423                         if (islower(*y))
424                             *y = toupper(*y);
425                 }
426             } else {
427                 UVAL = ntohl(UVAL);
428                 if (zfill) {
429                     x = "%03u.%03u.%03u.%03u";
430                 } else if (spsign) {
431                     x = "%3u.%3u.%3u.%3u";
432                 } else {
433                     x = "%u.%u.%u.%u";
434                 }
435                 sprintf(xbuf, x, (UVAL & 0xff000000) >> 24,
436                         (UVAL & 0x00ff0000) >> 16, (UVAL & 0x0000ff00) >> 8,
437                         (UVAL & 0x000000ff));
438                 x = xbuf;
439                 len = strlen(xbuf);
440             }
441             break;
442
443         case 'n':               /* report count so far */
444             if (lflag > 1) {
445                 llcountp = va_arg(ap, afs_intmax_t *);
446                 *llcountp = (long long)count;
447             } else if (lflag) {
448                 lcountp = va_arg(ap, long *);
449                 *lcountp = (long)count;
450             } else if (hflag) {
451                 hcountp = va_arg(ap, short *);
452                 *hcountp = (short)count;
453             } else {
454                 countp = va_arg(ap, int *);
455                 *countp = count;
456             }
457             continue;
458
459         default:                /* unknown specifier */
460             continue;
461         }
462
463         /* render the results */
464         if (len > avail)
465             len = avail;
466         if (!width)
467             width = len;
468         if (width > avail)
469             width = avail;
470         j = width - len;
471         if (j > 0) {
472             avail -= j;
473             count += j;
474         }
475
476         if (!ljust)
477             while (j-- > 0)
478                 *p++ = ' ';
479
480         strncpy(p, x, len);
481         avail -= len;
482         count += len;
483         p += len;
484
485         if (ljust)
486             while (j-- > 0)
487                 *p++ = ' ';
488     }
489     *p = 0;
490     return count;
491 }
492
493 int
494 afs_snprintf(char *p, size_t avail, const char *fmt, ...)
495 {
496     va_list ap;
497     int result;
498
499     va_start(ap, fmt);
500     result = afs_vsnprintf(p, avail, fmt, ap);
501     va_end(ap);
502     return result;
503 }
504
505 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_VSNPRINTF)
506
507 #ifdef AFS_AIX51_ENV
508 int
509 vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
510 #else
511 void
512 vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap)
513 #endif
514 {
515     int result;
516     result = afs_vsnprintf(p, avail, fmt, ap);
517 #ifdef AFS_AIX51_ENV
518     return result;
519 #endif
520 }
521 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
522
523 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_SNPRINTF)
524
525 #ifdef AFS_AIX51_ENV
526 int
527 snprintf(char *p, size_t avail, const char *fmt, ...)
528 #else
529 void
530 snprintf(char *p, unsigned int avail, char *fmt, ...)
531 #endif
532 {
533     va_list ap;
534     int result;
535
536     va_start(ap, fmt);
537     result = afs_vsnprintf(p, avail, fmt, ap);
538     va_end(ap);
539 #ifdef AFS_AIX51_ENV
540     return result;
541 #endif
542 }
543 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */