// TODO: merge snprintf and debug.c #include "sys/snprintf.h" #include "sys/string.h" union printfv { int value_int; unsigned int value_uint; long value_long; uint32_t value_uint32; uint64_t value_uint64; uintptr_t value_ptr; const char *value_str; }; static const char *s_print_xs_set0 = "0123456789abcdef"; static const char *s_print_xs_set1 = "0123456789ABCDEF"; static int vsnprintf_ds(int64_t x, char *res, int s, int sz) { if (!x) { res[0] = '0'; res[1] = 0; return 1; } int c; uint64_t v; if (sz) { if (s && x < 0) { v = (uint64_t) -x; } else { s = 0; v = (uint64_t) x; } } else { if (s && ((int32_t) x) < 0) { v = (uint64_t) -((int32_t) x); } else { s = 0; v = (uint64_t) x; } } c = 0; while (v) { res[c++] = '0' + v % 10; v /= 10; } if (s) { res[c++] = '-'; } res[c] = 0; for (int i = 0, j = c - 1; i < j; ++i, --j) { res[i] ^= res[j]; res[j] ^= res[i]; res[i] ^= res[j]; } return c; } static int vsnprintf_xs(uint64_t v, char *res, const char *set) { if (!v) { res[0] = '0'; res[1] = 0; return 1; } int c = 0; while (v) { res[c++] = set[v & 0xF]; v >>= 4; } res[c] = 0; for (int i = 0, j = c - 1; i < j; ++i, --j) { res[i] ^= res[j]; res[j] ^= res[i]; res[i] ^= res[j]; } return c; } int snprintf(char *buf, size_t len, const char *fmt, ...) { va_list args; va_start(args, fmt); int res = vsnprintf(buf, len, fmt, args); va_end(args); return res; } int vsnprintf(char *buf, size_t len, const char *fmt, va_list args) { union printfv val; char cbuf[64]; size_t p = 0; // padn > 0: // --------vvvvv // padn < 0: // vvvv---------- int padn; char padc; int pads; int clen; char c; #define __putc(ch) \ if (p == len - 1) { \ goto __end; \ } else { \ buf[p++] = ch; \ } #define __puts(s, l) \ if (padn >= 0) { \ if ((int) l < padn) { \ size_t padd = padn - l; \ if (p + padd < len + 1) { \ memset(buf + p, padc, padd); \ p += padd; \ } else { \ memset(buf + p, padc, len - 1 - p); \ p = len - 1; \ goto __end; \ } \ } \ if (p + l < len + 1) { \ strncpy(buf + p, s, l); \ p += l; \ } else { \ strncpy(buf + p, s, len - 1 - p); \ p = len - 1; \ goto __end; \ } \ } else { \ padn = -padn; \ if (p + l < len + 1) { \ strncpy(buf + p, s, l); \ p += l; \ } else { \ strncpy(buf + p, s, len - 1 - p); \ p = len - 1; \ goto __end; \ } \ if ((int) l < padn) { \ size_t padd = padn - l; \ if (p + padd < len + 1) { \ memset(buf + p, padc, padd); \ p += padd; \ } else { \ memset(buf + p, padc, len - 1 - p); \ p = len - 1; \ goto __end; \ } \ } \ } while ((c = *fmt)) { switch (c) { case '%': c = *++fmt; padn = 0; padc = ' '; pads = 1; if (c == '0') { padc = c; c = *++fmt; } if (c == '-') { pads = -1; c = *++fmt; } while (c >= '0' && c <= '9') { padn *= 10; padn += c - '0'; c = *++fmt; } padn *= pads; switch (c) { case 'l': // Not supported return -1; case 's': if ((val.value_str = va_arg(args, const char *))) { __puts(val.value_str, strlen(val.value_str)); } else { __puts("(null)", 6); } break; case 'p': val.value_ptr = va_arg(args, uintptr_t); __putc('0'); __putc('x'); padn = sizeof(uintptr_t) * 2; padc = '0'; clen = vsnprintf_xs(val.value_ptr, cbuf, s_print_xs_set0); __puts(cbuf, clen); break; case 'i': case 'd': val.value_int = va_arg(args, int); clen = vsnprintf_ds(val.value_int, cbuf, 1, 1); __puts(cbuf, clen); break; case 'u': val.value_uint = va_arg(args, unsigned int); clen = vsnprintf_ds(val.value_uint, cbuf, 0, 1); __puts(cbuf, clen); break; case 'x': val.value_uint32 = va_arg(args, uint32_t); clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set0); __puts(cbuf, clen); break; case 'X': val.value_uint32 = va_arg(args, uint32_t); clen = vsnprintf_xs(val.value_uint32, cbuf, s_print_xs_set1); __puts(cbuf, clen); break; case 'c': val.value_int = va_arg(args, int); __putc(val.value_int); break; default: __putc('%'); __putc(c); break; } break; default: __putc(c); break; } ++fmt; } #undef __puts #undef __putc __end: buf[p] = 0; return p; }