test-suite-pull-tools-directly-in-20020114
[openafs.git] / src / tests / 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
37 #include "xfiles.h"
38 #include "xf_errs.h"
39
40 #define SPBUFLEN 40
41 static char spbuf[SPBUFLEN + 1] = "";
42
43
44 #define MAXPREC 100
45
46 /* Generate an ASCII representation of an integer <val>, as follows:
47  * <base> indicates the base to be used (2-36)
48  * <uc> is nonzero if letter digits should be uppercase
49  * <prec> is the minimum number of digits
50  * The resulting number is stored in <buf>, which must be long enough
51  * to receive it.  The minimum length is <prec> or ceil(log{base}(val)),
52  * whichever is larger, plus room for a trailing NUL.
53  */
54 static void mkint(char *buf, unsigned long val, int base, int uc, int prec)
55 {
56   int len = 0, dig, i;
57
58   while (val) {
59     dig = val % base;
60     val = (val - dig) / base;
61     if (dig < 10)  dig = dig + '0';
62     else if (uc) dig = dig + 'A' - 10;
63     else         dig = dig + 'a' - 10;
64     buf[len++] = dig;
65   }
66   while (len < prec) buf[len++] = '0';
67   for (i = 0; i < (len + 1) / 2; i++) {
68     dig = buf[i];
69     buf[i] = buf[len - i - 1];
70     buf[len - i - 1] = dig;
71   }
72   buf[len] = 0;
73 }
74
75
76 /* Write spaces faster than one at a time */
77 static afs_uint32 wsp(XFILE *X, int count)
78 {
79   char *x;
80   afs_uint32 err;
81   int i;
82
83   if (!spbuf[0]) {
84     for (x = spbuf, i = SPBUFLEN; i; x++, i--) *x = ' ';
85   }
86
87   while (count > SPBUFLEN) {
88     err = xfwrite(X, spbuf, SPBUFLEN);
89     if (err) return err;
90     count -= SPBUFLEN;
91   }
92   if (count > 0) return xfwrite(X, spbuf, count);
93   return 0;
94 }
95
96
97 /* This function is a mostly-complete implementation of snprintf,
98  * with the following features:
99  *
100  *   - Actually obeys the length limit, which (unfortunately) many
101  *     implementations of snprintf do not.
102  *  
103  *   - Supports all the standard format specifiers for integers
104  *     (d, i, o, u, x, X), floating-point values (f, e, E, g, G),
105  *     and strings and characters (c, s, %), plus a few unusual
106  *     but useful ones described below.
107  *  
108  *   - Supports all the standard flags (-, 0, +, space, #).  These
109  *     flags are ignored if used when they are not appropriate.
110  *  
111  *   - Supports the standard size modifiers for short (h), long (h),
112  *     and double (L) arguments.  These modifiers are ignored if used
113  *     when they are not appropriate.
114  *  
115  *   - Supports minimum field width and precision, where appropriate,
116  *     including the use of '*' to specify a value given as an argument
117  *     instead of in the format string.  There is a maximum precision
118  *     of 100 digits.
119  *  
120  *   - At present, the 'p' specifier for printing pointers is not
121  *     implemented, because it is inherently non-portable and thus
122  *     can be implemented correctly only by the compiler's run-time
123  *     library.
124  *
125  *   - Floating-point specifier (%e, %f, %g) are implemented by
126  *     calling the standard sprintf, and thus may be unsafe.
127  *  
128  *   - The '%...$' notation is used primarily when the format string
129  *     is specified by the user, who knows but cannot change the order
130  *     of the arguments.  Such usage is inherently dangerous and
131  *     insecure; thus, it is not supported.
132  *  
133  * The custom format specifier '%I' is supported.  This specifier
134  * takes as its argument an unsigned long integer containing an
135  * IPv4 address in network byte order.  The address is rendered
136  * either as a hostname or as a dotted quad, as follows:
137  *  
138  *   - If precision is nonzero or unspecified, a hostname lookup
139  *     is attempted; if it is successful, the hostname is printed.
140  *     If the hostname lookup fails, the address is printed in
141  *     dotted-quad notation.
142  *  
143  *   - If precision is explicitly specified as 0, then the hostname
144  *     lookup is skipped, and dotted-quad notation is always used.
145  *  
146  *   - If a hostname is to be printed:
147  *     + The precision controls the maximum number of characters
148  *       printed, as with %s.
149  *     + If the '#' flag is specified, any letters in the hostname
150  *       will be forced to lower case before printing.
151  *     + If the '+' flag is specified, any letters in the hostname
152  *       will be forced to upper case before printing.  If both
153  *       '#' and '+' are given, the '+' flag will be ignored.
154  *     + The '0' and ' ' flags have no effect.
155  *  
156  *   - If a dotted quad is to be printed:
157  *     + The precision has no effect; dotted quads are always
158  *       7 to 12 characters in length, depending on the value
159  *       to be printed and the format flags used.
160  *     + If the '0' flag is given, each field (byte) of the address
161  *       will be padded with '0' on the left to three digits.
162  *     + If the ' ' flag is given, each field (byte) of the address
163  *       will be padded with spaces on the left to three digits.  If
164  *       both '0' and ' ' are given, the ' ' flag will be ignored.
165  *     + The '#' and '+' flags have no effect.
166  */
167
168 afs_uint32 vxfprintf(XFILE *X, char *fmt, va_list ap)
169 {
170   unsigned int width, precision, haveprec, len;
171   int ljust, plsign, spsign, altform, zfill;
172   int hflag, lflag, count, *countp, j;
173   char *x, *y, *lit = 0, xbuf[MAXPREC + 21], fbuf[20];
174   struct hostent *he;
175   struct in_addr ia;
176   unsigned long UVAL;
177   long SVAL, *lcountp;
178   double FVAL;
179   short *hcountp;
180   afs_uint32 err;
181
182   count = 0;
183   while (*fmt) {
184     if (*fmt != '%') {
185       if (!lit) lit = fmt;
186       fmt++;
187       count++;
188       continue;
189     }
190     if (lit) {
191       if ((err = xfwrite(X, lit, fmt - lit))) return err;
192       lit = 0;
193     }
194
195     /** Found a format specifier **/
196     ljust = plsign = spsign = altform = zfill = 0;
197     width = precision = haveprec = 0;
198     hflag = lflag = 0;
199     fmt++;
200
201     /* parse format flags */
202     while (*fmt) {
203       switch (*fmt) {
204         case '-': ljust   = 1; fmt++; continue;      /* left justify */
205         case '+': plsign  = 1; fmt++; continue;      /* use + or - */
206         case ' ': spsign  = 1; fmt++; continue;      /* use space or - */
207         case '#': altform = 1; fmt++; continue;      /* alternate form */
208         case '0': zfill   = 1; fmt++; continue;      /* pad with 0 */
209         default: break;
210       }
211       break;
212     }
213
214     /* parse minimum width */
215     if (*fmt == '*') {
216       width = va_arg(ap, int);
217       fmt++;
218     } else while (isdigit(*fmt)) {
219       width = (width * 10) + (*fmt - '0');
220       fmt++;
221     }
222
223     /* parse precision */
224     if (*fmt == '.') {
225       fmt++;
226       haveprec = 1;
227       if (*fmt == '*') {
228         precision = va_arg(ap, int);
229         fmt++;
230       } else while (isdigit(*fmt)) {
231         precision = (precision * 10) + (*fmt - '0');
232         fmt++;
233       }
234     }
235
236     /* parse size flags */
237     while (*fmt) {
238       switch (*fmt) {
239         case 'h': hflag   = 1; fmt++; continue;      /* short argument */
240         case 'l': lflag   = 1; fmt++; continue;      /* long argument */
241         default: break;
242       }
243       break;
244     }
245
246     /* parse format specifier */
247     if (!*fmt) break;
248     switch (*fmt++) {
249       case 'e':
250       case 'E':
251       case 'f':
252       case 'g':
253       case 'G':
254         FVAL = va_arg(ap, double);
255         sprintf(fbuf, "%%%s%s.*L%c", plsign ? "+" : (spsign ? " " : ""),
256                 altform ? "#" : "", fmt[-1]);
257         if (!haveprec) precision = 6;
258         if (precision > MAXPREC) 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) SVAL = va_arg(ap, long);
267         else if (hflag) SVAL = va_arg(ap, int);
268         else            SVAL = va_arg(ap, int);
269         UVAL = (SVAL < 0) ? -SVAL : SVAL;
270
271         if (SVAL < 0)    xbuf[0] = '-';
272         else if (plsign) xbuf[0] = '+';
273         else if (spsign) xbuf[0] = ' ';
274         else             xbuf[0] = 0;
275
276         if (!haveprec) {
277           if (zfill && !ljust) precision = width - !!xbuf[0];
278           else precision = 1;
279           if (precision < 1 + !!xbuf[0]) precision = 1 + !!xbuf[0];
280         }
281         if (precision > MAXPREC) precision = MAXPREC;
282
283         mkint(xbuf + 1, UVAL, 10, 0, precision);
284         x = xbuf + !xbuf[0];
285         len = strlen(x);
286         break;
287
288
289       case 'o': /* unsigned octal integer */
290         if      (lflag) UVAL = va_arg(ap, unsigned long);
291         else if (hflag) UVAL = va_arg(ap, unsigned int);
292         else            UVAL = va_arg(ap, unsigned int);
293
294         xbuf[0] = '0';
295
296         if (!haveprec) {
297           if (zfill && !ljust) precision = width;
298           else precision = 1;
299         }
300         if (precision > MAXPREC) precision = MAXPREC;
301
302         mkint(xbuf + 1, UVAL, 8, 0, precision);
303         x = xbuf + (xbuf[1] == '0' || !altform);
304         len = strlen(x);
305         break;
306
307       case 'u': /* unsigned decimal integer */
308         if      (lflag) UVAL = va_arg(ap, unsigned long);
309         else if (hflag) UVAL = va_arg(ap, unsigned int);
310         else            UVAL = va_arg(ap, unsigned int);
311
312         if (!haveprec) {
313           if (zfill && !ljust) precision = width;
314           else precision = 1;
315         }
316         if (precision > MAXPREC) precision = MAXPREC;
317
318         mkint(xbuf, UVAL, 10, 0, precision);
319         x = xbuf;
320         len = strlen(x);
321         break;
322
323       case 'x': 
324       case 'X': /* unsigned hexadecimal integer */
325         if      (lflag) UVAL = va_arg(ap, unsigned long);
326         else if (hflag) UVAL = va_arg(ap, unsigned int);
327         else            UVAL = va_arg(ap, unsigned int);
328
329         xbuf[0] = '0';
330         xbuf[1] = 'x';
331
332         if (!haveprec) {
333           if (zfill && !ljust) precision = width;
334           else precision = 1;
335         }
336         if (precision > MAXPREC) precision = MAXPREC;
337
338         mkint(xbuf + 2, UVAL, 16, 0, precision);
339         x = xbuf + ((altform && UVAL) ? 0 : 2);
340         len = strlen(x);
341         break;
342
343       case '%': /* literal % */
344         xbuf[0] = '%';
345         xbuf[1] = 0;
346         x = xbuf;
347         len = 1;
348         break;
349
350       case 'c': /* character */
351         xbuf[0] = va_arg(ap, int);
352         xbuf[1] = 0;
353         x = xbuf;
354         len = 1;
355         break;
356
357       case 's': /* string */
358         x = va_arg(ap, char *);
359         if (!x) x = "<null>";
360         len = strlen(x);
361         if (haveprec && precision < len) len = precision;
362         break;
363
364       case 'I': /* IP address:
365          * value is provided as a network-order unsigned long integer
366          * precision specifies max hostname length, as for %s
367          * if precision is explicitly 0, no hostname lookup is done
368          * if 0fill specified, IPaddr fields are 0-filled to 3 digits
369          * if spsign specified, IPaddr fields are space-filled to 3 digits
370          */
371         UVAL = va_arg(ap, unsigned long);
372         ia.s_addr = UVAL;
373         /* XXX: add support for an application-provided function
374          * for doing hostname lookups.  We don't do it automatically
375          * because on some platforms that would prevent us from
376          * being fully statically linked.
377          */
378         if (haveprec && !precision) he = 0;
379         else he = gethostbyaddr((char *)&ia, 4, AF_INET);
380         if (he) {
381           x = he->h_name;
382           len = strlen(x);
383           if (haveprec && precision < len) len = precision;
384           if (altform)
385             for (y = x; *y; y++) if (isupper(*y)) *y = tolower(*y);
386           else if (plsign)
387             for (y = x; *y; y++) if (islower(*y)) *y = toupper(*y);
388         } else {
389           UVAL = ntohl(UVAL);
390           if      (zfill)  x = "%03u.%03u.%03u.%03u";
391           else if (spsign) x = "%3u.%3u.%3u.%3u";
392           else             x = "%u.%u.%u.%u";
393           sprintf(xbuf, x,
394                   (UVAL & 0xff000000) >> 24, (UVAL & 0x00ff0000) >> 16,
395                   (UVAL & 0x0000ff00) >> 8,  (UVAL & 0x000000ff));
396           x = xbuf;
397           len = strlen(xbuf);
398         }
399         break;
400
401       case 'n': /* report count so far */
402         if (lflag) {
403           lcountp = va_arg(ap, long *);
404           *lcountp = count;
405         } else if (hflag) {
406           hcountp = va_arg(ap, short *);
407           *hcountp = count;
408         } else {
409           countp = va_arg(ap, int *);
410           *countp = count;
411         }
412         continue;
413
414       default: /* unknown specifier */
415         continue;
416     }
417
418     /* render the results */
419     if (!width)        width = len;
420     j = width - len;
421     if (j > 0) count += j;
422     count += len;
423
424     if (!ljust && (err = wsp(X, j))) return err;
425     if ((err = xfwrite(X, x, len))) return err;
426     if (ljust && (err = wsp(X, j))) return err;
427   }
428   if (lit && (err = xfwrite(X, lit, fmt - lit))) return err;
429   return 0;
430 lose:
431   return err;
432 }
433
434
435 afs_uint32 xfprintf(XFILE *X, char *fmt, ...)
436 {
437   va_list ap;
438   afs_uint32 err;
439
440   va_start(ap, fmt);
441   err = vxfprintf(X, fmt, ap);
442   va_end(ap);
443   return err;
444 }