blob: 2fcefd2831b88ffbb0eacb62c74bc9059578ef5a [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
Vadim Bendebury9b0584672014-11-29 22:05:09 -080013#if !CONFIG_ARCH_MIPS
14#define SUPPORT_64BIT_INTS
15#endif
16
Stefan Reinauer34e3a142004-05-28 15:07:03 +000017/* haha, don't need ctype.c */
18#define isdigit(c) ((c) >= '0' && (c) <= '9')
19#define is_digit isdigit
20#define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
21
Stefan Reinauer34e3a142004-05-28 15:07:03 +000022static int skip_atoi(const char **s)
23{
24 int i=0;
25
26 while (is_digit(**s))
27 i = i*10 + *((*s)++) - '0';
28 return i;
29}
30
31#define ZEROPAD 1 /* pad with zero */
32#define SIGN 2 /* unsigned/signed long */
33#define PLUS 4 /* show plus */
34#define SPACE 8 /* space if plus */
35#define LEFT 16 /* left justified */
36#define SPECIAL 32 /* 0x */
37#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
38
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +020039static int number(void (*tx_byte)(unsigned char byte, void *data),
Vadim Bendebury9b0584672014-11-29 22:05:09 -080040 unsigned long long inum, int base, int size, int precision, int type,
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +020041 void *data)
Stefan Reinauer34e3a142004-05-28 15:07:03 +000042{
43 char c,sign,tmp[66];
44 const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
45 int i;
46 int count = 0;
Vadim Bendebury9b0584672014-11-29 22:05:09 -080047#ifdef SUPPORT_64BIT_INTS
48 unsigned long long num = inum;
49#else
50 unsigned long num = (long)inum;
51
52 if (num != inum) {
53 /* Alert user to an incorrect result by printing #^!. */
54 call_tx('#');
55 call_tx('^');
56 call_tx('!');
57 }
58#endif
Stefan Reinauer34e3a142004-05-28 15:07:03 +000059
60 if (type & LARGE)
61 digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
62 if (type & LEFT)
63 type &= ~ZEROPAD;
64 if (base < 2 || base > 36)
65 return 0;
66 c = (type & ZEROPAD) ? '0' : ' ';
67 sign = 0;
68 if (type & SIGN) {
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +000069 if ((signed long long)num < 0) {
Stefan Reinauer34e3a142004-05-28 15:07:03 +000070 sign = '-';
71 num = -num;
72 size--;
73 } else if (type & PLUS) {
74 sign = '+';
75 size--;
76 } else if (type & SPACE) {
77 sign = ' ';
78 size--;
79 }
80 }
81 if (type & SPECIAL) {
82 if (base == 16)
83 size -= 2;
84 else if (base == 8)
85 size--;
86 }
87 i = 0;
88 if (num == 0)
89 tmp[i++]='0';
Ronald G. Minnich79e36d92013-01-30 14:29:34 -080090 else while (num != 0){
David Hendricksae0e8d32013-03-06 20:43:55 -080091 tmp[i++] = digits[num % base];
92 num /= base;
Ronald G. Minnich79e36d92013-01-30 14:29:34 -080093 }
Stefan Reinauer34e3a142004-05-28 15:07:03 +000094 if (i > precision)
95 precision = i;
96 size -= precision;
97 if (!(type&(ZEROPAD+LEFT)))
98 while(size-->0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010099 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000100 if (sign)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100101 call_tx(sign), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000102 if (type & SPECIAL) {
103 if (base==8)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100104 call_tx('0'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000105 else if (base==16) {
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100106 call_tx('0'), count++;
107 call_tx(digits[33]), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000108 }
109 }
110 if (!(type & LEFT))
111 while (size-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100112 call_tx(c), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000113 while (i < precision--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100114 call_tx('0'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000115 while (i-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100116 call_tx(tmp[i]), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000117 while (size-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100118 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000119 return count;
120}
121
122
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200123int vtxprintf(void (*tx_byte)(unsigned char byte, void *data),
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100124 const char *fmt, va_list args, void *data)
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000125{
126 int len;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000127 unsigned long long num;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000128 int i, base;
129 const char *s;
130
131 int flags; /* flags to number() */
132
133 int field_width; /* width of output field */
134 int precision; /* min. # of digits for integers; max
135 number of chars for from string */
Patrick Georgid01ed752014-01-18 16:56:36 +0100136 int qualifier; /* 'h', 'H', 'l', or 'L' for integer fields */
Stefan Reinauer14e22772010-04-27 06:56:47 +0000137
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000138 int count;
139
140 for (count=0; *fmt ; ++fmt) {
141 if (*fmt != '%') {
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100142 call_tx(*fmt), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000143 continue;
144 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000145
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000146 /* process flags */
147 flags = 0;
Patrick Georgic5fc7db2012-03-07 15:55:47 +0100148repeat:
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000149 ++fmt; /* this also skips first '%' */
150 switch (*fmt) {
151 case '-': flags |= LEFT; goto repeat;
152 case '+': flags |= PLUS; goto repeat;
153 case ' ': flags |= SPACE; goto repeat;
154 case '#': flags |= SPECIAL; goto repeat;
155 case '0': flags |= ZEROPAD; goto repeat;
156 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000157
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000158 /* get field width */
159 field_width = -1;
160 if (is_digit(*fmt))
161 field_width = skip_atoi(&fmt);
162 else if (*fmt == '*') {
163 ++fmt;
164 /* it's the next argument */
165 field_width = va_arg(args, int);
166 if (field_width < 0) {
167 field_width = -field_width;
168 flags |= LEFT;
169 }
170 }
171
172 /* get the precision */
173 precision = -1;
174 if (*fmt == '.') {
Stefan Reinauer14e22772010-04-27 06:56:47 +0000175 ++fmt;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000176 if (is_digit(*fmt))
177 precision = skip_atoi(&fmt);
178 else if (*fmt == '*') {
179 ++fmt;
180 /* it's the next argument */
181 precision = va_arg(args, int);
182 }
183 if (precision < 0)
184 precision = 0;
185 }
186
187 /* get the conversion qualifier */
188 qualifier = -1;
Stefan Reinauerbfff6de2012-05-15 13:28:07 -0700189 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'z') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000190 qualifier = *fmt;
191 ++fmt;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000192 if (*fmt == 'l') {
193 qualifier = 'L';
194 ++fmt;
195 }
Patrick Georgid01ed752014-01-18 16:56:36 +0100196 if (*fmt == 'h') {
197 qualifier = 'H';
198 ++fmt;
199 }
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000200 }
201
202 /* default base */
203 base = 10;
204
205 switch (*fmt) {
206 case 'c':
207 if (!(flags & LEFT))
208 while (--field_width > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100209 call_tx(' '), count++;
210 call_tx((unsigned char) va_arg(args, int)), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000211 while (--field_width > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100212 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000213 continue;
214
215 case 's':
216 s = va_arg(args, char *);
217 if (!s)
218 s = "<NULL>";
219
220 len = strnlen(s, precision);
221
222 if (!(flags & LEFT))
223 while (len < field_width--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100224 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000225 for (i = 0; i < len; ++i)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100226 call_tx(*s++), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000227 while (len < field_width--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100228 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000229 continue;
230
231 case 'p':
232 if (field_width == -1) {
233 field_width = 2*sizeof(void *);
234 flags |= ZEROPAD;
235 }
236 count += number(tx_byte,
237 (unsigned long) va_arg(args, void *), 16,
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200238 field_width, precision, flags, data);
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000239 continue;
240
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000241 case 'n':
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000242 if (qualifier == 'L') {
243 long long *ip = va_arg(args, long long *);
244 *ip = count;
245 } else if (qualifier == 'l') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000246 long * ip = va_arg(args, long *);
247 *ip = count;
248 } else {
249 int * ip = va_arg(args, int *);
250 *ip = count;
251 }
252 continue;
253
254 case '%':
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100255 call_tx('%'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000256 continue;
257
258 /* integer number formats - set up the flags and "break" */
259 case 'o':
260 base = 8;
261 break;
262
263 case 'X':
264 flags |= LARGE;
265 case 'x':
266 base = 16;
267 break;
268
269 case 'd':
270 case 'i':
271 flags |= SIGN;
272 case 'u':
273 break;
274
275 default:
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100276 call_tx('%'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000277 if (*fmt)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100278 call_tx(*fmt), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000279 else
280 --fmt;
281 continue;
282 }
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000283 if (qualifier == 'L') {
284 num = va_arg(args, unsigned long long);
285 } else if (qualifier == 'l') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000286 num = va_arg(args, unsigned long);
Stefan Reinauerbfff6de2012-05-15 13:28:07 -0700287 } else if (qualifier == 'z') {
288 num = va_arg(args, size_t);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000289 } else if (qualifier == 'h') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000290 num = (unsigned short) va_arg(args, int);
291 if (flags & SIGN)
292 num = (short) num;
Patrick Georgid01ed752014-01-18 16:56:36 +0100293 } else if (qualifier == 'H') {
294 num = (unsigned char) va_arg(args, int);
295 if (flags & SIGN)
296 num = (signed char) num;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000297 } else if (flags & SIGN) {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000298 num = va_arg(args, int);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000299 } else {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000300 num = va_arg(args, unsigned int);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000301 }
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200302 count += number(tx_byte, num, base, field_width, precision, flags, data);
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000303 }
304 return count;
305}