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