1/*
2 * Oh, it's a waste of space, but oh-so-yummy for debugging.  It's just
3 * initialization code anyway, so it doesn't take up space when we're
4 * actually running.  This version of printf() does not include 64-bit
5 * support.  "Live with it."
6 *
7 * Most of this code was shamelessly snarfed from the Linux kernel, then
8 * modified.  It's therefore GPL.
9 *
10 * printf() isn't actually needed to build syslinux.com, but during
11 * debugging it's handy.
12 */
13
14#include <stdarg.h>
15#include <stdio.h>
16#include "mystuff.h"
17
18static int strnlen(const char *s, int maxlen)
19{
20    const char *es = s;
21    while (*es && maxlen) {
22	es++;
23	maxlen--;
24    }
25
26    return (es - s);
27}
28
29#define ZEROPAD	1		/* pad with zero */
30#define SIGN	2		/* unsigned/signed long */
31#define PLUS	4		/* show plus */
32#define SPACE	8		/* space if plus */
33#define LEFT	16		/* left justified */
34#define SPECIAL	32		/* 0x */
35#define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
36
37#define do_div(n,base) ({ \
38int __res; \
39__res = ((unsigned long) n) % (unsigned) base; \
40n = ((unsigned long) n) / (unsigned) base; \
41__res; })
42
43static char *number(char *str, long num, int base, int size, int precision,
44		    int type)
45{
46    char c, sign, tmp[66];
47    const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
48    int i;
49
50    if (type & LARGE)
51	digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
52    if (type & LEFT)
53	type &= ~ZEROPAD;
54    if (base < 2 || base > 36)
55	return 0;
56    c = (type & ZEROPAD) ? '0' : ' ';
57    sign = 0;
58    if (type & SIGN) {
59	if (num < 0) {
60	    sign = '-';
61	    num = -num;
62	    size--;
63	} else if (type & PLUS) {
64	    sign = '+';
65	    size--;
66	} else if (type & SPACE) {
67	    sign = ' ';
68	    size--;
69	}
70    }
71    if (type & SPECIAL) {
72	if (base == 16)
73	    size -= 2;
74	else if (base == 8)
75	    size--;
76    }
77    i = 0;
78    if (num == 0)
79	tmp[i++] = '0';
80    else
81	while (num != 0)
82	    tmp[i++] = digits[do_div(num, base)];
83    if (i > precision)
84	precision = i;
85    size -= precision;
86    if (!(type & (ZEROPAD + LEFT)))
87	while (size-- > 0)
88	    *str++ = ' ';
89    if (sign)
90	*str++ = sign;
91    if (type & SPECIAL) {
92	if (base == 8)
93	    *str++ = '0';
94	else if (base == 16) {
95	    *str++ = '0';
96	    *str++ = digits[33];
97	}
98    }
99    if (!(type & LEFT))
100	while (size-- > 0)
101	    *str++ = c;
102    while (i < precision--)
103	*str++ = '0';
104    while (i-- > 0)
105	*str++ = tmp[i];
106    while (size-- > 0)
107	*str++ = ' ';
108    return str;
109}
110
111/* Forward decl. needed for IP address printing stuff... */
112int sprintf(char *buf, const char *fmt, ...);
113
114int vsprintf(char *buf, const char *fmt, va_list args)
115{
116    int len;
117    unsigned long num;
118    int i, base;
119    char *str;
120    const char *s;
121
122    int flags;			/* flags to number() */
123
124    int field_width;		/* width of output field */
125    int precision;		/* min. # of digits for integers; max
126				   number of chars for from string */
127    int qualifier;		/* 'h', 'l', or 'L' for integer fields */
128
129    for (str = buf; *fmt; ++fmt) {
130	if (*fmt != '%') {
131	    *str++ = *fmt;
132	    continue;
133	}
134
135	/* process flags */
136	flags = 0;
137repeat:
138	++fmt;			/* this also skips first '%' */
139	switch (*fmt) {
140	case '-':
141	    flags |= LEFT;
142	    goto repeat;
143	case '+':
144	    flags |= PLUS;
145	    goto repeat;
146	case ' ':
147	    flags |= SPACE;
148	    goto repeat;
149	case '#':
150	    flags |= SPECIAL;
151	    goto repeat;
152	case '0':
153	    flags |= ZEROPAD;
154	    goto repeat;
155	}
156
157	/* get field width */
158	field_width = -1;
159	if (isdigit(*fmt))
160	    field_width = skip_atou(&fmt);
161	else if (*fmt == '*') {
162	    ++fmt;
163	    /* it's the next argument */
164	    field_width = va_arg(args, int);
165	    if (field_width < 0) {
166		field_width = -field_width;
167		flags |= LEFT;
168	    }
169	}
170
171	/* get the precision */
172	precision = -1;
173	if (*fmt == '.') {
174	    ++fmt;
175	    if (isdigit(*fmt))
176		precision = skip_atou(&fmt);
177	    else if (*fmt == '*') {
178		++fmt;
179		/* it's the next argument */
180		precision = va_arg(args, int);
181	    }
182	    if (precision < 0)
183		precision = 0;
184	}
185
186	/* get the conversion qualifier */
187	qualifier = -1;
188	if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
189	    qualifier = *fmt;
190	    ++fmt;
191	}
192
193	/* default base */
194	base = 10;
195
196	switch (*fmt) {
197	case 'c':
198	    if (!(flags & LEFT))
199		while (--field_width > 0)
200		    *str++ = ' ';
201	    *str++ = (unsigned char)va_arg(args, int);
202	    while (--field_width > 0)
203		*str++ = ' ';
204	    continue;
205
206	case 's':
207	    s = va_arg(args, char *);
208	    len = strnlen(s, precision);
209
210	    if (!(flags & LEFT))
211		while (len < field_width--)
212		    *str++ = ' ';
213	    for (i = 0; i < len; ++i)
214		*str++ = *s++;
215	    while (len < field_width--)
216		*str++ = ' ';
217	    continue;
218
219	case 'p':
220	    if (field_width == -1) {
221		field_width = 2 * sizeof(void *);
222		flags |= ZEROPAD;
223	    }
224	    str = number(str,
225			 (unsigned long)va_arg(args, void *), 16,
226			 field_width, precision, flags);
227	    continue;
228
229	case 'n':
230	    if (qualifier == 'l') {
231		long *ip = va_arg(args, long *);
232		*ip = (str - buf);
233	    } else {
234		int *ip = va_arg(args, int *);
235		*ip = (str - buf);
236	    }
237	    continue;
238
239	case '%':
240	    *str++ = '%';
241	    continue;
242
243	    /* integer number formats - set up the flags and "break" */
244	case 'o':
245	    base = 8;
246	    break;
247
248	case 'X':
249	    flags |= LARGE;
250	case 'x':
251	    base = 16;
252	    break;
253
254	case 'd':
255	case 'i':
256	    flags |= SIGN;
257	case 'u':
258	    break;
259
260	default:
261	    *str++ = '%';
262	    if (*fmt)
263		*str++ = *fmt;
264	    else
265		--fmt;
266	    continue;
267	}
268	if (qualifier == 'l')
269	    num = va_arg(args, unsigned long);
270	else if (qualifier == 'h') {
271	    num = (unsigned short)va_arg(args, int);
272	    if (flags & SIGN)
273		num = (short)num;
274	} else if (flags & SIGN)
275	    num = va_arg(args, int);
276	else
277	    num = va_arg(args, unsigned int);
278	str = number(str, num, base, field_width, precision, flags);
279    }
280    *str = '\0';
281    return str - buf;
282}
283
284int sprintf(char *buf, const char *fmt, ...)
285{
286    va_list args;
287    int i;
288
289    va_start(args, fmt);
290    i = vsprintf(buf, fmt, args);
291    va_end(args);
292    return i;
293}
294
295int printf(const char *fmt, ...)
296{
297    char printf_buf[1024];
298    va_list args;
299    int printed;
300
301    va_start(args, fmt);
302    printed = vsprintf(printf_buf, fmt, args);
303    va_end(args);
304
305    puts(printf_buf);
306
307    return printed;
308}
309