Path: blob/master/tools/testing/selftests/kvm/lib/guest_sprintf.c
38236 views
// SPDX-License-Identifier: GPL-2.0-only1#include "test_util.h"2#include "kvm_util.h"3#include "ucall_common.h"45#define APPEND_BUFFER_SAFE(str, end, v) \6do { \7GUEST_ASSERT(str < end); \8*str++ = (v); \9} while (0)1011static int isdigit(int ch)12{13return (ch >= '0') && (ch <= '9');14}1516static int skip_atoi(const char **s)17{18int i = 0;1920while (isdigit(**s))21i = i * 10 + *((*s)++) - '0';22return i;23}2425#define ZEROPAD 1 /* pad with zero */26#define SIGN 2 /* unsigned/signed long */27#define PLUS 4 /* show plus */28#define SPACE 8 /* space if plus */29#define LEFT 16 /* left justified */30#define SMALL 32 /* Must be 32 == 0x20 */31#define SPECIAL 64 /* 0x */3233#define __do_div(n, base) \34({ \35int __res; \36\37__res = ((uint64_t) n) % (uint32_t) base; \38n = ((uint64_t) n) / (uint32_t) base; \39__res; \40})4142static char *number(char *str, const char *end, long num, int base, int size,43int precision, int type)44{45/* we are called with base 8, 10 or 16, only, thus don't need "G..." */46static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */4748char tmp[66];49char c, sign, locase;50int i;5152/*53* locase = 0 or 0x20. ORing digits or letters with 'locase'54* produces same digits or (maybe lowercased) letters55*/56locase = (type & SMALL);57if (type & LEFT)58type &= ~ZEROPAD;59if (base < 2 || base > 16)60return NULL;61c = (type & ZEROPAD) ? '0' : ' ';62sign = 0;63if (type & SIGN) {64if (num < 0) {65sign = '-';66num = -num;67size--;68} else if (type & PLUS) {69sign = '+';70size--;71} else if (type & SPACE) {72sign = ' ';73size--;74}75}76if (type & SPECIAL) {77if (base == 16)78size -= 2;79else if (base == 8)80size--;81}82i = 0;83if (num == 0)84tmp[i++] = '0';85else86while (num != 0)87tmp[i++] = (digits[__do_div(num, base)] | locase);88if (i > precision)89precision = i;90size -= precision;91if (!(type & (ZEROPAD + LEFT)))92while (size-- > 0)93APPEND_BUFFER_SAFE(str, end, ' ');94if (sign)95APPEND_BUFFER_SAFE(str, end, sign);96if (type & SPECIAL) {97if (base == 8)98APPEND_BUFFER_SAFE(str, end, '0');99else if (base == 16) {100APPEND_BUFFER_SAFE(str, end, '0');101APPEND_BUFFER_SAFE(str, end, 'x');102}103}104if (!(type & LEFT))105while (size-- > 0)106APPEND_BUFFER_SAFE(str, end, c);107while (i < precision--)108APPEND_BUFFER_SAFE(str, end, '0');109while (i-- > 0)110APPEND_BUFFER_SAFE(str, end, tmp[i]);111while (size-- > 0)112APPEND_BUFFER_SAFE(str, end, ' ');113114return str;115}116117int guest_vsnprintf(char *buf, int n, const char *fmt, va_list args)118{119char *str, *end;120const char *s;121uint64_t num;122int i, base;123int len;124125int flags; /* flags to number() */126127int field_width; /* width of output field */128int precision; /*129* min. # of digits for integers; max130* number of chars for from string131*/132int qualifier; /* 'h', 'l', or 'L' for integer fields */133134end = buf + n;135GUEST_ASSERT(buf < end);136GUEST_ASSERT(n > 0);137138for (str = buf; *fmt; ++fmt) {139if (*fmt != '%') {140APPEND_BUFFER_SAFE(str, end, *fmt);141continue;142}143144/* process flags */145flags = 0;146repeat:147++fmt; /* this also skips first '%' */148switch (*fmt) {149case '-':150flags |= LEFT;151goto repeat;152case '+':153flags |= PLUS;154goto repeat;155case ' ':156flags |= SPACE;157goto repeat;158case '#':159flags |= SPECIAL;160goto repeat;161case '0':162flags |= ZEROPAD;163goto repeat;164}165166/* get field width */167field_width = -1;168if (isdigit(*fmt))169field_width = skip_atoi(&fmt);170else if (*fmt == '*') {171++fmt;172/* it's the next argument */173field_width = va_arg(args, int);174if (field_width < 0) {175field_width = -field_width;176flags |= LEFT;177}178}179180/* get the precision */181precision = -1;182if (*fmt == '.') {183++fmt;184if (isdigit(*fmt))185precision = skip_atoi(&fmt);186else if (*fmt == '*') {187++fmt;188/* it's the next argument */189precision = va_arg(args, int);190}191if (precision < 0)192precision = 0;193}194195/* get the conversion qualifier */196qualifier = -1;197if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {198qualifier = *fmt;199++fmt;200}201202/*203* Play nice with %llu, %llx, etc. KVM selftests only support204* 64-bit builds, so just treat %ll* the same as %l*.205*/206if (qualifier == 'l' && *fmt == 'l')207++fmt;208209/* default base */210base = 10;211212switch (*fmt) {213case 'c':214if (!(flags & LEFT))215while (--field_width > 0)216APPEND_BUFFER_SAFE(str, end, ' ');217APPEND_BUFFER_SAFE(str, end,218(uint8_t)va_arg(args, int));219while (--field_width > 0)220APPEND_BUFFER_SAFE(str, end, ' ');221continue;222223case 's':224s = va_arg(args, char *);225len = strnlen(s, precision);226227if (!(flags & LEFT))228while (len < field_width--)229APPEND_BUFFER_SAFE(str, end, ' ');230for (i = 0; i < len; ++i)231APPEND_BUFFER_SAFE(str, end, *s++);232while (len < field_width--)233APPEND_BUFFER_SAFE(str, end, ' ');234continue;235236case 'p':237if (field_width == -1) {238field_width = 2 * sizeof(void *);239flags |= SPECIAL | SMALL | ZEROPAD;240}241str = number(str, end,242(uint64_t)va_arg(args, void *), 16,243field_width, precision, flags);244continue;245246case 'n':247if (qualifier == 'l') {248long *ip = va_arg(args, long *);249*ip = (str - buf);250} else {251int *ip = va_arg(args, int *);252*ip = (str - buf);253}254continue;255256case '%':257APPEND_BUFFER_SAFE(str, end, '%');258continue;259260/* integer number formats - set up the flags and "break" */261case 'o':262base = 8;263break;264265case 'x':266flags |= SMALL;267case 'X':268base = 16;269break;270271case 'd':272case 'i':273flags |= SIGN;274case 'u':275break;276277default:278APPEND_BUFFER_SAFE(str, end, '%');279if (*fmt)280APPEND_BUFFER_SAFE(str, end, *fmt);281else282--fmt;283continue;284}285if (qualifier == 'l')286num = va_arg(args, uint64_t);287else if (qualifier == 'h') {288num = (uint16_t)va_arg(args, int);289if (flags & SIGN)290num = (int16_t)num;291} else if (flags & SIGN)292num = va_arg(args, int);293else294num = va_arg(args, uint32_t);295str = number(str, end, num, base, field_width, precision, flags);296}297298GUEST_ASSERT(str < end);299*str = '\0';300return str - buf;301}302303int guest_snprintf(char *buf, int n, const char *fmt, ...)304{305va_list va;306int len;307308va_start(va, fmt);309len = guest_vsnprintf(buf, n, fmt, va);310va_end(va);311312return len;313}314315316