xref: /openbmc/u-boot/lib/tiny-printf.c (revision 2d48caa4)
1 /*
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 
16 struct 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() */
20 
21 	/* Output a character */
22 	void (*putc)(struct printf_info *info, char ch);
23 };
24 
25 void putc_normal(struct printf_info *info, char ch)
26 {
27 	putc(ch);
28 }
29 
30 static void out(struct printf_info *info, char c)
31 {
32 	*info->bf++ = c;
33 }
34 
35 static 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 
41 static void div_out(struct printf_info *info, unsigned int *num,
42 		    unsigned int div)
43 {
44 	unsigned char dgt = 0;
45 
46 	while (*num >= div) {
47 		*num -= div;
48 		dgt++;
49 	}
50 
51 	if (info->zs || dgt > 0)
52 		out_dgt(info, dgt);
53 }
54 
55 int _vprintf(struct printf_info *info, const char *fmt, va_list va)
56 {
57 	char ch;
58 	char *p;
59 	unsigned int num;
60 	char buf[12];
61 	unsigned int div;
62 
63 	while ((ch = *(fmt++))) {
64 		if (ch != '%') {
65 			info->putc(info, ch);
66 		} else {
67 			bool lz = false;
68 			int width = 0;
69 
70 			ch = *(fmt++);
71 			if (ch == '0') {
72 				ch = *(fmt++);
73 				lz = 1;
74 			}
75 
76 			if (ch >= '0' && ch <= '9') {
77 				width = 0;
78 				while (ch >= '0' && ch <= '9') {
79 					width = (width * 10) + ch - '0';
80 					ch = *fmt++;
81 				}
82 			}
83 			info->bf = buf;
84 			p = info->bf;
85 			info->zs = 0;
86 
87 			switch (ch) {
88 			case '\0':
89 				goto abort;
90 			case 'u':
91 			case 'd':
92 				num = va_arg(va, unsigned int);
93 				if (ch == 'd' && (int)num < 0) {
94 					num = -(int)num;
95 					out(info, '-');
96 				}
97 				if (!num) {
98 					out_dgt(info, 0);
99 				} else {
100 					for (div = 1000000000; div; div /= 10)
101 						div_out(info, &num, div);
102 				}
103 				break;
104 			case 'x':
105 				num = va_arg(va, unsigned int);
106 				if (!num) {
107 					out_dgt(info, 0);
108 				} else {
109 					for (div = 0x10000000; div; div /= 0x10)
110 						div_out(info, &num, div);
111 				}
112 				break;
113 			case 'c':
114 				out(info, (char)(va_arg(va, int)));
115 				break;
116 			case 's':
117 				p = va_arg(va, char*);
118 				break;
119 			case '%':
120 				out(info, '%');
121 			default:
122 				break;
123 			}
124 
125 			*info->bf = 0;
126 			info->bf = p;
127 			while (*info->bf++ && width > 0)
128 				width--;
129 			while (width-- > 0)
130 				info->putc(info, lz ? '0' : ' ');
131 			if (p) {
132 				while ((ch = *p++))
133 					info->putc(info, ch);
134 			}
135 		}
136 	}
137 
138 abort:
139 	return 0;
140 }
141 
142 int vprintf(const char *fmt, va_list va)
143 {
144 	struct printf_info info;
145 
146 	info.putc = putc_normal;
147 	return _vprintf(&info, fmt, va);
148 }
149 
150 int printf(const char *fmt, ...)
151 {
152 	struct printf_info info;
153 
154 	va_list va;
155 	int ret;
156 
157 	info.putc = putc_normal;
158 	va_start(va, fmt);
159 	ret = _vprintf(&info, fmt, va);
160 	va_end(va);
161 
162 	return ret;
163 }
164 
165 static void putc_outstr(struct printf_info *info, char ch)
166 {
167 	*info->outstr++ = ch;
168 }
169 
170 int sprintf(char *buf, const char *fmt, ...)
171 {
172 	struct printf_info info;
173 	va_list va;
174 	int ret;
175 
176 	va_start(va, fmt);
177 	info.outstr = buf;
178 	info.putc = putc_outstr;
179 	ret = _vprintf(&info, fmt, va);
180 	va_end(va);
181 	*info.outstr = '\0';
182 
183 	return ret;
184 }
185 
186 /* Note that size is ignored */
187 int snprintf(char *buf, size_t size, const char *fmt, ...)
188 {
189 	struct printf_info info;
190 	va_list va;
191 	int ret;
192 
193 	va_start(va, fmt);
194 	info.outstr = buf;
195 	info.putc = putc_outstr;
196 	ret = _vprintf(&info, fmt, va);
197 	va_end(va);
198 	*info.outstr = '\0';
199 
200 	return ret;
201 }
202 
203 void __assert_fail(const char *assertion, const char *file, unsigned line,
204 		   const char *function)
205 {
206 	/* This will not return */
207 	printf("%s:%u: %s: Assertion `%s' failed.", file, line, function,
208 	       assertion);
209 	hang();
210 }
211