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