- Remove all of the annoying $Id strings
[coreboot.git] / src / console / vsprintf.c
1 /*
2  *  linux/lib/vsprintf.c
3  *
4  *  Copyright (C) 1991, 1992  Linus Torvalds
5  */
6
7 /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
8 /*
9  * Wirzenius wrote this portably, Torvalds fucked it up :-)
10  */
11 #include <stdarg.h>
12 #include <string.h>
13
14 /* haha, don't need ctype.c */
15 #define isdigit(c)      ((c) >= '0' && (c) <= '9')
16 #define is_digit isdigit
17 #define isxdigit(c)     (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
18 #define islower(c)      ((c) >= 'a' && (c) <= 'z')
19 #define toupper(c) __toupper(c)
20
21 static inline unsigned char __toupper(unsigned char c)
22 {
23         if (islower(c))
24                 c -= 'a'-'A';
25         return c;
26 }
27
28
29 unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
30 {
31         unsigned long result = 0,value;
32
33         if (!base) {
34                 base = 10;
35                 if (*cp == '0') {
36                         base = 8;
37                         cp++;
38                         if ((*cp == 'x') && isxdigit(cp[1])) {
39                                 cp++;
40                                 base = 16;
41                         }
42                 }
43         }
44         while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
45             ? toupper(*cp) : *cp)-'A'+10) < base) {
46                 result = result*base + value;
47                 cp++;
48         }
49         if (endp)
50                 *endp = (char *)cp;
51         return result;
52 }
53
54 long simple_strtol(const char *cp,char **endp,unsigned int base)
55 {
56         if(*cp=='-')
57                 return -simple_strtoul(cp+1,endp,base);
58         return simple_strtoul(cp,endp,base);
59 }
60
61
62 static int skip_atoi(const char **s)
63 {
64         int i=0;
65
66         while (is_digit(**s))
67                 i = i*10 + *((*s)++) - '0';
68         return i;
69 }
70
71 #define ZEROPAD 1               /* pad with zero */
72 #define SIGN    2               /* unsigned/signed long */
73 #define PLUS    4               /* show plus */
74 #define SPACE   8               /* space if plus */
75 #define LEFT    16              /* left justified */
76 #define SPECIAL 32              /* 0x */
77 #define LARGE   64              /* use 'ABCDEF' instead of 'abcdef' */
78
79 #define do_div(n,base) ({ \
80 int __res; \
81 __res = ((unsigned long) n) % (unsigned) base; \
82 n = ((unsigned long) n) / (unsigned) base; \
83 __res; })
84
85 static int number(void (*tx_byte)(unsigned char byte), long num, int base, int size, int precision
86         ,int type)
87 {
88         char c,sign,tmp[66];
89         const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
90         int i;
91         int count = 0;
92
93         if (type & LARGE)
94                 digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
95         if (type & LEFT)
96                 type &= ~ZEROPAD;
97         if (base < 2 || base > 36)
98                 return 0;
99         c = (type & ZEROPAD) ? '0' : ' ';
100         sign = 0;
101         if (type & SIGN) {
102                 if (num < 0) {
103                         sign = '-';
104                         num = -num;
105                         size--;
106                 } else if (type & PLUS) {
107                         sign = '+';
108                         size--;
109                 } else if (type & SPACE) {
110                         sign = ' ';
111                         size--;
112                 }
113         }
114         if (type & SPECIAL) {
115                 if (base == 16)
116                         size -= 2;
117                 else if (base == 8)
118                         size--;
119         }
120         i = 0;
121         if (num == 0)
122                 tmp[i++]='0';
123         else while (num != 0)
124                 tmp[i++] = digits[do_div(num,base)];
125         if (i > precision)
126                 precision = i;
127         size -= precision;
128         if (!(type&(ZEROPAD+LEFT)))
129                 while(size-->0)
130                         tx_byte(' '), count++;
131         if (sign)
132                 tx_byte(sign), count++;
133         if (type & SPECIAL) {
134                 if (base==8)
135                         tx_byte('0'), count++;
136                 else if (base==16) {
137                         tx_byte('0'), count++;
138                         tx_byte(digits[33]), count++;
139                 }
140         }
141         if (!(type & LEFT))
142                 while (size-- > 0)
143                         tx_byte(c), count++;
144         while (i < precision--)
145                 tx_byte('0'), count++;
146         while (i-- > 0)
147                 tx_byte(tmp[i]), count++;
148         while (size-- > 0)
149                 tx_byte(' '), count++;
150         return count;
151 }
152
153
154 int vtxprintf(void (*tx_byte)(unsigned char byte), const char *fmt, va_list args)
155 {
156         int len;
157         unsigned long num;
158         int i, base;
159         const char *s;
160
161         int flags;              /* flags to number() */
162
163         int field_width;        /* width of output field */
164         int precision;          /* min. # of digits for integers; max
165                                    number of chars for from string */
166         int qualifier;          /* 'h', 'l', or 'L' for integer fields */
167         
168         int count;
169
170         for (count=0; *fmt ; ++fmt) {
171                 if (*fmt != '%') {
172                         tx_byte(*fmt), count++;
173                         continue;
174                 }
175                         
176                 /* process flags */
177                 flags = 0;
178                 repeat:
179                         ++fmt;          /* this also skips first '%' */
180                         switch (*fmt) {
181                                 case '-': flags |= LEFT; goto repeat;
182                                 case '+': flags |= PLUS; goto repeat;
183                                 case ' ': flags |= SPACE; goto repeat;
184                                 case '#': flags |= SPECIAL; goto repeat;
185                                 case '0': flags |= ZEROPAD; goto repeat;
186                                 }
187                 
188                 /* get field width */
189                 field_width = -1;
190                 if (is_digit(*fmt))
191                         field_width = skip_atoi(&fmt);
192                 else if (*fmt == '*') {
193                         ++fmt;
194                         /* it's the next argument */
195                         field_width = va_arg(args, int);
196                         if (field_width < 0) {
197                                 field_width = -field_width;
198                                 flags |= LEFT;
199                         }
200                 }
201
202                 /* get the precision */
203                 precision = -1;
204                 if (*fmt == '.') {
205                         ++fmt;  
206                         if (is_digit(*fmt))
207                                 precision = skip_atoi(&fmt);
208                         else if (*fmt == '*') {
209                                 ++fmt;
210                                 /* it's the next argument */
211                                 precision = va_arg(args, int);
212                         }
213                         if (precision < 0)
214                                 precision = 0;
215                 }
216
217                 /* get the conversion qualifier */
218                 qualifier = -1;
219                 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
220                         qualifier = *fmt;
221                         ++fmt;
222                 }
223
224                 /* default base */
225                 base = 10;
226
227                 switch (*fmt) {
228                 case 'c':
229                         if (!(flags & LEFT))
230                                 while (--field_width > 0)
231                                         tx_byte(' '), count++;
232                         tx_byte((unsigned char) va_arg(args, int)), count++;
233                         while (--field_width > 0)
234                                 tx_byte(' '), count++;
235                         continue;
236
237                 case 's':
238                         s = va_arg(args, char *);
239                         if (!s)
240                                 s = "<NULL>";
241
242                         len = strnlen(s, precision);
243
244                         if (!(flags & LEFT))
245                                 while (len < field_width--)
246                                         tx_byte(' '), count++;
247                         for (i = 0; i < len; ++i)
248                                 tx_byte(*s++), count++;
249                         while (len < field_width--)
250                                 tx_byte(' '), count++;
251                         continue;
252
253                 case 'p':
254                         if (field_width == -1) {
255                                 field_width = 2*sizeof(void *);
256                                 flags |= ZEROPAD;
257                         }
258                         count += number(tx_byte,
259                                 (unsigned long) va_arg(args, void *), 16,
260                                 field_width, precision, flags);
261                         continue;
262
263
264                 case 'n':
265                         if (qualifier == 'l') {
266                                 long * ip = va_arg(args, long *);
267                                 *ip = count;
268                         } else {
269                                 int * ip = va_arg(args, int *);
270                                 *ip = count;
271                         }
272                         continue;
273
274                 case '%':
275                         tx_byte('%'), count++;
276                         continue;
277
278                 /* integer number formats - set up the flags and "break" */
279                 case 'o':
280                         base = 8;
281                         break;
282
283                 case 'X':
284                         flags |= LARGE;
285                 case 'x':
286                         base = 16;
287                         break;
288
289                 case 'd':
290                 case 'i':
291                         flags |= SIGN;
292                 case 'u':
293                         break;
294
295                 default:
296                         tx_byte('%'), count++;
297                         if (*fmt)
298                                 tx_byte(*fmt), count++;
299                         else
300                                 --fmt;
301                         continue;
302                 }
303                 if (qualifier == 'l')
304                         num = va_arg(args, unsigned long);
305                 else if (qualifier == 'h') {
306                         num = (unsigned short) va_arg(args, int);
307                         if (flags & SIGN)
308                                 num = (short) num;
309                 } else if (flags & SIGN)
310                         num = va_arg(args, int);
311                 else
312                         num = va_arg(args, unsigned int);
313                 count += number(tx_byte, num, base, field_width, precision, flags);
314         }
315         return count;
316 }
317
318 /* FIXME this global makes vsprintf non-reentrant
319  */
320 static char *str_buf;
321 static void str_tx_byte(unsigned char byte)
322 {
323         *str_buf = byte;
324         str_buf++;
325 }
326
327 int vsprintf(char * buf, const char *fmt, va_list args)
328 {
329         int i;
330         str_buf = buf;
331         i = vtxprintf(str_tx_byte, fmt, args);
332         /* maeder/Ispiri -- The null termination was missing a deference */
333         /*                  and was just zeroing out the pointer instead */
334         *str_buf = '\0';
335         return i;
336 }
337
338 int sprintf(char * buf, const char *fmt, ...)
339 {
340         va_list args;
341         int i;
342
343         va_start(args, fmt);
344         i=vsprintf(buf,fmt,args);
345         va_end(args);
346         return i;
347 }