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