From e964f2375d067b829bbe82d2de779d3a2a8de86a Mon Sep 17 00:00:00 2001 From: NeGate Date: Sun, 5 Jun 2022 20:16:44 -0400 Subject: [PATCH] Added printf variants --- build.cmd | 3 + code/printf.h | 159 +++++++++++++++++++++++++++++++++++++++++ code/stdio.c | 99 +++++++++++++++++++++++++ code/string.c | 2 + inc/stdio.h | 19 +++-- inc/wchar.h | 60 ++++++++-------- platform/win32/entry.c | 7 ++ test/test.c | 10 ++- 8 files changed, 313 insertions(+), 46 deletions(-) create mode 100644 code/printf.h create mode 100644 code/stdio.c diff --git a/build.cmd b/build.cmd index a9ce1f0..dcf336b 100644 --- a/build.cmd +++ b/build.cmd @@ -4,6 +4,7 @@ setlocal enabledelayedexpansion set PLATFORM=win32 set CIABATTA_OPTIONS=-Iinc -g -gcodeview -nodefaultlibs -D_CRT_SECURE_NO_WARNINGS +del ciabatta.lib for /R code %%F in (*.c) do ( echo %%F clang -c -o build\%%~nF.obj %%F %CIABATTA_OPTIONS% @@ -15,3 +16,5 @@ for /R platform\%PLATFORM% %%F in (*.c) do ( llvm-ar rc ciabatta.lib build\*.obj clang test\test.c ciabatta.lib -lkernel32 -luser32 -lshell32 -nostdlib %CIABATTA_OPTIONS% +del build\*.obj + diff --git a/code/printf.h b/code/printf.h new file mode 100644 index 0000000..5c00884 --- /dev/null +++ b/code/printf.h @@ -0,0 +1,159 @@ +// 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 + 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; + } + } + + FMT_CHAR_TYPE ch = *fmt++; + switch (ch) { + 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; + } + + const char* characters = "0123456789abcdef"; + if (ch == 'X') characters = "0123456789ABCDEF"; + + 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 new file mode 100644 index 0000000..c518e3d --- /dev/null +++ b/code/stdio.c @@ -0,0 +1,99 @@ +#include +#include +#include + +// Even if the user doesn't enable LIB_EXT1 we still have it existing +size_t strnlen_s(const char *s, size_t maxsize); + +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" + +/////////////////////////////////////////////// +// Platform dependent +/////////////////////////////////////////////// +#if defined(_os_win) +#define WIN32_LEAN_AND_MEAN +#include + +// It's just mapped directly to HANDLE +struct FILE { + int unused; +}; + +static void file_write(void* ctx, size_t n, const char str[]) { + DWORD written = 0; + WriteFile((HANDLE) ctx, str, n, &written, NULL); +} +#else +#error "TODO: Implement this" +#endif + +/////////////////////////////////////////////// +// Platform indenpendent +/////////////////////////////////////////////// +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, file_write, fmt); + return result; +} + +int printf(const char *restrict fmt, ...) { + CALL_PRINTF(fmt_print_char, stdout, 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, file_write, fmt, args); +} + +int vprintf(const char *restrict fmt, va_list args) { + return fmt_print_char(stdout, 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/string.c b/code/string.c index 9b50811..b2e128b 100644 --- a/code/string.c +++ b/code/string.c @@ -49,6 +49,8 @@ size_t strlen(const char *s) { // __STDC_WANT_LIB_EXT1__ size_t strnlen_s(const char *s, size_t maxsize) { + if (s == NULL) return 0; + size_t i = 0; while (s[i] && i < maxsize) { i++; diff --git a/inc/stdio.h b/inc/stdio.h index 31cbbe0..17ca341 100644 --- a/inc/stdio.h +++ b/inc/stdio.h @@ -14,9 +14,9 @@ typedef int64_t fpos_t; #define FOPEN_MAX 20 #ifdef _os_win - #define FILENAME_MAX 260 +#define FILENAME_MAX 260 #else - #define FILENAME_MAX 4096 +#define FILENAME_MAX 4096 #endif #define L_tmpnam FILENAME_MAX @@ -27,10 +27,7 @@ typedef int64_t fpos_t; #define TMP_MAX INT_MAX -// TODO: complete these, they don't need to be macros btw, could be external globals -#define stderr ((FILE*)NULL) -#define stdin ((FILE*)NULL) -#define stdout ((FILE*)NULL) +extern FILE *stdout, *stderr, *stdin; int remove(const char *filename); int rename(const char *oldname, const char *newname); @@ -79,9 +76,9 @@ int ferror(FILE *stream); void perror(const char *s); #ifdef __STDC_WANT_LIB_EXT1__ - #define L_tmpnam_s L_tmpnam - #define TMP_MAX_S TMP_MAX - - errno_t tmpfile_s(FILE * restrict * restrict streamptr); - errno_t tmpnam_s(char *s, rsize_t maxsize); +#define L_tmpnam_s L_tmpnam +#define TMP_MAX_S TMP_MAX + +errno_t tmpfile_s(FILE * restrict * restrict streamptr); +errno_t tmpnam_s(char *s, rsize_t maxsize); #endif diff --git a/inc/wchar.h b/inc/wchar.h index 9d209f8..710bc34 100644 --- a/inc/wchar.h +++ b/inc/wchar.h @@ -8,7 +8,7 @@ typedef wchar_t wint_t; #define WCHAR_MAX 0xffff #ifndef WEOF - #define WEOF 0 +#define WEOF 0 #endif int fwprintf(FILE * restrict stream, const wchar_t * restrict format, ...); @@ -26,8 +26,7 @@ int wscanf(const wchar_t * restrict format, ...); wint_t fgetwc(FILE *stream); wchar_t *fgetws(wchar_t * restrict s, int n, FILE * restrict stream); wint_t fputwc(wchar_t c, FILE *stream); -int fputws(const wchar_t * restrict s, -FILE * restrict stream); +int fputws(const wchar_t * restrict s, FILE * restrict stream); int fwide(FILE *stream, int mode); wint_t getwc(FILE *stream); wint_t getwchar(void); @@ -73,32 +72,29 @@ size_t mbsrtowcs(wchar_t * restrict dst, const char ** restrict src, size_t len, size_t wcsrtombs(char * restrict dst, const wchar_t ** restrict src, size_t len, mbstate_t * restrict ps); #ifdef __STDC_WANT_LIB_EXT1__ - int fwprintf_s(FILE * restrict stream, const wchar_t * restrict format, ...); - int fwscanf_s(FILE * restrict stream, const wchar_t * restrict format, ...); - int snwprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, ...); - int swprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, ...); - int swscanf_s(const wchar_t * restrict s, const wchar_t * restrict format, ...); - int vfwprintf_s(FILE * restrict stream, const wchar_t * restrict format, va_list arg); - int vfwscanf_s(FILE * restrict stream, const wchar_t * restrict format, va_list arg); - int vsnwprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, va_list arg); - int vswprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, va_list arg); - int vswscanf_s(const wchar_t * restrict s, const wchar_t * restrict format, va_list arg); - int vwprintf_s(const wchar_t * restrict format, va_list arg); - int vwscanf_s(const wchar_t * restrict format, va_list arg); - int wprintf_s(const wchar_t * restrict format, ...); - int wscanf_s(const wchar_t * restrict format, ...); - errno_t wcscpy_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2); - errno_t wcsncpy_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2, rsize_t n); - errno_t wmemcpy_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2, rsize_t n); - errno_t wmemmove_s(wchar_t *s1, rsize_t s1max, const wchar_t *s2, rsize_t n); - errno_t wcscat_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2); - errno_t wcsncat_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2, rsize_t n); - wchar_t *wcstok_s(wchar_t * restrict s1, rsize_t * restrict s1max, - const wchar_t * restrict s2, wchar_t ** restrict ptr); - size_t wcsnlen_s(const wchar_t *s, size_t maxsize); - errno_t wcrtomb_s(size_t * restrict retval, - char * restrict s, rsize_t smax, - wchar_t wc, mbstate_t * restrict ps); - errno_t mbsrtowcs_s(size_t * restrict retval, wchar_t * restrict dst, rsize_t dstmax, const char ** restrict src, rsize_t len, mbstate_t * restrict ps); - errno_t wcsrtombs_s(size_t * restrict retval, char * restrict dst, rsize_t dstmax, const wchar_t ** restrict src, rsize_t len, mbstate_t * restrict ps); -#endif \ No newline at end of file +int fwprintf_s(FILE * restrict stream, const wchar_t * restrict format, ...); +int fwscanf_s(FILE * restrict stream, const wchar_t * restrict format, ...); +int snwprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, ...); +int swprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, ...); +int swscanf_s(const wchar_t * restrict s, const wchar_t * restrict format, ...); +int vfwprintf_s(FILE * restrict stream, const wchar_t * restrict format, va_list arg); +int vfwscanf_s(FILE * restrict stream, const wchar_t * restrict format, va_list arg); +int vsnwprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, va_list arg); +int vswprintf_s(wchar_t * restrict s, rsize_t n, const wchar_t * restrict format, va_list arg); +int vswscanf_s(const wchar_t * restrict s, const wchar_t * restrict format, va_list arg); +int vwprintf_s(const wchar_t * restrict format, va_list arg); +int vwscanf_s(const wchar_t * restrict format, va_list arg); +int wprintf_s(const wchar_t * restrict format, ...); +int wscanf_s(const wchar_t * restrict format, ...); +errno_t wcscpy_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2); +errno_t wcsncpy_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2, rsize_t n); +errno_t wmemcpy_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2, rsize_t n); +errno_t wmemmove_s(wchar_t *s1, rsize_t s1max, const wchar_t *s2, rsize_t n); +errno_t wcscat_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2); +errno_t wcsncat_s(wchar_t * restrict s1, rsize_t s1max, const wchar_t * restrict s2, rsize_t n); +wchar_t *wcstok_s(wchar_t * restrict s1, rsize_t * restrict s1max, const wchar_t * restrict s2, wchar_t ** restrict ptr); +size_t wcsnlen_s(const wchar_t *s, size_t maxsize); +errno_t wcrtomb_s(size_t * restrict retval, char * restrict s, rsize_t smax, wchar_t wc, mbstate_t * restrict ps); +errno_t mbsrtowcs_s(size_t * restrict retval, wchar_t * restrict dst, rsize_t dstmax, const char ** restrict src, rsize_t len, mbstate_t * restrict ps); +errno_t wcsrtombs_s(size_t * restrict retval, char * restrict dst, rsize_t dstmax, const wchar_t ** restrict src, rsize_t len, mbstate_t * restrict ps); +#endif diff --git a/platform/win32/entry.c b/platform/win32/entry.c index 447cb88..94896eb 100644 --- a/platform/win32/entry.c +++ b/platform/win32/entry.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "win32.h" @@ -58,6 +59,12 @@ void mainCRTStartup() { convert_wide_chars_to_ansi(args[i], args_wide[i], wide_len); } + // Initialize terminal + stdout = (FILE*) GetStdHandle(STD_OUTPUT_HANDLE); + stderr = (FILE*) GetStdHandle(STD_ERROR_HANDLE); + stdin = (FILE*) GetStdHandle(STD_INPUT_HANDLE); + + // Initialize heap _os_heap heap_data = { .handle = heap, }; diff --git a/test/test.c b/test/test.c index d895fce..9ca0053 100644 --- a/test/test.c +++ b/test/test.c @@ -1,10 +1,14 @@ #include #include +#include int main(int argc, char** argv) { - /*for (int i = 0; i < argv; i++) { - printf("[%d] = %s\n", argv[i]); - }*/ + for (int i = 0; i < argc; i++) { + printf("[%d] = %s\n", i, argv[i]); + } + + printf("%d\n", 583875381); + printf("%.*s", (int)3, "Hello"); for (char c = 'a'; c != 'z'; ++c) { assert(isupper(toupper(c)));