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