1/*
2 * Copyright (c) 1995-2003 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#include <config.h>
35#include <stdio.h>
36#include <stdarg.h>
37#include <stdlib.h>
38#include <string.h>
39#include <ctype.h>
40#include "roken.h"
41#include <assert.h>
42
43enum format_flags {
44    minus_flag     =  1,
45    plus_flag      =  2,
46    space_flag     =  4,
47    alternate_flag =  8,
48    zero_flag      = 16
49};
50
51/*
52 * Common state
53 */
54
55struct snprintf_state {
56    unsigned char *str;
57    unsigned char *s;
58    unsigned char *theend;
59    size_t sz;
60    size_t max_sz;
61    void (*append_char)(struct snprintf_state *, unsigned char);
62    /* XXX - methods */
63};
64
65#if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
66static int
67sn_reserve (struct snprintf_state *state, size_t n)
68{
69    return state->s + n > state->theend;
70}
71
72static void
73sn_append_char (struct snprintf_state *state, unsigned char c)
74{
75    if (!sn_reserve (state, 1))
76	*state->s++ = c;
77}
78#endif
79
80static int
81as_reserve (struct snprintf_state *state, size_t n)
82{
83    if (state->s + n > state->theend) {
84	int off = state->s - state->str;
85	unsigned char *tmp;
86
87	if (state->max_sz && state->sz >= state->max_sz)
88	    return 1;
89
90	state->sz = max(state->sz * 2, state->sz + n);
91	if (state->max_sz)
92	    state->sz = min(state->sz, state->max_sz);
93	tmp = realloc (state->str, state->sz);
94	if (tmp == NULL)
95	    return 1;
96	state->str = tmp;
97	state->s = state->str + off;
98	state->theend = state->str + state->sz - 1;
99    }
100    return 0;
101}
102
103static void
104as_append_char (struct snprintf_state *state, unsigned char c)
105{
106    if(!as_reserve (state, 1))
107	*state->s++ = c;
108}
109
110/* longest integer types */
111
112#ifdef HAVE_LONG_LONG
113typedef unsigned long long u_longest;
114typedef long long longest;
115#else
116typedef unsigned long u_longest;
117typedef long longest;
118#endif
119
120
121
122static size_t
123pad(struct snprintf_state *state, int width, char c)
124{
125    size_t len = 0;
126    while(width-- > 0){
127	(*state->append_char)(state,  c);
128	++len;
129    }
130    return len;
131}
132
133/* return true if we should use alternatve hex form */
134static int
135use_alternative (int flags, u_longest num, unsigned base)
136{
137    return (flags & alternate_flag) && base == 16 && num != 0;
138}
139
140static int
141append_number(struct snprintf_state *state,
142	      u_longest num, unsigned base, const char *rep,
143	      int width, int prec, int flags, int minusp)
144{
145    int len = 0;
146    u_longest n = num;
147    char nstr[64]; /* enough for <192 bit octal integers */
148    int nstart, nlen;
149    char signchar;
150
151    /* given precision, ignore zero flag */
152    if(prec != -1)
153	flags &= ~zero_flag;
154    else
155	prec = 1;
156
157    /* format number as string */
158    nstart = sizeof(nstr);
159    nlen = 0;
160    nstr[--nstart] = '\0';
161    do {
162	assert(nstart > 0);
163	nstr[--nstart] = rep[n % base];
164	++nlen;
165	n /= base;
166    } while(n);
167
168    /* zero value with zero precision should produce no digits */
169    if(prec == 0 && num == 0) {
170	nlen--;
171	nstart++;
172    }
173
174    /* figure out what char to use for sign */
175    if(minusp)
176	signchar = '-';
177    else if((flags & plus_flag))
178	signchar = '+';
179    else if((flags & space_flag))
180	signchar = ' ';
181    else
182	signchar = '\0';
183
184    if((flags & alternate_flag) && base == 8) {
185	/* if necessary, increase the precision to
186	   make first digit a zero */
187
188	/* XXX C99 claims (regarding # and %o) that "if the value and
189           precision are both 0, a single 0 is printed", but there is
190           no such wording for %x. This would mean that %#.o would
191           output "0", but %#.x "". This does not make sense, and is
192           also not what other printf implementations are doing. */
193
194	if(prec <= nlen && nstr[nstart] != '0' && nstr[nstart] != '\0')
195	    prec = nlen + 1;
196    }
197
198    /* possible formats:
199       pad | sign | alt | zero | digits
200       sign | alt | zero | digits | pad   minus_flag
201       sign | alt | zero | digits zero_flag */
202
203    /* if not right justifying or padding with zeros, we need to
204       compute the length of the rest of the string, and then pad with
205       spaces */
206    if(!(flags & (minus_flag | zero_flag))) {
207	if(prec > nlen)
208	    width -= prec;
209	else
210	    width -= nlen;
211
212	if(use_alternative(flags, num, base))
213	    width -= 2;
214
215	if(signchar != '\0')
216	    width--;
217
218	/* pad to width */
219	len += pad(state, width, ' ');
220    }
221    if(signchar != '\0') {
222	(*state->append_char)(state, signchar);
223	++len;
224    }
225    if(use_alternative(flags, num, base)) {
226	(*state->append_char)(state, '0');
227	(*state->append_char)(state, rep[10] + 23); /* XXX */
228	len += 2;
229    }
230    if(flags & zero_flag) {
231	/* pad to width with zeros */
232	if(prec - nlen > width - len - nlen)
233	    len += pad(state, prec - nlen, '0');
234	else
235	    len += pad(state, width - len - nlen, '0');
236    } else
237	/* pad to prec with zeros */
238	len += pad(state, prec - nlen, '0');
239
240    while(nstr[nstart] != '\0') {
241	(*state->append_char)(state, nstr[nstart++]);
242	++len;
243    }
244
245    if(flags & minus_flag)
246	len += pad(state, width - len, ' ');
247
248    return len;
249}
250
251/*
252 * return length
253 */
254
255static size_t
256append_string (struct snprintf_state *state,
257	       const unsigned char *arg,
258	       int width,
259	       int prec,
260	       int flags)
261{
262    size_t len = 0;
263
264    if(arg == NULL)
265	arg = (const unsigned char*)"(null)";
266
267    if(prec != -1)
268	width -= prec;
269    else
270	width -= strlen((const char *)arg);
271    if(!(flags & minus_flag))
272	len += pad(state, width, ' ');
273
274    if (prec != -1) {
275	while (*arg && prec--) {
276	    (*state->append_char) (state, *arg++);
277	    ++len;
278	}
279    } else {
280	while (*arg) {
281	    (*state->append_char) (state, *arg++);
282	    ++len;
283	}
284    }
285    if(flags & minus_flag)
286	len += pad(state, width, ' ');
287    return len;
288}
289
290static int
291append_char(struct snprintf_state *state,
292	    unsigned char arg,
293	    int width,
294	    int flags)
295{
296    int len = 0;
297
298    while(!(flags & minus_flag) && --width > 0) {
299	(*state->append_char) (state, ' ')    ;
300	++len;
301    }
302    (*state->append_char) (state, arg);
303    ++len;
304    while((flags & minus_flag) && --width > 0) {
305	(*state->append_char) (state, ' ');
306	++len;
307    }
308    return 0;
309}
310
311/*
312 * This can't be made into a function...
313 */
314
315#ifdef HAVE_LONG_LONG
316
317#define PARSE_INT_FORMAT(res, arg, unsig) \
318if (long_long_flag) \
319     res = (unsig long long)va_arg(arg, unsig long long); \
320else if (long_flag) \
321     res = (unsig long)va_arg(arg, unsig long); \
322else if (size_t_flag) \
323     res = (unsig long)va_arg(arg, size_t); \
324else if (short_flag) \
325     res = (unsig short)va_arg(arg, unsig int); \
326else \
327     res = (unsig int)va_arg(arg, unsig int)
328
329#else
330
331#define PARSE_INT_FORMAT(res, arg, unsig) \
332if (long_flag) \
333     res = (unsig long)va_arg(arg, unsig long); \
334else if (size_t_flag) \
335     res = (unsig long)va_arg(arg, size_t); \
336else if (short_flag) \
337     res = (unsig short)va_arg(arg, unsig int); \
338else \
339     res = (unsig int)va_arg(arg, unsig int)
340
341#endif
342
343/*
344 * zyxprintf - return length, as snprintf
345 */
346
347static size_t
348xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap)
349{
350    const unsigned char *format = (const unsigned char *)char_format;
351    unsigned char c;
352    size_t len = 0;
353
354    while((c = *format++)) {
355	if (c == '%') {
356	    int flags          = 0;
357	    int width          = 0;
358	    int prec           = -1;
359	    int size_t_flag    = 0;
360	    int long_long_flag = 0;
361	    int long_flag      = 0;
362	    int short_flag     = 0;
363
364	    /* flags */
365	    while((c = *format++)){
366		if(c == '-')
367		    flags |= minus_flag;
368		else if(c == '+')
369		    flags |= plus_flag;
370		else if(c == ' ')
371		    flags |= space_flag;
372		else if(c == '#')
373		    flags |= alternate_flag;
374		else if(c == '0')
375		    flags |= zero_flag;
376		else if(c == '\'')
377		    ; /* just ignore */
378		else
379		    break;
380	    }
381
382	    if((flags & space_flag) && (flags & plus_flag))
383		flags ^= space_flag;
384
385	    if((flags & minus_flag) && (flags & zero_flag))
386		flags ^= zero_flag;
387
388	    /* width */
389	    if (isdigit(c))
390		do {
391		    width = width * 10 + c - '0';
392		    c = *format++;
393		} while(isdigit(c));
394	    else if(c == '*') {
395		width = va_arg(ap, int);
396		c = *format++;
397	    }
398
399	    /* precision */
400	    if (c == '.') {
401		prec = 0;
402		c = *format++;
403		if (isdigit(c))
404		    do {
405			prec = prec * 10 + c - '0';
406			c = *format++;
407		    } while(isdigit(c));
408		else if (c == '*') {
409		    prec = va_arg(ap, int);
410		    c = *format++;
411		}
412	    }
413
414	    /* size */
415
416	    if (c == 'h') {
417		short_flag = 1;
418		c = *format++;
419	    } else if (c == 'z') {
420		size_t_flag = 1;
421		c = *format++;
422	    } else if (c == 'l') {
423		long_flag = 1;
424		c = *format++;
425		if (c == 'l') {
426		    long_long_flag = 1;
427		    c = *format++;
428		}
429	    }
430
431	    if(c != 'd' && c != 'i')
432		flags &= ~(plus_flag | space_flag);
433
434	    switch (c) {
435	    case 'c' :
436		append_char(state, va_arg(ap, int), width, flags);
437		++len;
438		break;
439	    case 's' :
440		len += append_string(state,
441				     va_arg(ap, unsigned char*),
442				     width,
443				     prec,
444				     flags);
445		break;
446	    case 'd' :
447	    case 'i' : {
448		longest arg;
449		u_longest num;
450		int minusp = 0;
451
452		PARSE_INT_FORMAT(arg, ap, signed);
453
454		if (arg < 0) {
455		    minusp = 1;
456		    num = -arg;
457		} else
458		    num = arg;
459
460		len += append_number (state, num, 10, "0123456789",
461				      width, prec, flags, minusp);
462		break;
463	    }
464	    case 'u' : {
465		u_longest arg;
466
467		PARSE_INT_FORMAT(arg, ap, unsigned);
468
469		len += append_number (state, arg, 10, "0123456789",
470				      width, prec, flags, 0);
471		break;
472	    }
473	    case 'o' : {
474		u_longest arg;
475
476		PARSE_INT_FORMAT(arg, ap, unsigned);
477
478		len += append_number (state, arg, 010, "01234567",
479				      width, prec, flags, 0);
480		break;
481	    }
482	    case 'x' : {
483		u_longest arg;
484
485		PARSE_INT_FORMAT(arg, ap, unsigned);
486
487		len += append_number (state, arg, 0x10, "0123456789abcdef",
488				      width, prec, flags, 0);
489		break;
490	    }
491	    case 'X' :{
492		u_longest arg;
493
494		PARSE_INT_FORMAT(arg, ap, unsigned);
495
496		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
497				      width, prec, flags, 0);
498		break;
499	    }
500	    case 'p' : {
501		u_longest arg = (u_longest)va_arg(ap, void*);
502
503		len += append_number (state, arg, 0x10, "0123456789ABCDEF",
504				      width, prec, flags, 0);
505		break;
506	    }
507	    case 'n' : {
508		int *arg = va_arg(ap, int*);
509		*arg = state->s - state->str;
510		break;
511	    }
512	    case '\0' :
513		--format;
514		/* FALLTHROUGH */
515	    case '%' :
516		(*state->append_char)(state, c);
517		++len;
518		break;
519	    default :
520		(*state->append_char)(state, '%');
521		(*state->append_char)(state, c);
522		len += 2;
523		break;
524	    }
525	} else {
526	    (*state->append_char) (state, c);
527	    ++len;
528	}
529    }
530    return len;
531}
532
533#if !defined(HAVE_SNPRINTF) || defined(TEST_SNPRINTF)
534ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
535rk_snprintf (char *str, size_t sz, const char *format, ...)
536{
537    va_list args;
538    int ret;
539
540    va_start(args, format);
541    ret = vsnprintf (str, sz, format, args);
542    va_end(args);
543
544#ifdef PARANOIA
545    {
546	int ret2;
547	char *tmp;
548
549	tmp = malloc (sz);
550	if (tmp == NULL)
551	    abort ();
552
553	va_start(args, format);
554	ret2 = vsprintf (tmp, format, args);
555	va_end(args);
556	if (ret != ret2 || strcmp(str, tmp))
557	    abort ();
558	free (tmp);
559    }
560#endif
561
562    return ret;
563}
564#endif
565
566#if !defined(HAVE_ASPRINTF) || defined(TEST_SNPRINTF)
567ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
568rk_asprintf (char **ret, const char *format, ...)
569{
570    va_list args;
571    int val;
572
573    va_start(args, format);
574    val = vasprintf (ret, format, args);
575    va_end(args);
576
577#ifdef PARANOIA
578    {
579	int ret2;
580	char *tmp;
581	tmp = malloc (val + 1);
582	if (tmp == NULL)
583	    abort ();
584
585	va_start(args, format);
586	ret2 = vsprintf (tmp, format, args);
587	va_end(args);
588	if (val != ret2 || strcmp(*ret, tmp))
589	    abort ();
590	free (tmp);
591    }
592#endif
593
594    return val;
595}
596#endif
597
598#if !defined(HAVE_ASNPRINTF) || defined(TEST_SNPRINTF)
599ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
600rk_asnprintf (char **ret, size_t max_sz, const char *format, ...)
601{
602    va_list args;
603    int val;
604
605    va_start(args, format);
606    val = vasnprintf (ret, max_sz, format, args);
607
608#ifdef PARANOIA
609    {
610	int ret2;
611	char *tmp;
612	tmp = malloc (val + 1);
613	if (tmp == NULL)
614	    abort ();
615
616	ret2 = vsprintf (tmp, format, args);
617	if (val != ret2 || strcmp(*ret, tmp))
618	    abort ();
619	free (tmp);
620    }
621#endif
622
623    va_end(args);
624    return val;
625}
626#endif
627
628#if !defined(HAVE_VASPRINTF) || defined(TEST_SNPRINTF)
629ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
630rk_vasprintf (char **ret, const char *format, va_list args)
631{
632    return vasnprintf (ret, 0, format, args);
633}
634#endif
635
636
637#if !defined(HAVE_VASNPRINTF) || defined(TEST_SNPRINTF)
638ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
639rk_vasnprintf (char **ret, size_t max_sz, const char *format, va_list args)
640{
641    size_t st;
642    struct snprintf_state state;
643
644    state.max_sz = max_sz;
645    state.sz     = 1;
646    state.str    = malloc(state.sz);
647    if (state.str == NULL) {
648	*ret = NULL;
649	return -1;
650    }
651    state.s = state.str;
652    state.theend = state.s + state.sz - 1;
653    state.append_char = as_append_char;
654
655    st = xyzprintf (&state, format, args);
656    if (st > state.sz) {
657	free (state.str);
658	*ret = NULL;
659	return -1;
660    } else {
661	char *tmp;
662
663	*state.s = '\0';
664	tmp = realloc (state.str, st+1);
665	if (tmp == NULL) {
666	    free (state.str);
667	    *ret = NULL;
668	    return -1;
669	}
670	*ret = tmp;
671	return st;
672    }
673}
674#endif
675
676#if !defined(HAVE_VSNPRINTF) || defined(TEST_SNPRINTF)
677ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
678rk_vsnprintf (char *str, size_t sz, const char *format, va_list args)
679{
680    struct snprintf_state state;
681    int ret;
682    unsigned char *ustr = (unsigned char *)str;
683
684    state.max_sz = 0;
685    state.sz     = sz;
686    state.str    = ustr;
687    state.s      = ustr;
688    state.theend = ustr + sz - (sz > 0);
689    state.append_char = sn_append_char;
690
691    ret = xyzprintf (&state, format, args);
692    if (state.s != NULL && sz != 0)
693	*state.s = '\0';
694    return ret;
695}
696#endif
697