1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* -*- linux-c -*- ------------------------------------------------------- * |
3 | * |
4 | * Copyright (C) 1991, 1992 Linus Torvalds |
5 | * Copyright 2007 rPath, Inc. - All Rights Reserved |
6 | * |
7 | * ----------------------------------------------------------------------- */ |
8 | |
9 | /* |
10 | * Oh, it's a waste of space, but oh-so-yummy for debugging. This |
11 | * version of printf() does not include 64-bit support. "Live with |
12 | * it." |
13 | * |
14 | */ |
15 | |
16 | #include "boot.h" |
17 | |
18 | static int skip_atoi(const char **s) |
19 | { |
20 | int i = 0; |
21 | |
22 | while (isdigit(ch: **s)) |
23 | i = i * 10 + *((*s)++) - '0'; |
24 | return i; |
25 | } |
26 | |
27 | #define ZEROPAD 1 /* pad with zero */ |
28 | #define SIGN 2 /* unsigned/signed long */ |
29 | #define PLUS 4 /* show plus */ |
30 | #define SPACE 8 /* space if plus */ |
31 | #define LEFT 16 /* left justified */ |
32 | #define SMALL 32 /* Must be 32 == 0x20 */ |
33 | #define SPECIAL 64 /* 0x */ |
34 | |
35 | #define __do_div(n, base) ({ \ |
36 | int __res; \ |
37 | __res = ((unsigned long) n) % (unsigned) base; \ |
38 | n = ((unsigned long) n) / (unsigned) base; \ |
39 | __res; }) |
40 | |
41 | static char *number(char *str, long num, int base, int size, int precision, |
42 | int type) |
43 | { |
44 | /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ |
45 | static const char digits[16] = "0123456789ABCDEF" ; /* "GHIJKLMNOPQRSTUVWXYZ"; */ |
46 | |
47 | char tmp[66]; |
48 | char c, sign, locase; |
49 | int i; |
50 | |
51 | /* locase = 0 or 0x20. ORing digits or letters with 'locase' |
52 | * produces same digits or (maybe lowercased) letters */ |
53 | locase = (type & SMALL); |
54 | if (type & LEFT) |
55 | type &= ~ZEROPAD; |
56 | if (base < 2 || base > 16) |
57 | return NULL; |
58 | c = (type & ZEROPAD) ? '0' : ' '; |
59 | sign = 0; |
60 | if (type & SIGN) { |
61 | if (num < 0) { |
62 | sign = '-'; |
63 | num = -num; |
64 | size--; |
65 | } else if (type & PLUS) { |
66 | sign = '+'; |
67 | size--; |
68 | } else if (type & SPACE) { |
69 | sign = ' '; |
70 | size--; |
71 | } |
72 | } |
73 | if (type & SPECIAL) { |
74 | if (base == 16) |
75 | size -= 2; |
76 | else if (base == 8) |
77 | size--; |
78 | } |
79 | i = 0; |
80 | if (num == 0) |
81 | tmp[i++] = '0'; |
82 | else |
83 | while (num != 0) |
84 | tmp[i++] = (digits[__do_div(num, base)] | locase); |
85 | if (i > precision) |
86 | precision = i; |
87 | size -= precision; |
88 | if (!(type & (ZEROPAD + LEFT))) |
89 | while (size-- > 0) |
90 | *str++ = ' '; |
91 | if (sign) |
92 | *str++ = sign; |
93 | if (type & SPECIAL) { |
94 | if (base == 8) |
95 | *str++ = '0'; |
96 | else if (base == 16) { |
97 | *str++ = '0'; |
98 | *str++ = ('X' | locase); |
99 | } |
100 | } |
101 | if (!(type & LEFT)) |
102 | while (size-- > 0) |
103 | *str++ = c; |
104 | while (i < precision--) |
105 | *str++ = '0'; |
106 | while (i-- > 0) |
107 | *str++ = tmp[i]; |
108 | while (size-- > 0) |
109 | *str++ = ' '; |
110 | return str; |
111 | } |
112 | |
113 | int vsprintf(char *buf, const char *fmt, va_list args) |
114 | { |
115 | int len; |
116 | unsigned long num; |
117 | int i, base; |
118 | char *str; |
119 | const char *s; |
120 | |
121 | int flags; /* flags to number() */ |
122 | |
123 | int field_width; /* width of output field */ |
124 | int precision; /* min. # of digits for integers; max |
125 | number of chars for from string */ |
126 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ |
127 | |
128 | for (str = buf; *fmt; ++fmt) { |
129 | if (*fmt != '%') { |
130 | *str++ = *fmt; |
131 | continue; |
132 | } |
133 | |
134 | /* process flags */ |
135 | flags = 0; |
136 | repeat: |
137 | ++fmt; /* this also skips first '%' */ |
138 | switch (*fmt) { |
139 | case '-': |
140 | flags |= LEFT; |
141 | goto repeat; |
142 | case '+': |
143 | flags |= PLUS; |
144 | goto repeat; |
145 | case ' ': |
146 | flags |= SPACE; |
147 | goto repeat; |
148 | case '#': |
149 | flags |= SPECIAL; |
150 | goto repeat; |
151 | case '0': |
152 | flags |= ZEROPAD; |
153 | goto repeat; |
154 | } |
155 | |
156 | /* get field width */ |
157 | field_width = -1; |
158 | if (isdigit(ch: *fmt)) |
159 | field_width = skip_atoi(s: &fmt); |
160 | else if (*fmt == '*') { |
161 | ++fmt; |
162 | /* it's the next argument */ |
163 | field_width = va_arg(args, int); |
164 | if (field_width < 0) { |
165 | field_width = -field_width; |
166 | flags |= LEFT; |
167 | } |
168 | } |
169 | |
170 | /* get the precision */ |
171 | precision = -1; |
172 | if (*fmt == '.') { |
173 | ++fmt; |
174 | if (isdigit(ch: *fmt)) |
175 | precision = skip_atoi(s: &fmt); |
176 | else if (*fmt == '*') { |
177 | ++fmt; |
178 | /* it's the next argument */ |
179 | precision = va_arg(args, int); |
180 | } |
181 | if (precision < 0) |
182 | precision = 0; |
183 | } |
184 | |
185 | /* get the conversion qualifier */ |
186 | qualifier = -1; |
187 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { |
188 | qualifier = *fmt; |
189 | ++fmt; |
190 | } |
191 | |
192 | /* default base */ |
193 | base = 10; |
194 | |
195 | switch (*fmt) { |
196 | case 'c': |
197 | if (!(flags & LEFT)) |
198 | while (--field_width > 0) |
199 | *str++ = ' '; |
200 | *str++ = (unsigned char)va_arg(args, int); |
201 | while (--field_width > 0) |
202 | *str++ = ' '; |
203 | continue; |
204 | |
205 | case 's': |
206 | s = va_arg(args, char *); |
207 | len = strnlen(s, maxlen: precision); |
208 | |
209 | if (!(flags & LEFT)) |
210 | while (len < field_width--) |
211 | *str++ = ' '; |
212 | for (i = 0; i < len; ++i) |
213 | *str++ = *s++; |
214 | while (len < field_width--) |
215 | *str++ = ' '; |
216 | continue; |
217 | |
218 | case 'p': |
219 | if (field_width == -1) { |
220 | field_width = 2 * sizeof(void *); |
221 | flags |= ZEROPAD; |
222 | } |
223 | str = number(str, |
224 | num: (unsigned long)va_arg(args, void *), base: 16, |
225 | size: field_width, precision, type: flags); |
226 | continue; |
227 | |
228 | case 'n': |
229 | if (qualifier == 'l') { |
230 | long *ip = va_arg(args, long *); |
231 | *ip = (str - buf); |
232 | } else { |
233 | int *ip = va_arg(args, int *); |
234 | *ip = (str - buf); |
235 | } |
236 | continue; |
237 | |
238 | case '%': |
239 | *str++ = '%'; |
240 | continue; |
241 | |
242 | /* integer number formats - set up the flags and "break" */ |
243 | case 'o': |
244 | base = 8; |
245 | break; |
246 | |
247 | case 'x': |
248 | flags |= SMALL; |
249 | case 'X': |
250 | base = 16; |
251 | break; |
252 | |
253 | case 'd': |
254 | case 'i': |
255 | flags |= SIGN; |
256 | case 'u': |
257 | break; |
258 | |
259 | default: |
260 | *str++ = '%'; |
261 | if (*fmt) |
262 | *str++ = *fmt; |
263 | else |
264 | --fmt; |
265 | continue; |
266 | } |
267 | if (qualifier == 'l') |
268 | num = va_arg(args, unsigned long); |
269 | else if (qualifier == 'h') { |
270 | num = (unsigned short)va_arg(args, int); |
271 | if (flags & SIGN) |
272 | num = (short)num; |
273 | } else if (flags & SIGN) |
274 | num = va_arg(args, int); |
275 | else |
276 | num = va_arg(args, unsigned int); |
277 | str = number(str, num, base, size: field_width, precision, type: flags); |
278 | } |
279 | *str = '\0'; |
280 | return str - buf; |
281 | } |
282 | |
283 | int sprintf(char *buf, const char *fmt, ...) |
284 | { |
285 | va_list args; |
286 | int i; |
287 | |
288 | va_start(args, fmt); |
289 | i = vsprintf(buf, fmt, args); |
290 | va_end(args); |
291 | return i; |
292 | } |
293 | |
294 | int printf(const char *fmt, ...) |
295 | { |
296 | char printf_buf[1024]; |
297 | va_list args; |
298 | int printed; |
299 | |
300 | va_start(args, fmt); |
301 | printed = vsprintf(buf: printf_buf, fmt, args); |
302 | va_end(args); |
303 | |
304 | puts(printf_buf); |
305 | |
306 | return printed; |
307 | } |
308 | |