155682Smarkm/*
2120945Snectar * Copyright (c) 1995-2003 Kungliga Tekniska H�gskolan
355682Smarkm * (Royal Institute of Technology, Stockholm, Sweden).
455682Smarkm * All rights reserved.
555682Smarkm *
655682Smarkm * Redistribution and use in source and binary forms, with or without
755682Smarkm * modification, are permitted provided that the following conditions
855682Smarkm * are met:
955682Smarkm *
1055682Smarkm * 1. Redistributions of source code must retain the above copyright
1155682Smarkm *    notice, this list of conditions and the following disclaimer.
1255682Smarkm *
1355682Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1455682Smarkm *    notice, this list of conditions and the following disclaimer in the
1555682Smarkm *    documentation and/or other materials provided with the distribution.
1655682Smarkm *
1755682Smarkm * 3. Neither the name of the Institute nor the names of its contributors
1855682Smarkm *    may be used to endorse or promote products derived from this software
1955682Smarkm *    without specific prior written permission.
2055682Smarkm *
2155682Smarkm * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
2255682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2355682Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2455682Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
2555682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2655682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2755682Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2855682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2955682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3055682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3155682Smarkm * SUCH DAMAGE.
3255682Smarkm */
3355682Smarkm
3455682Smarkm#ifdef HAVE_CONFIG_H
3555682Smarkm#include <config.h>
36178825SdfrRCSID("$Id: snprintf.c 21005 2007-06-08 01:54:35Z lha $");
3755682Smarkm#endif
38178825Sdfr#if defined(TEST_SNPRINTF)
39178825Sdfr#include "snprintf-test.h"
40178825Sdfr#endif /* TEST_SNPRINTF */
4155682Smarkm#include <stdio.h>
4255682Smarkm#include <stdarg.h>
4355682Smarkm#include <stdlib.h>
4455682Smarkm#include <string.h>
4555682Smarkm#include <ctype.h>
46178825Sdfr#include "roken.h"
47178825Sdfr#include <assert.h>
4855682Smarkm
4955682Smarkmenum format_flags {
5055682Smarkm    minus_flag     =  1,
5155682Smarkm    plus_flag      =  2,
5255682Smarkm    space_flag     =  4,
5355682Smarkm    alternate_flag =  8,
5455682Smarkm    zero_flag      = 16
5555682Smarkm};
5655682Smarkm
5755682Smarkm/*
5855682Smarkm * Common state
5955682Smarkm */
6055682Smarkm
6190926Snectarstruct snprintf_state {
62178825Sdfr    unsigned char *str;
63178825Sdfr    unsigned char *s;
64178825Sdfr    unsigned char *theend;
65178825Sdfr    size_t sz;
66178825Sdfr    size_t max_sz;
67178825Sdfr    void (*append_char)(struct snprintf_state *, unsigned char);
68178825Sdfr    /* XXX - methods */
6955682Smarkm};
7055682Smarkm
7190926Snectar#if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
7255682Smarkmstatic int
7390926Snectarsn_reserve (struct snprintf_state *state, size_t n)
7455682Smarkm{
75178825Sdfr    return state->s + n > state->theend;
7655682Smarkm}
7755682Smarkm
7890926Snectarstatic void
7990926Snectarsn_append_char (struct snprintf_state *state, unsigned char c)
8055682Smarkm{
81178825Sdfr    if (!sn_reserve (state, 1))
82178825Sdfr	*state->s++ = c;
8355682Smarkm}
8455682Smarkm#endif
8555682Smarkm
8655682Smarkmstatic int
8790926Snectaras_reserve (struct snprintf_state *state, size_t n)
8855682Smarkm{
89178825Sdfr    if (state->s + n > state->theend) {
90178825Sdfr	int off = state->s - state->str;
91178825Sdfr	unsigned char *tmp;
9255682Smarkm
93178825Sdfr	if (state->max_sz && state->sz >= state->max_sz)
94178825Sdfr	    return 1;
9555682Smarkm
96178825Sdfr	state->sz = max(state->sz * 2, state->sz + n);
97178825Sdfr	if (state->max_sz)
98178825Sdfr	    state->sz = min(state->sz, state->max_sz);
99178825Sdfr	tmp = realloc (state->str, state->sz);
100178825Sdfr	if (tmp == NULL)
101178825Sdfr	    return 1;
102178825Sdfr	state->str = tmp;
103178825Sdfr	state->s = state->str + off;
104178825Sdfr	state->theend = state->str + state->sz - 1;
105178825Sdfr    }
106178825Sdfr    return 0;
10755682Smarkm}
10855682Smarkm
10990926Snectarstatic void
11090926Snectaras_append_char (struct snprintf_state *state, unsigned char c)
11155682Smarkm{
112178825Sdfr    if(!as_reserve (state, 1))
113178825Sdfr	*state->s++ = c;
11455682Smarkm}
11555682Smarkm
11690926Snectar/* longest integer types */
11790926Snectar
11890926Snectar#ifdef HAVE_LONG_LONG
11990926Snectartypedef unsigned long long u_longest;
12090926Snectartypedef long long longest;
12190926Snectar#else
12290926Snectartypedef unsigned long u_longest;
12390926Snectartypedef long longest;
12490926Snectar#endif
12590926Snectar
12690926Snectar
127178825Sdfr
12855682Smarkmstatic int
129178825Sdfrpad(struct snprintf_state *state, int width, char c)
130178825Sdfr{
131178825Sdfr    int len = 0;
132178825Sdfr    while(width-- > 0){
133178825Sdfr	(*state->append_char)(state,  c);
134178825Sdfr	++len;
135178825Sdfr    }
136178825Sdfr    return len;
137178825Sdfr}
138178825Sdfr
139178825Sdfr/* return true if we should use alternatve hex form */
140178825Sdfrstatic int
14190926Snectaruse_alternative (int flags, u_longest num, unsigned base)
14290926Snectar{
143178825Sdfr    return (flags & alternate_flag) && base == 16 && num != 0;
14490926Snectar}
14590926Snectar
14690926Snectarstatic int
14790926Snectarappend_number(struct snprintf_state *state,
148102644Snectar	      u_longest num, unsigned base, const char *rep,
14955682Smarkm	      int width, int prec, int flags, int minusp)
15055682Smarkm{
151178825Sdfr    int len = 0;
152178825Sdfr    u_longest n = num;
153178825Sdfr    char nstr[64]; /* enough for <192 bit octal integers */
154178825Sdfr    int nstart, nlen;
155178825Sdfr    char signchar;
15655682Smarkm
157178825Sdfr    /* given precision, ignore zero flag */
158178825Sdfr    if(prec != -1)
159178825Sdfr	flags &= ~zero_flag;
160178825Sdfr    else
161178825Sdfr	prec = 1;
162178825Sdfr
163178825Sdfr    /* format number as string */
164178825Sdfr    nstart = sizeof(nstr);
165178825Sdfr    nlen = 0;
166178825Sdfr    nstr[--nstart] = '\0';
167178825Sdfr    do {
168178825Sdfr	assert(nstart > 0);
169178825Sdfr	nstr[--nstart] = rep[n % base];
170178825Sdfr	++nlen;
171178825Sdfr	n /= base;
172178825Sdfr    } while(n);
173178825Sdfr
174178825Sdfr    /* zero value with zero precision should produce no digits */
175178825Sdfr    if(prec == 0 && num == 0) {
176178825Sdfr	nlen--;
177178825Sdfr	nstart++;
17855682Smarkm    }
179178825Sdfr
180178825Sdfr    /* figure out what char to use for sign */
181178825Sdfr    if(minusp)
182178825Sdfr	signchar = '-';
183178825Sdfr    else if((flags & plus_flag))
184178825Sdfr	signchar = '+';
185178825Sdfr    else if((flags & space_flag))
186178825Sdfr	signchar = ' ';
187178825Sdfr    else
188178825Sdfr	signchar = '\0';
189178825Sdfr
190178825Sdfr    if((flags & alternate_flag) && base == 8) {
191178825Sdfr	/* if necessary, increase the precision to
192178825Sdfr	   make first digit a zero */
193178825Sdfr
194178825Sdfr	/* XXX C99 claims (regarding # and %o) that "if the value and
195178825Sdfr           precision are both 0, a single 0 is printed", but there is
196178825Sdfr           no such wording for %x. This would mean that %#.o would
197178825Sdfr           output "0", but %#.x "". This does not make sense, and is
198178825Sdfr           also not what other printf implementations are doing. */
199178825Sdfr
200178825Sdfr	if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0')
201178825Sdfr	    prec = nlen + 1;
20255682Smarkm    }
203178825Sdfr
204178825Sdfr    /* possible formats:
205178825Sdfr       pad | sign | alt | zero | digits
206178825Sdfr       sign | alt | zero | digits | pad   minus_flag
207178825Sdfr       sign | alt | zero | digits zero_flag */
208178825Sdfr
209178825Sdfr    /* if not right justifying or padding with zeros, we need to
210178825Sdfr       compute the length of the rest of the string, and then pad with
211178825Sdfr       spaces */
212178825Sdfr    if(!(flags & (minus_flag | zero_flag))) {
213178825Sdfr	if(prec > nlen)
214178825Sdfr	    width -= prec;
215178825Sdfr	else
216178825Sdfr	    width -= nlen;
217178825Sdfr
218178825Sdfr	if(use_alternative(flags, num, base))
219178825Sdfr	    width -= 2;
220178825Sdfr
221178825Sdfr	if(signchar != '\0')
222178825Sdfr	    width--;
223178825Sdfr
224178825Sdfr	/* pad to width */
225178825Sdfr	len += pad(state, width, ' ');
22655682Smarkm    }
227178825Sdfr    if(signchar != '\0') {
228178825Sdfr	(*state->append_char)(state, signchar);
229178825Sdfr	++len;
230178825Sdfr    }
231178825Sdfr    if(use_alternative(flags, num, base)) {
232178825Sdfr	(*state->append_char)(state, '0');
233178825Sdfr	(*state->append_char)(state, rep[10] + 23); /* XXX */
234178825Sdfr	len += 2;
235178825Sdfr    }
236178825Sdfr    if(flags & zero_flag) {
237178825Sdfr	/* pad to width with zeros */
238178825Sdfr	if(prec - nlen > width - len - nlen)
239178825Sdfr	    len += pad(state, prec - nlen, '0');
240178825Sdfr	else
241178825Sdfr	    len += pad(state, width - len - nlen, '0');
242178825Sdfr    } else
243178825Sdfr	/* pad to prec with zeros */
244178825Sdfr	len += pad(state, prec - nlen, '0');
245178825Sdfr
246178825Sdfr    while(nstr[nstart] != '\0') {
247178825Sdfr	(*state->append_char)(state, nstr[nstart++]);
248178825Sdfr	++len;
249178825Sdfr    }
250178825Sdfr
251178825Sdfr    if(flags & minus_flag)
252178825Sdfr	len += pad(state, width - len, ' ');
253178825Sdfr
254178825Sdfr    return len;
25555682Smarkm}
25655682Smarkm
25790926Snectar/*
25890926Snectar * return length
25990926Snectar */
26090926Snectar
26155682Smarkmstatic int
26290926Snectarappend_string (struct snprintf_state *state,
26390926Snectar	       const unsigned char *arg,
26455682Smarkm	       int width,
26555682Smarkm	       int prec,
26655682Smarkm	       int flags)
26755682Smarkm{
26890926Snectar    int len = 0;
26990926Snectar
27072445Sassar    if(arg == NULL)
27190926Snectar	arg = (const unsigned char*)"(null)";
27272445Sassar
27372445Sassar    if(prec != -1)
27472445Sassar	width -= prec;
27572445Sassar    else
27690926Snectar	width -= strlen((const char *)arg);
27772445Sassar    if(!(flags & minus_flag))
278178825Sdfr	len += pad(state, width, ' ');
279178825Sdfr
28072445Sassar    if (prec != -1) {
28190926Snectar	while (*arg && prec--) {
28290926Snectar	    (*state->append_char) (state, *arg++);
28390926Snectar	    ++len;
28490926Snectar	}
28572445Sassar    } else {
28690926Snectar	while (*arg) {
28790926Snectar	    (*state->append_char) (state, *arg++);
28890926Snectar	    ++len;
28990926Snectar	}
29072445Sassar    }
29172445Sassar    if(flags & minus_flag)
292178825Sdfr	len += pad(state, width, ' ');
29390926Snectar    return len;
29455682Smarkm}
29555682Smarkm
29655682Smarkmstatic int
29790926Snectarappend_char(struct snprintf_state *state,
29855682Smarkm	    unsigned char arg,
29955682Smarkm	    int width,
30055682Smarkm	    int flags)
30155682Smarkm{
302178825Sdfr    int len = 0;
30390926Snectar
304178825Sdfr    while(!(flags & minus_flag) && --width > 0) {
305178825Sdfr	(*state->append_char) (state, ' ')    ;
306178825Sdfr	++len;
307178825Sdfr    }
308178825Sdfr    (*state->append_char) (state, arg);
30990926Snectar    ++len;
310178825Sdfr    while((flags & minus_flag) && --width > 0) {
311178825Sdfr	(*state->append_char) (state, ' ');
312178825Sdfr	++len;
313178825Sdfr    }
314178825Sdfr    return 0;
31555682Smarkm}
31655682Smarkm
31755682Smarkm/*
31855682Smarkm * This can't be made into a function...
31955682Smarkm */
32055682Smarkm
32190926Snectar#ifdef HAVE_LONG_LONG
32290926Snectar
32355682Smarkm#define PARSE_INT_FORMAT(res, arg, unsig) \
32490926Snectarif (long_long_flag) \
32590926Snectar     res = (unsig long long)va_arg(arg, unsig long long); \
32690926Snectarelse if (long_flag) \
32790926Snectar     res = (unsig long)va_arg(arg, unsig long); \
328178825Sdfrelse if (size_t_flag) \
329178825Sdfr     res = (unsig long)va_arg(arg, size_t); \
33090926Snectarelse if (short_flag) \
33190926Snectar     res = (unsig short)va_arg(arg, unsig int); \
33290926Snectarelse \
33390926Snectar     res = (unsig int)va_arg(arg, unsig int)
33490926Snectar
33590926Snectar#else
33690926Snectar
33790926Snectar#define PARSE_INT_FORMAT(res, arg, unsig) \
33855682Smarkmif (long_flag) \
33955682Smarkm     res = (unsig long)va_arg(arg, unsig long); \
340178825Sdfrelse if (size_t_flag) \
341178825Sdfr     res = (unsig long)va_arg(arg, size_t); \
34255682Smarkmelse if (short_flag) \
34357422Smarkm     res = (unsig short)va_arg(arg, unsig int); \
34455682Smarkmelse \
34555682Smarkm     res = (unsig int)va_arg(arg, unsig int)
34655682Smarkm
34790926Snectar#endif
34890926Snectar
34955682Smarkm/*
35090926Snectar * zyxprintf - return length, as snprintf
35155682Smarkm */
35255682Smarkm
35355682Smarkmstatic int
35490926Snectarxyzprintf (struct snprintf_state *state, const char *char_format, va_list ap)
35555682Smarkm{
356178825Sdfr    const unsigned char *format = (const unsigned char *)char_format;
357178825Sdfr    unsigned char c;
358178825Sdfr    int len = 0;
35955682Smarkm
360178825Sdfr    while((c = *format++)) {
361178825Sdfr	if (c == '%') {
362178825Sdfr	    int flags          = 0;
363178825Sdfr	    int width          = 0;
364178825Sdfr	    int prec           = -1;
365178825Sdfr	    int size_t_flag    = 0;
366178825Sdfr	    int long_long_flag = 0;
367178825Sdfr	    int long_flag      = 0;
368178825Sdfr	    int short_flag     = 0;
36955682Smarkm
370178825Sdfr	    /* flags */
371178825Sdfr	    while((c = *format++)){
372178825Sdfr		if(c == '-')
373178825Sdfr		    flags |= minus_flag;
374178825Sdfr		else if(c == '+')
375178825Sdfr		    flags |= plus_flag;
376178825Sdfr		else if(c == ' ')
377178825Sdfr		    flags |= space_flag;
378178825Sdfr		else if(c == '#')
379178825Sdfr		    flags |= alternate_flag;
380178825Sdfr		else if(c == '0')
381178825Sdfr		    flags |= zero_flag;
382178825Sdfr		else if(c == '\'')
383178825Sdfr		    ; /* just ignore */
384178825Sdfr		else
385178825Sdfr		    break;
386178825Sdfr	    }
38755682Smarkm
388178825Sdfr	    if((flags & space_flag) && (flags & plus_flag))
389178825Sdfr		flags ^= space_flag;
39055682Smarkm
391178825Sdfr	    if((flags & minus_flag) && (flags & zero_flag))
392178825Sdfr		flags ^= zero_flag;
39355682Smarkm
394178825Sdfr	    /* width */
395178825Sdfr	    if (isdigit(c))
396178825Sdfr		do {
397178825Sdfr		    width = width * 10 + c - '0';
398178825Sdfr		    c = *format++;
399178825Sdfr		} while(isdigit(c));
400178825Sdfr	    else if(c == '*') {
401178825Sdfr		width = va_arg(ap, int);
402178825Sdfr		c = *format++;
403178825Sdfr	    }
40455682Smarkm
405178825Sdfr	    /* precision */
406178825Sdfr	    if (c == '.') {
407178825Sdfr		prec = 0;
408178825Sdfr		c = *format++;
409178825Sdfr		if (isdigit(c))
410178825Sdfr		    do {
411178825Sdfr			prec = prec * 10 + c - '0';
412178825Sdfr			c = *format++;
413178825Sdfr		    } while(isdigit(c));
414178825Sdfr		else if (c == '*') {
415178825Sdfr		    prec = va_arg(ap, int);
416178825Sdfr		    c = *format++;
417178825Sdfr		}
418178825Sdfr	    }
41955682Smarkm
420178825Sdfr	    /* size */
42155682Smarkm
422178825Sdfr	    if (c == 'h') {
423178825Sdfr		short_flag = 1;
424178825Sdfr		c = *format++;
425178825Sdfr	    } else if (c == 'z') {
426178825Sdfr		size_t_flag = 1;
427178825Sdfr		c = *format++;
428178825Sdfr	    } else if (c == 'l') {
429178825Sdfr		long_flag = 1;
430178825Sdfr		c = *format++;
431178825Sdfr		if (c == 'l') {
432178825Sdfr		    long_long_flag = 1;
433178825Sdfr		    c = *format++;
434178825Sdfr		}
435178825Sdfr	    }
43655682Smarkm
437178825Sdfr	    if(c != 'd' && c != 'i')
438178825Sdfr		flags &= ~(plus_flag | space_flag);
43955682Smarkm
440178825Sdfr	    switch (c) {
441178825Sdfr	    case 'c' :
442178825Sdfr		append_char(state, va_arg(ap, int), width, flags);
443178825Sdfr		++len;
444178825Sdfr		break;
445178825Sdfr	    case 's' :
446178825Sdfr		len += append_string(state,
447178825Sdfr				     va_arg(ap, unsigned char*),
448178825Sdfr				     width,
449178825Sdfr				     prec,
450178825Sdfr				     flags);
451178825Sdfr		break;
452178825Sdfr	    case 'd' :
453178825Sdfr	    case 'i' : {
454178825Sdfr		longest arg;
455178825Sdfr		u_longest num;
456178825Sdfr		int minusp = 0;
45755682Smarkm
458178825Sdfr		PARSE_INT_FORMAT(arg, ap, signed);
45955682Smarkm
460178825Sdfr		if (arg < 0) {
461178825Sdfr		    minusp = 1;
462178825Sdfr		    num = -arg;
463178825Sdfr		} else
464178825Sdfr		    num = arg;
46555682Smarkm
466178825Sdfr		len += append_number (state, num, 10, "0123456789",
467178825Sdfr				      width, prec, flags, minusp);
468178825Sdfr		break;
469178825Sdfr	    }
470178825Sdfr	    case 'u' : {
471178825Sdfr		u_longest arg;
47255682Smarkm
473178825Sdfr		PARSE_INT_FORMAT(arg, ap, unsigned);
47455682Smarkm
475178825Sdfr		len += append_number (state, arg, 10, "0123456789",
476178825Sdfr				      width, prec, flags, 0);
477178825Sdfr		break;
478178825Sdfr	    }
479178825Sdfr	    case 'o' : {
480178825Sdfr		u_longest arg;
48155682Smarkm
482178825Sdfr		PARSE_INT_FORMAT(arg, ap, unsigned);
48355682Smarkm
484178825Sdfr		len += append_number (state, arg, 010, "01234567",
485178825Sdfr				      width, prec, flags, 0);
486178825Sdfr		break;
487178825Sdfr	    }
488178825Sdfr	    case 'x' : {
489178825Sdfr		u_longest arg;
49055682Smarkm
491178825Sdfr		PARSE_INT_FORMAT(arg, ap, unsigned);
49255682Smarkm
493178825Sdfr		len += append_number (state, arg, 0x10, "0123456789abcdef",
494178825Sdfr				      width, prec, flags, 0);
495178825Sdfr		break;
496178825Sdfr	    }
497178825Sdfr	    case 'X' :{
498178825Sdfr		u_longest arg;
49955682Smarkm
500178825Sdfr		PARSE_INT_FORMAT(arg, ap, unsigned);
50155682Smarkm
502178825Sdfr		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
503178825Sdfr				      width, prec, flags, 0);
504178825Sdfr		break;
505178825Sdfr	    }
506178825Sdfr	    case 'p' : {
507178825Sdfr		unsigned long arg = (unsigned long)va_arg(ap, void*);
508178825Sdfr
509178825Sdfr		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
510178825Sdfr				      width, prec, flags, 0);
511178825Sdfr		break;
512178825Sdfr	    }
513178825Sdfr	    case 'n' : {
514178825Sdfr		int *arg = va_arg(ap, int*);
515178825Sdfr		*arg = state->s - state->str;
516178825Sdfr		break;
517178825Sdfr	    }
518178825Sdfr	    case '\0' :
519178825Sdfr		--format;
520178825Sdfr		/* FALLTHROUGH */
521178825Sdfr	    case '%' :
522178825Sdfr		(*state->append_char)(state, c);
523178825Sdfr		++len;
524178825Sdfr		break;
525178825Sdfr	    default :
526178825Sdfr		(*state->append_char)(state, '%');
527178825Sdfr		(*state->append_char)(state, c);
528178825Sdfr		len += 2;
529178825Sdfr		break;
530178825Sdfr	    }
531178825Sdfr	} else {
532178825Sdfr	    (*state->append_char) (state, c);
533178825Sdfr	    ++len;
534178825Sdfr	}
53590926Snectar    }
536178825Sdfr    return len;
53755682Smarkm}
53855682Smarkm
53990926Snectar#if !defined(HAVE_SNPRINTF) || defined(TEST_SNPRINTF)
540178825Sdfrint ROKEN_LIB_FUNCTION
54155682Smarkmsnprintf (char *str, size_t sz, const char *format, ...)
54255682Smarkm{
543178825Sdfr    va_list args;
544178825Sdfr    int ret;
54555682Smarkm
546178825Sdfr    va_start(args, format);
547178825Sdfr    ret = vsnprintf (str, sz, format, args);
548178825Sdfr    va_end(args);
54955682Smarkm
55055682Smarkm#ifdef PARANOIA
551178825Sdfr    {
552178825Sdfr	int ret2;
553178825Sdfr	char *tmp;
55455682Smarkm
555178825Sdfr	tmp = malloc (sz);
556178825Sdfr	if (tmp == NULL)
557178825Sdfr	    abort ();
55855682Smarkm
559178825Sdfr	va_start(args, format);
560178825Sdfr	ret2 = vsprintf (tmp, format, args);
561178825Sdfr	va_end(args);
562178825Sdfr	if (ret != ret2 || strcmp(str, tmp))
563178825Sdfr	    abort ();
564178825Sdfr	free (tmp);
565178825Sdfr    }
56655682Smarkm#endif
56755682Smarkm
568178825Sdfr    return ret;
56955682Smarkm}
57055682Smarkm#endif
57155682Smarkm
57290926Snectar#if !defined(HAVE_ASPRINTF) || defined(TEST_SNPRINTF)
573178825Sdfrint ROKEN_LIB_FUNCTION
57455682Smarkmasprintf (char **ret, const char *format, ...)
57555682Smarkm{
576178825Sdfr    va_list args;
577178825Sdfr    int val;
57855682Smarkm
579178825Sdfr    va_start(args, format);
580178825Sdfr    val = vasprintf (ret, format, args);
581178825Sdfr    va_end(args);
58255682Smarkm
58355682Smarkm#ifdef PARANOIA
584178825Sdfr    {
585178825Sdfr	int ret2;
586178825Sdfr	char *tmp;
587178825Sdfr	tmp = malloc (val + 1);
588178825Sdfr	if (tmp == NULL)
589178825Sdfr	    abort ();
59055682Smarkm
591178825Sdfr	va_start(args, format);
592178825Sdfr	ret2 = vsprintf (tmp, format, args);
593178825Sdfr	va_end(args);
594178825Sdfr	if (val != ret2 || strcmp(*ret, tmp))
595178825Sdfr	    abort ();
596178825Sdfr	free (tmp);
597178825Sdfr    }
59855682Smarkm#endif
59955682Smarkm
600178825Sdfr    return val;
60155682Smarkm}
60255682Smarkm#endif
60355682Smarkm
60490926Snectar#if !defined(HAVE_ASNPRINTF) || defined(TEST_SNPRINTF)
605178825Sdfrint ROKEN_LIB_FUNCTION
60655682Smarkmasnprintf (char **ret, size_t max_sz, const char *format, ...)
60755682Smarkm{
608178825Sdfr    va_list args;
609178825Sdfr    int val;
61055682Smarkm
611178825Sdfr    va_start(args, format);
612178825Sdfr    val = vasnprintf (ret, max_sz, format, args);
61355682Smarkm
61455682Smarkm#ifdef PARANOIA
615178825Sdfr    {
616178825Sdfr	int ret2;
617178825Sdfr	char *tmp;
618178825Sdfr	tmp = malloc (val + 1);
619178825Sdfr	if (tmp == NULL)
620178825Sdfr	    abort ();
62155682Smarkm
622178825Sdfr	ret2 = vsprintf (tmp, format, args);
623178825Sdfr	if (val != ret2 || strcmp(*ret, tmp))
624178825Sdfr	    abort ();
625178825Sdfr	free (tmp);
626178825Sdfr    }
62755682Smarkm#endif
62855682Smarkm
629178825Sdfr    va_end(args);
630178825Sdfr    return val;
63155682Smarkm}
63255682Smarkm#endif
63355682Smarkm
63490926Snectar#if !defined(HAVE_VASPRINTF) || defined(TEST_SNPRINTF)
635178825Sdfrint ROKEN_LIB_FUNCTION
63655682Smarkmvasprintf (char **ret, const char *format, va_list args)
63755682Smarkm{
638178825Sdfr    return vasnprintf (ret, 0, format, args);
63955682Smarkm}
64055682Smarkm#endif
64155682Smarkm
64255682Smarkm
64390926Snectar#if !defined(HAVE_VASNPRINTF) || defined(TEST_SNPRINTF)
644178825Sdfrint ROKEN_LIB_FUNCTION
64555682Smarkmvasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
64655682Smarkm{
647178825Sdfr    int st;
648178825Sdfr    struct snprintf_state state;
64955682Smarkm
650178825Sdfr    state.max_sz = max_sz;
651178825Sdfr    state.sz     = 1;
652178825Sdfr    state.str    = malloc(state.sz);
653178825Sdfr    if (state.str == NULL) {
654178825Sdfr	*ret = NULL;
655178825Sdfr	return -1;
656178825Sdfr    }
657178825Sdfr    state.s = state.str;
658178825Sdfr    state.theend = state.s + state.sz - 1;
659178825Sdfr    state.append_char = as_append_char;
66055682Smarkm
661178825Sdfr    st = xyzprintf (&state, format, args);
662178825Sdfr    if (st > state.sz) {
663178825Sdfr	free (state.str);
664178825Sdfr	*ret = NULL;
665178825Sdfr	return -1;
666178825Sdfr    } else {
667178825Sdfr	char *tmp;
66855682Smarkm
669178825Sdfr	*state.s = '\0';
670178825Sdfr	tmp = realloc (state.str, st+1);
671178825Sdfr	if (tmp == NULL) {
672178825Sdfr	    free (state.str);
673178825Sdfr	    *ret = NULL;
674178825Sdfr	    return -1;
675178825Sdfr	}
676178825Sdfr	*ret = tmp;
677178825Sdfr	return st;
67855682Smarkm    }
67955682Smarkm}
68055682Smarkm#endif
68155682Smarkm
68290926Snectar#if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
683178825Sdfrint ROKEN_LIB_FUNCTION
68455682Smarkmvsnprintf (char *str, size_t sz, const char *format, va_list args)
68555682Smarkm{
686178825Sdfr    struct snprintf_state state;
687178825Sdfr    int ret;
688178825Sdfr    unsigned char *ustr = (unsigned char *)str;
68955682Smarkm
690178825Sdfr    state.max_sz = 0;
691178825Sdfr    state.sz     = sz;
692178825Sdfr    state.str    = ustr;
693178825Sdfr    state.s      = ustr;
694178825Sdfr    state.theend = ustr + sz - (sz > 0);
695178825Sdfr    state.append_char = sn_append_char;
69655682Smarkm
697178825Sdfr    ret = xyzprintf (&state, format, args);
698178825Sdfr    if (state.s != NULL && sz != 0)
699178825Sdfr	*state.s = '\0';
700178825Sdfr    return ret;
70155682Smarkm}
70255682Smarkm#endif
703