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