blob: b515fb8c7f80fb6b9ac8fdbb0ab305fda64d3411 [file] [log] [blame]
Stefan Reinauer34e3a142004-05-28 15:07:03 +00001/* vtxprintf.c, from
2 * linux/lib/vsprintf.c
3 *
4 * Copyright (C) 1991, 1992 Linus Torvalds
5 */
6
Stefan Reinauer3aa067f2012-04-02 13:24:04 -07007#include <console/console.h>
Stefan Reinauer52fc6b12009-10-24 13:06:04 +00008#include <console/vtxprintf.h>
Edward O'Callaghan0ddb8262014-06-17 18:37:08 +10009#include <string.h>
Stefan Reinauer34e3a142004-05-28 15:07:03 +000010
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +020011#define call_tx(x) tx_byte(x, data)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010012
Stefan Reinauer34e3a142004-05-28 15:07:03 +000013/* haha, don't need ctype.c */
14#define isdigit(c) ((c) >= '0' && (c) <= '9')
15#define is_digit isdigit
16#define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
17
Stefan Reinauer34e3a142004-05-28 15:07:03 +000018static int skip_atoi(const char **s)
19{
20 int i=0;
21
22 while (is_digit(**s))
23 i = i*10 + *((*s)++) - '0';
24 return i;
25}
26
27#define ZEROPAD 1 /* pad with zero */
28#define SIGN 2 /* unsigned/signed long */
29#define PLUS 4 /* show plus */
30#define SPACE 8 /* space if plus */
31#define LEFT 16 /* left justified */
32#define SPECIAL 32 /* 0x */
33#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
34
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +020035static int number(void (*tx_byte)(unsigned char byte, void *data),
36 unsigned long long num, int base, int size, int precision, int type,
37 void *data)
Stefan Reinauer34e3a142004-05-28 15:07:03 +000038{
39 char c,sign,tmp[66];
40 const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
41 int i;
42 int count = 0;
43
44 if (type & LARGE)
45 digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
46 if (type & LEFT)
47 type &= ~ZEROPAD;
48 if (base < 2 || base > 36)
49 return 0;
50 c = (type & ZEROPAD) ? '0' : ' ';
51 sign = 0;
52 if (type & SIGN) {
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +000053 if ((signed long long)num < 0) {
Stefan Reinauer34e3a142004-05-28 15:07:03 +000054 sign = '-';
55 num = -num;
56 size--;
57 } else if (type & PLUS) {
58 sign = '+';
59 size--;
60 } else if (type & SPACE) {
61 sign = ' ';
62 size--;
63 }
64 }
65 if (type & SPECIAL) {
66 if (base == 16)
67 size -= 2;
68 else if (base == 8)
69 size--;
70 }
71 i = 0;
72 if (num == 0)
73 tmp[i++]='0';
Ronald G. Minnich79e36d92013-01-30 14:29:34 -080074 else while (num != 0){
David Hendricksae0e8d32013-03-06 20:43:55 -080075 tmp[i++] = digits[num % base];
76 num /= base;
Ronald G. Minnich79e36d92013-01-30 14:29:34 -080077 }
Stefan Reinauer34e3a142004-05-28 15:07:03 +000078 if (i > precision)
79 precision = i;
80 size -= precision;
81 if (!(type&(ZEROPAD+LEFT)))
82 while(size-->0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010083 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +000084 if (sign)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010085 call_tx(sign), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +000086 if (type & SPECIAL) {
87 if (base==8)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010088 call_tx('0'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +000089 else if (base==16) {
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010090 call_tx('0'), count++;
91 call_tx(digits[33]), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +000092 }
93 }
94 if (!(type & LEFT))
95 while (size-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010096 call_tx(c), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +000097 while (i < precision--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010098 call_tx('0'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +000099 while (i-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100100 call_tx(tmp[i]), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000101 while (size-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100102 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000103 return count;
104}
105
106
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200107int vtxprintf(void (*tx_byte)(unsigned char byte, void *data),
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100108 const char *fmt, va_list args, void *data)
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000109{
110 int len;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000111 unsigned long long num;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000112 int i, base;
113 const char *s;
114
115 int flags; /* flags to number() */
116
117 int field_width; /* width of output field */
118 int precision; /* min. # of digits for integers; max
119 number of chars for from string */
Patrick Georgid01ed752014-01-18 16:56:36 +0100120 int qualifier; /* 'h', 'H', 'l', or 'L' for integer fields */
Stefan Reinauer14e22772010-04-27 06:56:47 +0000121
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000122 int count;
123
124 for (count=0; *fmt ; ++fmt) {
125 if (*fmt != '%') {
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100126 call_tx(*fmt), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000127 continue;
128 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000129
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000130 /* process flags */
131 flags = 0;
Patrick Georgic5fc7db2012-03-07 15:55:47 +0100132repeat:
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000133 ++fmt; /* this also skips first '%' */
134 switch (*fmt) {
135 case '-': flags |= LEFT; goto repeat;
136 case '+': flags |= PLUS; goto repeat;
137 case ' ': flags |= SPACE; goto repeat;
138 case '#': flags |= SPECIAL; goto repeat;
139 case '0': flags |= ZEROPAD; goto repeat;
140 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000141
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000142 /* get field width */
143 field_width = -1;
144 if (is_digit(*fmt))
145 field_width = skip_atoi(&fmt);
146 else if (*fmt == '*') {
147 ++fmt;
148 /* it's the next argument */
149 field_width = va_arg(args, int);
150 if (field_width < 0) {
151 field_width = -field_width;
152 flags |= LEFT;
153 }
154 }
155
156 /* get the precision */
157 precision = -1;
158 if (*fmt == '.') {
Stefan Reinauer14e22772010-04-27 06:56:47 +0000159 ++fmt;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000160 if (is_digit(*fmt))
161 precision = skip_atoi(&fmt);
162 else if (*fmt == '*') {
163 ++fmt;
164 /* it's the next argument */
165 precision = va_arg(args, int);
166 }
167 if (precision < 0)
168 precision = 0;
169 }
170
171 /* get the conversion qualifier */
172 qualifier = -1;
Stefan Reinauerbfff6de2012-05-15 13:28:07 -0700173 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'z') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000174 qualifier = *fmt;
175 ++fmt;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000176 if (*fmt == 'l') {
177 qualifier = 'L';
178 ++fmt;
179 }
Patrick Georgid01ed752014-01-18 16:56:36 +0100180 if (*fmt == 'h') {
181 qualifier = 'H';
182 ++fmt;
183 }
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000184 }
185
186 /* default base */
187 base = 10;
188
189 switch (*fmt) {
190 case 'c':
191 if (!(flags & LEFT))
192 while (--field_width > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100193 call_tx(' '), count++;
194 call_tx((unsigned char) va_arg(args, int)), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000195 while (--field_width > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100196 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000197 continue;
198
199 case 's':
200 s = va_arg(args, char *);
201 if (!s)
202 s = "<NULL>";
203
204 len = strnlen(s, precision);
205
206 if (!(flags & LEFT))
207 while (len < field_width--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100208 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000209 for (i = 0; i < len; ++i)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100210 call_tx(*s++), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000211 while (len < field_width--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100212 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000213 continue;
214
215 case 'p':
216 if (field_width == -1) {
217 field_width = 2*sizeof(void *);
218 flags |= ZEROPAD;
219 }
220 count += number(tx_byte,
221 (unsigned long) va_arg(args, void *), 16,
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200222 field_width, precision, flags, data);
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000223 continue;
224
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000225 case 'n':
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000226 if (qualifier == 'L') {
227 long long *ip = va_arg(args, long long *);
228 *ip = count;
229 } else if (qualifier == 'l') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000230 long * ip = va_arg(args, long *);
231 *ip = count;
232 } else {
233 int * ip = va_arg(args, int *);
234 *ip = count;
235 }
236 continue;
237
238 case '%':
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100239 call_tx('%'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000240 continue;
241
242 /* integer number formats - set up the flags and "break" */
243 case 'o':
244 base = 8;
245 break;
246
247 case 'X':
248 flags |= LARGE;
249 case 'x':
250 base = 16;
251 break;
252
253 case 'd':
254 case 'i':
255 flags |= SIGN;
256 case 'u':
257 break;
258
259 default:
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100260 call_tx('%'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000261 if (*fmt)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100262 call_tx(*fmt), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000263 else
264 --fmt;
265 continue;
266 }
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000267 if (qualifier == 'L') {
268 num = va_arg(args, unsigned long long);
269 } else if (qualifier == 'l') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000270 num = va_arg(args, unsigned long);
Stefan Reinauerbfff6de2012-05-15 13:28:07 -0700271 } else if (qualifier == 'z') {
272 num = va_arg(args, size_t);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000273 } else if (qualifier == 'h') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000274 num = (unsigned short) va_arg(args, int);
275 if (flags & SIGN)
276 num = (short) num;
Patrick Georgid01ed752014-01-18 16:56:36 +0100277 } else if (qualifier == 'H') {
278 num = (unsigned char) va_arg(args, int);
279 if (flags & SIGN)
280 num = (signed char) num;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000281 } else if (flags & SIGN) {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000282 num = va_arg(args, int);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000283 } else {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000284 num = va_arg(args, unsigned int);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000285 }
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200286 count += number(tx_byte, num, base, field_width, precision, flags, data);
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000287 }
288 return count;
289}