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