diff options
Diffstat (limited to 'pk/snprintf.c')
-rw-r--r-- | pk/snprintf.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/pk/snprintf.c b/pk/snprintf.c new file mode 100644 index 0000000..1544a6c --- /dev/null +++ b/pk/snprintf.c @@ -0,0 +1,98 @@ +// See LICENSE for license details. + +#include <stdint.h> +#include <string.h> +#include <stdarg.h> +#include <stdbool.h> + +int vsnprintf(char* out, size_t n, const char* s, va_list vl) +{ + bool format = false; + bool longarg = false; + size_t pos = 0; + for( ; *s; s++) + { + if(format) + { + switch(*s) + { + case 'l': + longarg = true; + break; + case 'p': + longarg = true; + if (++pos < n) out[pos-1] = '0'; + if (++pos < n) out[pos-1] = 'x'; + case 'x': + { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + for(int i = 2*(longarg ? sizeof(long) : sizeof(int))-1; i >= 0; i--) { + int d = (num >> (4*i)) & 0xF; + if (++pos < n) out[pos-1] = (d < 10 ? '0'+d : 'a'+d-10); + } + longarg = false; + format = false; + break; + } + case 'd': + { + long num = longarg ? va_arg(vl, long) : va_arg(vl, int); + if (num < 0) { + num = -num; + if (++pos < n) out[pos-1] = '-'; + } + long digits = 1; + for (long nn = num; nn /= 10; digits++) + ; + for (int i = digits-1; i >= 0; i--) { + if (pos + i + 1 < n) out[pos + i] = '0' + (num % 10); + num /= 10; + } + pos += digits; + longarg = false; + format = false; + break; + } + case 's': + { + const char* s2 = va_arg(vl, const char*); + while (*s2) { + if (++pos < n) + out[pos-1] = *s2; + s2++; + } + longarg = false; + format = false; + break; + } + case 'c': + { + if (++pos < n) out[pos-1] = (char)va_arg(vl,int); + longarg = false; + format = false; + break; + } + default: + break; + } + } + else if(*s == '%') + format = true; + else + if (++pos < n) out[pos-1] = *s; + } + if (pos < n) + out[pos] = 0; + else if (n) + out[n-1] = 0; + return pos; +} + +int snprintf(char* out, size_t n, const char* s, ...) +{ + va_list vl; + va_start(vl, s); + int res = vsnprintf(out, n, s, vl); + va_end(vl); + return res; +} |