Created
December 31, 2020 00:24
-
-
Save qookei/aa44912ba776a3c888cdd149c80d2943 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdarg.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <ctype.h> | |
#define FMT_PUT(dst, len, c) {\ | |
if(!(len) && check_bounds) goto end;\ | |
*(dst)++ = (c);\ | |
len--;\ | |
ret++;\ | |
} | |
static const char *digits_upper = "0123456789ABCDEF"; | |
static const char *digits_lower = "0123456789abcdef"; | |
static char *num_fmt(char *buf, size_t buf_len, uint64_t i, int base, int padding, char pad_with, int handle_signed, int upper, int len, char plus_char) { | |
int neg = (int64_t)i < 0 && handle_signed; | |
if (neg) | |
i = (uint64_t)(-((int64_t)i)); | |
char *ptr = buf + buf_len - 1; | |
*ptr = '\0'; | |
const char *digits = upper ? digits_upper : digits_lower; | |
do { | |
*--ptr = digits[i % base]; | |
if (padding) | |
padding--; | |
if (len > 0) | |
len--; | |
buf_len--; | |
} while ((i /= base) != 0 && (len == -1 || len) && buf_len); | |
while (padding && buf_len) { | |
*--ptr = pad_with; | |
padding--; | |
buf_len--; | |
} | |
if (!buf_len) | |
return ptr; | |
if (neg) | |
*--ptr = '-'; | |
if (!neg && plus_char) | |
*--ptr = plus_char; | |
return ptr; | |
} | |
#define NUM_BUF_LEN 48 | |
static int internal_format_string(int check_bounds, char *buf, size_t len, const char *fmt, va_list arg) { | |
int ret = 0; | |
uint64_t i; | |
char *s; | |
char num_buf[NUM_BUF_LEN]; | |
while(*fmt && (len || !check_bounds)) { | |
if (*fmt != '%') { | |
*buf++ = *fmt; | |
fmt++; | |
continue; | |
} | |
fmt++; | |
int padding = 0; | |
char pad_with = ' '; | |
int wide = 0, upper = 0; | |
char plus_char = 0; | |
int alternative = 0; | |
int precision = 0; | |
if (*fmt == '0') { | |
pad_with = '0'; | |
fmt++; | |
} else if (*fmt == '#') { | |
alternative = 1; | |
fmt++; | |
} else if (*fmt == ' ') { | |
plus_char = ' '; | |
fmt++; | |
} else if (*fmt == '+') { | |
plus_char = '+'; | |
fmt++; | |
} else if (*fmt == '-') { | |
// TODO: left align with padding | |
fmt++; | |
} | |
if (*fmt == '*') { | |
padding = va_arg(arg, int); | |
fmt++; | |
} else while (isdigit(*fmt)) { | |
padding *= 10; | |
padding += *fmt++ - '0'; | |
} | |
// next character is the precision | |
if (*fmt == '.') { | |
fmt++; | |
if (*fmt == '*') { | |
precision = va_arg(arg, int); | |
fmt++; | |
} else while (isdigit(*fmt)) { | |
precision *= 10; | |
precision += *fmt++ - '0'; | |
} | |
} else { | |
precision = -1; | |
} | |
while (*fmt == 'l') { | |
wide = 1; | |
fmt++; | |
} | |
while (*fmt == 'h') { | |
// XXX: handle narrower types in a special way? | |
fmt++; | |
} | |
upper = *fmt == 'X' || *fmt == 'P'; | |
switch (*fmt) { | |
case 'c': { | |
i = va_arg(arg, int); | |
FMT_PUT(buf, len, i); | |
break; | |
} | |
case 'i': | |
case 'd': { | |
if (wide) | |
i = va_arg(arg, long int); | |
else | |
i = va_arg(arg, int); | |
char *c = num_fmt(num_buf, NUM_BUF_LEN, i, 10, padding, pad_with, 1, 0, -1, plus_char); | |
while (*c) { | |
FMT_PUT(buf, len, *c); | |
c++; | |
} | |
break; | |
} | |
case 'u': { | |
if (wide) | |
i = va_arg(arg, long int); | |
else | |
i = va_arg(arg, int) & 0xFFFFFFFFul; | |
char *c = num_fmt(num_buf, NUM_BUF_LEN, i, 10, padding, pad_with, 0, 0, -1, plus_char); | |
while (*c) { | |
FMT_PUT(buf, len, *c); | |
c++; | |
} | |
break; | |
} | |
case 'o': { | |
if (wide) | |
i = va_arg(arg, long int); | |
else | |
i = va_arg(arg, int); | |
char *c = num_fmt(num_buf, NUM_BUF_LEN, i, 8, padding, pad_with, 0, 0, -1, plus_char); | |
if (alternative) | |
FMT_PUT(buf, len, '0'); | |
while (*c) { | |
FMT_PUT(buf, len, *c); | |
c++; | |
} | |
break; | |
} | |
case 'X': | |
case 'x': { | |
if (wide) | |
i = va_arg(arg, long int); | |
else | |
i = va_arg(arg, int); | |
char *c = num_fmt(num_buf, NUM_BUF_LEN, i, 16, padding, pad_with, 0, upper, wide ? 16 : 8, plus_char); | |
if (alternative) { | |
FMT_PUT(buf, len, '0'); | |
FMT_PUT(buf, len, upper ? 'X' : 'x'); | |
} | |
while (*c) { | |
FMT_PUT(buf, len, *c); | |
c++; | |
} | |
break; | |
} | |
case 'P': | |
case 'p': { | |
i = (uintptr_t)(va_arg(arg, void *)); | |
char *c = num_fmt(num_buf, NUM_BUF_LEN, i, 16, padding, pad_with, 0, upper, sizeof(uintptr_t) / 4, plus_char); | |
while (*c) { | |
FMT_PUT(buf, len, *c); | |
c++; | |
} | |
break; | |
} | |
case 's': { | |
s = va_arg(arg, char *); | |
while (*s && precision) { | |
FMT_PUT(buf, len, *s); | |
s++; | |
if (precision > 0) | |
precision--; | |
} | |
break; | |
} | |
case '%': { | |
FMT_PUT(buf, len, '%'); | |
break; | |
} | |
} | |
fmt++; | |
} | |
end: | |
if (len) | |
*buf++ = '\0'; | |
return ret; | |
} | |
int vsnprintf(char *buf, size_t len, const char *fmt, va_list arg) { | |
return internal_format_string(1, buf, len, fmt, arg); | |
} | |
int vsprintf(char *buf, const char *fmt, va_list arg) { | |
return internal_format_string(0, buf, 0, fmt, arg); | |
} | |
int snprintf(char *buf, size_t len, const char *fmt, ...) { | |
va_list va; | |
va_start(va, fmt); | |
int ret = vsnprintf(buf, len, fmt, va); | |
va_end(va); | |
return ret; | |
} | |
int sprintf(char *buf, const char *fmt, ...) { | |
va_list va; | |
va_start(va, fmt); | |
int ret = vsprintf(buf, fmt, va); | |
va_end(va); | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment