xref: /freebsd/crypto/heimdal/lib/roken/snprintf.c (revision c19800e8)
1b528cefcSMark Murray /*
2bbd80c28SJacques Vidrine  * Copyright (c) 1995-2003 Kungliga Tekniska H�gskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #ifdef HAVE_CONFIG_H
35b528cefcSMark Murray #include <config.h>
36c19800e8SDoug Rabson RCSID("$Id: snprintf.c 21005 2007-06-08 01:54:35Z lha $");
37b528cefcSMark Murray #endif
38c19800e8SDoug Rabson #if defined(TEST_SNPRINTF)
39c19800e8SDoug Rabson #include "snprintf-test.h"
40c19800e8SDoug Rabson #endif /* TEST_SNPRINTF */
41b528cefcSMark Murray #include <stdio.h>
42b528cefcSMark Murray #include <stdarg.h>
43b528cefcSMark Murray #include <stdlib.h>
44b528cefcSMark Murray #include <string.h>
45b528cefcSMark Murray #include <ctype.h>
46c19800e8SDoug Rabson #include "roken.h"
47c19800e8SDoug Rabson #include <assert.h>
48b528cefcSMark Murray 
49b528cefcSMark Murray enum format_flags {
50b528cefcSMark Murray     minus_flag     =  1,
51b528cefcSMark Murray     plus_flag      =  2,
52b528cefcSMark Murray     space_flag     =  4,
53b528cefcSMark Murray     alternate_flag =  8,
54b528cefcSMark Murray     zero_flag      = 16
55b528cefcSMark Murray };
56b528cefcSMark Murray 
57b528cefcSMark Murray /*
58b528cefcSMark Murray  * Common state
59b528cefcSMark Murray  */
60b528cefcSMark Murray 
614137ff4cSJacques Vidrine struct snprintf_state {
62b528cefcSMark Murray     unsigned char *str;
63b528cefcSMark Murray     unsigned char *s;
64b528cefcSMark Murray     unsigned char *theend;
65b528cefcSMark Murray     size_t sz;
66b528cefcSMark Murray     size_t max_sz;
674137ff4cSJacques Vidrine     void (*append_char)(struct snprintf_state *, unsigned char);
68b528cefcSMark Murray     /* XXX - methods */
69b528cefcSMark Murray };
70b528cefcSMark Murray 
714137ff4cSJacques Vidrine #if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
72b528cefcSMark Murray static int
734137ff4cSJacques Vidrine sn_reserve (struct snprintf_state *state, size_t n)
74b528cefcSMark Murray {
75b528cefcSMark Murray     return state->s + n > state->theend;
76b528cefcSMark Murray }
77b528cefcSMark Murray 
784137ff4cSJacques Vidrine static void
794137ff4cSJacques Vidrine sn_append_char (struct snprintf_state *state, unsigned char c)
80b528cefcSMark Murray {
814137ff4cSJacques Vidrine     if (!sn_reserve (state, 1))
82b528cefcSMark Murray 	*state->s++ = c;
83b528cefcSMark Murray }
84b528cefcSMark Murray #endif
85b528cefcSMark Murray 
86b528cefcSMark Murray static int
874137ff4cSJacques Vidrine as_reserve (struct snprintf_state *state, size_t n)
88b528cefcSMark Murray {
89b528cefcSMark Murray     if (state->s + n > state->theend) {
90b528cefcSMark Murray 	int off = state->s - state->str;
91b528cefcSMark Murray 	unsigned char *tmp;
92b528cefcSMark Murray 
93b528cefcSMark Murray 	if (state->max_sz && state->sz >= state->max_sz)
94b528cefcSMark Murray 	    return 1;
95b528cefcSMark Murray 
96b528cefcSMark Murray 	state->sz = max(state->sz * 2, state->sz + n);
97b528cefcSMark Murray 	if (state->max_sz)
98b528cefcSMark Murray 	    state->sz = min(state->sz, state->max_sz);
99b528cefcSMark Murray 	tmp = realloc (state->str, state->sz);
100b528cefcSMark Murray 	if (tmp == NULL)
101b528cefcSMark Murray 	    return 1;
102b528cefcSMark Murray 	state->str = tmp;
103b528cefcSMark Murray 	state->s = state->str + off;
104b528cefcSMark Murray 	state->theend = state->str + state->sz - 1;
105b528cefcSMark Murray     }
106b528cefcSMark Murray     return 0;
107b528cefcSMark Murray }
108b528cefcSMark Murray 
1094137ff4cSJacques Vidrine static void
1104137ff4cSJacques Vidrine as_append_char (struct snprintf_state *state, unsigned char c)
111b528cefcSMark Murray {
1124137ff4cSJacques Vidrine     if(!as_reserve (state, 1))
113b528cefcSMark Murray 	*state->s++ = c;
114b528cefcSMark Murray }
1154137ff4cSJacques Vidrine 
1164137ff4cSJacques Vidrine /* longest integer types */
1174137ff4cSJacques Vidrine 
1184137ff4cSJacques Vidrine #ifdef HAVE_LONG_LONG
1194137ff4cSJacques Vidrine typedef unsigned long long u_longest;
1204137ff4cSJacques Vidrine typedef long long longest;
1214137ff4cSJacques Vidrine #else
1224137ff4cSJacques Vidrine typedef unsigned long u_longest;
1234137ff4cSJacques Vidrine typedef long longest;
1244137ff4cSJacques Vidrine #endif
1254137ff4cSJacques Vidrine 
1264137ff4cSJacques Vidrine 
127c19800e8SDoug Rabson 
128c19800e8SDoug Rabson static int
129c19800e8SDoug Rabson pad(struct snprintf_state *state, int width, char c)
130c19800e8SDoug Rabson {
131c19800e8SDoug Rabson     int len = 0;
132c19800e8SDoug Rabson     while(width-- > 0){
133c19800e8SDoug Rabson 	(*state->append_char)(state,  c);
134c19800e8SDoug Rabson 	++len;
135c19800e8SDoug Rabson     }
136c19800e8SDoug Rabson     return len;
137c19800e8SDoug Rabson }
138c19800e8SDoug Rabson 
139c19800e8SDoug Rabson /* return true if we should use alternatve hex form */
1404137ff4cSJacques Vidrine static int
1414137ff4cSJacques Vidrine use_alternative (int flags, u_longest num, unsigned base)
1424137ff4cSJacques Vidrine {
143c19800e8SDoug Rabson     return (flags & alternate_flag) && base == 16 && num != 0;
144b528cefcSMark Murray }
145b528cefcSMark Murray 
146b528cefcSMark Murray static int
1474137ff4cSJacques Vidrine append_number(struct snprintf_state *state,
1488373020dSJacques Vidrine 	      u_longest num, unsigned base, const char *rep,
149b528cefcSMark Murray 	      int width, int prec, int flags, int minusp)
150b528cefcSMark Murray {
151b528cefcSMark Murray     int len = 0;
1524137ff4cSJacques Vidrine     u_longest n = num;
153c19800e8SDoug Rabson     char nstr[64]; /* enough for <192 bit octal integers */
154c19800e8SDoug Rabson     int nstart, nlen;
155c19800e8SDoug Rabson     char signchar;
156b528cefcSMark Murray 
157b528cefcSMark Murray     /* given precision, ignore zero flag */
158b528cefcSMark Murray     if(prec != -1)
159b528cefcSMark Murray 	flags &= ~zero_flag;
160b528cefcSMark Murray     else
161b528cefcSMark Murray 	prec = 1;
162c19800e8SDoug Rabson 
163c19800e8SDoug Rabson     /* format number as string */
164c19800e8SDoug Rabson     nstart = sizeof(nstr);
165c19800e8SDoug Rabson     nlen = 0;
166c19800e8SDoug Rabson     nstr[--nstart] = '\0';
167b528cefcSMark Murray     do {
168c19800e8SDoug Rabson 	assert(nstart > 0);
169c19800e8SDoug Rabson 	nstr[--nstart] = rep[n % base];
170c19800e8SDoug Rabson 	++nlen;
1714137ff4cSJacques Vidrine 	n /= base;
1724137ff4cSJacques Vidrine     } while(n);
173c19800e8SDoug Rabson 
174c19800e8SDoug Rabson     /* zero value with zero precision should produce no digits */
175c19800e8SDoug Rabson     if(prec == 0 && num == 0) {
176c19800e8SDoug Rabson 	nlen--;
177c19800e8SDoug Rabson 	nstart++;
178b528cefcSMark Murray     }
179c19800e8SDoug Rabson 
180c19800e8SDoug Rabson     /* figure out what char to use for sign */
181c19800e8SDoug Rabson     if(minusp)
182c19800e8SDoug Rabson 	signchar = '-';
183c19800e8SDoug Rabson     else if((flags & plus_flag))
184c19800e8SDoug Rabson 	signchar = '+';
185c19800e8SDoug Rabson     else if((flags & space_flag))
186c19800e8SDoug Rabson 	signchar = ' ';
187c19800e8SDoug Rabson     else
188c19800e8SDoug Rabson 	signchar = '\0';
189c19800e8SDoug Rabson 
190c19800e8SDoug Rabson     if((flags & alternate_flag) && base == 8) {
191c19800e8SDoug Rabson 	/* if necessary, increase the precision to
192c19800e8SDoug Rabson 	   make first digit a zero */
193c19800e8SDoug Rabson 
194c19800e8SDoug Rabson 	/* XXX C99 claims (regarding # and %o) that "if the value and
195c19800e8SDoug Rabson            precision are both 0, a single 0 is printed", but there is
196c19800e8SDoug Rabson            no such wording for %x. This would mean that %#.o would
197c19800e8SDoug Rabson            output "0", but %#.x "". This does not make sense, and is
198c19800e8SDoug Rabson            also not what other printf implementations are doing. */
199c19800e8SDoug Rabson 
200c19800e8SDoug Rabson 	if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0')
201c19800e8SDoug Rabson 	    prec = nlen + 1;
202c19800e8SDoug Rabson     }
203c19800e8SDoug Rabson 
204c19800e8SDoug Rabson     /* possible formats:
205c19800e8SDoug Rabson        pad | sign | alt | zero | digits
206c19800e8SDoug Rabson        sign | alt | zero | digits | pad   minus_flag
207c19800e8SDoug Rabson        sign | alt | zero | digits zero_flag */
208c19800e8SDoug Rabson 
209c19800e8SDoug Rabson     /* if not right justifying or padding with zeros, we need to
210c19800e8SDoug Rabson        compute the length of the rest of the string, and then pad with
211c19800e8SDoug Rabson        spaces */
212c19800e8SDoug Rabson     if(!(flags & (minus_flag | zero_flag))) {
213c19800e8SDoug Rabson 	if(prec > nlen)
214c19800e8SDoug Rabson 	    width -= prec;
215c19800e8SDoug Rabson 	else
216c19800e8SDoug Rabson 	    width -= nlen;
217c19800e8SDoug Rabson 
2184137ff4cSJacques Vidrine 	if(use_alternative(flags, num, base))
219c19800e8SDoug Rabson 	    width -= 2;
220c19800e8SDoug Rabson 
221c19800e8SDoug Rabson 	if(signchar != '\0')
222b528cefcSMark Murray 	    width--;
223c19800e8SDoug Rabson 
224c19800e8SDoug Rabson 	/* pad to width */
225c19800e8SDoug Rabson 	len += pad(state, width, ' ');
226b528cefcSMark Murray     }
227c19800e8SDoug Rabson     if(signchar != '\0') {
228c19800e8SDoug Rabson 	(*state->append_char)(state, signchar);
229c19800e8SDoug Rabson 	++len;
230b528cefcSMark Murray     }
2314137ff4cSJacques Vidrine     if(use_alternative(flags, num, base)) {
2324137ff4cSJacques Vidrine 	(*state->append_char)(state, '0');
233c19800e8SDoug Rabson 	(*state->append_char)(state, rep[10] + 23); /* XXX */
234c19800e8SDoug Rabson 	len += 2;
235b528cefcSMark Murray     }
236c19800e8SDoug Rabson     if(flags & zero_flag) {
237c19800e8SDoug Rabson 	/* pad to width with zeros */
238c19800e8SDoug Rabson 	if(prec - nlen > width - len - nlen)
239c19800e8SDoug Rabson 	    len += pad(state, prec - nlen, '0');
240c19800e8SDoug Rabson 	else
241c19800e8SDoug Rabson 	    len += pad(state, width - len - nlen, '0');
242c19800e8SDoug Rabson     } else
243c19800e8SDoug Rabson 	/* pad to prec with zeros */
244c19800e8SDoug Rabson 	len += pad(state, prec - nlen, '0');
245c19800e8SDoug Rabson 
246c19800e8SDoug Rabson     while(nstr[nstart] != '\0') {
247c19800e8SDoug Rabson 	(*state->append_char)(state, nstr[nstart++]);
2484137ff4cSJacques Vidrine 	++len;
249b528cefcSMark Murray     }
250c19800e8SDoug Rabson 
251b528cefcSMark Murray     if(flags & minus_flag)
252c19800e8SDoug Rabson 	len += pad(state, width - len, ' ');
253c19800e8SDoug Rabson 
2544137ff4cSJacques Vidrine     return len;
255b528cefcSMark Murray }
256b528cefcSMark Murray 
2574137ff4cSJacques Vidrine /*
2584137ff4cSJacques Vidrine  * return length
2594137ff4cSJacques Vidrine  */
2604137ff4cSJacques Vidrine 
261b528cefcSMark Murray static int
2624137ff4cSJacques Vidrine append_string (struct snprintf_state *state,
2634137ff4cSJacques Vidrine 	       const unsigned char *arg,
264b528cefcSMark Murray 	       int width,
265b528cefcSMark Murray 	       int prec,
266b528cefcSMark Murray 	       int flags)
267b528cefcSMark Murray {
2684137ff4cSJacques Vidrine     int len = 0;
2694137ff4cSJacques Vidrine 
2705e9cd1aeSAssar Westerlund     if(arg == NULL)
2714137ff4cSJacques Vidrine 	arg = (const unsigned char*)"(null)";
2725e9cd1aeSAssar Westerlund 
273b528cefcSMark Murray     if(prec != -1)
274b528cefcSMark Murray 	width -= prec;
275b528cefcSMark Murray     else
2764137ff4cSJacques Vidrine 	width -= strlen((const char *)arg);
277b528cefcSMark Murray     if(!(flags & minus_flag))
278c19800e8SDoug Rabson 	len += pad(state, width, ' ');
279c19800e8SDoug Rabson 
280b528cefcSMark Murray     if (prec != -1) {
2814137ff4cSJacques Vidrine 	while (*arg && prec--) {
2824137ff4cSJacques Vidrine 	    (*state->append_char) (state, *arg++);
2834137ff4cSJacques Vidrine 	    ++len;
2844137ff4cSJacques Vidrine 	}
285b528cefcSMark Murray     } else {
2864137ff4cSJacques Vidrine 	while (*arg) {
2874137ff4cSJacques Vidrine 	    (*state->append_char) (state, *arg++);
2884137ff4cSJacques Vidrine 	    ++len;
2894137ff4cSJacques Vidrine 	}
290b528cefcSMark Murray     }
291b528cefcSMark Murray     if(flags & minus_flag)
292c19800e8SDoug Rabson 	len += pad(state, width, ' ');
2934137ff4cSJacques Vidrine     return len;
294b528cefcSMark Murray }
295b528cefcSMark Murray 
296b528cefcSMark Murray static int
2974137ff4cSJacques Vidrine append_char(struct snprintf_state *state,
298b528cefcSMark Murray 	    unsigned char arg,
299b528cefcSMark Murray 	    int width,
300b528cefcSMark Murray 	    int flags)
301b528cefcSMark Murray {
3024137ff4cSJacques Vidrine     int len = 0;
303b528cefcSMark Murray 
3044137ff4cSJacques Vidrine     while(!(flags & minus_flag) && --width > 0) {
3054137ff4cSJacques Vidrine 	(*state->append_char) (state, ' ')    ;
3064137ff4cSJacques Vidrine 	++len;
3074137ff4cSJacques Vidrine     }
3084137ff4cSJacques Vidrine     (*state->append_char) (state, arg);
3094137ff4cSJacques Vidrine     ++len;
3104137ff4cSJacques Vidrine     while((flags & minus_flag) && --width > 0) {
3114137ff4cSJacques Vidrine 	(*state->append_char) (state, ' ');
3124137ff4cSJacques Vidrine 	++len;
3134137ff4cSJacques Vidrine     }
314b528cefcSMark Murray     return 0;
315b528cefcSMark Murray }
316b528cefcSMark Murray 
317b528cefcSMark Murray /*
318b528cefcSMark Murray  * This can't be made into a function...
319b528cefcSMark Murray  */
320b528cefcSMark Murray 
3214137ff4cSJacques Vidrine #ifdef HAVE_LONG_LONG
3224137ff4cSJacques Vidrine 
3234137ff4cSJacques Vidrine #define PARSE_INT_FORMAT(res, arg, unsig) \
3244137ff4cSJacques Vidrine if (long_long_flag) \
3254137ff4cSJacques Vidrine      res = (unsig long long)va_arg(arg, unsig long long); \
3264137ff4cSJacques Vidrine else if (long_flag) \
3274137ff4cSJacques Vidrine      res = (unsig long)va_arg(arg, unsig long); \
328c19800e8SDoug Rabson else if (size_t_flag) \
329c19800e8SDoug Rabson      res = (unsig long)va_arg(arg, size_t); \
3304137ff4cSJacques Vidrine else if (short_flag) \
3314137ff4cSJacques Vidrine      res = (unsig short)va_arg(arg, unsig int); \
3324137ff4cSJacques Vidrine else \
3334137ff4cSJacques Vidrine      res = (unsig int)va_arg(arg, unsig int)
3344137ff4cSJacques Vidrine 
3354137ff4cSJacques Vidrine #else
3364137ff4cSJacques Vidrine 
337b528cefcSMark Murray #define PARSE_INT_FORMAT(res, arg, unsig) \
338b528cefcSMark Murray if (long_flag) \
339b528cefcSMark Murray      res = (unsig long)va_arg(arg, unsig long); \
340c19800e8SDoug Rabson else if (size_t_flag) \
341c19800e8SDoug Rabson      res = (unsig long)va_arg(arg, size_t); \
342b528cefcSMark Murray else if (short_flag) \
343d61f1c79SMark Murray      res = (unsig short)va_arg(arg, unsig int); \
344b528cefcSMark Murray else \
345b528cefcSMark Murray      res = (unsig int)va_arg(arg, unsig int)
346b528cefcSMark Murray 
3474137ff4cSJacques Vidrine #endif
3484137ff4cSJacques Vidrine 
349b528cefcSMark Murray /*
3504137ff4cSJacques Vidrine  * zyxprintf - return length, as snprintf
351b528cefcSMark Murray  */
352b528cefcSMark Murray 
353b528cefcSMark Murray static int
3544137ff4cSJacques Vidrine xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap)
355b528cefcSMark Murray {
356b528cefcSMark Murray     const unsigned char *format = (const unsigned char *)char_format;
357b528cefcSMark Murray     unsigned char c;
3584137ff4cSJacques Vidrine     int len = 0;
359b528cefcSMark Murray 
360b528cefcSMark Murray     while((c = *format++)) {
361b528cefcSMark Murray 	if (c == '%') {
362b528cefcSMark Murray 	    int flags          = 0;
363b528cefcSMark Murray 	    int width          = 0;
364b528cefcSMark Murray 	    int prec           = -1;
365c19800e8SDoug Rabson 	    int size_t_flag    = 0;
3664137ff4cSJacques Vidrine 	    int long_long_flag = 0;
367b528cefcSMark Murray 	    int long_flag      = 0;
368b528cefcSMark Murray 	    int short_flag     = 0;
369b528cefcSMark Murray 
370b528cefcSMark Murray 	    /* flags */
371b528cefcSMark Murray 	    while((c = *format++)){
372b528cefcSMark Murray 		if(c == '-')
373b528cefcSMark Murray 		    flags |= minus_flag;
374b528cefcSMark Murray 		else if(c == '+')
375b528cefcSMark Murray 		    flags |= plus_flag;
376b528cefcSMark Murray 		else if(c == ' ')
377b528cefcSMark Murray 		    flags |= space_flag;
378b528cefcSMark Murray 		else if(c == '#')
379b528cefcSMark Murray 		    flags |= alternate_flag;
380b528cefcSMark Murray 		else if(c == '0')
381b528cefcSMark Murray 		    flags |= zero_flag;
382bbd80c28SJacques Vidrine 		else if(c == '\'')
383bbd80c28SJacques Vidrine 		    ; /* just ignore */
384b528cefcSMark Murray 		else
385b528cefcSMark Murray 		    break;
386b528cefcSMark Murray 	    }
387b528cefcSMark Murray 
388b528cefcSMark Murray 	    if((flags & space_flag) && (flags & plus_flag))
389b528cefcSMark Murray 		flags ^= space_flag;
390b528cefcSMark Murray 
391b528cefcSMark Murray 	    if((flags & minus_flag) && (flags & zero_flag))
392b528cefcSMark Murray 		flags ^= zero_flag;
393b528cefcSMark Murray 
394b528cefcSMark Murray 	    /* width */
395b528cefcSMark Murray 	    if (isdigit(c))
396b528cefcSMark Murray 		do {
397b528cefcSMark Murray 		    width = width * 10 + c - '0';
398b528cefcSMark Murray 		    c = *format++;
399b528cefcSMark Murray 		} while(isdigit(c));
400b528cefcSMark Murray 	    else if(c == '*') {
401b528cefcSMark Murray 		width = va_arg(ap, int);
402b528cefcSMark Murray 		c = *format++;
403b528cefcSMark Murray 	    }
404b528cefcSMark Murray 
405b528cefcSMark Murray 	    /* precision */
406b528cefcSMark Murray 	    if (c == '.') {
407b528cefcSMark Murray 		prec = 0;
408b528cefcSMark Murray 		c = *format++;
409b528cefcSMark Murray 		if (isdigit(c))
410b528cefcSMark Murray 		    do {
411b528cefcSMark Murray 			prec = prec * 10 + c - '0';
412b528cefcSMark Murray 			c = *format++;
413b528cefcSMark Murray 		    } while(isdigit(c));
414b528cefcSMark Murray 		else if (c == '*') {
415b528cefcSMark Murray 		    prec = va_arg(ap, int);
416b528cefcSMark Murray 		    c = *format++;
417b528cefcSMark Murray 		}
418b528cefcSMark Murray 	    }
419b528cefcSMark Murray 
420b528cefcSMark Murray 	    /* size */
421b528cefcSMark Murray 
422b528cefcSMark Murray 	    if (c == 'h') {
423b528cefcSMark Murray 		short_flag = 1;
424b528cefcSMark Murray 		c = *format++;
425c19800e8SDoug Rabson 	    } else if (c == 'z') {
426c19800e8SDoug Rabson 		size_t_flag = 1;
427c19800e8SDoug Rabson 		c = *format++;
428b528cefcSMark Murray 	    } else if (c == 'l') {
429b528cefcSMark Murray 		long_flag = 1;
430b528cefcSMark Murray 		c = *format++;
4314137ff4cSJacques Vidrine 		if (c == 'l') {
4324137ff4cSJacques Vidrine 		    long_long_flag = 1;
4334137ff4cSJacques Vidrine 		    c = *format++;
4344137ff4cSJacques Vidrine 		}
435b528cefcSMark Murray 	    }
436b528cefcSMark Murray 
437c19800e8SDoug Rabson 	    if(c != 'd' && c != 'i')
438c19800e8SDoug Rabson 		flags &= ~(plus_flag | space_flag);
439c19800e8SDoug Rabson 
440b528cefcSMark Murray 	    switch (c) {
441b528cefcSMark Murray 	    case 'c' :
4424137ff4cSJacques Vidrine 		append_char(state, va_arg(ap, int), width, flags);
4434137ff4cSJacques Vidrine 		++len;
444b528cefcSMark Murray 		break;
445b528cefcSMark Murray 	    case 's' :
4464137ff4cSJacques Vidrine 		len += append_string(state,
447b528cefcSMark Murray 				     va_arg(ap, unsigned char*),
448b528cefcSMark Murray 				     width,
449b528cefcSMark Murray 				     prec,
4504137ff4cSJacques Vidrine 				     flags);
451b528cefcSMark Murray 		break;
452b528cefcSMark Murray 	    case 'd' :
453b528cefcSMark Murray 	    case 'i' : {
4544137ff4cSJacques Vidrine 		longest arg;
4554137ff4cSJacques Vidrine 		u_longest num;
456b528cefcSMark Murray 		int minusp = 0;
457b528cefcSMark Murray 
458b528cefcSMark Murray 		PARSE_INT_FORMAT(arg, ap, signed);
459b528cefcSMark Murray 
460b528cefcSMark Murray 		if (arg < 0) {
461b528cefcSMark Murray 		    minusp = 1;
462b528cefcSMark Murray 		    num = -arg;
463b528cefcSMark Murray 		} else
464b528cefcSMark Murray 		    num = arg;
465b528cefcSMark Murray 
4664137ff4cSJacques Vidrine 		len += append_number (state, num, 10, "0123456789",
4674137ff4cSJacques Vidrine 				      width, prec, flags, minusp);
468b528cefcSMark Murray 		break;
469b528cefcSMark Murray 	    }
470b528cefcSMark Murray 	    case 'u' : {
4714137ff4cSJacques Vidrine 		u_longest arg;
472b528cefcSMark Murray 
473b528cefcSMark Murray 		PARSE_INT_FORMAT(arg, ap, unsigned);
474b528cefcSMark Murray 
4754137ff4cSJacques Vidrine 		len += append_number (state, arg, 10, "0123456789",
4764137ff4cSJacques Vidrine 				      width, prec, flags, 0);
477b528cefcSMark Murray 		break;
478b528cefcSMark Murray 	    }
479b528cefcSMark Murray 	    case 'o' : {
4804137ff4cSJacques Vidrine 		u_longest arg;
481b528cefcSMark Murray 
482b528cefcSMark Murray 		PARSE_INT_FORMAT(arg, ap, unsigned);
483b528cefcSMark Murray 
4844137ff4cSJacques Vidrine 		len += append_number (state, arg, 010, "01234567",
4854137ff4cSJacques Vidrine 				      width, prec, flags, 0);
486b528cefcSMark Murray 		break;
487b528cefcSMark Murray 	    }
488b528cefcSMark Murray 	    case 'x' : {
4894137ff4cSJacques Vidrine 		u_longest arg;
490b528cefcSMark Murray 
491b528cefcSMark Murray 		PARSE_INT_FORMAT(arg, ap, unsigned);
492b528cefcSMark Murray 
4934137ff4cSJacques Vidrine 		len += append_number (state, arg, 0x10, "0123456789abcdef",
4944137ff4cSJacques Vidrine 				      width, prec, flags, 0);
495b528cefcSMark Murray 		break;
496b528cefcSMark Murray 	    }
497b528cefcSMark Murray 	    case 'X' :{
4984137ff4cSJacques Vidrine 		u_longest arg;
499b528cefcSMark Murray 
500b528cefcSMark Murray 		PARSE_INT_FORMAT(arg, ap, unsigned);
501b528cefcSMark Murray 
5024137ff4cSJacques Vidrine 		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
5034137ff4cSJacques Vidrine 				      width, prec, flags, 0);
504b528cefcSMark Murray 		break;
505b528cefcSMark Murray 	    }
506b528cefcSMark Murray 	    case 'p' : {
507b528cefcSMark Murray 		unsigned long arg = (unsigned long)va_arg(ap, void*);
508b528cefcSMark Murray 
5094137ff4cSJacques Vidrine 		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
5104137ff4cSJacques Vidrine 				      width, prec, flags, 0);
511b528cefcSMark Murray 		break;
512b528cefcSMark Murray 	    }
513b528cefcSMark Murray 	    case 'n' : {
514b528cefcSMark Murray 		int *arg = va_arg(ap, int*);
515b528cefcSMark Murray 		*arg = state->s - state->str;
516b528cefcSMark Murray 		break;
517b528cefcSMark Murray 	    }
518b528cefcSMark Murray 	    case '\0' :
519b528cefcSMark Murray 		--format;
520b528cefcSMark Murray 		/* FALLTHROUGH */
521b528cefcSMark Murray 	    case '%' :
5224137ff4cSJacques Vidrine 		(*state->append_char)(state, c);
5234137ff4cSJacques Vidrine 		++len;
524b528cefcSMark Murray 		break;
525b528cefcSMark Murray 	    default :
5264137ff4cSJacques Vidrine 		(*state->append_char)(state, '%');
5274137ff4cSJacques Vidrine 		(*state->append_char)(state, c);
5284137ff4cSJacques Vidrine 		len += 2;
529b528cefcSMark Murray 		break;
530b528cefcSMark Murray 	    }
5314137ff4cSJacques Vidrine 	} else {
5324137ff4cSJacques Vidrine 	    (*state->append_char) (state, c);
5334137ff4cSJacques Vidrine 	    ++len;
534b528cefcSMark Murray 	}
5354137ff4cSJacques Vidrine     }
5364137ff4cSJacques Vidrine     return len;
537b528cefcSMark Murray }
538b528cefcSMark Murray 
5394137ff4cSJacques Vidrine #if !defined(HAVE_SNPRINTF) || defined(TEST_SNPRINTF)
540c19800e8SDoug Rabson int ROKEN_LIB_FUNCTION
541b528cefcSMark Murray snprintf (char *str, size_t sz, const char *format, ...)
542b528cefcSMark Murray {
543b528cefcSMark Murray     va_list args;
544b528cefcSMark Murray     int ret;
545b528cefcSMark Murray 
546b528cefcSMark Murray     va_start(args, format);
547b528cefcSMark Murray     ret = vsnprintf (str, sz, format, args);
5484137ff4cSJacques Vidrine     va_end(args);
549b528cefcSMark Murray 
550b528cefcSMark Murray #ifdef PARANOIA
551b528cefcSMark Murray     {
552b528cefcSMark Murray 	int ret2;
553b528cefcSMark Murray 	char *tmp;
554b528cefcSMark Murray 
555b528cefcSMark Murray 	tmp = malloc (sz);
556b528cefcSMark Murray 	if (tmp == NULL)
557b528cefcSMark Murray 	    abort ();
558b528cefcSMark Murray 
5594137ff4cSJacques Vidrine 	va_start(args, format);
560b528cefcSMark Murray 	ret2 = vsprintf (tmp, format, args);
5614137ff4cSJacques Vidrine 	va_end(args);
562b528cefcSMark Murray 	if (ret != ret2 || strcmp(str, tmp))
563b528cefcSMark Murray 	    abort ();
564b528cefcSMark Murray 	free (tmp);
565b528cefcSMark Murray     }
566b528cefcSMark Murray #endif
567b528cefcSMark Murray 
568b528cefcSMark Murray     return ret;
569b528cefcSMark Murray }
570b528cefcSMark Murray #endif
571b528cefcSMark Murray 
5724137ff4cSJacques Vidrine #if !defined(HAVE_ASPRINTF) || defined(TEST_SNPRINTF)
573c19800e8SDoug Rabson int ROKEN_LIB_FUNCTION
574b528cefcSMark Murray asprintf (char **ret, const char *format, ...)
575b528cefcSMark Murray {
576b528cefcSMark Murray     va_list args;
577b528cefcSMark Murray     int val;
578b528cefcSMark Murray 
579b528cefcSMark Murray     va_start(args, format);
580b528cefcSMark Murray     val = vasprintf (ret, format, args);
5814137ff4cSJacques Vidrine     va_end(args);
582b528cefcSMark Murray 
583b528cefcSMark Murray #ifdef PARANOIA
584b528cefcSMark Murray     {
585b528cefcSMark Murray 	int ret2;
586b528cefcSMark Murray 	char *tmp;
587b528cefcSMark Murray 	tmp = malloc (val + 1);
588b528cefcSMark Murray 	if (tmp == NULL)
589b528cefcSMark Murray 	    abort ();
590b528cefcSMark Murray 
5914137ff4cSJacques Vidrine 	va_start(args, format);
592b528cefcSMark Murray 	ret2 = vsprintf (tmp, format, args);
5934137ff4cSJacques Vidrine 	va_end(args);
594b528cefcSMark Murray 	if (val != ret2 || strcmp(*ret, tmp))
595b528cefcSMark Murray 	    abort ();
596b528cefcSMark Murray 	free (tmp);
597b528cefcSMark Murray     }
598b528cefcSMark Murray #endif
599b528cefcSMark Murray 
600b528cefcSMark Murray     return val;
601b528cefcSMark Murray }
602b528cefcSMark Murray #endif
603b528cefcSMark Murray 
6044137ff4cSJacques Vidrine #if !defined(HAVE_ASNPRINTF) || defined(TEST_SNPRINTF)
605c19800e8SDoug Rabson int ROKEN_LIB_FUNCTION
606b528cefcSMark Murray asnprintf (char **ret, size_t max_sz, const char *format, ...)
607b528cefcSMark Murray {
608b528cefcSMark Murray     va_list args;
609b528cefcSMark Murray     int val;
610b528cefcSMark Murray 
611b528cefcSMark Murray     va_start(args, format);
612b528cefcSMark Murray     val = vasnprintf (ret, max_sz, format, args);
613b528cefcSMark Murray 
614b528cefcSMark Murray #ifdef PARANOIA
615b528cefcSMark Murray     {
616b528cefcSMark Murray 	int ret2;
617b528cefcSMark Murray 	char *tmp;
618b528cefcSMark Murray 	tmp = malloc (val + 1);
619b528cefcSMark Murray 	if (tmp == NULL)
620b528cefcSMark Murray 	    abort ();
621b528cefcSMark Murray 
622b528cefcSMark Murray 	ret2 = vsprintf (tmp, format, args);
623b528cefcSMark Murray 	if (val != ret2 || strcmp(*ret, tmp))
624b528cefcSMark Murray 	    abort ();
625b528cefcSMark Murray 	free (tmp);
626b528cefcSMark Murray     }
627b528cefcSMark Murray #endif
628b528cefcSMark Murray 
629b528cefcSMark Murray     va_end(args);
630b528cefcSMark Murray     return val;
631b528cefcSMark Murray }
632b528cefcSMark Murray #endif
633b528cefcSMark Murray 
6344137ff4cSJacques Vidrine #if !defined(HAVE_VASPRINTF) || defined(TEST_SNPRINTF)
635c19800e8SDoug Rabson int ROKEN_LIB_FUNCTION
636b528cefcSMark Murray vasprintf (char **ret, const char *format, va_list args)
637b528cefcSMark Murray {
638b528cefcSMark Murray     return vasnprintf (ret, 0, format, args);
639b528cefcSMark Murray }
640b528cefcSMark Murray #endif
641b528cefcSMark Murray 
642b528cefcSMark Murray 
6434137ff4cSJacques Vidrine #if !defined(HAVE_VASNPRINTF) || defined(TEST_SNPRINTF)
644c19800e8SDoug Rabson int ROKEN_LIB_FUNCTION
645b528cefcSMark Murray vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
646b528cefcSMark Murray {
647b528cefcSMark Murray     int st;
6484137ff4cSJacques Vidrine     struct snprintf_state state;
649b528cefcSMark Murray 
650b528cefcSMark Murray     state.max_sz = max_sz;
651b528cefcSMark Murray     state.sz     = 1;
652b528cefcSMark Murray     state.str    = malloc(state.sz);
653b528cefcSMark Murray     if (state.str == NULL) {
654b528cefcSMark Murray 	*ret = NULL;
655b528cefcSMark Murray 	return -1;
656b528cefcSMark Murray     }
657b528cefcSMark Murray     state.s = state.str;
658b528cefcSMark Murray     state.theend = state.s + state.sz - 1;
659b528cefcSMark Murray     state.append_char = as_append_char;
660b528cefcSMark Murray 
661b528cefcSMark Murray     st = xyzprintf (&state, format, args);
6624137ff4cSJacques Vidrine     if (st > state.sz) {
663b528cefcSMark Murray 	free (state.str);
664b528cefcSMark Murray 	*ret = NULL;
665b528cefcSMark Murray 	return -1;
666b528cefcSMark Murray     } else {
667b528cefcSMark Murray 	char *tmp;
668b528cefcSMark Murray 
669b528cefcSMark Murray 	*state.s = '\0';
6704137ff4cSJacques Vidrine 	tmp = realloc (state.str, st+1);
671b528cefcSMark Murray 	if (tmp == NULL) {
672b528cefcSMark Murray 	    free (state.str);
673b528cefcSMark Murray 	    *ret = NULL;
674b528cefcSMark Murray 	    return -1;
675b528cefcSMark Murray 	}
676b528cefcSMark Murray 	*ret = tmp;
6774137ff4cSJacques Vidrine 	return st;
678b528cefcSMark Murray     }
679b528cefcSMark Murray }
680b528cefcSMark Murray #endif
681b528cefcSMark Murray 
6824137ff4cSJacques Vidrine #if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
683c19800e8SDoug Rabson int ROKEN_LIB_FUNCTION
684b528cefcSMark Murray vsnprintf (char *str, size_t sz, const char *format, va_list args)
685b528cefcSMark Murray {
6864137ff4cSJacques Vidrine     struct snprintf_state state;
687b528cefcSMark Murray     int ret;
688b528cefcSMark Murray     unsigned char *ustr = (unsigned char *)str;
689b528cefcSMark Murray 
690b528cefcSMark Murray     state.max_sz = 0;
691b528cefcSMark Murray     state.sz     = sz;
692b528cefcSMark Murray     state.str    = ustr;
693b528cefcSMark Murray     state.s      = ustr;
6944137ff4cSJacques Vidrine     state.theend = ustr + sz - (sz > 0);
695b528cefcSMark Murray     state.append_char = sn_append_char;
696b528cefcSMark Murray 
697b528cefcSMark Murray     ret = xyzprintf (&state, format, args);
698c19800e8SDoug Rabson     if (state.s != NULL && sz != 0)
699b528cefcSMark Murray 	*state.s = '\0';
7004137ff4cSJacques Vidrine     return ret;
701b528cefcSMark Murray }
702b528cefcSMark Murray #endif
703