blob: dfa843240fc89484bbe82e939eb4d8d12fc3a315 [file] [log] [blame]
Stefan Roese363ab7b2015-11-23 07:00:22 +01001/*
2 * Tiny printf version for SPL
3 *
4 * Copied from:
5 * http://www.sparetimelabs.com/printfrevisited/printfrevisited.php
6 *
7 * Copyright (C) 2004,2008 Kustaa Nyholm
8 *
9 * SPDX-License-Identifier: LGPL-2.1+
10 */
11
12#include <common.h>
13#include <stdarg.h>
14#include <serial.h>
15
Simon Glass5958cbe2016-08-04 21:58:14 -060016struct printf_info {
17 char *bf; /* Digit buffer */
18 char zs; /* non-zero if a digit has been written */
19 char *outstr; /* Next output position for sprintf() */
Stefan Roese363ab7b2015-11-23 07:00:22 +010020
Simon Glass5958cbe2016-08-04 21:58:14 -060021 /* Output a character */
22 void (*putc)(struct printf_info *info, char ch);
23};
Simon Glass49138f82016-05-14 14:02:53 -060024
Simon Glass5958cbe2016-08-04 21:58:14 -060025void putc_normal(struct printf_info *info, char ch)
Stefan Roese363ab7b2015-11-23 07:00:22 +010026{
Simon Glass5958cbe2016-08-04 21:58:14 -060027 putc(ch);
Stefan Roese363ab7b2015-11-23 07:00:22 +010028}
29
Simon Glass5958cbe2016-08-04 21:58:14 -060030static void out(struct printf_info *info, char c)
Stefan Roese363ab7b2015-11-23 07:00:22 +010031{
Simon Glass5958cbe2016-08-04 21:58:14 -060032 *info->bf++ = c;
Stefan Roese363ab7b2015-11-23 07:00:22 +010033}
34
Simon Glass5958cbe2016-08-04 21:58:14 -060035static void out_dgt(struct printf_info *info, char dgt)
36{
37 out(info, dgt + (dgt < 10 ? '0' : 'a' - 10));
38 info->zs = 1;
39}
40
Andre Przywara32118682017-01-02 11:48:28 +000041static void div_out(struct printf_info *info, unsigned long *num,
42 unsigned long div)
Stefan Roese363ab7b2015-11-23 07:00:22 +010043{
44 unsigned char dgt = 0;
45
Stefan Roese26a552c2015-11-16 15:26:34 +010046 while (*num >= div) {
47 *num -= div;
Stefan Roese363ab7b2015-11-23 07:00:22 +010048 dgt++;
49 }
50
Simon Glass5958cbe2016-08-04 21:58:14 -060051 if (info->zs || dgt > 0)
52 out_dgt(info, dgt);
Stefan Roese363ab7b2015-11-23 07:00:22 +010053}
54
Simon Glass5958cbe2016-08-04 21:58:14 -060055int _vprintf(struct printf_info *info, const char *fmt, va_list va)
Stefan Roese363ab7b2015-11-23 07:00:22 +010056{
Stefan Roese363ab7b2015-11-23 07:00:22 +010057 char ch;
58 char *p;
Andre Przywara32118682017-01-02 11:48:28 +000059 unsigned long num;
Stefan Roese26a552c2015-11-16 15:26:34 +010060 char buf[12];
Andre Przywara32118682017-01-02 11:48:28 +000061 unsigned long div;
Stefan Roese363ab7b2015-11-23 07:00:22 +010062
Stefan Roese363ab7b2015-11-23 07:00:22 +010063 while ((ch = *(fmt++))) {
64 if (ch != '%') {
Simon Glass5958cbe2016-08-04 21:58:14 -060065 info->putc(info, ch);
Stefan Roese363ab7b2015-11-23 07:00:22 +010066 } else {
Simon Glassb5afcad2016-05-14 14:02:52 -060067 bool lz = false;
68 int width = 0;
Andre Przywara32118682017-01-02 11:48:28 +000069 bool islong = false;
Stefan Roese363ab7b2015-11-23 07:00:22 +010070
71 ch = *(fmt++);
Andre Przywara7b303b82017-01-02 11:48:29 +000072 if (ch == '-')
73 ch = *(fmt++);
74
Stefan Roese363ab7b2015-11-23 07:00:22 +010075 if (ch == '0') {
76 ch = *(fmt++);
77 lz = 1;
78 }
79
80 if (ch >= '0' && ch <= '9') {
Simon Glassb5afcad2016-05-14 14:02:52 -060081 width = 0;
Stefan Roese363ab7b2015-11-23 07:00:22 +010082 while (ch >= '0' && ch <= '9') {
Simon Glassb5afcad2016-05-14 14:02:52 -060083 width = (width * 10) + ch - '0';
Stefan Roese363ab7b2015-11-23 07:00:22 +010084 ch = *fmt++;
85 }
86 }
Andre Przywara32118682017-01-02 11:48:28 +000087 if (ch == 'l') {
88 ch = *(fmt++);
89 islong = true;
90 }
91
Simon Glass5958cbe2016-08-04 21:58:14 -060092 info->bf = buf;
93 p = info->bf;
94 info->zs = 0;
Stefan Roese363ab7b2015-11-23 07:00:22 +010095
96 switch (ch) {
Simon Glassb5afcad2016-05-14 14:02:52 -060097 case '\0':
Stefan Roese363ab7b2015-11-23 07:00:22 +010098 goto abort;
99 case 'u':
100 case 'd':
Andre Przywara32118682017-01-02 11:48:28 +0000101 div = 1000000000;
102 if (islong) {
103 num = va_arg(va, unsigned long);
104 if (sizeof(long) > 4)
105 div *= div * 10;
106 } else {
107 num = va_arg(va, unsigned int);
108 }
109
110 if (ch == 'd') {
111 if (islong && (long)num < 0) {
112 num = -(long)num;
113 out(info, '-');
114 } else if (!islong && (int)num < 0) {
115 num = -(int)num;
116 out(info, '-');
117 }
Stefan Roese363ab7b2015-11-23 07:00:22 +0100118 }
Simon Glassbdfb70b2016-01-05 09:30:57 -0700119 if (!num) {
Simon Glass5958cbe2016-08-04 21:58:14 -0600120 out_dgt(info, 0);
Simon Glassbdfb70b2016-01-05 09:30:57 -0700121 } else {
Andre Przywara32118682017-01-02 11:48:28 +0000122 for (; div; div /= 10)
Simon Glass5958cbe2016-08-04 21:58:14 -0600123 div_out(info, &num, div);
Simon Glassbdfb70b2016-01-05 09:30:57 -0700124 }
Stefan Roese363ab7b2015-11-23 07:00:22 +0100125 break;
126 case 'x':
Andre Przywara32118682017-01-02 11:48:28 +0000127 if (islong) {
128 num = va_arg(va, unsigned long);
129 div = 1UL << (sizeof(long) * 8 - 4);
130 } else {
131 num = va_arg(va, unsigned int);
132 div = 0x10000000;
133 }
Simon Glassbdfb70b2016-01-05 09:30:57 -0700134 if (!num) {
Simon Glass5958cbe2016-08-04 21:58:14 -0600135 out_dgt(info, 0);
Simon Glassbdfb70b2016-01-05 09:30:57 -0700136 } else {
Andre Przywara32118682017-01-02 11:48:28 +0000137 for (; div; div /= 0x10)
Simon Glass5958cbe2016-08-04 21:58:14 -0600138 div_out(info, &num, div);
Simon Glassbdfb70b2016-01-05 09:30:57 -0700139 }
Stefan Roese363ab7b2015-11-23 07:00:22 +0100140 break;
141 case 'c':
Simon Glass5958cbe2016-08-04 21:58:14 -0600142 out(info, (char)(va_arg(va, int)));
Stefan Roese363ab7b2015-11-23 07:00:22 +0100143 break;
144 case 's':
145 p = va_arg(va, char*);
146 break;
147 case '%':
Simon Glass5958cbe2016-08-04 21:58:14 -0600148 out(info, '%');
Stefan Roese363ab7b2015-11-23 07:00:22 +0100149 default:
150 break;
151 }
152
Simon Glass5958cbe2016-08-04 21:58:14 -0600153 *info->bf = 0;
154 info->bf = p;
155 while (*info->bf++ && width > 0)
Simon Glassb5afcad2016-05-14 14:02:52 -0600156 width--;
157 while (width-- > 0)
Simon Glass5958cbe2016-08-04 21:58:14 -0600158 info->putc(info, lz ? '0' : ' ');
Simon Glass5bd9f6c2015-12-29 05:22:46 -0700159 if (p) {
160 while ((ch = *p++))
Simon Glass5958cbe2016-08-04 21:58:14 -0600161 info->putc(info, ch);
Simon Glass5bd9f6c2015-12-29 05:22:46 -0700162 }
Stefan Roese363ab7b2015-11-23 07:00:22 +0100163 }
164 }
165
166abort:
Stefan Roese363ab7b2015-11-23 07:00:22 +0100167 return 0;
168}
Sjoerd Simons1168ed52015-12-04 23:27:37 +0100169
Hans de Goede5f183982016-06-10 21:03:34 +0200170int vprintf(const char *fmt, va_list va)
171{
Simon Glass5958cbe2016-08-04 21:58:14 -0600172 struct printf_info info;
173
174 info.putc = putc_normal;
175 return _vprintf(&info, fmt, va);
Hans de Goede5f183982016-06-10 21:03:34 +0200176}
177
Sjoerd Simons1168ed52015-12-04 23:27:37 +0100178int printf(const char *fmt, ...)
179{
Simon Glass5958cbe2016-08-04 21:58:14 -0600180 struct printf_info info;
181
Sjoerd Simons1168ed52015-12-04 23:27:37 +0100182 va_list va;
183 int ret;
184
Simon Glass5958cbe2016-08-04 21:58:14 -0600185 info.putc = putc_normal;
Sjoerd Simons1168ed52015-12-04 23:27:37 +0100186 va_start(va, fmt);
Simon Glass5958cbe2016-08-04 21:58:14 -0600187 ret = _vprintf(&info, fmt, va);
Simon Glass49138f82016-05-14 14:02:53 -0600188 va_end(va);
189
190 return ret;
191}
192
Simon Glass5958cbe2016-08-04 21:58:14 -0600193static void putc_outstr(struct printf_info *info, char ch)
Simon Glass49138f82016-05-14 14:02:53 -0600194{
Simon Glass5958cbe2016-08-04 21:58:14 -0600195 *info->outstr++ = ch;
Simon Glass49138f82016-05-14 14:02:53 -0600196}
197
Marek Vasut8dc095b2016-05-31 23:12:46 +0200198int sprintf(char *buf, const char *fmt, ...)
Simon Glass49138f82016-05-14 14:02:53 -0600199{
Simon Glass5958cbe2016-08-04 21:58:14 -0600200 struct printf_info info;
Simon Glass49138f82016-05-14 14:02:53 -0600201 va_list va;
202 int ret;
203
204 va_start(va, fmt);
Simon Glass5958cbe2016-08-04 21:58:14 -0600205 info.outstr = buf;
206 info.putc = putc_outstr;
207 ret = _vprintf(&info, fmt, va);
Sjoerd Simons1168ed52015-12-04 23:27:37 +0100208 va_end(va);
Simon Glass5958cbe2016-08-04 21:58:14 -0600209 *info.outstr = '\0';
Sjoerd Simons1168ed52015-12-04 23:27:37 +0100210
211 return ret;
212}
Marek Vasut8dc095b2016-05-31 23:12:46 +0200213
214/* Note that size is ignored */
215int snprintf(char *buf, size_t size, const char *fmt, ...)
216{
Simon Glass5958cbe2016-08-04 21:58:14 -0600217 struct printf_info info;
Marek Vasut8dc095b2016-05-31 23:12:46 +0200218 va_list va;
219 int ret;
220
221 va_start(va, fmt);
Simon Glass5958cbe2016-08-04 21:58:14 -0600222 info.outstr = buf;
223 info.putc = putc_outstr;
224 ret = _vprintf(&info, fmt, va);
Marek Vasut8dc095b2016-05-31 23:12:46 +0200225 va_end(va);
Simon Glass5958cbe2016-08-04 21:58:14 -0600226 *info.outstr = '\0';
Marek Vasut8dc095b2016-05-31 23:12:46 +0200227
228 return ret;
229}
Simon Glassaffe9ee2016-07-04 11:58:13 -0600230
231void __assert_fail(const char *assertion, const char *file, unsigned line,
232 const char *function)
233{
234 /* This will not return */
235 printf("%s:%u: %s: Assertion `%s' failed.", file, line, function,
236 assertion);
237 hang();
238}