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