blob: 88295a2c1a237029f6733e62679f85b24098f5da [file] [log] [blame]
Damien Roth4e7e9872016-01-16 18:59:51 -07001/*
2 * This file is part of the coreboot project.
Stefan Reinauer34e3a142004-05-28 15:07:03 +00003 *
4 * Copyright (C) 1991, 1992 Linus Torvalds
Damien Roth4e7e9872016-01-16 18:59:51 -07005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * vtxprintf.c, originally from linux/lib/vsprintf.c
Stefan Reinauer34e3a142004-05-28 15:07:03 +000016 */
17
Stefan Reinauer3aa067f2012-04-02 13:24:04 -070018#include <console/console.h>
Stefan Reinauer52fc6b122009-10-24 13:06:04 +000019#include <console/vtxprintf.h>
Edward O'Callaghan0ddb8262014-06-17 18:37:08 +100020#include <string.h>
Stefan Reinauer34e3a142004-05-28 15:07:03 +000021
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +020022#define call_tx(x) tx_byte(x, data)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +010023
Martin Rothd3bb09d2017-06-25 11:50:58 -060024#if !IS_ENABLED(CONFIG_ARCH_MIPS)
Vadim Bendebury9b0584672014-11-29 22:05:09 -080025#define SUPPORT_64BIT_INTS
26#endif
27
Stefan Reinauer34e3a142004-05-28 15:07:03 +000028/* haha, don't need ctype.c */
29#define isdigit(c) ((c) >= '0' && (c) <= '9')
30#define is_digit isdigit
31#define isxdigit(c) (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
32
Stefan Reinauer34e3a142004-05-28 15:07:03 +000033static int skip_atoi(const char **s)
34{
35 int i=0;
36
37 while (is_digit(**s))
38 i = i*10 + *((*s)++) - '0';
39 return i;
40}
41
42#define ZEROPAD 1 /* pad with zero */
43#define SIGN 2 /* unsigned/signed long */
44#define PLUS 4 /* show plus */
45#define SPACE 8 /* space if plus */
46#define LEFT 16 /* left justified */
47#define SPECIAL 32 /* 0x */
48#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
49
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +020050static int number(void (*tx_byte)(unsigned char byte, void *data),
Vadim Bendebury9b0584672014-11-29 22:05:09 -080051 unsigned long long inum, int base, int size, int precision, int type,
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +020052 void *data)
Stefan Reinauer34e3a142004-05-28 15:07:03 +000053{
54 char c,sign,tmp[66];
55 const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
56 int i;
57 int count = 0;
Vadim Bendebury9b0584672014-11-29 22:05:09 -080058#ifdef SUPPORT_64BIT_INTS
59 unsigned long long num = inum;
60#else
61 unsigned long num = (long)inum;
62
63 if (num != inum) {
64 /* Alert user to an incorrect result by printing #^!. */
65 call_tx('#');
66 call_tx('^');
67 call_tx('!');
68 }
69#endif
Stefan Reinauer34e3a142004-05-28 15:07:03 +000070
71 if (type & LARGE)
72 digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
73 if (type & LEFT)
74 type &= ~ZEROPAD;
75 if (base < 2 || base > 36)
76 return 0;
77 c = (type & ZEROPAD) ? '0' : ' ';
78 sign = 0;
79 if (type & SIGN) {
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +000080 if ((signed long long)num < 0) {
Stefan Reinauer34e3a142004-05-28 15:07:03 +000081 sign = '-';
82 num = -num;
83 size--;
84 } else if (type & PLUS) {
85 sign = '+';
86 size--;
87 } else if (type & SPACE) {
88 sign = ' ';
89 size--;
90 }
91 }
92 if (type & SPECIAL) {
93 if (base == 16)
94 size -= 2;
95 else if (base == 8)
96 size--;
97 }
98 i = 0;
99 if (num == 0)
100 tmp[i++]='0';
Ronald G. Minnich79e36d92013-01-30 14:29:34 -0800101 else while (num != 0){
David Hendricksae0e8d32013-03-06 20:43:55 -0800102 tmp[i++] = digits[num % base];
103 num /= base;
Ronald G. Minnich79e36d92013-01-30 14:29:34 -0800104 }
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000105 if (i > precision)
106 precision = i;
107 size -= precision;
108 if (!(type&(ZEROPAD+LEFT)))
Elyes HAOUAS3c804082016-08-25 21:02:20 +0200109 while (size-->0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100110 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000111 if (sign)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100112 call_tx(sign), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000113 if (type & SPECIAL) {
Elyes HAOUAS20767ba2018-04-26 10:00:35 +0200114 if (base == 8)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100115 call_tx('0'), count++;
Elyes HAOUAS20767ba2018-04-26 10:00:35 +0200116 else if (base == 16) {
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100117 call_tx('0'), count++;
118 call_tx(digits[33]), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000119 }
120 }
121 if (!(type & LEFT))
122 while (size-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100123 call_tx(c), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000124 while (i < precision--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100125 call_tx('0'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000126 while (i-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100127 call_tx(tmp[i]), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000128 while (size-- > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100129 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000130 return count;
131}
132
133
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200134int vtxprintf(void (*tx_byte)(unsigned char byte, void *data),
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100135 const char *fmt, va_list args, void *data)
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000136{
137 int len;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000138 unsigned long long num;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000139 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 */
Patrick Georgid01ed752014-01-18 16:56:36 +0100147 int qualifier; /* 'h', 'H', 'l', or 'L' for integer fields */
Stefan Reinauer14e22772010-04-27 06:56:47 +0000148
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000149 int count;
150
151 for (count=0; *fmt ; ++fmt) {
152 if (*fmt != '%') {
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100153 call_tx(*fmt), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000154 continue;
155 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000156
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000157 /* process flags */
158 flags = 0;
Patrick Georgic5fc7db2012-03-07 15:55:47 +0100159repeat:
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000160 ++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 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000168
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000169 /* 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 == '.') {
Stefan Reinauer14e22772010-04-27 06:56:47 +0000186 ++fmt;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000187 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;
Stefan Reinauerbfff6de2012-05-15 13:28:07 -0700200 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'z') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000201 qualifier = *fmt;
202 ++fmt;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000203 if (*fmt == 'l') {
204 qualifier = 'L';
205 ++fmt;
206 }
Patrick Georgid01ed752014-01-18 16:56:36 +0100207 if (*fmt == 'h') {
208 qualifier = 'H';
209 ++fmt;
210 }
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000211 }
212
213 /* default base */
214 base = 10;
215
216 switch (*fmt) {
217 case 'c':
218 if (!(flags & LEFT))
219 while (--field_width > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100220 call_tx(' '), count++;
221 call_tx((unsigned char) va_arg(args, int)), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000222 while (--field_width > 0)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100223 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000224 continue;
225
226 case 's':
227 s = va_arg(args, char *);
228 if (!s)
229 s = "<NULL>";
230
Martin Rothad6c8852016-11-18 11:35:01 -0700231 len = strnlen(s, (size_t)precision);
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000232
233 if (!(flags & LEFT))
234 while (len < field_width--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100235 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000236 for (i = 0; i < len; ++i)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100237 call_tx(*s++), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000238 while (len < field_width--)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100239 call_tx(' '), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000240 continue;
241
242 case 'p':
243 if (field_width == -1) {
244 field_width = 2*sizeof(void *);
245 flags |= ZEROPAD;
246 }
247 count += number(tx_byte,
248 (unsigned long) va_arg(args, void *), 16,
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200249 field_width, precision, flags, data);
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000250 continue;
251
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000252 case 'n':
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000253 if (qualifier == 'L') {
254 long long *ip = va_arg(args, long long *);
255 *ip = count;
256 } else if (qualifier == 'l') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000257 long * ip = va_arg(args, long *);
258 *ip = count;
259 } else {
260 int * ip = va_arg(args, int *);
261 *ip = count;
262 }
263 continue;
264
265 case '%':
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100266 call_tx('%'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000267 continue;
268
269 /* integer number formats - set up the flags and "break" */
270 case 'o':
271 base = 8;
272 break;
273
274 case 'X':
275 flags |= LARGE;
276 case 'x':
277 base = 16;
278 break;
279
280 case 'd':
281 case 'i':
282 flags |= SIGN;
283 case 'u':
284 break;
285
286 default:
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100287 call_tx('%'), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000288 if (*fmt)
Vladimir Serbinenkof421b332013-11-26 21:43:05 +0100289 call_tx(*fmt), count++;
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000290 else
291 --fmt;
292 continue;
293 }
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000294 if (qualifier == 'L') {
295 num = va_arg(args, unsigned long long);
296 } else if (qualifier == 'l') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000297 num = va_arg(args, unsigned long);
Stefan Reinauerbfff6de2012-05-15 13:28:07 -0700298 } else if (qualifier == 'z') {
299 num = va_arg(args, size_t);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000300 } else if (qualifier == 'h') {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000301 num = (unsigned short) va_arg(args, int);
302 if (flags & SIGN)
303 num = (short) num;
Patrick Georgid01ed752014-01-18 16:56:36 +0100304 } else if (qualifier == 'H') {
305 num = (unsigned char) va_arg(args, int);
306 if (flags & SIGN)
307 num = (signed char) num;
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000308 } else if (flags & SIGN) {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000309 num = va_arg(args, int);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000310 } else {
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000311 num = va_arg(args, unsigned int);
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000312 }
Kyösti Mälkkib04e0ff2014-02-04 14:28:17 +0200313 count += number(tx_byte, num, base, field_width, precision, flags, data);
Stefan Reinauer34e3a142004-05-28 15:07:03 +0000314 }
315 return count;
316}