0d804083e3e52d5cadb9836b5a4e841101b76007
[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 = 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, len;
141     int ljust, plsign, spsign, altform, zfill;
142     int hflag, lflag, count, *countp, j;
143     char *x, *y, xbuf[MAXPREC + 21], fbuf[20];
144     struct hostent *he;
145     struct in_addr ia;
146     afs_uintmax_t UVAL;
147     afs_intmax_t SVAL;
148     afs_intmax_t *llcountp;
149     long *lcountp;
150     double FVAL;
151     short *hcountp;
152
153     count = 0;
154     avail--;
155     while (*fmt && avail) {
156         if (*fmt != '%') {
157             *p++ = *fmt++;
158             avail--;
159             count++;
160             continue;
161         }
162
163     /** Found a format specifier **/
164         ljust = plsign = spsign = altform = zfill = 0;
165         width = precision = haveprec = 0;
166         hflag = lflag = 0;
167         fmt++;
168
169         /* parse format flags */
170         while (*fmt) {
171             switch (*fmt) {
172             case '-':
173                 ljust = 1;
174                 fmt++;
175                 continue;       /* left justify */
176             case '+':
177                 plsign = 1;
178                 fmt++;
179                 continue;       /* use + or - */
180             case ' ':
181                 spsign = 1;
182                 fmt++;
183                 continue;       /* use space or - */
184             case '#':
185                 altform = 1;
186                 fmt++;
187                 continue;       /* alternate form */
188             case '0':
189                 zfill = 1;
190                 fmt++;
191                 continue;       /* pad with 0 */
192             default:
193                 break;
194             }
195             break;
196         }
197
198         /* parse minimum width */
199         if (*fmt == '*') {
200             width = va_arg(ap, int);
201             fmt++;
202         } else
203             while (isdigit(*fmt)) {
204                 width = (width * 10) + (*fmt - '0');
205                 fmt++;
206             }
207
208         /* parse precision */
209         if (*fmt == '.') {
210             fmt++;
211             haveprec = 1;
212             if (*fmt == '*') {
213                 precision = va_arg(ap, int);
214                 fmt++;
215             } else
216                 while (isdigit(*fmt)) {
217                     precision = (precision * 10) + (*fmt - '0');
218                     fmt++;
219                 }
220         }
221
222         /* parse size flags */
223         while (*fmt) {
224             switch (*fmt) {
225             case 'h':
226                 hflag = 1;
227                 fmt++;
228                 continue;       /* short argument */
229             case 'l':
230                 lflag += 1;
231                 fmt++;
232                 continue;       /* long argument */
233             case 'L':
234                 lflag = 2;
235                 fmt++;
236                 continue;       /* long long argument */
237             default:
238                 break;
239             }
240             break;
241         }
242
243         /* parse format specifier */
244         if (!*fmt)
245             break;
246         switch (*fmt++) {
247         case 'e':
248         case 'E':
249         case 'f':
250         case 'g':
251         case 'G':
252             FVAL = va_arg(ap, double);
253             sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""),
254                     altform ? "#" : "", fmt[-1]);
255             if (!haveprec)
256                 precision = 6;
257             if (precision > MAXPREC)
258                 precision = MAXPREC;
259             sprintf(xbuf, fbuf, precision, FVAL);
260             x = xbuf;
261             len = strlen(x);
262             break;
263
264         case 'i':
265         case 'd':               /* signed decimal integer */
266             if (lflag > 1)
267                 SVAL = va_arg(ap, afs_intmax_t);
268             else if (lflag)
269                 SVAL = va_arg(ap, long);
270             else if (hflag)
271                 SVAL = va_arg(ap, int);
272             else
273                 SVAL = va_arg(ap, int);
274             UVAL = (SVAL < 0) ? -SVAL : SVAL;
275
276             if (SVAL < 0)
277                 xbuf[0] = '-';
278             else if (plsign)
279                 xbuf[0] = '+';
280             else if (spsign)
281                 xbuf[0] = ' ';
282             else
283                 xbuf[0] = 0;
284
285             if (!haveprec) {
286                 if (zfill && !ljust)
287                     precision = width - !!xbuf[0];
288                 else
289                     precision = 1;
290                 if (precision < 1 + !!xbuf[0])
291                     precision = 1 + !!xbuf[0];
292             }
293             if (precision > MAXPREC)
294                 precision = MAXPREC;
295
296             mkint(xbuf + 1, UVAL, 10, 0, precision);
297             x = xbuf + !xbuf[0];
298             len = strlen(x);
299             break;
300
301
302         case 'o':               /* unsigned octal integer */
303             if (lflag > 1)
304                 UVAL = va_arg(ap, afs_uintmax_t);
305             else if (lflag)
306                 UVAL = va_arg(ap, unsigned long);
307             else if (hflag)
308                 UVAL = va_arg(ap, unsigned int);
309             else
310                 UVAL = va_arg(ap, unsigned int);
311
312             xbuf[0] = '0';
313
314             if (!haveprec) {
315                 if (zfill && !ljust)
316                     precision = width;
317                 else
318                     precision = 1;
319             }
320             if (precision > MAXPREC)
321                 precision = MAXPREC;
322
323             mkint(xbuf + 1, UVAL, 8, 0, precision);
324             x = xbuf + (xbuf[1] == '0' || !altform);
325             len = strlen(x);
326             break;
327
328         case 'u':               /* unsigned decimal integer */
329             if (lflag > 1)
330                 UVAL = va_arg(ap, afs_uintmax_t);
331             else if (lflag)
332                 UVAL = va_arg(ap, unsigned long);
333             else if (hflag)
334                 UVAL = va_arg(ap, unsigned int);
335             else
336                 UVAL = va_arg(ap, unsigned int);
337
338             if (!haveprec) {
339                 if (zfill && !ljust)
340                     precision = width;
341                 else
342                     precision = 1;
343             }
344             if (precision > MAXPREC)
345                 precision = MAXPREC;
346
347             mkint(xbuf, UVAL, 10, 0, precision);
348             x = xbuf;
349             len = strlen(x);
350             break;
351
352         case 'x':
353         case 'X':               /* unsigned hexadecimal integer */
354             if (lflag > 1)
355                 UVAL = va_arg(ap, afs_uintmax_t);
356             else if (lflag)
357                 UVAL = va_arg(ap, unsigned long);
358             else if (hflag)
359                 UVAL = va_arg(ap, unsigned int);
360             else
361                 UVAL = va_arg(ap, unsigned int);
362
363             xbuf[0] = '0';
364             xbuf[1] = 'x';
365
366             if (!haveprec) {
367                 if (zfill && !ljust)
368                     precision = width;
369                 else
370                     precision = 1;
371             }
372             if (precision > MAXPREC)
373                 precision = MAXPREC;
374
375             mkint(xbuf + 2, UVAL, 16, 0, precision);
376             x = xbuf + ((altform && UVAL) ? 0 : 2);
377             len = strlen(x);
378             break;
379
380         case '%':               /* literal % */
381             xbuf[0] = '%';
382             xbuf[1] = 0;
383             x = xbuf;
384             len = 1;
385             break;
386
387         case 'c':               /* character */
388             xbuf[0] = va_arg(ap, int);
389             xbuf[1] = 0;
390             x = xbuf;
391             len = 1;
392             break;
393
394         case 's':               /* string */
395             x = va_arg(ap, char *);
396             if (!x)
397                 x = "<null>";
398             len = strlen(x);
399             if (haveprec && precision < len)
400                 len = precision;
401             break;
402
403         case 'I':               /* IP address:
404                                  * value is provided as a network-order unsigned long integer
405                                  * precision specifies max hostname length, as for %s
406                                  * if precision is explicitly 0, no hostname lookup is done
407                                  * if 0fill specified, IPaddr fields are 0-filled to 3 digits
408                                  * if spsign specified, IPaddr fields are space-filled to 3 digits
409                                  */
410             UVAL = va_arg(ap, unsigned long);
411             ia.s_addr = UVAL;
412             if (haveprec && !precision)
413                 he = 0;
414             else
415                 he = gethostbyaddr((char *)&ia, 4, AF_INET);
416             if (he) {
417                 x = he->h_name;
418                 len = strlen(x);
419                 if (haveprec && precision < len)
420                     len = precision;
421                 if (altform) {
422                     for (y = x; *y; y++)
423                         if (isupper(*y))
424                             *y = tolower(*y);
425                 } else if (plsign) {
426                     for (y = x; *y; y++)
427                         if (islower(*y))
428                             *y = toupper(*y);
429                 }
430             } else {
431                 UVAL = ntohl(UVAL);
432                 if (zfill) {
433                     x = "%03u.%03u.%03u.%03u";
434                 } else if (spsign) {
435                     x = "%3u.%3u.%3u.%3u";
436                 } else {
437                     x = "%u.%u.%u.%u";
438                 }
439                 sprintf(xbuf, x, (UVAL & 0xff000000) >> 24,
440                         (UVAL & 0x00ff0000) >> 16, (UVAL & 0x0000ff00) >> 8,
441                         (UVAL & 0x000000ff));
442                 x = xbuf;
443                 len = strlen(xbuf);
444             }
445             break;
446
447         case 'n':               /* report count so far */
448             if (lflag > 1) {
449                 llcountp = va_arg(ap, afs_intmax_t *);
450                 *llcountp = (afs_intmax_t)count;
451             } else if (lflag) {
452                 lcountp = va_arg(ap, long *);
453                 *lcountp = (long)count;
454             } else if (hflag) {
455                 hcountp = va_arg(ap, short *);
456                 *hcountp = (short)count;
457             } else {
458                 countp = va_arg(ap, int *);
459                 *countp = count;
460             }
461             continue;
462
463         default:                /* unknown specifier */
464             continue;
465         }
466
467         /* render the results */
468         if (len > avail)
469             len = avail;
470         if (!width)
471             width = len;
472         if (width > avail)
473             width = avail;
474         j = width - len;
475         if (j > 0) {
476             avail -= j;
477             count += j;
478         }
479
480         if (!ljust)
481             while (j-- > 0)
482                 *p++ = ' ';
483
484         strncpy(p, x, len);
485         avail -= len;
486         count += len;
487         p += len;
488
489         if (ljust)
490             while (j-- > 0)
491                 *p++ = ' ';
492     }
493     *p = 0;
494     return count;
495 }
496
497 int
498 afs_snprintf(char *p, size_t avail, const char *fmt, ...)
499 {
500     va_list ap;
501     int result;
502
503     va_start(ap, fmt);
504     result = afs_vsnprintf(p, avail, fmt, ap);
505     va_end(ap);
506     return result;
507 }
508
509 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_VSNPRINTF)
510
511 #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV)
512 int
513 vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
514 #else
515 void
516 vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap)
517 #endif
518 {
519     int result;
520     result = afs_vsnprintf(p, avail, fmt, ap);
521 #ifdef AFS_AIX51_ENV
522     return result;
523 #endif
524 }
525 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
526
527 #ifndef AFS_NT40_ENV
528 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_SNPRINTF)
529
530 #ifdef AFS_AIX51_ENV
531 int
532 snprintf(char *p, size_t avail, const char *fmt, ...)
533 #else
534 void
535 snprintf(char *p, unsigned int avail, char *fmt, ...)
536 #endif
537 {
538     va_list ap;
539     int result;
540
541     va_start(ap, fmt);
542     result = afs_vsnprintf(p, avail, fmt, ap);
543     va_end(ap);
544 #ifdef AFS_AIX51_ENV
545     return result;
546 #endif
547 }
548 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
549 #endif /* AFS_NT40_ENV */