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