14b6da826SThomas Huth /* 24b6da826SThomas Huth * libc printf and friends 34b6da826SThomas Huth * 44b6da826SThomas Huth * This code is free software; you can redistribute it and/or modify it 54b6da826SThomas Huth * under the terms of the GNU Library General Public License version 2. 64b6da826SThomas Huth */ 74b6da826SThomas Huth 87d36db35SAvi Kivity #include "libcflat.h" 9*2a9c2e2fSNikos Nikoleris #include "ctype.h" 107d36db35SAvi Kivity 11cb12ecccSAndrew Jones #define BUFSZ 2000 12cb12ecccSAndrew Jones 137d36db35SAvi Kivity typedef struct pstream { 147d36db35SAvi Kivity char *buffer; 157d36db35SAvi Kivity int remain; 167d36db35SAvi Kivity int added; 177d36db35SAvi Kivity } pstream_t; 187d36db35SAvi Kivity 192c978d9bSAndrew Jones typedef struct strprops { 202c978d9bSAndrew Jones char pad; 212c978d9bSAndrew Jones int npad; 228ef44442SRadim Krčmář bool alternate; 23*2a9c2e2fSNikos Nikoleris int precision; 242c978d9bSAndrew Jones } strprops_t; 252c978d9bSAndrew Jones 267d36db35SAvi Kivity static void addchar(pstream_t *p, char c) 277d36db35SAvi Kivity { 287d36db35SAvi Kivity if (p->remain) { 297d36db35SAvi Kivity *p->buffer++ = c; 307d36db35SAvi Kivity --p->remain; 317d36db35SAvi Kivity } 327d36db35SAvi Kivity ++p->added; 337d36db35SAvi Kivity } 347d36db35SAvi Kivity 3523b8916bSThomas Huth static void print_str(pstream_t *p, const char *s, strprops_t props) 367d36db35SAvi Kivity { 372c978d9bSAndrew Jones const char *s_orig = s; 382c978d9bSAndrew Jones int npad = props.npad; 392c978d9bSAndrew Jones 402c978d9bSAndrew Jones if (npad > 0) { 412c978d9bSAndrew Jones npad -= strlen(s_orig); 422c978d9bSAndrew Jones while (npad > 0) { 432c978d9bSAndrew Jones addchar(p, props.pad); 442c978d9bSAndrew Jones --npad; 452c978d9bSAndrew Jones } 462c978d9bSAndrew Jones } 472c978d9bSAndrew Jones 48*2a9c2e2fSNikos Nikoleris while (*s && props.precision--) 497d36db35SAvi Kivity addchar(p, *s++); 502c978d9bSAndrew Jones 512c978d9bSAndrew Jones if (npad < 0) { 522c978d9bSAndrew Jones props.pad = ' '; /* ignore '0' flag with '-' flag */ 532c978d9bSAndrew Jones npad += strlen(s_orig); 542c978d9bSAndrew Jones while (npad < 0) { 552c978d9bSAndrew Jones addchar(p, props.pad); 562c978d9bSAndrew Jones ++npad; 572c978d9bSAndrew Jones } 582c978d9bSAndrew Jones } 597d36db35SAvi Kivity } 607d36db35SAvi Kivity 617d36db35SAvi Kivity static char digits[16] = "0123456789abcdef"; 627d36db35SAvi Kivity 6323b8916bSThomas Huth static void print_int(pstream_t *ps, long long n, int base, strprops_t props) 647d36db35SAvi Kivity { 657d36db35SAvi Kivity char buf[sizeof(long) * 3 + 2], *p = buf; 667d36db35SAvi Kivity int s = 0, i; 677d36db35SAvi Kivity 687d36db35SAvi Kivity if (n < 0) { 697d36db35SAvi Kivity n = -n; 707d36db35SAvi Kivity s = 1; 717d36db35SAvi Kivity } 727d36db35SAvi Kivity 737d36db35SAvi Kivity while (n) { 747d36db35SAvi Kivity *p++ = digits[n % base]; 757d36db35SAvi Kivity n /= base; 767d36db35SAvi Kivity } 777d36db35SAvi Kivity 78*2a9c2e2fSNikos Nikoleris while (p == buf || (p - buf < props.precision)) 79*2a9c2e2fSNikos Nikoleris *p++ = '0'; 80*2a9c2e2fSNikos Nikoleris props.precision = -1; 81*2a9c2e2fSNikos Nikoleris 827d36db35SAvi Kivity if (s) 837d36db35SAvi Kivity *p++ = '-'; 847d36db35SAvi Kivity 857d36db35SAvi Kivity for (i = 0; i < (p - buf) / 2; ++i) { 867d36db35SAvi Kivity char tmp; 877d36db35SAvi Kivity 887d36db35SAvi Kivity tmp = buf[i]; 897d36db35SAvi Kivity buf[i] = p[-1 - i]; 907d36db35SAvi Kivity p[-1 - i] = tmp; 917d36db35SAvi Kivity } 927d36db35SAvi Kivity 937d36db35SAvi Kivity *p = 0; 947d36db35SAvi Kivity 952c978d9bSAndrew Jones print_str(ps, buf, props); 967d36db35SAvi Kivity } 977d36db35SAvi Kivity 9823b8916bSThomas Huth static void print_unsigned(pstream_t *ps, unsigned long long n, int base, 992c978d9bSAndrew Jones strprops_t props) 1007d36db35SAvi Kivity { 1018ef44442SRadim Krčmář char buf[sizeof(long) * 3 + 3], *p = buf; 1027d36db35SAvi Kivity int i; 1037d36db35SAvi Kivity 1047d36db35SAvi Kivity while (n) { 1057d36db35SAvi Kivity *p++ = digits[n % base]; 1067d36db35SAvi Kivity n /= base; 1077d36db35SAvi Kivity } 1087d36db35SAvi Kivity 1097d36db35SAvi Kivity if (p == buf) 110*2a9c2e2fSNikos Nikoleris props.alternate = false; 111*2a9c2e2fSNikos Nikoleris 112*2a9c2e2fSNikos Nikoleris while (p == buf || (p - buf < props.precision)) 1137d36db35SAvi Kivity *p++ = '0'; 114*2a9c2e2fSNikos Nikoleris props.precision = -1; 115*2a9c2e2fSNikos Nikoleris 116*2a9c2e2fSNikos Nikoleris if (props.alternate && base == 16) { 117c9af8739SRadim Krčmář if (props.pad == '0') { 118c9af8739SRadim Krčmář addchar(ps, '0'); 119c9af8739SRadim Krčmář addchar(ps, 'x'); 120c9af8739SRadim Krčmář 121c9af8739SRadim Krčmář if (props.npad > 0) 122c9af8739SRadim Krčmář props.npad = MAX(props.npad - 2, 0); 123c9af8739SRadim Krčmář } else { 1248ef44442SRadim Krčmář *p++ = 'x'; 1258ef44442SRadim Krčmář *p++ = '0'; 1268ef44442SRadim Krčmář } 127c9af8739SRadim Krčmář } 1287d36db35SAvi Kivity 1297d36db35SAvi Kivity for (i = 0; i < (p - buf) / 2; ++i) { 1307d36db35SAvi Kivity char tmp; 1317d36db35SAvi Kivity 1327d36db35SAvi Kivity tmp = buf[i]; 1337d36db35SAvi Kivity buf[i] = p[-1 - i]; 1347d36db35SAvi Kivity p[-1 - i] = tmp; 1357d36db35SAvi Kivity } 1367d36db35SAvi Kivity 1377d36db35SAvi Kivity *p = 0; 1387d36db35SAvi Kivity 1392c978d9bSAndrew Jones print_str(ps, buf, props); 1402c978d9bSAndrew Jones } 1412c978d9bSAndrew Jones 1422c978d9bSAndrew Jones static int fmtnum(const char **fmt) 1432c978d9bSAndrew Jones { 1442c978d9bSAndrew Jones const char *f = *fmt; 1452c978d9bSAndrew Jones int len = 0, num; 1462c978d9bSAndrew Jones 1472c978d9bSAndrew Jones if (*f == '-') 1482c978d9bSAndrew Jones ++f, ++len; 1492c978d9bSAndrew Jones 1502c978d9bSAndrew Jones while (*f >= '0' && *f <= '9') 1512c978d9bSAndrew Jones ++f, ++len; 1522c978d9bSAndrew Jones 1532c978d9bSAndrew Jones num = atol(*fmt); 1542c978d9bSAndrew Jones *fmt += len; 1552c978d9bSAndrew Jones return num; 1567d36db35SAvi Kivity } 1577d36db35SAvi Kivity 158*2a9c2e2fSNikos Nikoleris /* 159*2a9c2e2fSNikos Nikoleris * Adapted from drivers/firmware/efi/libstub/vsprintf.c 160*2a9c2e2fSNikos Nikoleris */ 161*2a9c2e2fSNikos Nikoleris static int skip_atoi(const char **s) 162*2a9c2e2fSNikos Nikoleris { 163*2a9c2e2fSNikos Nikoleris int i = 0; 164*2a9c2e2fSNikos Nikoleris 165*2a9c2e2fSNikos Nikoleris do { 166*2a9c2e2fSNikos Nikoleris i = i*10 + *((*s)++) - '0'; 167*2a9c2e2fSNikos Nikoleris } while (isdigit(**s)); 168*2a9c2e2fSNikos Nikoleris 169*2a9c2e2fSNikos Nikoleris return i; 170*2a9c2e2fSNikos Nikoleris } 171*2a9c2e2fSNikos Nikoleris 172*2a9c2e2fSNikos Nikoleris /* 173*2a9c2e2fSNikos Nikoleris * Adapted from drivers/firmware/efi/libstub/vsprintf.c 174*2a9c2e2fSNikos Nikoleris */ 175*2a9c2e2fSNikos Nikoleris static int get_int(const char **fmt, va_list *ap) 176*2a9c2e2fSNikos Nikoleris { 177*2a9c2e2fSNikos Nikoleris if (isdigit(**fmt)) 178*2a9c2e2fSNikos Nikoleris return skip_atoi(fmt); 179*2a9c2e2fSNikos Nikoleris 180*2a9c2e2fSNikos Nikoleris if (**fmt == '*') { 181*2a9c2e2fSNikos Nikoleris ++(*fmt); 182*2a9c2e2fSNikos Nikoleris /* it's the next argument */ 183*2a9c2e2fSNikos Nikoleris return va_arg(*ap, int); 184*2a9c2e2fSNikos Nikoleris } 185*2a9c2e2fSNikos Nikoleris return 0; 186*2a9c2e2fSNikos Nikoleris } 187*2a9c2e2fSNikos Nikoleris 1887d36db35SAvi Kivity int vsnprintf(char *buf, int size, const char *fmt, va_list va) 1897d36db35SAvi Kivity { 1907d36db35SAvi Kivity pstream_t s; 191*2a9c2e2fSNikos Nikoleris va_list args; 192*2a9c2e2fSNikos Nikoleris 193*2a9c2e2fSNikos Nikoleris /* 194*2a9c2e2fSNikos Nikoleris * We want to pass our input va_list to helper functions by reference, 195*2a9c2e2fSNikos Nikoleris * but there's an annoying edge case. If va_list was originally passed 196*2a9c2e2fSNikos Nikoleris * to us by value, we could just pass &ap down to the helpers. This is 197*2a9c2e2fSNikos Nikoleris * the case on, for example, X86_32. 198*2a9c2e2fSNikos Nikoleris * However, on X86_64 (and possibly others), va_list is actually a 199*2a9c2e2fSNikos Nikoleris * size-1 array containing a structure. Our function parameter ap has 200*2a9c2e2fSNikos Nikoleris * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1], 201*2a9c2e2fSNikos Nikoleris * which is what will be expected by a function taking a va_list * 202*2a9c2e2fSNikos Nikoleris * parameter. 203*2a9c2e2fSNikos Nikoleris * One standard way to solve this mess is by creating a copy in a local 204*2a9c2e2fSNikos Nikoleris * variable of type va_list and then passing a pointer to that local 205*2a9c2e2fSNikos Nikoleris * copy instead, which is what we do here. 206*2a9c2e2fSNikos Nikoleris */ 207*2a9c2e2fSNikos Nikoleris va_copy(args, va); 2087d36db35SAvi Kivity 2097d36db35SAvi Kivity s.buffer = buf; 2107d36db35SAvi Kivity s.remain = size - 1; 2117d36db35SAvi Kivity s.added = 0; 2127d36db35SAvi Kivity while (*fmt) { 2137d36db35SAvi Kivity char f = *fmt++; 2147d36db35SAvi Kivity int nlong = 0; 2152c978d9bSAndrew Jones strprops_t props; 2162c978d9bSAndrew Jones memset(&props, 0, sizeof(props)); 2172c978d9bSAndrew Jones props.pad = ' '; 218*2a9c2e2fSNikos Nikoleris props.precision = -1; 2197d36db35SAvi Kivity 2207d36db35SAvi Kivity if (f != '%') { 2217d36db35SAvi Kivity addchar(&s, f); 2227d36db35SAvi Kivity continue; 2237d36db35SAvi Kivity } 2247d36db35SAvi Kivity morefmt: 2257d36db35SAvi Kivity f = *fmt++; 2267d36db35SAvi Kivity switch (f) { 2277d36db35SAvi Kivity case '%': 2287d36db35SAvi Kivity addchar(&s, '%'); 2297d36db35SAvi Kivity break; 2303d7d5195SMichael S. Tsirkin case 'c': 231*2a9c2e2fSNikos Nikoleris addchar(&s, va_arg(args, int)); 2323d7d5195SMichael S. Tsirkin break; 2337d36db35SAvi Kivity case '\0': 2347d36db35SAvi Kivity --fmt; 2357d36db35SAvi Kivity break; 236*2a9c2e2fSNikos Nikoleris case '.': 237*2a9c2e2fSNikos Nikoleris props.pad = ' '; 238*2a9c2e2fSNikos Nikoleris props.precision = get_int(&fmt, &args); 239*2a9c2e2fSNikos Nikoleris goto morefmt; 240c9af8739SRadim Krčmář case '#': 241c9af8739SRadim Krčmář props.alternate = true; 242c9af8739SRadim Krčmář goto morefmt; 2432c978d9bSAndrew Jones case '0': 2442c978d9bSAndrew Jones props.pad = '0'; 2452c978d9bSAndrew Jones ++fmt; 2462c978d9bSAndrew Jones /* fall through */ 2472c978d9bSAndrew Jones case '1' ... '9': 2482c978d9bSAndrew Jones case '-': 2492c978d9bSAndrew Jones --fmt; 2502c978d9bSAndrew Jones props.npad = fmtnum(&fmt); 2512c978d9bSAndrew Jones goto morefmt; 2527d36db35SAvi Kivity case 'l': 2537d36db35SAvi Kivity ++nlong; 2547d36db35SAvi Kivity goto morefmt; 255cda042caSPaolo Bonzini case 't': 256cda042caSPaolo Bonzini case 'z': 257cda042caSPaolo Bonzini /* Here we only care that sizeof(size_t) == sizeof(long). 258cda042caSPaolo Bonzini * On a 32-bit platform it doesn't matter that size_t is 259cda042caSPaolo Bonzini * typedef'ed to int or long; va_arg will work either way. 260cda042caSPaolo Bonzini * Same for ptrdiff_t (%td). 261cda042caSPaolo Bonzini */ 262cda042caSPaolo Bonzini nlong = 1; 263cda042caSPaolo Bonzini goto morefmt; 2647d36db35SAvi Kivity case 'd': 2657d36db35SAvi Kivity switch (nlong) { 2667d36db35SAvi Kivity case 0: 267*2a9c2e2fSNikos Nikoleris print_int(&s, va_arg(args, int), 10, props); 2687d36db35SAvi Kivity break; 2697d36db35SAvi Kivity case 1: 270*2a9c2e2fSNikos Nikoleris print_int(&s, va_arg(args, long), 10, props); 2717d36db35SAvi Kivity break; 2727d36db35SAvi Kivity default: 273*2a9c2e2fSNikos Nikoleris print_int(&s, va_arg(args, long long), 10, props); 2747d36db35SAvi Kivity break; 2757d36db35SAvi Kivity } 2767d36db35SAvi Kivity break; 2773a08f439SAlex Bennée case 'u': 2783a08f439SAlex Bennée switch (nlong) { 2793a08f439SAlex Bennée case 0: 280*2a9c2e2fSNikos Nikoleris print_unsigned(&s, va_arg(args, unsigned int), 10, props); 2813a08f439SAlex Bennée break; 2823a08f439SAlex Bennée case 1: 283*2a9c2e2fSNikos Nikoleris print_unsigned(&s, va_arg(args, unsigned long), 10, props); 2843a08f439SAlex Bennée break; 2853a08f439SAlex Bennée default: 286*2a9c2e2fSNikos Nikoleris print_unsigned(&s, va_arg(args, unsigned long long), 10, props); 2873a08f439SAlex Bennée break; 2883a08f439SAlex Bennée } 2893a08f439SAlex Bennée break; 2907d36db35SAvi Kivity case 'x': 2917d36db35SAvi Kivity switch (nlong) { 2927d36db35SAvi Kivity case 0: 293*2a9c2e2fSNikos Nikoleris print_unsigned(&s, va_arg(args, unsigned int), 16, props); 2947d36db35SAvi Kivity break; 2957d36db35SAvi Kivity case 1: 296*2a9c2e2fSNikos Nikoleris print_unsigned(&s, va_arg(args, unsigned long), 16, props); 2977d36db35SAvi Kivity break; 2987d36db35SAvi Kivity default: 299*2a9c2e2fSNikos Nikoleris print_unsigned(&s, va_arg(args, unsigned long long), 16, props); 3007d36db35SAvi Kivity break; 3017d36db35SAvi Kivity } 3027d36db35SAvi Kivity break; 3037d36db35SAvi Kivity case 'p': 3048ef44442SRadim Krčmář props.alternate = true; 305*2a9c2e2fSNikos Nikoleris print_unsigned(&s, (unsigned long)va_arg(args, void *), 16, props); 3067d36db35SAvi Kivity break; 3077d36db35SAvi Kivity case 's': 308*2a9c2e2fSNikos Nikoleris print_str(&s, va_arg(args, const char *), props); 3097d36db35SAvi Kivity break; 3107d36db35SAvi Kivity default: 3117d36db35SAvi Kivity addchar(&s, f); 3127d36db35SAvi Kivity break; 3137d36db35SAvi Kivity } 3147d36db35SAvi Kivity } 315*2a9c2e2fSNikos Nikoleris va_end(args); 3167d36db35SAvi Kivity *s.buffer = 0; 3177d36db35SAvi Kivity return s.added; 3187d36db35SAvi Kivity } 3197d36db35SAvi Kivity 3207d36db35SAvi Kivity int snprintf(char *buf, int size, const char *fmt, ...) 3217d36db35SAvi Kivity { 3227d36db35SAvi Kivity va_list va; 3237d36db35SAvi Kivity int r; 3247d36db35SAvi Kivity 3257d36db35SAvi Kivity va_start(va, fmt); 3267d36db35SAvi Kivity r = vsnprintf(buf, size, fmt, va); 3277d36db35SAvi Kivity va_end(va); 3287d36db35SAvi Kivity return r; 3297d36db35SAvi Kivity } 3307d36db35SAvi Kivity 331cb12ecccSAndrew Jones int vprintf(const char *fmt, va_list va) 332cb12ecccSAndrew Jones { 333cb12ecccSAndrew Jones char buf[BUFSZ]; 334cb12ecccSAndrew Jones int r; 335cb12ecccSAndrew Jones 336cb12ecccSAndrew Jones r = vsnprintf(buf, sizeof(buf), fmt, va); 337cb12ecccSAndrew Jones puts(buf); 338cb12ecccSAndrew Jones return r; 339cb12ecccSAndrew Jones } 340cb12ecccSAndrew Jones 3417d36db35SAvi Kivity int printf(const char *fmt, ...) 3427d36db35SAvi Kivity { 3437d36db35SAvi Kivity va_list va; 344cb12ecccSAndrew Jones char buf[BUFSZ]; 3457d36db35SAvi Kivity int r; 3467d36db35SAvi Kivity 3477d36db35SAvi Kivity va_start(va, fmt); 3487d36db35SAvi Kivity r = vsnprintf(buf, sizeof buf, fmt, va); 3497d36db35SAvi Kivity va_end(va); 3507d36db35SAvi Kivity puts(buf); 3517d36db35SAvi Kivity return r; 3527d36db35SAvi Kivity } 353840375e1SPeter Feiner 354840375e1SPeter Feiner void binstr(unsigned long x, char out[BINSTR_SZ]) 355840375e1SPeter Feiner { 356840375e1SPeter Feiner int i; 357840375e1SPeter Feiner char *c; 358840375e1SPeter Feiner int n; 359840375e1SPeter Feiner 360840375e1SPeter Feiner n = sizeof(unsigned long) * 8; 361840375e1SPeter Feiner i = 0; 362840375e1SPeter Feiner c = &out[0]; 363840375e1SPeter Feiner for (;;) { 364840375e1SPeter Feiner *c++ = (x & (1ul << (n - i - 1))) ? '1' : '0'; 365840375e1SPeter Feiner i++; 366840375e1SPeter Feiner 367840375e1SPeter Feiner if (i == n) { 368840375e1SPeter Feiner *c = '\0'; 369840375e1SPeter Feiner break; 370840375e1SPeter Feiner } 371840375e1SPeter Feiner if (i % 4 == 0) 372840375e1SPeter Feiner *c++ = '\''; 373840375e1SPeter Feiner } 374840375e1SPeter Feiner assert(c + 1 - &out[0] == BINSTR_SZ); 375840375e1SPeter Feiner } 376840375e1SPeter Feiner 377840375e1SPeter Feiner void print_binstr(unsigned long x) 378840375e1SPeter Feiner { 379840375e1SPeter Feiner char out[BINSTR_SZ]; 380840375e1SPeter Feiner binstr(x, out); 381840375e1SPeter Feiner printf("%s", out); 382840375e1SPeter Feiner } 383