Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / tools / dumpscan / 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 }
469 } else {
470 UVAL = ntohl(UVAL);
471 if (zfill)
472 x = "%03u.%03u.%03u.%03u";
473 else if (spsign)
474 x = "%3u.%3u.%3u.%3u";
475 else
476 x = "%u.%u.%u.%u";
477 sprintf(xbuf, x, (UVAL & 0xff000000) >> 24,
478 (UVAL & 0x00ff0000) >> 16, (UVAL & 0x0000ff00) >> 8,
479 (UVAL & 0x000000ff));
480 x = xbuf;
481 len = strlen(xbuf);
482 }
483 break;
484
485 case 'n': /* report count so far */
486 if (lflag) {
487 lcountp = va_arg(ap, long *);
488 *lcountp = count;
489 } else if (hflag) {
490 hcountp = va_arg(ap, short *);
491 *hcountp = count;
492 } else {
493 countp = va_arg(ap, int *);
494 *countp = count;
495 }
496 continue;
497
498 default: /* unknown specifier */
499 continue;
500 }
501
502 /* render the results */
503 if (!width)
504 width = len;
505 j = width - len;
506 if (j > 0)
507 count += j;
508 count += len;
509
510 if (!ljust && (err = wsp(X, j)))
511 return err;
512 if ((err = xfwrite(X, x, len)))
513 return err;
514 if (ljust && (err = wsp(X, j)))
515 return err;
516 }
517 if (lit && (err = xfwrite(X, lit, fmt - lit)))
518 return err;
519 return 0;
520 }
521
522
523 afs_uint32
524 xfprintf(XFILE * X, char *fmt, ...)
525 {
526 va_list ap;
527 afs_uint32 err;
528
529 va_start(ap, fmt);
530 err = vxfprintf(X, fmt, ap);
531 va_end(ap);
532 return err;
533 }