blob: 0b8512faf5b95c07477f146b0d0c8fe700344f57 [file] [log] [blame]
Stefan Roese7d9cde12015-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 Glass45313e82016-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 Roese7d9cde12015-11-23 07:00:22 +010020
Simon Glass45313e82016-08-04 21:58:14 -060021 /* Output a character */
22 void (*putc)(struct printf_info *info, char ch);
23};
Simon Glass5c411d82016-05-14 14:02:53 -060024
Simon Glass45313e82016-08-04 21:58:14 -060025void putc_normal(struct printf_info *info, char ch)
Stefan Roese7d9cde12015-11-23 07:00:22 +010026{
Simon Glass45313e82016-08-04 21:58:14 -060027 putc(ch);
Stefan Roese7d9cde12015-11-23 07:00:22 +010028}
29
Simon Glass45313e82016-08-04 21:58:14 -060030static void out(struct printf_info *info, char c)
Stefan Roese7d9cde12015-11-23 07:00:22 +010031{
Simon Glass45313e82016-08-04 21:58:14 -060032 *info->bf++ = c;
Stefan Roese7d9cde12015-11-23 07:00:22 +010033}
34
Simon Glass45313e82016-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 Przywaraa28e1d92017-01-02 11:48:28 +000041static void div_out(struct printf_info *info, unsigned long *num,
42 unsigned long div)
Stefan Roese7d9cde12015-11-23 07:00:22 +010043{
44 unsigned char dgt = 0;
45
Stefan Roesea5ecdd02015-11-16 15:26:34 +010046 while (*num >= div) {
47 *num -= div;
Stefan Roese7d9cde12015-11-23 07:00:22 +010048 dgt++;
49 }
50
Simon Glass45313e82016-08-04 21:58:14 -060051 if (info->zs || dgt > 0)
52 out_dgt(info, dgt);
Stefan Roese7d9cde12015-11-23 07:00:22 +010053}
54
Simon Glass45313e82016-08-04 21:58:14 -060055int _vprintf(struct printf_info *info, const char *fmt, va_list va)
Stefan Roese7d9cde12015-11-23 07:00:22 +010056{
Stefan Roese7d9cde12015-11-23 07:00:22 +010057 char ch;
58 char *p;
Andre Przywaraa28e1d92017-01-02 11:48:28 +000059 unsigned long num;
Stefan Roesea5ecdd02015-11-16 15:26:34 +010060 char buf[12];
Andre Przywaraa28e1d92017-01-02 11:48:28 +000061 unsigned long div;
Stefan Roese7d9cde12015-11-23 07:00:22 +010062
Stefan Roese7d9cde12015-11-23 07:00:22 +010063 while ((ch = *(fmt++))) {
64 if (ch != '%') {
Simon Glass45313e82016-08-04 21:58:14 -060065 info->putc(info, ch);
Stefan Roese7d9cde12015-11-23 07:00:22 +010066 } else {
Simon Glass1fb67602016-05-14 14:02:52 -060067 bool lz = false;
68 int width = 0;
Andre Przywaraa28e1d92017-01-02 11:48:28 +000069 bool islong = false;
Stefan Roese7d9cde12015-11-23 07:00:22 +010070
71 ch = *(fmt++);
72 if (ch == '0') {
73 ch = *(fmt++);
74 lz = 1;
75 }
76
77 if (ch >= '0' && ch <= '9') {
Simon Glass1fb67602016-05-14 14:02:52 -060078 width = 0;
Stefan Roese7d9cde12015-11-23 07:00:22 +010079 while (ch >= '0' && ch <= '9') {
Simon Glass1fb67602016-05-14 14:02:52 -060080 width = (width * 10) + ch - '0';
Stefan Roese7d9cde12015-11-23 07:00:22 +010081 ch = *fmt++;
82 }
83 }
Andre Przywaraa28e1d92017-01-02 11:48:28 +000084 if (ch == 'l') {
85 ch = *(fmt++);
86 islong = true;
87 }
88
Simon Glass45313e82016-08-04 21:58:14 -060089 info->bf = buf;
90 p = info->bf;
91 info->zs = 0;
Stefan Roese7d9cde12015-11-23 07:00:22 +010092
93 switch (ch) {
Simon Glass1fb67602016-05-14 14:02:52 -060094 case '\0':
Stefan Roese7d9cde12015-11-23 07:00:22 +010095 goto abort;
96 case 'u':
97 case 'd':
Andre Przywaraa28e1d92017-01-02 11:48:28 +000098 div = 1000000000;
99 if (islong) {
100 num = va_arg(va, unsigned long);
101 if (sizeof(long) > 4)
102 div *= div * 10;
103 } else {
104 num = va_arg(va, unsigned int);
105 }
106
107 if (ch == 'd') {
108 if (islong && (long)num < 0) {
109 num = -(long)num;
110 out(info, '-');
111 } else if (!islong && (int)num < 0) {
112 num = -(int)num;
113 out(info, '-');
114 }
Stefan Roese7d9cde12015-11-23 07:00:22 +0100115 }
Simon Glass74b13202016-01-05 09:30:57 -0700116 if (!num) {
Simon Glass45313e82016-08-04 21:58:14 -0600117 out_dgt(info, 0);
Simon Glass74b13202016-01-05 09:30:57 -0700118 } else {
Andre Przywaraa28e1d92017-01-02 11:48:28 +0000119 for (; div; div /= 10)
Simon Glass45313e82016-08-04 21:58:14 -0600120 div_out(info, &num, div);
Simon Glass74b13202016-01-05 09:30:57 -0700121 }
Stefan Roese7d9cde12015-11-23 07:00:22 +0100122 break;
123 case 'x':
Andre Przywaraa28e1d92017-01-02 11:48:28 +0000124 if (islong) {
125 num = va_arg(va, unsigned long);
126 div = 1UL << (sizeof(long) * 8 - 4);
127 } else {
128 num = va_arg(va, unsigned int);
129 div = 0x10000000;
130 }
Simon Glass74b13202016-01-05 09:30:57 -0700131 if (!num) {
Simon Glass45313e82016-08-04 21:58:14 -0600132 out_dgt(info, 0);
Simon Glass74b13202016-01-05 09:30:57 -0700133 } else {
Andre Przywaraa28e1d92017-01-02 11:48:28 +0000134 for (; div; div /= 0x10)
Simon Glass45313e82016-08-04 21:58:14 -0600135 div_out(info, &num, div);
Simon Glass74b13202016-01-05 09:30:57 -0700136 }
Stefan Roese7d9cde12015-11-23 07:00:22 +0100137 break;
138 case 'c':
Simon Glass45313e82016-08-04 21:58:14 -0600139 out(info, (char)(va_arg(va, int)));
Stefan Roese7d9cde12015-11-23 07:00:22 +0100140 break;
141 case 's':
142 p = va_arg(va, char*);
143 break;
144 case '%':
Simon Glass45313e82016-08-04 21:58:14 -0600145 out(info, '%');
Stefan Roese7d9cde12015-11-23 07:00:22 +0100146 default:
147 break;
148 }
149
Simon Glass45313e82016-08-04 21:58:14 -0600150 *info->bf = 0;
151 info->bf = p;
152 while (*info->bf++ && width > 0)
Simon Glass1fb67602016-05-14 14:02:52 -0600153 width--;
154 while (width-- > 0)
Simon Glass45313e82016-08-04 21:58:14 -0600155 info->putc(info, lz ? '0' : ' ');
Simon Glass8e316812015-12-29 05:22:46 -0700156 if (p) {
157 while ((ch = *p++))
Simon Glass45313e82016-08-04 21:58:14 -0600158 info->putc(info, ch);
Simon Glass8e316812015-12-29 05:22:46 -0700159 }
Stefan Roese7d9cde12015-11-23 07:00:22 +0100160 }
161 }
162
163abort:
Stefan Roese7d9cde12015-11-23 07:00:22 +0100164 return 0;
165}
Sjoerd Simons962a43c2015-12-04 23:27:37 +0100166
Hans de Goededa70b4d2016-06-10 21:03:34 +0200167int vprintf(const char *fmt, va_list va)
168{
Simon Glass45313e82016-08-04 21:58:14 -0600169 struct printf_info info;
170
171 info.putc = putc_normal;
172 return _vprintf(&info, fmt, va);
Hans de Goededa70b4d2016-06-10 21:03:34 +0200173}
174
Sjoerd Simons962a43c2015-12-04 23:27:37 +0100175int printf(const char *fmt, ...)
176{
Simon Glass45313e82016-08-04 21:58:14 -0600177 struct printf_info info;
178
Sjoerd Simons962a43c2015-12-04 23:27:37 +0100179 va_list va;
180 int ret;
181
Simon Glass45313e82016-08-04 21:58:14 -0600182 info.putc = putc_normal;
Sjoerd Simons962a43c2015-12-04 23:27:37 +0100183 va_start(va, fmt);
Simon Glass45313e82016-08-04 21:58:14 -0600184 ret = _vprintf(&info, fmt, va);
Sjoerd Simons962a43c2015-12-04 23:27:37 +0100185 va_end(va);
186
187 return ret;
188}
Simon Glass5c411d82016-05-14 14:02:53 -0600189
Simon Glass45313e82016-08-04 21:58:14 -0600190static void putc_outstr(struct printf_info *info, char ch)
Simon Glass5c411d82016-05-14 14:02:53 -0600191{
Simon Glass45313e82016-08-04 21:58:14 -0600192 *info->outstr++ = ch;
Simon Glass5c411d82016-05-14 14:02:53 -0600193}
194
Marek Vasutabeb2722016-05-31 23:12:46 +0200195int sprintf(char *buf, const char *fmt, ...)
Simon Glass5c411d82016-05-14 14:02:53 -0600196{
Simon Glass45313e82016-08-04 21:58:14 -0600197 struct printf_info info;
Simon Glass5c411d82016-05-14 14:02:53 -0600198 va_list va;
199 int ret;
200
201 va_start(va, fmt);
Simon Glass45313e82016-08-04 21:58:14 -0600202 info.outstr = buf;
203 info.putc = putc_outstr;
204 ret = _vprintf(&info, fmt, va);
Simon Glass5c411d82016-05-14 14:02:53 -0600205 va_end(va);
Simon Glass45313e82016-08-04 21:58:14 -0600206 *info.outstr = '\0';
Simon Glass5c411d82016-05-14 14:02:53 -0600207
208 return ret;
209}
Marek Vasutabeb2722016-05-31 23:12:46 +0200210
211/* Note that size is ignored */
212int snprintf(char *buf, size_t size, const char *fmt, ...)
213{
Simon Glass45313e82016-08-04 21:58:14 -0600214 struct printf_info info;
Marek Vasutabeb2722016-05-31 23:12:46 +0200215 va_list va;
216 int ret;
217
218 va_start(va, fmt);
Simon Glass45313e82016-08-04 21:58:14 -0600219 info.outstr = buf;
220 info.putc = putc_outstr;
221 ret = _vprintf(&info, fmt, va);
Marek Vasutabeb2722016-05-31 23:12:46 +0200222 va_end(va);
Simon Glass45313e82016-08-04 21:58:14 -0600223 *info.outstr = '\0';
Marek Vasutabeb2722016-05-31 23:12:46 +0200224
225 return ret;
226}
Simon Glasse2409132016-07-04 11:58:13 -0600227
228void __assert_fail(const char *assertion, const char *file, unsigned line,
229 const char *function)
230{
231 /* This will not return */
232 printf("%s:%u: %s: Assertion `%s' failed.", file, line, function,
233 assertion);
234 hang();
235}