5a5004a591f0be684f67a3bc9c93d32b5bf47022
[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 <string.h>
14 #ifndef AFS_NT40_ENV
15 #include <netinet/in.h>
16 #include <netdb.h>
17 #ifndef HAVE_VSYSLOG
18 #include <syslog.h>
19 #endif
20 #else
21 #include <winsock2.h>
22 #endif
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>
25 #endif
26
27 #define MAXPREC 100
28
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.
36  */
37 static void
38 mkint(char *buf, afs_uintmax_t val, int base, int uc, unsigned prec)
39 {
40     int len = 0, dig, i;
41
42     while (val) {
43         dig = (int) (val % base);
44         val = (val - dig) / base;
45         if (dig < 10)
46             dig = dig + '0';
47         else if (uc)
48             dig = dig + 'A' - 10;
49         else
50             dig = dig + 'a' - 10;
51         buf[len++] = dig;
52     }
53     while (len < prec)
54         buf[len++] = '0';
55     for (i = 0; i < (len + 1) / 2; i++) {
56         dig = buf[i];
57         buf[i] = buf[len - i - 1];
58         buf[len - i - 1] = dig;
59     }
60     buf[len] = 0;
61 }
62
63
64 /* This function is a mostly-complete implementation of snprintf,
65  * with the following features:
66  *
67  *   - Actually obeys the length limit, which (unfortunately) many
68  *     implementations of snprintf do not.
69  *  
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.
74  *  
75  *   - Supports all the standard flags (-, 0, +, space, #).  These
76  *     flags are ignored if used when they are not appropriate.
77  *  
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.
81  *  
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
85  *     of 100 digits.
86  *  
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
90  *     library.
91  *
92  *   - Floating-point specifier (%e, %f, %g) are implemented by
93  *     calling the standard sprintf, and thus may be unsafe.
94  *  
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.
99  *  
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:
104  *  
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.
109  *  
110  *   - If precision is explicitly specified as 0, then the hostname
111  *     lookup is skipped, and dotted-quad notation is always used.
112  *  
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.
122  *  
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.
133  */
134 int
135 afs_vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
136 {
137     unsigned int width, precision, haveprec;
138     size_t len;
139     int ljust, plsign, spsign, altform, zfill;
140     int hflag, lflag, count, *countp, j;
141     char *x, *y, xbuf[MAXPREC + 21], fbuf[20];
142     struct hostent *he;
143     struct in_addr ia;
144     afs_uintmax_t UVAL;
145     afs_intmax_t SVAL;
146     afs_intmax_t *llcountp;
147     long *lcountp;
148     double FVAL;
149     short *hcountp;
150
151     count = 0;
152     avail--;
153     while (*fmt && avail) {
154         if (*fmt != '%') {
155             *p++ = *fmt++;
156             avail--;
157             count++;
158             continue;
159         }
160
161     /** Found a format specifier **/
162         ljust = plsign = spsign = altform = zfill = 0;
163         width = precision = haveprec = 0;
164         hflag = lflag = 0;
165         fmt++;
166
167         /* parse format flags */
168         while (*fmt) {
169             switch (*fmt) {
170             case '-':
171                 ljust = 1;
172                 fmt++;
173                 continue;       /* left justify */
174             case '+':
175                 plsign = 1;
176                 fmt++;
177                 continue;       /* use + or - */
178             case ' ':
179                 spsign = 1;
180                 fmt++;
181                 continue;       /* use space or - */
182             case '#':
183                 altform = 1;
184                 fmt++;
185                 continue;       /* alternate form */
186             case '0':
187                 zfill = 1;
188                 fmt++;
189                 continue;       /* pad with 0 */
190             default:
191                 break;
192             }
193             break;
194         }
195
196         /* parse minimum width */
197         if (*fmt == '*') {
198             width = va_arg(ap, int);
199             fmt++;
200         } else
201             while (isdigit(*fmt)) {
202                 width = (width * 10) + (*fmt - '0');
203                 fmt++;
204             }
205
206         /* parse precision */
207         if (*fmt == '.') {
208             fmt++;
209             haveprec = 1;
210             if (*fmt == '*') {
211                 precision = va_arg(ap, int);
212                 fmt++;
213             } else
214                 while (isdigit(*fmt)) {
215                     precision = (precision * 10) + (*fmt - '0');
216                     fmt++;
217                 }
218         }
219
220         /* parse size flags */
221         while (*fmt) {
222             switch (*fmt) {
223             case 'h':
224                 hflag = 1;
225                 fmt++;
226                 continue;       /* short argument */
227             case 'l':
228                 lflag += 1;
229                 fmt++;
230                 continue;       /* long argument */
231             case 'L':
232                 lflag = 2;
233                 fmt++;
234                 continue;       /* long long argument */
235             default:
236                 break;
237             }
238             break;
239         }
240
241         /* parse format specifier */
242         if (!*fmt)
243             break;
244         switch (*fmt++) {
245         case 'e':
246         case 'E':
247         case 'f':
248         case 'g':
249         case 'G':
250             FVAL = va_arg(ap, double);
251             sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""),
252                     altform ? "#" : "", fmt[-1]);
253             if (!haveprec)
254                 precision = 6;
255             if (precision > MAXPREC)
256                 precision = MAXPREC;
257             sprintf(xbuf, fbuf, precision, FVAL);
258             x = xbuf;
259             len = strlen(x);
260             break;
261
262         case 'i':
263         case 'd':               /* signed decimal integer */
264             if (lflag > 1)
265                 SVAL = va_arg(ap, afs_intmax_t);
266             else if (lflag)
267                 SVAL = va_arg(ap, long);
268             else if (hflag)
269                 SVAL = va_arg(ap, int);
270             else
271                 SVAL = va_arg(ap, int);
272             UVAL = (SVAL < 0) ? -SVAL : SVAL;
273
274             if (SVAL < 0)
275                 xbuf[0] = '-';
276             else if (plsign)
277                 xbuf[0] = '+';
278             else if (spsign)
279                 xbuf[0] = ' ';
280             else
281                 xbuf[0] = 0;
282
283             if (!haveprec) {
284                 if (zfill && !ljust)
285                     precision = width - !!xbuf[0];
286                 else
287                     precision = 1;
288                 if (precision < 1 + !!xbuf[0])
289                     precision = 1 + !!xbuf[0];
290             }
291             if (precision > MAXPREC)
292                 precision = MAXPREC;
293
294             mkint(xbuf + 1, UVAL, 10, 0, precision);
295             x = xbuf + !xbuf[0];
296             len = strlen(x);
297             break;
298
299
300         case 'o':               /* unsigned octal integer */
301             if (lflag > 1)
302                 UVAL = va_arg(ap, afs_uintmax_t);
303             else if (lflag)
304                 UVAL = va_arg(ap, unsigned long);
305             else if (hflag)
306                 UVAL = va_arg(ap, unsigned int);
307             else
308                 UVAL = va_arg(ap, unsigned int);
309
310             xbuf[0] = '0';
311
312             if (!haveprec) {
313                 if (zfill && !ljust)
314                     precision = width;
315                 else
316                     precision = 1;
317             }
318             if (precision > MAXPREC)
319                 precision = MAXPREC;
320
321             mkint(xbuf + 1, UVAL, 8, 0, precision);
322             x = xbuf + (xbuf[1] == '0' || !altform);
323             len = strlen(x);
324             break;
325
326         case 'u':               /* unsigned decimal integer */
327             if (lflag > 1)
328                 UVAL = va_arg(ap, afs_uintmax_t);
329             else if (lflag)
330                 UVAL = va_arg(ap, unsigned long);
331             else if (hflag)
332                 UVAL = va_arg(ap, unsigned int);
333             else
334                 UVAL = va_arg(ap, unsigned int);
335
336             if (!haveprec) {
337                 if (zfill && !ljust)
338                     precision = width;
339                 else
340                     precision = 1;
341             }
342             if (precision > MAXPREC)
343                 precision = MAXPREC;
344
345             mkint(xbuf, UVAL, 10, 0, precision);
346             x = xbuf;
347             len = strlen(x);
348             break;
349
350         case 'p':               /* unsigned decimal integer */
351             UVAL = va_arg(ap, void *);
352
353             xbuf[0] = '0';
354             xbuf[1] = 'x';
355
356             if (!haveprec) {
357                 if (zfill && !ljust)
358                     precision = width;
359                 else
360                     precision = 1;
361             }
362             if (precision > MAXPREC)
363                 precision = MAXPREC;
364
365             mkint(xbuf + 2, UVAL, 16, 0, precision);
366             x = xbuf + ((altform && UVAL) ? 0 : 2);
367             len = strlen(x);
368             break;
369
370         case 'x':
371         case 'X':               /* unsigned hexadecimal integer */
372             if (lflag > 1)
373                 UVAL = va_arg(ap, afs_uintmax_t);
374             else if (lflag)
375                 UVAL = va_arg(ap, unsigned long);
376             else if (hflag)
377                 UVAL = va_arg(ap, unsigned int);
378             else
379                 UVAL = va_arg(ap, unsigned int);
380
381             xbuf[0] = '0';
382             xbuf[1] = 'x';
383
384             if (!haveprec) {
385                 if (zfill && !ljust)
386                     precision = width;
387                 else
388                     precision = 1;
389             }
390             if (precision > MAXPREC)
391                 precision = MAXPREC;
392
393             mkint(xbuf + 2, UVAL, 16, 0, precision);
394             x = xbuf + ((altform && UVAL) ? 0 : 2);
395             len = strlen(x);
396             break;
397
398         case '%':               /* literal % */
399             xbuf[0] = '%';
400             xbuf[1] = 0;
401             x = xbuf;
402             len = 1;
403             break;
404
405         case 'c':               /* character */
406             xbuf[0] = va_arg(ap, int);
407             xbuf[1] = 0;
408             x = xbuf;
409             len = 1;
410             break;
411
412         case 's':               /* string */
413             x = va_arg(ap, char *);
414             if (!x)
415                 x = "<null>";
416             len = strlen(x);
417             if (haveprec && precision < len)
418                 len = precision;
419             break;
420
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
427                                  */
428             UVAL = va_arg(ap, unsigned long);
429             ia.s_addr = (unsigned long)UVAL;
430             if (haveprec && !precision)
431                 he = 0;
432             else
433                 he = gethostbyaddr((char *)&ia, 4, AF_INET);
434             if (he) {
435                 x = he->h_name;
436                 len = strlen(x);
437                 if (haveprec && precision < len)
438                     len = precision;
439                 if (altform) {
440                     for (y = x; *y; y++)
441                         if (isupper(*y))
442                             *y = tolower(*y);
443                 } else if (plsign) {
444                     for (y = x; *y; y++)
445                         if (islower(*y))
446                             *y = toupper(*y);
447                 }
448             } else {
449                 UVAL = ntohl((unsigned long)UVAL);
450                 if (zfill) {
451                     x = "%03u.%03u.%03u.%03u";
452                 } else if (spsign) {
453                     x = "%3u.%3u.%3u.%3u";
454                 } else {
455                     x = "%u.%u.%u.%u";
456                 }
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));
462                 x = xbuf;
463                 len = strlen(xbuf);
464             }
465             break;
466
467         case 'n':               /* report count so far */
468             if (lflag > 1) {
469                 llcountp = va_arg(ap, afs_intmax_t *);
470                 *llcountp = (afs_intmax_t)count;
471             } else if (lflag) {
472                 lcountp = va_arg(ap, long *);
473                 *lcountp = (long)count;
474             } else if (hflag) {
475                 hcountp = va_arg(ap, short *);
476                 *hcountp = (short)count;
477             } else {
478                 countp = va_arg(ap, int *);
479                 *countp = count;
480             }
481             continue;
482
483         default:                /* unknown specifier */
484             continue;
485         }
486
487         /* render the results */
488         if (len > avail)
489             len = avail;
490         if (!width)
491             width = len;
492         if (width > avail)
493             width = avail;
494         j = width - len;
495         if (j > 0) {
496             avail -= j;
497             count += j;
498         }
499
500         if (!ljust)
501             while (j-- > 0)
502                 *p++ = ' ';
503
504         strncpy(p, x, len);
505         avail -= len;
506         count += len;
507         p += len;
508
509         if (ljust)
510             while (j-- > 0)
511                 *p++ = ' ';
512     }
513     *p = 0;
514     return count;
515 }
516
517 int
518 afs_snprintf(char *p, size_t avail, const char *fmt, ...)
519 {
520     va_list ap;
521     int result;
522
523     va_start(ap, fmt);
524     result = afs_vsnprintf(p, avail, fmt, ap);
525     va_end(ap);
526     return result;
527 }
528
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)
530
531 #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV)
532 int
533 vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
534 #else
535 void
536 vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap)
537 #endif
538 {
539     int result;
540     result = afs_vsnprintf(p, avail, fmt, ap);
541 #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV)
542     return result;
543 #endif
544 }
545 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
546
547 #ifndef AFS_NT40_ENV
548 #ifndef HAVE_VSYSLOG
549 void
550 vsyslog(int priority, const char *format, va_list args)
551 {
552   char buf[1024];
553   vsnprintf(buf, sizeof(buf), format, args);
554   syslog(priority, "%s", buf);
555 }
556 #endif
557
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)
559
560 #ifdef AFS_AIX51_ENV
561 int
562 snprintf(char *p, size_t avail, const char *fmt, ...)
563 #else
564 void
565 snprintf(char *p, unsigned int avail, char *fmt, ...)
566 #endif
567 {
568     va_list ap;
569     int result;
570
571     va_start(ap, fmt);
572     result = afs_vsnprintf(p, avail, fmt, ap);
573     va_end(ap);
574 #ifdef AFS_AIX51_ENV
575     return result;
576 #endif
577 }
578 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
579 #endif /* AFS_NT40_ENV */