| /* |
| * libc printf and friends |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU Library General Public License version 2. |
| */ |
| |
| #include "libcflat.h" |
| |
| #define BUFSZ 2000 |
| |
| typedef struct pstream { |
| char *buffer; |
| int remain; |
| int added; |
| } pstream_t; |
| |
| typedef struct strprops { |
| char pad; |
| int npad; |
| bool alternate; |
| } strprops_t; |
| |
| static void addchar(pstream_t *p, char c) |
| { |
| if (p->remain) { |
| *p->buffer++ = c; |
| --p->remain; |
| } |
| ++p->added; |
| } |
| |
| static void print_str(pstream_t *p, const char *s, strprops_t props) |
| { |
| const char *s_orig = s; |
| int npad = props.npad; |
| |
| if (npad > 0) { |
| npad -= strlen(s_orig); |
| while (npad > 0) { |
| addchar(p, props.pad); |
| --npad; |
| } |
| } |
| |
| while (*s) |
| addchar(p, *s++); |
| |
| if (npad < 0) { |
| props.pad = ' '; /* ignore '0' flag with '-' flag */ |
| npad += strlen(s_orig); |
| while (npad < 0) { |
| addchar(p, props.pad); |
| ++npad; |
| } |
| } |
| } |
| |
| static char digits[16] = "0123456789abcdef"; |
| |
| static void print_int(pstream_t *ps, long long n, int base, strprops_t props) |
| { |
| char buf[sizeof(long) * 3 + 2], *p = buf; |
| int s = 0, i; |
| |
| if (n < 0) { |
| n = -n; |
| s = 1; |
| } |
| |
| while (n) { |
| *p++ = digits[n % base]; |
| n /= base; |
| } |
| |
| if (s) |
| *p++ = '-'; |
| |
| if (p == buf) |
| *p++ = '0'; |
| |
| for (i = 0; i < (p - buf) / 2; ++i) { |
| char tmp; |
| |
| tmp = buf[i]; |
| buf[i] = p[-1 - i]; |
| p[-1 - i] = tmp; |
| } |
| |
| *p = 0; |
| |
| print_str(ps, buf, props); |
| } |
| |
| static void print_unsigned(pstream_t *ps, unsigned long long n, int base, |
| strprops_t props) |
| { |
| char buf[sizeof(long) * 3 + 3], *p = buf; |
| int i; |
| |
| while (n) { |
| *p++ = digits[n % base]; |
| n /= base; |
| } |
| |
| if (p == buf) |
| *p++ = '0'; |
| else if (props.alternate && base == 16) { |
| if (props.pad == '0') { |
| addchar(ps, '0'); |
| addchar(ps, 'x'); |
| |
| if (props.npad > 0) |
| props.npad = MAX(props.npad - 2, 0); |
| } else { |
| *p++ = 'x'; |
| *p++ = '0'; |
| } |
| } |
| |
| for (i = 0; i < (p - buf) / 2; ++i) { |
| char tmp; |
| |
| tmp = buf[i]; |
| buf[i] = p[-1 - i]; |
| p[-1 - i] = tmp; |
| } |
| |
| *p = 0; |
| |
| print_str(ps, buf, props); |
| } |
| |
| static int fmtnum(const char **fmt) |
| { |
| const char *f = *fmt; |
| int len = 0, num; |
| |
| if (*f == '-') |
| ++f, ++len; |
| |
| while (*f >= '0' && *f <= '9') |
| ++f, ++len; |
| |
| num = atol(*fmt); |
| *fmt += len; |
| return num; |
| } |
| |
| int vsnprintf(char *buf, int size, const char *fmt, va_list va) |
| { |
| pstream_t s; |
| |
| s.buffer = buf; |
| s.remain = size - 1; |
| s.added = 0; |
| while (*fmt) { |
| char f = *fmt++; |
| int nlong = 0; |
| strprops_t props; |
| memset(&props, 0, sizeof(props)); |
| props.pad = ' '; |
| |
| if (f != '%') { |
| addchar(&s, f); |
| continue; |
| } |
| morefmt: |
| f = *fmt++; |
| switch (f) { |
| case '%': |
| addchar(&s, '%'); |
| break; |
| case 'c': |
| addchar(&s, va_arg(va, int)); |
| break; |
| case '\0': |
| --fmt; |
| break; |
| case '#': |
| props.alternate = true; |
| goto morefmt; |
| case '0': |
| props.pad = '0'; |
| ++fmt; |
| /* fall through */ |
| case '1' ... '9': |
| case '-': |
| --fmt; |
| props.npad = fmtnum(&fmt); |
| goto morefmt; |
| case 'l': |
| ++nlong; |
| goto morefmt; |
| case 't': |
| case 'z': |
| /* Here we only care that sizeof(size_t) == sizeof(long). |
| * On a 32-bit platform it doesn't matter that size_t is |
| * typedef'ed to int or long; va_arg will work either way. |
| * Same for ptrdiff_t (%td). |
| */ |
| nlong = 1; |
| goto morefmt; |
| case 'd': |
| switch (nlong) { |
| case 0: |
| print_int(&s, va_arg(va, int), 10, props); |
| break; |
| case 1: |
| print_int(&s, va_arg(va, long), 10, props); |
| break; |
| default: |
| print_int(&s, va_arg(va, long long), 10, props); |
| break; |
| } |
| break; |
| case 'u': |
| switch (nlong) { |
| case 0: |
| print_unsigned(&s, va_arg(va, unsigned), 10, props); |
| break; |
| case 1: |
| print_unsigned(&s, va_arg(va, unsigned long), 10, props); |
| break; |
| default: |
| print_unsigned(&s, va_arg(va, unsigned long long), 10, props); |
| break; |
| } |
| break; |
| case 'x': |
| switch (nlong) { |
| case 0: |
| print_unsigned(&s, va_arg(va, unsigned), 16, props); |
| break; |
| case 1: |
| print_unsigned(&s, va_arg(va, unsigned long), 16, props); |
| break; |
| default: |
| print_unsigned(&s, va_arg(va, unsigned long long), 16, props); |
| break; |
| } |
| break; |
| case 'p': |
| props.alternate = true; |
| print_unsigned(&s, (unsigned long)va_arg(va, void *), 16, props); |
| break; |
| case 's': |
| print_str(&s, va_arg(va, const char *), props); |
| break; |
| default: |
| addchar(&s, f); |
| break; |
| } |
| } |
| *s.buffer = 0; |
| return s.added; |
| } |
| |
| int snprintf(char *buf, int size, const char *fmt, ...) |
| { |
| va_list va; |
| int r; |
| |
| va_start(va, fmt); |
| r = vsnprintf(buf, size, fmt, va); |
| va_end(va); |
| return r; |
| } |
| |
| int vprintf(const char *fmt, va_list va) |
| { |
| char buf[BUFSZ]; |
| int r; |
| |
| r = vsnprintf(buf, sizeof(buf), fmt, va); |
| puts(buf); |
| return r; |
| } |
| |
| int printf(const char *fmt, ...) |
| { |
| va_list va; |
| char buf[BUFSZ]; |
| int r; |
| |
| va_start(va, fmt); |
| r = vsnprintf(buf, sizeof buf, fmt, va); |
| va_end(va); |
| puts(buf); |
| return r; |
| } |
| |
| void binstr(unsigned long x, char out[BINSTR_SZ]) |
| { |
| int i; |
| char *c; |
| int n; |
| |
| n = sizeof(unsigned long) * 8; |
| i = 0; |
| c = &out[0]; |
| for (;;) { |
| *c++ = (x & (1ul << (n - i - 1))) ? '1' : '0'; |
| i++; |
| |
| if (i == n) { |
| *c = '\0'; |
| break; |
| } |
| if (i % 4 == 0) |
| *c++ = '\''; |
| } |
| assert(c + 1 - &out[0] == BINSTR_SZ); |
| } |
| |
| void print_binstr(unsigned long x) |
| { |
| char out[BINSTR_SZ]; |
| binstr(x, out); |
| printf("%s", out); |
| } |