diff options
Diffstat (limited to 'lib/libc-headless/stdio/sprintf.c')
-rw-r--r-- | lib/libc-headless/stdio/sprintf.c | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/lib/libc-headless/stdio/sprintf.c b/lib/libc-headless/stdio/sprintf.c new file mode 100644 index 0000000..2881bd5 --- /dev/null +++ b/lib/libc-headless/stdio/sprintf.c @@ -0,0 +1,124 @@ +#include <stdio.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <ctype.h> + +#define VSNPRINTF_PUTC(c) if(size > written) str[written++] = c; else written++ + +int +vsnprintf(char *restrict str, size_t size, const char *restrict format, va_list ap) +{ + int written = 0; + for(int fmti = 0; format[fmti] != 0; fmti++) { + char fmtc = format[fmti]; + if(fmtc != '%') { + VSNPRINTF_PUTC(fmtc); + continue; + } + + /* Flags */ + bool alternate = false; + bool zeropad = false; + bool leftadj = false; + bool signspace = false; + bool showplus = false; + fmtc = format[++fmti]; + + switch(fmtc) { + case '#': alternate = true; fmti++; break; + case '0': zeropad = true; fmti++; break; + case '-': leftadj = true; fmti++; break; + case ' ': signspace = true; fmti++; break; + case '+': showplus = true; fmti++; break; + default: break; + } + + /* Field width. */ + fmtc = format[fmti]; + //TODO: Implement field width + + /* Precision. */ + fmtc = format[fmti]; + //TODO: Implement precision + + /* Length modifier */ + fmtc = format[fmti]; + //TODO: Implement length modifier. + + /* Conversion specifier. */ + bool is_signed = true; + bool is_caps = false; + int radix = 10; + fmtc = format[fmti]; + switch(fmtc) + { + case 'X': + is_caps = true; + case 'x': + radix = 16; + case 'u': + is_signed = false; + case 'd': + case 'i': + goto conversion_int; + case 'p': + alternate = true; + radix = 16; + is_signed = false; + is_caps = true; + goto conversion_int; + case '%': + VSNPRINTF_PUTC('%'); + break; + } + continue; + +conversion_int: + { + uintmax_t value = va_arg(ap, uintmax_t); + bool negative = is_signed && (intmax_t)value < 0; + if(negative) value = (uintmax_t)(-(intmax_t)value); + size_t numc = 0; + + /*Count the number of characters to put in the buffer to represent + * the value in ASCII.*/ + for(uintmax_t v = value; v != 0; v /= radix, numc++); + if(numc == 0) numc++; + + /* Reserve space for alternate repersentation.*/ + if(showplus && !negative) { VSNPRINTF_PUTC('+'); } + if(negative) { VSNPRINTF_PUTC('-'); } + if(signspace && !negative) { VSNPRINTF_PUTC(' '); } + if(alternate) { + VSNPRINTF_PUTC('0'); + if(radix == 16) { + VSNPRINTF_PUTC('x'); + } + if(alternate && radix == 2) { + VSNPRINTF_PUTC('b'); + } + } + + /* Try to put characters into the buffer*/ + if(value == 0) { + VSNPRINTF_PUTC('0'); + } + else { + for(uintmax_t v = value, i = numc - 1; v != 0; v /= radix, i--) { + if(written + i >= size) continue; + int digit = v % radix; + char c = digit > 10 ? (digit + 'a' - 10) : (digit + '0'); + if(is_caps) c = toupper(c); + str[written + i] = c; + } + written += numc; + } + continue; + } + } + VSNPRINTF_PUTC(0); + return written - 1; +} + + |