1 // SPDX-License-Identifier: GPL-2.0-only 2 #include "test_util.h" 3 #include "kvm_util.h" 4 #include "ucall_common.h" 5 6 #define APPEND_BUFFER_SAFE(str, end, v) \ 7 do { \ 8 GUEST_ASSERT(str < end); \ 9 *str++ = (v); \ 10 } while (0) 11 12 static int isdigit(int ch) 13 { 14 return (ch >= '0') && (ch <= '9'); 15 } 16 17 static int skip_atoi(const char **s) 18 { 19 int i = 0; 20 21 while (isdigit(**s)) 22 i = i * 10 + *((*s)++) - '0'; 23 return i; 24 } 25 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 */ 33 34 #define __do_div(n, base) \ 35 ({ \ 36 int __res; \ 37 \ 38 __res = ((uint64_t) n) % (uint32_t) base; \ 39 n = ((uint64_t) n) / (uint32_t) base; \ 40 __res; \ 41 }) 42 43 static char *number(char *str, const char *end, long num, int base, int size, 44 int precision, int type) 45 { 46 /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 47 static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ 48 49 char tmp[66]; 50 char c, sign, locase; 51 int i; 52 53 /* 54 * locase = 0 or 0x20. ORing digits or letters with 'locase' 55 * produces same digits or (maybe lowercased) letters 56 */ 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 APPEND_BUFFER_SAFE(str, end, ' '); 95 if (sign) 96 APPEND_BUFFER_SAFE(str, end, sign); 97 if (type & SPECIAL) { 98 if (base == 8) 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'); 103 } 104 } 105 if (!(type & LEFT)) 106 while (size-- > 0) 107 APPEND_BUFFER_SAFE(str, end, c); 108 while (i < precision--) 109 APPEND_BUFFER_SAFE(str, end, '0'); 110 while (i-- > 0) 111 APPEND_BUFFER_SAFE(str, end, tmp[i]); 112 while (size-- > 0) 113 APPEND_BUFFER_SAFE(str, end, ' '); 114 115 return str; 116 } 117 118 int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args) 119 { 120 char *str, *end; 121 const char *s; 122 uint64_t num; 123 int i, base; 124 int len; 125 126 int flags; /* flags to number() */ 127 128 int field_width; /* width of output field */ 129 int precision; /* 130 * min. # of digits for integers; max 131 * number of chars for from string 132 */ 133 int qualifier; /* 'h', 'l', or 'L' for integer fields */ 134 135 end = buf + n; 136 GUEST_ASSERT(buf < end); 137 GUEST_ASSERT(n > 0); 138 139 for (str = buf; *fmt; ++fmt) { 140 if (*fmt != '%') { 141 APPEND_BUFFER_SAFE(str, end, *fmt); 142 continue; 143 } 144 145 /* process flags */ 146 flags = 0; 147 repeat: 148 ++fmt; /* this also skips first '%' */ 149 switch (*fmt) { 150 case '-': 151 flags |= LEFT; 152 goto repeat; 153 case '+': 154 flags |= PLUS; 155 goto repeat; 156 case ' ': 157 flags |= SPACE; 158 goto repeat; 159 case '#': 160 flags |= SPECIAL; 161 goto repeat; 162 case '0': 163 flags |= ZEROPAD; 164 goto repeat; 165 } 166 167 /* get field width */ 168 field_width = -1; 169 if (isdigit(*fmt)) 170 field_width = skip_atoi(&fmt); 171 else if (*fmt == '*') { 172 ++fmt; 173 /* it's the next argument */ 174 field_width = va_arg(args, int); 175 if (field_width < 0) { 176 field_width = -field_width; 177 flags |= LEFT; 178 } 179 } 180 181 /* get the precision */ 182 precision = -1; 183 if (*fmt == '.') { 184 ++fmt; 185 if (isdigit(*fmt)) 186 precision = skip_atoi(&fmt); 187 else if (*fmt == '*') { 188 ++fmt; 189 /* it's the next argument */ 190 precision = va_arg(args, int); 191 } 192 if (precision < 0) 193 precision = 0; 194 } 195 196 /* get the conversion qualifier */ 197 qualifier = -1; 198 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { 199 qualifier = *fmt; 200 ++fmt; 201 } 202 203 /* default base */ 204 base = 10; 205 206 switch (*fmt) { 207 case 'c': 208 if (!(flags & LEFT)) 209 while (--field_width > 0) 210 APPEND_BUFFER_SAFE(str, end, ' '); 211 APPEND_BUFFER_SAFE(str, end, 212 (uint8_t)va_arg(args, int)); 213 while (--field_width > 0) 214 APPEND_BUFFER_SAFE(str, end, ' '); 215 continue; 216 217 case 's': 218 s = va_arg(args, char *); 219 len = strnlen(s, precision); 220 221 if (!(flags & LEFT)) 222 while (len < field_width--) 223 APPEND_BUFFER_SAFE(str, end, ' '); 224 for (i = 0; i < len; ++i) 225 APPEND_BUFFER_SAFE(str, end, *s++); 226 while (len < field_width--) 227 APPEND_BUFFER_SAFE(str, end, ' '); 228 continue; 229 230 case 'p': 231 if (field_width == -1) { 232 field_width = 2 * sizeof(void *); 233 flags |= SPECIAL | SMALL | ZEROPAD; 234 } 235 str = number(str, end, 236 (uint64_t)va_arg(args, void *), 16, 237 field_width, precision, flags); 238 continue; 239 240 case 'n': 241 if (qualifier == 'l') { 242 long *ip = va_arg(args, long *); 243 *ip = (str - buf); 244 } else { 245 int *ip = va_arg(args, int *); 246 *ip = (str - buf); 247 } 248 continue; 249 250 case '%': 251 APPEND_BUFFER_SAFE(str, end, '%'); 252 continue; 253 254 /* integer number formats - set up the flags and "break" */ 255 case 'o': 256 base = 8; 257 break; 258 259 case 'x': 260 flags |= SMALL; 261 case 'X': 262 base = 16; 263 break; 264 265 case 'd': 266 case 'i': 267 flags |= SIGN; 268 case 'u': 269 break; 270 271 default: 272 APPEND_BUFFER_SAFE(str, end, '%'); 273 if (*fmt) 274 APPEND_BUFFER_SAFE(str, end, *fmt); 275 else 276 --fmt; 277 continue; 278 } 279 if (qualifier == 'l') 280 num = va_arg(args, uint64_t); 281 else if (qualifier == 'h') { 282 num = (uint16_t)va_arg(args, int); 283 if (flags & SIGN) 284 num = (int16_t)num; 285 } else if (flags & SIGN) 286 num = va_arg(args, int); 287 else 288 num = va_arg(args, uint32_t); 289 str = number(str, end, num, base, field_width, precision, flags); 290 } 291 292 GUEST_ASSERT(str < end); 293 *str = '\0'; 294 return str - buf; 295 } 296 297 int guest_snprintf(char *buf, int n, const char *fmt, ...) 298 { 299 va_list va; 300 int len; 301 302 va_start(va, fmt); 303 len = guest_vsnprintf(buf, n, fmt, va); 304 va_end(va); 305 306 return len; 307 } 308