DEVEL15-util-snprintf-replacement-20090624
[openafs.git] / src / util / snprintf.c
1 /*
2  * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 /* snprintf.c - Formatted, length-limited print to a string */
35
36 #include <afsconfig.h>
37 #include <afs/param.h>
38
39 RCSID
40     ("$Header$");
41
42 #include <sys/types.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <ctype.h>
47 #include <string.h>
48 #ifndef AFS_NT40_ENV
49 #include <netinet/in.h>
50 #include <netdb.h>
51 #ifndef HAVE_VSYSLOG
52 #include <syslog.h>
53 #endif
54 #else
55 #include <winsock2.h>
56 #endif
57 #if defined(AFS_AIX32_ENV) || defined(AFS_SUN_ENV) || defined(AFS_XBSD_ENV) || defined(AFS_HPUX_ENV) || defined(AFS_SGI65_ENV)
58 #include <sys/socket.h>
59 #endif
60
61 /* This is an enhanced version of the *printf functionality shipped 
62  * with Heimdal.  In addition to the standard Unix formatting types
63  * this version also supports Microsoft's I32 and I64 type modifiers
64  * and the OpenAFS I type which is used to generate output from 
65  * network byte order IPv4 addresses (either dotted notation or 
66  * hostname lookups.  Implementation details follow:
67  *
68  *   - Actually obeys the length limit, which (unfortunately) many
69  *     implementations of snprintf do not.
70  *  
71  *   - Supports all the standard format specifiers for integers
72  *     (d, i, o, u, x, X), floating-point values (f, e, E, g, G),
73  *     and strings and characters (c, s, %), plus a few unusual
74  *     but useful ones described below.
75  *
76  *   - The Microsoft integral size modifiers I32 and I64 are 
77  *     supported.  I32 is equivalent to 'l'.
78  *     I64 is equivalent to 'll'.
79  *  
80  *   - Supports all the standard flags (-, 0, +, space, #).  These
81  *     flags are ignored if used when they are not appropriate.
82  *  
83  *   - Supports the standard size modifiers for short (h), long (h),
84  *     and double (L) arguments.  These modifiers are ignored if used
85  *     when they are not appropriate.
86  *  
87  *   - Supports minimum field width and precision, where appropriate,
88  *     including the use of '*' to specify a value given as an argument
89  *     instead of in the format string.  There is a maximum precision
90  *     of 100 digits.
91  *  
92  *   - The 'p' specifier for printing pointers is implemented using
93  *     compile time knowledge.  (AFS_64BITPOINTER_ENV)
94  *
95  *   - Floating-point specifier (%e, %f, %g) are implemented by
96  *     calling the standard sprintf, and thus may be unsafe.
97  *  
98  *   - The '%...$' notation is used primarily when the format string
99  *     is specified by the user, who knows but cannot change the order
100  *     of the arguments.  Such usage is inherently dangerous and
101  *     insecure; thus, it is not supported.
102  *
103  *   - Passing in a format and an NULL buffer is supported.  This
104  *     will compute the size of the buffer required by the format
105  *     and the provided input parameters.
106  *  
107  * The custom format specifier '%I' is supported.  This specifier
108  * takes as its argument an unsigned long integer containing an
109  * IPv4 address in network byte order.  The address is rendered
110  * either as a hostname or as a dotted quad, as follows:
111  *  
112  *   - If precision is nonzero or unspecified, a hostname lookup
113  *     is attempted; if it is successful, the hostname is printed.
114  *     If the hostname lookup fails, the address is printed in
115  *     dotted-quad notation.
116  *  
117  *   - If precision is explicitly specified as 0, then the hostname
118  *     lookup is skipped, and dotted-quad notation is always used.
119  *  
120  *   - If a hostname is to be printed:
121  *     + The precision controls the maximum number of characters
122  *       printed, as with %s.
123  *     + If the '#' flag is specified, any letters in the hostname
124  *       will be forced to lower case before printing.
125  *     + If the '+' flag is specified, any letters in the hostname
126  *       will be forced to upper case before printing.  If both
127  *       '#' and '+' are given, the '+' flag will be ignored.
128  *     + The '0' and ' ' flags have no effect.
129  *  
130  *   - If a dotted quad is to be printed:
131  *     + The precision has no effect; dotted quads are always
132  *       7 to 12 characters in length, depending on the value
133  *       to be printed and the format flags used.
134  *     + If the '0' flag is given, each field (byte) of the address
135  *       will be padded with '0' on the left to three digits.
136  *     + If the ' ' flag is given, each field (byte) of the address
137  *       will be padded with spaces on the left to three digits.  If
138  *       both '0' and ' ' are given, the ' ' flag will be ignored.
139  *     + The '#' and '+' flags have no effect.
140  *
141  * A test program exists in src/util/tests/snprintf_tests.c.
142  */
143
144 #define MAXPREC 100 
145
146 enum format_flags {
147     minus_flag     =  1,
148     plus_flag      =  2,
149     space_flag     =  4,
150     alternate_flag =  8,
151     zero_flag      = 16
152 };
153
154 /*
155  * Common state
156  */
157
158 struct snprintf_state {
159     unsigned char *str;
160     unsigned char *s;
161     unsigned char *theend;
162     size_t sz;
163     size_t max_sz;
164     void (*append_char)(struct snprintf_state *, unsigned char);
165     /* XXX - methods */
166 };
167
168 static int
169 afs_sn_reserve (struct snprintf_state *state, size_t n)
170 {
171     return state->s + n > state->theend;
172 }
173
174 static void
175 afs_sn_append_char (struct snprintf_state *state, unsigned char c)
176 {
177     if (!afs_sn_reserve (state, 1))
178         *state->s++ = c;
179 }
180
181 static int
182 as_reserve (struct snprintf_state *state, size_t n)
183 {
184     if (state->s + n > state->theend) {
185         size_t off = state->s - state->str;
186         unsigned char *tmp;
187
188         if (state->max_sz && state->sz >= state->max_sz)
189             return 1;
190
191         state->sz = max(state->sz * 2, state->sz + n);
192         if (state->max_sz)
193             state->sz = min(state->sz, state->max_sz);
194         tmp = (unsigned char *)realloc (state->str, state->sz);
195         if (tmp == NULL)
196             return 1;
197         state->str = tmp;
198         state->s = state->str + off;
199         state->theend = state->str + state->sz - 1;
200     }
201     return 0;
202 }
203
204 static void
205 as_append_char (struct snprintf_state *state, unsigned char c)
206 {
207     if(!as_reserve (state, 1))
208         *state->s++ = c;
209 }
210
211 /* longest integer types */
212
213 #ifdef AFS_64BIT_ENV
214 typedef afs_uint64 u_longest;
215 typedef afs_int64 longest;
216 #else
217 typedef afs_uint32 u_longest;
218 typedef afs_int32 longest;
219 #endif
220
221 static int
222 pad(struct snprintf_state *state, int width, char c)
223 {
224     int len = 0;
225     while(width-- > 0){
226         (*state->append_char)(state,  c);
227         ++len;
228     }
229     return len;
230 }
231
232 /* return true if we should use alternatve hex form */
233 static int
234 use_alternative (int flags, u_longest num, unsigned base)
235 {
236     return (flags & alternate_flag) && base == 16 && num != 0;
237 }
238
239 static int
240 append_number(struct snprintf_state *state,
241               u_longest num, unsigned base, const char *rep,
242               int width, int prec, int flags, int minusp)
243 {
244     int len = 0;
245     u_longest n = num;
246     char nstr[MAXPREC]; /* enough for <192 bit octal integers */
247     int nstart, nlen;
248     char signchar;
249
250     /* given precision, ignore zero flag */
251     if(prec != -1)
252         flags &= ~zero_flag;
253     else
254         prec = 1;
255
256     /* format number as string */
257     nstart = sizeof(nstr);
258     nlen = 0;
259     nstr[--nstart] = '\0';
260
261     do {
262         nstr[--nstart] = rep[n % base];
263         ++nlen;
264         n /= base;
265     } while(n);
266
267     /* zero value with zero precision should produce no digits */
268     if(prec == 0 && num == 0) {
269         nlen--;
270         nstart++;
271     }
272
273     /* figure out what char to use for sign */
274     if(minusp)
275         signchar = '-';
276     else if((flags & plus_flag))
277         signchar = '+';
278     else if((flags & space_flag))
279         signchar = ' ';
280     else
281         signchar = '\0';
282
283     if((flags & alternate_flag) && base == 8) {
284         /* if necessary, increase the precision to
285            make first digit a zero */
286
287         /* XXX C99 claims (regarding # and %o) that "if the value and
288            precision are both 0, a single 0 is printed", but there is
289            no such wording for %x. This would mean that %#.o would
290            output "0", but %#.x "". This does not make sense, and is
291            also not what other printf implementations are doing. */
292         
293         if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0')
294             prec = nlen + 1;
295     }
296
297     /* possible formats:
298        pad | sign | alt | zero | digits
299        sign | alt | zero | digits | pad   minus_flag
300        sign | alt | zero | digits zero_flag */
301
302     /* if not right justifying or padding with zeros, we need to
303        compute the length of the rest of the string, and then pad with
304        spaces */
305     if(!(flags & (minus_flag | zero_flag))) {
306         if(prec > nlen)
307             width -= prec;
308         else
309             width -= nlen;
310         
311         if(use_alternative(flags, num, base))
312             width -= 2;
313         
314         if(signchar != '\0')
315             width--;
316         
317         /* pad to width */
318         len += pad(state, width, ' ');
319     }
320     if(signchar != '\0') {
321         (*state->append_char)(state, signchar);
322         ++len;
323     }
324     if(use_alternative(flags, num, base)) {
325         (*state->append_char)(state, '0');
326         (*state->append_char)(state, rep[10] + 23); /* XXX */
327         len += 2;
328     }
329     if(flags & zero_flag) {
330         /* pad to width with zeros */
331         if(prec - nlen > width - len - nlen)
332             len += pad(state, prec - nlen, '0');
333         else
334             len += pad(state, width - len - nlen, '0');
335     } else
336         /* pad to prec with zeros */
337         len += pad(state, prec - nlen, '0');
338         
339     while(nstr[nstart] != '\0') {
340         (*state->append_char)(state, nstr[nstart++]);
341         ++len;
342     }
343         
344     if(flags & minus_flag)
345         len += pad(state, width - len, ' ');
346
347     return len;
348 }
349
350 /*
351  * return length
352  */
353
354 static int
355 append_string (struct snprintf_state *state,
356                const unsigned char *arg,
357                int width,
358                int prec,
359                int flags)
360 {
361     int len = 0;
362
363     if(arg == NULL)
364         arg = (const unsigned char*)"(null)";
365
366     if(prec != -1)
367         width -= prec;
368     else
369         width -= (int)strlen((const char *)arg);
370     if(!(flags & minus_flag))
371         len += pad(state, width, ' ');
372
373     if (prec != -1) {
374         while (*arg && prec--) {
375             (*state->append_char) (state, *arg++);
376             ++len;
377         }
378     } else {
379         while (*arg) {
380             (*state->append_char) (state, *arg++);
381             ++len;
382         }
383     }
384     if(flags & minus_flag)
385         len += pad(state, width, ' ');
386     return len;
387 }
388
389 static int
390 append_char(struct snprintf_state *state,
391             unsigned char arg,
392             int width,
393             int flags)
394 {
395     int len = 0;
396
397     while(!(flags & minus_flag) && --width > 0) {
398         (*state->append_char) (state, ' ')    ;
399         ++len;
400     }
401     (*state->append_char) (state, arg);
402     ++len;
403     while((flags & minus_flag) && --width > 0) {
404         (*state->append_char) (state, ' ');
405         ++len;
406     }
407     return 0;
408 }
409
410 #define MAXPREC 100 
411 static int
412 append_float(struct snprintf_state *state,
413              char type,
414              double arg,
415              int width,
416              int prec,
417              int flags)
418 {
419     int len = 0;
420     char fbuf[20], xbuf[MAXPREC + 21];
421
422     sprintf(fbuf, "%%%s%s.*L%c", 
423             (flags & plus_flag) ? "+" : ((flags & space_flag) ? " " : ((flags & minus_flag) ? "-" : "")),
424             (flags & alternate_flag) ? "#" : "", type);
425     if (prec == -1)
426         prec = 6;
427     if (prec > MAXPREC)
428         prec = MAXPREC;
429     sprintf(xbuf, fbuf, prec, arg);
430
431     len = append_string(state, (unsigned char *)xbuf, width, -1, 0);
432     return len;
433 }
434
435 static int
436 append_address(struct snprintf_state *state,
437                afs_uint32 arg,
438                int width,
439                int prec,
440                int flags)
441 {
442     int len = 0;
443     struct hostent * he;
444     struct in_addr ia;
445     char * x, *y;
446
447     /* IP address:
448      * value is provided as a network-order unsigned long integer
449      * precision specifies max hostname length, as for %s
450      * if precision is explicitly 0, no hostname lookup is done
451      * if 0fill specified, IPaddr fields are 0-filled to 3 digits
452      * if spsign specified, IPaddr fields are space-filled to 3 digits
453      */
454     ia.s_addr = arg;
455     if (prec == 0)
456         he = 0;
457     else
458         he = gethostbyaddr((char *)&ia, 4, AF_INET);
459     if (he) {
460         x = he->h_name;
461         len = (int)strlen(x);
462         if (prec != -1 && prec < len)
463             width = prec;
464         else 
465             width = len;
466         if (flags & alternate_flag) {
467             for (y = x; *y; y++)
468                 if (isupper(*y))
469                     *y = tolower(*y);
470         } else if (flags & plus_flag) {
471             for (y = x; *y; y++)
472                 if (islower(*y))
473                     *y = toupper(*y);
474         }
475         len = append_string(state, (unsigned char *)x, width, prec, 0);
476     } else {
477         char xbuf[16];
478         arg = ntohl(arg);
479         if (flags & zero_flag) {
480             x = "%03u.%03u.%03u.%03u";
481         } else if (flags & space_flag) {
482             x = "%3u.%3u.%3u.%3u";
483         } else {
484             x = "%u.%u.%u.%u";
485         }
486         /* typecast to whatever '%u' is! */
487         sprintf(xbuf, x, (unsigned int)((arg & 0xff000000) >> 24),
488                          (unsigned int)((arg & 0x00ff0000) >> 16), 
489                          (unsigned int)((arg & 0x0000ff00) >> 8),
490                          (unsigned int)(arg & 0x000000ff));
491         len = append_string(state, (unsigned char *)xbuf, 0, -1, 0);
492     }        
493
494     return len;
495 }
496
497 /*
498  * This can't be made into a function...
499  */
500
501 #if defined(AFS_64BIT_ENV)
502 #if defined(AFS_NT40_ENV)
503
504 #define PARSE_INT_FORMAT(res, arg, unsig) \
505 if (long_long_flag) \
506      res = (unsig __int64)va_arg(arg, unsig __int64); \
507 else if (long_flag || addr_flag) \
508      res = (unsig long)va_arg(arg, unsig long); \
509 else if (size_t_flag) \
510      res = (size_t)va_arg(arg, size_t); \
511 else if (short_flag) \
512      res = (unsig short)va_arg(arg, unsig int); \
513 else \
514      res = (unsig int)va_arg(arg, unsig int)
515
516 #else /* AFS_NT40_ENV */
517
518 #define PARSE_INT_FORMAT(res, arg, unsig) \
519 if (long_long_flag) \
520      res = (unsig long long)va_arg(arg, unsig long long); \
521 else if (long_flag || addr_flag) \
522      res = (unsig long)va_arg(arg, unsig long); \
523 else if (size_t_flag) \
524      res = (size_t)va_arg(arg, size_t); \
525 else if (short_flag) \
526      res = (unsig short)va_arg(arg, unsig int); \
527 else \
528      res = (unsig int)va_arg(arg, unsig int)
529 #endif
530
531 #else
532
533 #define PARSE_INT_FORMAT(res, arg, unsig) \
534 if (long_flag || addr_flag) \
535      res = (afs_uint32)va_arg(arg, afs_uint32); \
536 else if (size_t_flag) \
537      res = (size_t)va_arg(arg, size_t); \
538 else if (short_flag) \
539      res = (unsig short)va_arg(arg, unsig int); \
540 else \
541      res = (unsig int)va_arg(arg, unsig int)
542
543 #endif
544
545 /*
546  * zyxprintf - return length, as snprintf
547  */
548
549 static int
550 xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap)
551 {
552     const unsigned char *format = (const unsigned char *)char_format;
553     unsigned char c;
554     int len = 0;
555
556     while((c = *format++)) {
557         if (c == '%') {
558             int flags          = 0;
559             int width          = 0;
560             int prec           = -1;
561             int size_t_flag    = 0;
562             int long_long_flag = 0;
563             int long_flag      = 0;
564             int short_flag     = 0;
565             int addr_flag      = 0;
566
567             /* flags */
568             while((c = *format++)){
569                 if(c == '-')
570                     flags |= minus_flag;
571                 else if(c == '+')
572                     flags |= plus_flag;
573                 else if(c == ' ')
574                     flags |= space_flag;
575                 else if(c == '#')
576                     flags |= alternate_flag;
577                 else if(c == '0')
578                     flags |= zero_flag;
579                 else if(c == '\'')
580                     ; /* just ignore */
581                 else
582                     break;
583             }
584
585             if((flags & space_flag) && (flags & plus_flag))
586                 flags ^= space_flag;
587
588             if((flags & minus_flag) && (flags & zero_flag))
589                 flags ^= zero_flag;
590
591             /* width */
592             if (isdigit(c))
593                 do {
594                     width = width * 10 + c - '0';
595                     c = *format++;
596                 } while(isdigit(c));
597             else if(c == '*') {
598                 width = va_arg(ap, int);
599                 c = *format++;
600             }
601
602             /* precision */
603             if (c == '.') {
604                 prec = 0;
605                 c = *format++;
606                 if (isdigit(c))
607                     do {
608                         prec = prec * 10 + c - '0';
609                         c = *format++;
610                     } while(isdigit(c));
611                 else if (c == '*') {
612                     prec = va_arg(ap, int);
613                     c = *format++;
614                 }
615             }
616
617             /* size */
618
619             if (c == 'h') {
620                 short_flag = 1;
621                 c = *format++;
622             } else if (c == 'z') {
623                 size_t_flag = 1;
624                 c = *format++;
625             } else if (c == 'l') {
626                 long_flag = 1;
627                 c = *format++;
628                 if (c == 'l') {
629                     long_long_flag = 1;
630                     c = *format++;
631                 }
632             } else if (c == 'I') {
633                 /* This could be either Microsoft I{32,64} notation */
634                 c = *format++;
635                 if (c == '3') {
636                     long_flag = 1;
637                     c = *format++;
638                     if (c == '2') {
639                         c = *format++;
640                     }
641                 } else if (c == '6') {
642                     long_flag = 1;
643                     c = *format++;
644                     if (c == '4') {
645                         long_long_flag = 1;
646                         c = *format++;
647                     }
648                 } else {
649                     /* Or the OpenAFS special %I meaning network address */
650                     addr_flag = 1;
651                     --format;
652                     c = 'I';
653                 }
654             } else if (c == 'p') {
655                 flags |= zero_flag;
656                 if (prec == -1)
657                     prec = 2 * sizeof(void *);
658                 if (sizeof(void *) == sizeof(afs_uint64))
659                     long_long_flag = 1;
660                 else if (sizeof(void *) == sizeof(afs_uint32))
661                     long_flag = 1;
662                 else 
663                     long_flag = 1;
664             }
665
666             if(c != 'd' && c != 'i' && c != 'I')
667                 flags &= ~(plus_flag | space_flag);
668
669             switch (c) {
670             case 'c' :
671                 append_char(state, va_arg(ap, int), width, flags);
672                 ++len;
673                 break;
674             case 's' :
675                 len += append_string(state,
676                                      va_arg(ap, unsigned char*),
677                                      width,
678                                      prec,
679                                      flags);
680                 break;
681             case 'd' :
682             case 'i' : {
683                 longest arg;
684                 u_longest num;
685                 int minusp = 0;
686
687                 PARSE_INT_FORMAT(arg, ap, signed);
688
689                 if (arg < 0) {
690                     minusp = 1;
691                     num = -arg;
692                 } else
693                     num = arg;
694
695                 len += append_number (state, num, 10, "0123456789",
696                                       width, prec, flags, minusp);
697                 break;
698             }
699             case 'u' : {
700                 u_longest arg;
701
702                 PARSE_INT_FORMAT(arg, ap, unsigned);
703
704                 len += append_number (state, arg, 10, "0123456789",
705                                       width, prec, flags, 0);
706                 break;
707             }
708             case 'o' : {
709                 u_longest arg;
710
711                 PARSE_INT_FORMAT(arg, ap, unsigned);
712
713                 len += append_number (state, arg, 010, "01234567",
714                                       width, prec, flags, 0);
715                 break;
716             }
717             case 'x' : {
718                 u_longest arg;
719
720                 PARSE_INT_FORMAT(arg, ap, unsigned);
721
722                 len += append_number (state, arg, 0x10, "0123456789abcdef",
723                                       width, prec, flags, 0);
724                 break;
725             }
726             case 'X' :{
727                 u_longest arg;
728
729                 PARSE_INT_FORMAT(arg, ap, unsigned);
730
731                 len += append_number (state, arg, 0x10, "0123456789ABCDEF",
732                                       width, prec, flags, 0);
733                 break;
734             }
735             case 'p' : {
736 #ifdef AFS_64BITPOINTER_ENV
737                 u_longest arg = (u_longest)va_arg(ap, void*);
738 #else
739                 u_longest arg = (unsigned long)va_arg(ap, void*);
740 #endif
741                 len += append_number (state, arg, 0x10, "0123456789ABCDEF",
742                                       width, prec, flags, 0);
743                 break;
744             }
745             case 'n' : {
746                 int *arg = va_arg(ap, int*);
747                 *arg = (int)(state->s - state->str);
748                 break;
749             }
750             case 'I' : {
751                 u_longest arg;
752
753                 PARSE_INT_FORMAT(arg, ap, unsigned);
754
755                 len += append_address (state, (unsigned long)arg, width, prec, flags);
756                 break;
757             }
758             case 'e':
759             case 'E':
760             case 'f':
761             case 'g':
762             case 'G': {
763                 double arg = (double)va_arg(ap, double);
764
765                 len += append_float (state, c, arg, width, prec, flags);
766                 break;
767             }
768             case '\0' :
769                 --format;
770                 /* FALLTHROUGH */
771             case '%' :
772                 (*state->append_char)(state, c);
773                 ++len;
774                 break;
775             default :
776                 (*state->append_char)(state, '%');
777                 (*state->append_char)(state, c);
778                 len += 2;
779                 break;
780             }
781         } else {
782             (*state->append_char) (state, c);
783             ++len;
784         }
785     }
786     return len;
787 }
788
789
790 int
791 afs_vsnprintf (char *str, size_t sz, const char *format, va_list args)
792 {
793     struct snprintf_state state;
794     int ret;
795     unsigned char *ustr = (unsigned char *)str;
796
797     state.max_sz = 0;
798     state.sz     = sz;
799     state.str    = ustr;
800     state.s      = ustr;
801     state.theend = ustr + sz - (sz > 0);
802     state.append_char = afs_sn_append_char;
803
804     ret = xyzprintf (&state, format, args);
805     if (state.s != NULL && sz != 0)
806         *state.s = '\0';
807     return ret;
808 }
809
810 int
811 afs_snprintf (char *str, size_t sz, const char *format, ...)
812 {
813     va_list args;
814     int ret;
815
816     va_start(args, format);
817     ret = afs_vsnprintf (str, sz, format, args);
818     va_end(args);
819
820 #ifdef PARANOIA
821     {
822         int ret2;
823         unsigned char *tmp;
824
825         tmp = (unsigned char *)malloc (sz);
826         if (tmp == NULL)
827             abort ();
828
829         va_start(args, format);
830         ret2 = afs_vsprintf (tmp, format, args);
831         va_end(args);
832         if (ret != ret2 || strcmp(str, tmp))
833             abort ();
834         free (tmp);
835     }
836 #endif
837
838     return ret;
839 }
840
841 int
842 afs_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
843 {
844     int st;
845     struct snprintf_state state;
846
847     state.max_sz = max_sz;
848     state.sz     = 1;
849     state.str    = (unsigned char *)malloc(state.sz);
850     if (state.str == NULL) {
851         *ret = NULL;
852         return -1;
853     }
854     state.s = state.str;
855     state.theend = state.s + state.sz - 1;
856     state.append_char = as_append_char;
857
858     st = xyzprintf (&state, format, args);
859     if (st > state.sz) {
860         free (state.str);
861         *ret = NULL;
862         return -1;
863     } else {
864         char *tmp;
865
866         *state.s = '\0';
867         tmp = (char *)realloc (state.str, st+1);
868         if (tmp == NULL) {
869             free (state.str);
870             *ret = NULL;
871             return -1;
872         }
873         *ret = tmp;
874         return st;
875     }
876 }
877
878 int
879 afs_vasprintf (char **ret, const char *format, va_list args)
880 {
881     return afs_vasnprintf (ret, 0, format, args);
882 }
883
884 int
885 afs_asprintf (char **ret, const char *format, ...)
886 {
887     va_list args;
888     int val;
889
890     va_start(args, format);
891     val = afs_vasprintf (ret, format, args);
892     va_end(args);
893
894 #ifdef PARANOIA
895     {
896         int ret2;
897         unsigned char *tmp;
898         tmp = (unsigned char *)malloc (val + 1);
899         if (tmp == NULL)
900             abort ();
901
902         va_start(args, format);
903         ret2 = afs_vsprintf (tmp, format, args);
904         va_end(args);
905         if (val != ret2 || strcmp(*ret, tmp))
906             abort ();
907         free (tmp);
908     }
909 #endif
910
911     return val;
912 }
913
914 int
915 afs_asnprintf (char **ret, size_t max_sz, const char *format, ...)
916 {
917     va_list args;
918     int val;
919
920     va_start(args, format);
921     val = afs_vasnprintf (ret, max_sz, format, args);
922
923 #ifdef PARANOIA
924     {
925         int ret2;
926         unsigned char *tmp;
927         tmp = (unsigned char *)malloc (val + 1);
928         if (tmp == NULL)
929             abort ();
930
931         ret2 = afs_vsprintf (tmp, format, args);
932         if (val != ret2 || strcmp(*ret, tmp))
933             abort ();
934         free (tmp);
935     }
936 #endif
937
938     va_end(args);
939     return val;
940 }
941
942 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
943
944 #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV)
945 int
946 vsnprintf(char *p, size_t avail, const char *fmt, va_list ap)
947 #else
948 void
949 vsnprintf(char *p, unsigned int avail, char *fmt, va_list ap)
950 #endif
951 {
952     int result;
953     result = afs_vsnprintf(p, avail, fmt, ap);
954 #if defined(AFS_AIX51_ENV) || defined(AFS_NT40_ENV)
955     return result;
956 #endif
957 }
958 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
959
960 #ifndef AFS_NT40_ENV
961 #ifndef HAVE_VSYSLOG
962 void
963 vsyslog(int priority, const char *format, va_list args)
964 {
965   char buf[1024];
966   afs_vsnprintf(buf, sizeof(buf), format, args);
967   syslog(priority, "%s", buf);
968 }
969 #endif
970
971 #if defined(AFS_OSF20_ENV) && !defined(AFS_DUX50_ENV) || defined(AFS_AIX32_ENV) || (defined(AFS_SUN55_ENV) && !defined(AFS_SUN56_ENV)) || !defined(HAVE_SNPRINTF)
972
973 #ifdef AFS_AIX51_ENV
974 int
975 snprintf(char *p, size_t avail, const char *fmt, ...)
976 #else
977 void
978 snprintf(char *p, unsigned int avail, char *fmt, ...)
979 #endif
980 {
981     va_list ap;
982     int result;
983
984     va_start(ap, fmt);
985     result = afs_vsnprintf(p, avail, fmt, ap);
986     va_end(ap);
987 #ifdef AFS_AIX51_ENV
988     return result;
989 #endif
990 }
991 #endif /* AFS_OSF20_ENV || AFS_AIX32_ENV */
992 #endif /* AFS_NT40_ENV */