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