#include #include #include #include #include #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; }