diff --git a/bake.cmd b/bake.cmd index a243b17..505c460 100644 --- a/bake.cmd +++ b/bake.cmd @@ -51,5 +51,5 @@ del build\*.obj :skip_crt_compilation echo Compiling test.. -clang -fno-builtin test\test_math.c ciabatta.lib -std=c11 -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS% +clang -fno-builtin test\test_printf.c ciabatta.lib -std=c11 -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS% ::cl test\test_math.c /Iinc -D_CRT_SECURE_NO_WARNINGS /Z7 /link ciabatta.lib kernel32.lib user32.lib shell32.lib -nostdlib -nodefaultlibs diff --git a/code/math/exp/exp.c b/code/math/exp/exp.c new file mode 100644 index 0000000..e0a9a37 --- /dev/null +++ b/code/math/exp/exp.c @@ -0,0 +1,8 @@ + +#include +#include +#include +#include + +#define rln2 1.4426950408889634073599246810018921374266459541529859341354494069 + diff --git a/code/os/win/win_io.c b/code/os/win/win_io.c index 22551de..fed519a 100644 --- a/code/os/win/win_io.c +++ b/code/os/win/win_io.c @@ -86,6 +86,11 @@ void _os_file_write(void* ctx, size_t n, const char str[]) { WriteFile((HANDLE) ctx, str, n, &written, NULL); } +void _os_file_write_char(void* ctx, char ch) { + DWORD written = 0; + WriteFile((HANDLE) ctx, &ch, 1, &written, NULL); +} + int system(const char* string) { int wchars_required = MultiByteToWideChar(65001 /* UTF8 */, 0, string, -1, NULL, 0); wchar_t* cmd_line = malloc(sizeof(L"cmd.exe ") + (wchars_required * sizeof(wchar_t))); diff --git a/code/printf.h b/code/printf.h deleted file mode 100644 index acaff47..0000000 --- a/code/printf.h +++ /dev/null @@ -1,220 +0,0 @@ - -#include - -//TODO: verify printf("%d", 0). From the code it looked like it would print -// an empty string. - -// NOTE: this file doesn't exist in a vacuum, it's a template for generating -// the formatted print, you should define FMT_CHAR_TYPE before including it -inline static int FMT_FUNC_NAME (void *ctx, OutputFunc out, const FMT_CHAR_TYPE *fmt, va_list args) { - size_t full_length = 0; - - while (*fmt) { - // skip any normal non percent text - const FMT_CHAR_TYPE *start = fmt; - while (*fmt && *fmt != '%') fmt++; - - // print all those non percent text - out(ctx, fmt - start, start); - full_length += (fmt - start); - - // null terminated - if (*fmt == '\0') break; - fmt += 1; - - if (*fmt == '%') { - out(ctx, 1, "%"); - fmt++; - continue; - } - - unsigned int precision = 0; - if (*fmt == '.') { - // custom precision - fmt++; - - if (isdigit(*fmt)) { - // just a small atoi - // TODO: handle overflow, just in case(?) - while (isdigit(*fmt)) { - precision *= 10u; - precision += (*fmt - '0'); - - fmt++; - } - } else if (*fmt == '*') { - int p = va_arg(args, int); - - precision = p >= 0 ? ((unsigned int)p) : 0; - fmt++; - } - } - - // integer length specifiers - enum { - CHAR, - SHORT, - INT, - LONG, - LLONG - } int_length = INT; - - if (*fmt == 'l') { - fmt++; - - if (*fmt == 'l') { - int_length = LLONG; - fmt++; - } else { - int_length = LONG; - } - } else if (*fmt == 'h') { - fmt++; - - if (*fmt == 'h') { - int_length = CHAR; - fmt++; - } else { - int_length = SHORT; - } - } else if (*fmt == 'z') { - fmt++; - - int_length = _Generic((size_t)0, - unsigned char: CHAR, - unsigned short: SHORT, - unsigned int: INT, - unsigned long: LONG, - unsigned long long: LLONG); - } - - FMT_CHAR_TYPE ch = *fmt++; - const char* characters = "0123456789abcdef"; - if (ch == 'X') characters = "0123456789ABCDEF"; - - switch (ch) { - case 'c': { - const char chr = va_arg(args, int); - out(ctx, 1, &chr); - full_length ++; - break; - } - case 'f': - case 'L': { - double d = va_arg(args, double); - - if(signbit(d)) { // TODO: negative zero - out(ctx, 1, "-"); - d = -d; - } - - if(isinf(d)) { - out(ctx, sizeof"inf"-1, "inf"); - break; - } - else if(isnan(d)) { - out(ctx, sizeof"nan"-1, "nan"); - break; - } - - uint64_t w = (uint64_t)d; - d -= w; - FMT_CHAR_TYPE buffer[20]; - size_t len = sizeof(buffer); - do { - buffer[--len] = characters[w % 10]; - w /= 10; - } while(w != 0); - out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len); - - char dot = '.'; - out(ctx, 1, &dot); - - for(int i = 0; i != 6; ++i) { - d *= 10; - int dv = (int)d; - d -= dv; - char digit = characters[dv]; - out(ctx, 1, &digit); - } - } break; - case 's': { - const FMT_CHAR_TYPE *str = va_arg(args, FMT_CHAR_TYPE*); - size_t len = FMT_STRLEN_S(str, precision ? precision : SIZE_MAX); - - out(ctx, len, str); - full_length += len; - break; - } - - case 'b': - case 'o': - case 'i': - case 'u': - case 'd': - case 'x': - case 'X': { - int base = 10; - switch (ch) { - case 'X': case 'x': base = 16; break; - case 'o': base = 8; break; - case 'b': base = 2; break; - default: base = 10; break; - } - - uintmax_t i; - if (ch == 'd' || ch == 'i') { - intmax_t num = 0; - switch (int_length) { - case CHAR: num = (char) va_arg(args, int); break; - case SHORT: num = (short) va_arg(args, int); break; - case INT: num = va_arg(args, int); break; - case LONG: num = va_arg(args, long); break; - case LLONG: num = va_arg(args, long long); break; - default: break; - } - - if (num < 0) { - out(ctx, 1, "-"); - i = -num; - full_length += 1; - } else { - i = num; - } - } else { - switch (int_length) { - case CHAR: i = (char) va_arg(args, int); break; - case SHORT: i = (short) va_arg(args, int); break; - case INT: i = va_arg(args, int); break; - case LONG: i = va_arg(args, long); break; - case LLONG: i = va_arg(args, long long); break; - default: i = 0; break; - } - } - - if (i == 0) { - out(ctx, 1, "0"); - full_length += 1; - } else { - FMT_CHAR_TYPE buffer[20]; - size_t len = sizeof(buffer); - - // we build the number in reverse - while (i != 0) { - buffer[--len] = characters[i % base]; - i /= base; - } - - out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len); - full_length += (sizeof(buffer) - len); - } - break; - } - - default: break; - } - } - - return full_length; -} - diff --git a/code/stdio.c b/code/stdio.c deleted file mode 100644 index b233a25..0000000 --- a/code/stdio.c +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include -#include - -#include <_os.h> - -#define __STDC_WANT_LIB_EXT1__ 1 -#include - -int remove(const char *filename) -{ - return _os_del_file(filename); -} - -int rename(const char *old, const char *new) -{ - return _os_mov_file(old, new); -} - -char *tmpnam(char *s) -{ - static char static_buffer[L_tmpnam]; - char *buffer = s; - if(s == NULL) buffer = static_buffer; - return _os_tmpname(buffer); -} - -FILE *fopen(const char *restrict filename, const char *restrict mode) -{ - // Basically defined UB here by introducing missing modes - // It is simpler to implement that way I think. - int base_mode = mode[0]; - int binary = 0; - int exclusive = 0; - int update = 0; - for(; *mode != 0; ++mode) { - if(*mode == 'x') exclusive = 1; - if(*mode == 'b') binary = 1; - if(*mode == '+') update = 1; - } - if(base_mode == 'r' && exclusive) return NULL; - if(base_mode == 'a' && exclusive) return NULL; - - _OS_ModeFlags mode_flags = { - .base_mode = base_mode, - .binary = binary, - .update = update, - .exclusive = exclusive, - }; - - return _os_fopen(filename, mode_flags); -} - -int fclose(FILE *stream) -{ - return _os_fclose(stream); -} - -// TODO:kekw: -FILE *freopen( - const char *restrict filename, - const char *restrict mode, - FILE *restrict stream) -{ - fclose(stream); - return NULL; -} - -typedef void(*OutputFunc)(void* ctx, size_t n, const char str[]); - -/////////////////////////////////////////////// -// Instantiate formatted print functions -/////////////////////////////////////////////// -// TODO: instantiate wide char variants of print -#define FMT_FUNC_NAME fmt_print_char -#define FMT_CHAR_TYPE char -#define FMT_STRLEN_S(s, maxsize) strnlen_s(s, maxsize) -#include "printf.h" - -typedef struct { - size_t used, capacity; - char* string; -} StrPrintCtx; - -FILE *stdout, *stderr, *stdin; - -#define CALL_PRINTF(fmt_func, ctx, out, fmt) \ - va_list args; \ - va_start(args, fmt); \ - int result = fmt_func(ctx, out, fmt, args); \ - va_end(args) - -static void string_write(void *ctx, size_t n, const char *restrict str) { - StrPrintCtx *c = ctx; - memcpy(c->string + c->used, str, n); - c->used += n; -} - -int fprintf(FILE *file, const char *restrict fmt, ...) { - CALL_PRINTF(fmt_print_char, file, _os_file_write, fmt); - return result; -} - -int printf(const char *restrict fmt, ...) { - CALL_PRINTF(fmt_print_char, stdout, _os_file_write, fmt); - return result; -} - -int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) { - StrPrintCtx ctx = { 0, n, s }; - CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt); - return result; -} - -int sprintf(char *restrict s, const char *restrict fmt, ...) { - StrPrintCtx ctx = { 0, SIZE_MAX, s }; - CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt); - return result; -} - -int vfprintf(FILE *file, const char *restrict fmt, va_list args) { - return fmt_print_char(file, _os_file_write, fmt, args); -} - -int vprintf(const char *restrict fmt, va_list args) { - return fmt_print_char(stdout, _os_file_write, fmt, args); -} - -int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list args) { - StrPrintCtx ctx = { 0, n, s }; - return fmt_print_char(&ctx, string_write, fmt, args); -} - -int vsprintf(char *restrict s, const char *restrict fmt, va_list args) { - StrPrintCtx ctx = { 0, SIZE_MAX, s }; - return fmt_print_char(&ctx, string_write, fmt, args); -} diff --git a/code/stdio/files.c b/code/stdio/files.c new file mode 100644 index 0000000..bca958b --- /dev/null +++ b/code/stdio/files.c @@ -0,0 +1,65 @@ + +#include +#include <_os.h> + +FILE *stdout; +FILE *stderr; +FILE *stdin; + +int remove(const char *filename) +{ + return _os_del_file(filename); +} + +int rename(const char *old, const char *new) +{ + return _os_mov_file(old, new); +} + +char *tmpnam(char *s) { + static char static_buffer[L_tmpnam]; + char *buffer = s; + if(s == NULL) buffer = static_buffer; + return _os_tmpname(buffer); +} + +FILE *fopen(const char *restrict filename, const char *restrict mode) +{ + // Basically defined UB here by introducing missing modes + // It is simpler to implement that way I think. + int base_mode = mode[0]; + int binary = 0; + int exclusive = 0; + int update = 0; + for(; *mode != 0; ++mode) { + if(*mode == 'x') exclusive = 1; + if(*mode == 'b') binary = 1; + if(*mode == '+') update = 1; + } + if(base_mode == 'r' && exclusive) return NULL; + if(base_mode == 'a' && exclusive) return NULL; + + _OS_ModeFlags mode_flags = { + .base_mode = base_mode, + .binary = binary, + .update = update, + .exclusive = exclusive, + }; + + return _os_fopen(filename, mode_flags); +} + +int fclose(FILE *stream) +{ + return _os_fclose(stream); +} + +// TODO:kekw: +FILE *freopen( + const char *restrict filename, + const char *restrict mode, + FILE *restrict stream) +{ + fclose(stream); + return NULL; +} diff --git a/code/stdio/fmt_print.h b/code/stdio/fmt_print.h new file mode 100644 index 0000000..9e846d0 --- /dev/null +++ b/code/stdio/fmt_print.h @@ -0,0 +1,718 @@ + +#include +#include +#include +#include +#include + +// This stuff is kinda related to what's going on in this file, so I left it +// in a rather ugly manner here. +#if !defined(once) + #define once + + #define countof(arr) (sizeof(arr)/sizeof(arr[0])) + + #define CALL_PRINTF(fmt_func, ctx, out, fmt) \ + va_list args; \ + va_start(args, fmt); \ + int result = fmt_func(ctx, out, fmt, args); \ + va_end(args) + + enum ArgLength { + LEN_HH, + LEN_H, + LEN_I, + LEN_L, + LEN_LL, + LEN_J, + LEN_Z, + LEN_T, + } typedef ArgLength; + + enum ArgConv { + CONV_INT, + CONV_FLT, + CONV_EXP, + CONV_SCI, + CONV_CHAR, + CONV_STR, + CONV_PTR, + CONV_N, + CONV_PERCENT, + } typedef ArgConv; + + struct Format typedef Format; + struct Format { + bool flag_left; + bool flag_sign; + bool flag_space; + bool flag_form; + bool flag_zero; + bool flag_upper; + bool flag_unsigned; + int base; + int width; + int prec; + ArgLength arg_len; + ArgConv arg_conv; + }; + + inline static uint64_t get_int_arg(Format *fmt, va_list args, bool *signp) { + bool sign = 0; + uint64_t num = 0; + + // Read the integer from the args and split it into tuple of (sign, digits) + // If we've got HH or H we need to parse as int, and do the necessary conv + // to unsigned if needed. + if(fmt->arg_len < LEN_I) { + int ch = va_arg(args, int); + if(ch < 0) { + if(fmt->flag_unsigned) { + num = ch + (fmt->arg_len == LEN_HH? UCHAR_MAX : USHRT_MAX); + } + else { + num = -ch; + sign = 1; + } + } + } + // Otherwise we see whether the number is signed or unsigned. For unsigned + // numbers we just parse according to the length, for signed we do that + // plus handle the sign + else if(fmt->flag_unsigned) { + switch(fmt->arg_len) { + case LEN_I: num = va_arg(args, unsigned int); break; + case LEN_L: num = va_arg(args, unsigned long); break; + case LEN_LL: num = va_arg(args, unsigned long long); break; + case LEN_J: num = va_arg(args, uintmax_t); break; + case LEN_Z: num = va_arg(args, size_t); break; + case LEN_T: num = va_arg(args, size_t); break; + default:; + } + } + else { + // TODO: signed size_t + int64_t i = 0; + switch(fmt->arg_len) { + case LEN_I: i = va_arg(args, int); break; + case LEN_L: i = va_arg(args, long); break; + case LEN_LL: i = va_arg(args, long long); break; + case LEN_J: i = va_arg(args, uintmax_t); break; + case LEN_Z: i = va_arg(args, int64_t); break; + case LEN_T: i = va_arg(args, ptrdiff_t); break; + default:; + } + if(i < 0) { + num = -i; + sign = 1; + } + else { + num = i; + } + } + + *signp = sign; + return num; + } +#endif + +// Generic printf function that is used to print to stdout, strings and files. +// The basic idea is to make generic printf that uses output function as +// parameter and that function is distinct for printing to files, strings etc. +// Furthermore, this generic printf is abbstracted over character type, and +// depending on the macro fchar it could print in char's or wchar_t's. + +typedef void (suffix(out_func_t))(void* ctx, fchar ch); + +inline static int suffix(fmt_atoi)(fchar const *str, int *value) { + int i = 0; + int val = 0; + while('0' <= str[i] && str[i] <= '9') { + int digit = str[i] - '0'; + if(val > (INT_MAX - digit) / 10) { + return -1; + } + val = 10*val + digit; + ++i; + } + *value = val; + return i; +} + +inline static int suffix(fmt_parse)( + Format *format, + fchar const *str, + va_list args +) { + *format = (Format){0}; + // Parse flags + int i = 0; + for(;;) { + if (str[i] == '-') format->flag_left = true; + else if(str[i] == '+') format->flag_sign = true; + else if(str[i] == ' ') format->flag_space = true; + else if(str[i] == '#') format->flag_form = true; + else if(str[i] == '0') format->flag_zero = true; + else break; + ++i; + } + // Optional width specifier + int width = 0; + if(str[i] == '*') { + ++i; + width = va_arg(args, int); + if(width < 0) { + width = -width; + format->flag_zero = true; + } + } + else { + int width_len = suffix(fmt_atoi)(str+i, &width); + if(width_len < 0) return -1; + i += width_len; + } + // Optional precision specifier + int prec = 0; + if(str[i] == '.') { + ++i; + if(str[i] == '*') { + ++i; + prec = va_arg(args, int); + if(prec < 0) { + prec = 0; + } + } + else { + int prec_len = suffix(fmt_atoi)(str+i, &prec); + if(prec_len < 0) return -1; + i += prec_len; + } + } + format->width = width; + format->prec = prec; + // Optional length modifier + format->arg_len = LEN_I; + if(str[i] == 'h') { + ++i; + format->arg_len = LEN_H; + if(str[i] == 'h') { + ++i; + format->arg_len = LEN_HH; + } + } + else if(str[i] == 'l') { + ++i; + format->arg_len = LEN_L; + if(str[i] == 'l') { + ++i; + format->arg_len = LEN_LL; + } + } + else if(str[i] == 'j') { + ++i; + format->arg_len = LEN_J; + } + else if(str[i] == 'z') { + ++i; + format->arg_len = LEN_Z; + } + else if(str[i] == 't') { + ++i; + format->arg_len = LEN_T; + } + else if(str[i] == 'L') { + ++i; + format->arg_len = LEN_L; + } + // Conversion specifier + switch(str[i]) { + case 'd': + case 'i': + format->arg_conv = CONV_INT; + format->base = 10; + break; + case 'u': + format->arg_conv = CONV_INT; + format->flag_unsigned = true; + format->base = 10; + break; + case 'o': + format->arg_conv = CONV_INT; + format->flag_unsigned = true; + format->base = 8; + break; + case 'x': + format->arg_conv = CONV_INT; + format->flag_unsigned = true; + format->base = 16; + break; + case 'X': + format->arg_conv = CONV_INT; + format->flag_unsigned = true; + format->flag_upper = true; + format->base = 16; + break; + case 'f': + format->arg_conv = CONV_FLT; + format->base = 10; + break; + case 'F': + format->arg_conv = CONV_FLT; + format->flag_upper = true; + format->base = 10; + break; + case 'e': + format->arg_conv = CONV_EXP; + format->base = 10; + break; + case 'E': + format->arg_conv = CONV_EXP; + format->flag_upper = true; + format->base = 10; + break; + case 'g': + format->arg_conv = CONV_SCI; + format->base = 10; + break; + case 'G': + format->arg_conv = CONV_SCI; + format->flag_upper = true; + format->base = 10; + break; + case 'a': + format->arg_conv = CONV_FLT; + format->base = 16; + break; + case 'A': + format->arg_conv = CONV_FLT; + format->flag_upper = true; + format->base = 16; + break; + case 'c': + format->arg_conv = CONV_CHAR; + break; + case 's': + format->arg_conv = CONV_STR; + break; + case 'p': + format->arg_conv = CONV_PTR; + break; + case 'n': + format->arg_conv = CONV_N; + break; + case '%': + format->arg_conv = CONV_PERCENT; + break; + default: return -1; + } + ++i; + // Ignoring flags etc + if(format->flag_sign && format->flag_zero) { + format->flag_zero = 0; + } + if(format->flag_sign && format->flag_space) { + format->flag_space = 0; + } + + if(format->prec != 0) { + format->flag_zero = 0; + } + else if(format->flag_zero) { + // HACK: NOTE: I hate printf formats + format->prec = format->width; + format->width = 0; + } + if(format->arg_conv == CONV_FLT && format->prec == 0) { + format->prec = 6; + } + return i; +} + +inline static int suffix(fmt_fprint_int)( + void *ctx, + suffix(out_func_t) *out, + Format *fmt, + va_list args +) { + int w = 0; + + bool sign; + uint64_t num = get_int_arg(fmt, args, &sign); + + // Find the actual length the number takes up + int prefix_len = 0; + int digits_num = 0; + if(fmt->flag_space || fmt->flag_sign || sign) { + prefix_len = 1; + } + if(num != 0 && fmt->flag_form) { + if(fmt->base == 16) prefix_len += 2; // 0x + if(fmt->base == 8) digits_num += 1; // 0 + } + + { + uint64_t digits = num; + do { + digits_num += 1; + digits /= fmt->base; + } while(digits != 0); + } + int s_len = prefix_len + digits_num; // length of significant chars + int d_len = s_len; // length of all displayed chars + int zpad = 0; // pre-pad of zeroes from precision + if(digits_num < fmt->prec) { + zpad = fmt->prec - digits_num; + d_len = s_len + zpad; + } + + // See if padding is necessary and extract info about padding + bool left_pad = false; + bool right_pad = true; + char pad_ch = ' '; + int pad_len = 0; + if(!sign && fmt->flag_space) { // make space part of pad + d_len += 1; + pad_len = 1; + } + if(d_len < fmt->width) { + pad_len = fmt->width - d_len; + } + if(fmt->flag_left) { + left_pad = true; + right_pad = false; + } + if(fmt->flag_zero) { + pad_ch = '0'; + } + + // Fill up an array of significant digits + char digits_arr[33] = {0}; + char *digits = digits_arr + countof(digits_arr); + { + uint64_t temp = num; + do { + int digit = temp % fmt->base; + char ch = 0; + if(digit < 10) ch = digit + '0'; + else if(fmt->flag_upper) ch = digit - 10 + 'A'; + else ch = digit - 10 + 'a'; + *--digits = ch; + temp /= fmt->base; + } while(temp != 0); + } + + // Start displaying number + // Left-pad if necessary + if(left_pad) while(pad_len--) { + out(ctx, pad_ch); + ++w; + } + + // Print sign + if(sign) { out(ctx, '-'); ++w; } + else if(fmt->flag_sign) { out(ctx, '+'); ++w; } + else if(fmt->flag_space) { out(ctx, ' '); ++w; } + + // Print 0x, 0X + if(num != 0 && fmt->base == 16 && fmt->flag_form) { + out(ctx, '0'); + if(fmt->flag_upper) out(ctx, 'X'); + else out(ctx, 'x'); + w += 2; + } + + // Print zpad + while(zpad--) { + out(ctx, '0'); + ++ w; + } + + // Print digit string + while(*digits != 0) { + out(ctx, *digits); + ++digits; + ++w; + } + + // Right-pad if necessary + if(right_pad) while(pad_len--) { + out(ctx, pad_ch); + ++w; + } + + return w; +} + +inline static int suffix(fmt_fprint_ptr)( + void *ctx, + suffix(out_func_t) *out, + Format *fmt, + va_list args +) { + int w = 0; + uintptr_t num = va_arg(args, uintptr_t); + + int s_len = 2 + 16; // length of significant chars + int d_len = s_len; // length of all displayed chars + int zpad = 0; // pre-pad of zeroes from precision + if(16 < fmt->prec) { + zpad = fmt->prec - 16; + d_len = s_len + zpad; + } + + // See if padding is necessary and extract info about padding + bool left_pad = true; + bool right_pad = false; + char pad_ch = ' '; + int pad_len = 0; + if(d_len < fmt->width) { + pad_len = fmt->width - d_len; + } + if(fmt->flag_left) { + left_pad = false; + right_pad = true; + } + if(fmt->flag_zero) { + pad_ch = '0'; + } + + // Fill up an array of significant digits + char digits_arr[33] = {0}; + char *digits = digits_arr + countof(digits_arr); + { + uint64_t temp = num; + do { + int digit = temp % 16; + char ch = 0; + if(digit < 10) ch = digit + '0'; + else if(fmt->flag_upper) ch = digit - 10 + 'A'; + else ch = digit - 10 + 'a'; + *--digits = ch; + temp /= 16; + } while(s_len-- != 0); + } + + // Start displaying number + // Left-pad if necessary + if(left_pad) while(pad_len--) { + out(ctx, pad_ch); + ++w; + } + + out(ctx, '0'); + out(ctx, 'x'); + w += 2; + + // Print zpad + while(zpad--) { + out(ctx, '0'); + ++ w; + } + + // Print digit string + while(*digits != 0) { + out(ctx, *digits); + ++digits; + ++w; + } + + // Right-pad if necessary + if(right_pad) while(pad_len--) { + out(ctx, pad_ch); + ++w; + } + + return w; +} + +inline static int suffix(fmt_fprint_char)( + void *ctx, + suffix(out_func_t) *out, + Format *fmt, + va_list args +) { + int w = 0; + bool sign; + uint64_t num = get_int_arg(fmt, args, &sign); + + // Calculate padding + bool left_pad = true; + bool right_pad = false; + uint64_t pad = 0; + if(1 < fmt->width) { + pad = fmt->width - 1; + } + if(fmt->flag_left) { + left_pad = false; + right_pad = true; + } + + // Print left pad + if(left_pad) while(pad--) { + out(ctx, ' '); + ++w; + } + + out(ctx, num); + ++w; + + // Print right pad + if(right_pad) while(pad--) { + out(ctx, ' '); + ++w; + } + + return w; +} + +inline static int suffix(fmt_just_fucking_print_float_someone_improve_this_later)( + void *ctx, + suffix(out_func_t) *out, + Format *fmt, + va_list args +) { + int w = 0; + double f = va_arg(args, double); + uint64_t i = (uint64_t)f; + + char digits_arr[33] = {0}; + char *digits = digits_arr + countof(digits_arr); + { + uint64_t t = i; + do { + char ch = (t % 10) + '0'; + *--digits = ch; + t /= 10; + } while(t-- != 0); + } + + while(*digits != 0) { + out(ctx, *digits); + ++digits; + ++w; + } + + f -= i; + uint64_t nprec = fmt->prec; + if(nprec != 0) { + out(ctx, '.'); + w++; + } + + for(int i = 0; i != fmt->prec; ++i) { + f *= 10; + uint64_t digit = (uint64_t)f; + out(ctx, digit + '0'); + ++w; + f -= digit; + } + + return w; +} + + +inline static int suffix(fmt_fprint_str)( + void *ctx, + suffix(out_func_t) *out, + Format *fmt, + va_list args +) { + int w = 0; + char *str = va_arg(args, char *); + uint64_t len = strlen(str); + + // Calculate padding + bool left_pad = true; + bool right_pad = false; + uint64_t pad = 0; + if(len < fmt->width) { + pad = fmt->width - len; + } + if(fmt->flag_left) { + left_pad = false; + right_pad = true; + } + + // Print left pad + if(left_pad) while(pad--) { + out(ctx, ' '); + ++w; + } + + while(*str) { + out(ctx, *str++); + ++w; + } + + // Print right pad + if(right_pad) while(pad--) { + out(ctx, ' '); + ++w; + } + + return w; +} + +inline static int suffix(fmt_print)( + void *ctx, + suffix(out_func_t) *out, + const fchar *fmt, + va_list args +) { + int w = 0; // N chars output + int i = 0; // N chars read from fmt + while(fmt[i] != 0) { + if(fmt[i] == '%') { + ++i; + Format format; + int fmt_len = suffix(fmt_parse)(&format, fmt+i, args); + if(fmt_len < 0) return -1; + i += fmt_len; + int written; + switch(format.arg_conv) { + case CONV_INT: { + written = suffix(fmt_fprint_int)(ctx, out, &format, args); + } break; + case CONV_FLT: { + written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args); + } break; + case CONV_EXP: { + written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args); + } break; + case CONV_SCI: { + written = suffix(fmt_just_fucking_print_float_someone_improve_this_later)(ctx, out, &format, args); + } break; + case CONV_CHAR: { + written = suffix(fmt_fprint_char)(ctx, out, &format, args); + } break; + case CONV_STR: { + written = suffix(fmt_fprint_str)(ctx, out, &format, args); + } break; + case CONV_PTR: { + written = suffix(fmt_fprint_ptr)(ctx, out, &format, args); + } break; + case CONV_N: { + int *n = va_arg(args, int *); + *n = w; + written = 0; + } break; + case CONV_PERCENT: { + out(ctx, '%'); + written = 1; + } break; + if(written < 0) { + return -1; + } + w += written; + } + } + // Print non-formatted characters as god intended + else while(fmt[i] != '%' && fmt[i] != 0) { + out(ctx, fmt[i]); + ++i; + ++w; + } + } + return i; +} + diff --git a/code/stdio/stdio.c b/code/stdio/stdio.c new file mode 100644 index 0000000..b018207 --- /dev/null +++ b/code/stdio/stdio.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +#include <_os.h> + +#define __STDC_WANT_LIB_EXT1__ 1 +#include + +// Instantiate generic printf function for byte strings +// TODO: instantiate wide char variants of print +#define suffix(name) name ## _char +#define fchar char +#define fstrlen(s, maxsize) strnlen_s(s, maxsize) +#include "fmt_print.h" + +typedef struct { + size_t used, capacity; + char* string; +} StrPrintCtx; + +static void string_write(void *ctx, char ch) { + StrPrintCtx *c = ctx; + c->string[c->used++] = ch; +} + +int fprintf(FILE *file, const char *restrict fmt, ...) { + CALL_PRINTF(fmt_print_char, file, _os_file_write_char, fmt); + return result; +} + +int printf(const char *restrict fmt, ...) { + CALL_PRINTF(fmt_print_char, stdout, _os_file_write_char, fmt); + return result; +} + +int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) { + StrPrintCtx ctx = { 0, n, s }; + CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt); + return result; +} + +int sprintf(char *restrict s, const char *restrict fmt, ...) { + StrPrintCtx ctx = { 0, SIZE_MAX, s }; + CALL_PRINTF(fmt_print_char, &ctx, string_write, fmt); + return result; +} + +int vfprintf(FILE *file, const char *restrict fmt, va_list args) { + return fmt_print_char(file, _os_file_write_char, fmt, args); +} + +int vprintf(const char *restrict fmt, va_list args) { + return fmt_print_char(stdout, _os_file_write_char, fmt, args); +} + +int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list args) { + StrPrintCtx ctx = { 0, n, s }; + return fmt_print_char(&ctx, string_write, fmt, args); +} + +int vsprintf(char *restrict s, const char *restrict fmt, va_list args) { + StrPrintCtx ctx = { 0, SIZE_MAX, s }; + return fmt_print_char(&ctx, string_write, fmt, args); +} diff --git a/inc/_os.h b/inc/_os.h index 5623840..994a282 100644 --- a/inc/_os.h +++ b/inc/_os.h @@ -36,6 +36,7 @@ char *_os_tmpname(char *buffer); FILE *_os_fopen(char const *restrict name, _OS_ModeFlags flags); int _os_fclose(FILE *file); void _os_file_write(void* ctx, size_t n, const char str[]); +void _os_file_write_char(void* ctx, char ch); void _os_init_eh(); diff --git a/test/test_printf.c b/test/test_printf.c new file mode 100644 index 0000000..3d58b8b --- /dev/null +++ b/test/test_printf.c @@ -0,0 +1,8 @@ + +#include +#include + +int main() { + printf("%f\n", 3.1415); + return 0; +}