1 // SPDX-License-Identifier: GPL-2.0-only
4 #include "ucall_common.h"
6 #define APPEND_BUFFER_SAFE(str, end, v) \
8 GUEST_ASSERT(str < end); \
12 static int isdigit(int ch)
14 return (ch >= '0') && (ch <= '9');
17 static int skip_atoi(const char **s)
22 i = i * 10 + *((*s)++) - '0';
26 #define ZEROPAD 1 /* pad with zero */
27 #define SIGN 2 /* unsigned/signed long */
28 #define PLUS 4 /* show plus */
29 #define SPACE 8 /* space if plus */
30 #define LEFT 16 /* left justified */
31 #define SMALL 32 /* Must be 32 == 0x20 */
32 #define SPECIAL 64 /* 0x */
34 #define __do_div(n, base) \
38 __res = ((uint64_t) n) % (uint32_t) base; \
39 n = ((uint64_t) n) / (uint32_t) base; \
43 static char *number(char *str, const char *end, long num, int base, int size,
44 int precision, int type)
46 /* we are called with base 8, 10 or 16, only, thus don't need "G..." */
47 static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
54 * locase = 0 or 0x20. ORing digits or letters with 'locase'
55 * produces same digits or (maybe lowercased) letters
57 locase = (type & SMALL);
60 if (base < 2 || base > 16)
62 c = (type & ZEROPAD) ? '0' : ' ';
69 } else if (type & PLUS) {
72 } else if (type & SPACE) {
88 tmp[i++] = (digits[__do_div(num, base)] | locase);
92 if (!(type & (ZEROPAD + LEFT)))
94 APPEND_BUFFER_SAFE(str, end, ' ');
96 APPEND_BUFFER_SAFE(str, end, sign);
99 APPEND_BUFFER_SAFE(str, end, '0');
100 else if (base == 16) {
101 APPEND_BUFFER_SAFE(str, end, '0');
102 APPEND_BUFFER_SAFE(str, end, 'x');
107 APPEND_BUFFER_SAFE(str, end, c);
108 while (i < precision--)
109 APPEND_BUFFER_SAFE(str, end, '0');
111 APPEND_BUFFER_SAFE(str, end, tmp[i]);
113 APPEND_BUFFER_SAFE(str, end, ' ');
118 int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args)
126 int flags; /* flags to number() */
128 int field_width; /* width of output field */
130 * min. # of digits for integers; max
131 * number of chars for from string
133 int qualifier; /* 'h', 'l', or 'L' for integer fields */
136 GUEST_ASSERT(buf < end);
139 for (str = buf; *fmt; ++fmt) {
141 APPEND_BUFFER_SAFE(str, end, *fmt);
148 ++fmt; /* this also skips first '%' */
167 /* get field width */
170 field_width = skip_atoi(&fmt);
171 else if (*fmt == '*') {
173 /* it's the next argument */
174 field_width = va_arg(args, int);
175 if (field_width < 0) {
176 field_width = -field_width;
181 /* get the precision */
186 precision = skip_atoi(&fmt);
187 else if (*fmt == '*') {
189 /* it's the next argument */
190 precision = va_arg(args, int);
196 /* get the conversion qualifier */
198 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
204 * Play nice with %llu, %llx, etc. KVM selftests only support
205 * 64-bit builds, so just treat %ll* the same as %l*.
207 if (qualifier == 'l' && *fmt == 'l')
216 while (--field_width > 0)
217 APPEND_BUFFER_SAFE(str, end, ' ');
218 APPEND_BUFFER_SAFE(str, end,
219 (uint8_t)va_arg(args, int));
220 while (--field_width > 0)
221 APPEND_BUFFER_SAFE(str, end, ' ');
225 s = va_arg(args, char *);
226 len = strnlen(s, precision);
229 while (len < field_width--)
230 APPEND_BUFFER_SAFE(str, end, ' ');
231 for (i = 0; i < len; ++i)
232 APPEND_BUFFER_SAFE(str, end, *s++);
233 while (len < field_width--)
234 APPEND_BUFFER_SAFE(str, end, ' ');
238 if (field_width == -1) {
239 field_width = 2 * sizeof(void *);
240 flags |= SPECIAL | SMALL | ZEROPAD;
242 str = number(str, end,
243 (uint64_t)va_arg(args, void *), 16,
244 field_width, precision, flags);
248 if (qualifier == 'l') {
249 long *ip = va_arg(args, long *);
252 int *ip = va_arg(args, int *);
258 APPEND_BUFFER_SAFE(str, end, '%');
261 /* integer number formats - set up the flags and "break" */
279 APPEND_BUFFER_SAFE(str, end, '%');
281 APPEND_BUFFER_SAFE(str, end, *fmt);
286 if (qualifier == 'l')
287 num = va_arg(args, uint64_t);
288 else if (qualifier == 'h') {
289 num = (uint16_t)va_arg(args, int);
292 } else if (flags & SIGN)
293 num = va_arg(args, int);
295 num = va_arg(args, uint32_t);
296 str = number(str, end, num, base, field_width, precision, flags);
299 GUEST_ASSERT(str < end);
304 int guest_snprintf(char *buf, int n, const char *fmt, ...)
310 len = guest_vsnprintf(buf, n, fmt, va);