diff --git a/build.bat b/build.bat deleted file mode 100644 index ca66e4d..0000000 --- a/build.bat +++ /dev/null @@ -1 +0,0 @@ -zig cc -o test.exe main.c -lWs2_32 -lWininet -Wall -Wpedantic -Wno-newline-eof \ No newline at end of file diff --git a/build.c b/build.c index b38ec93..8d32fe9 100644 --- a/build.c +++ b/build.c @@ -1,17 +1,43 @@ -// remember to link Ws2_32 (sockets, server, http) and Wininet (https) if you want to do networking stuff! +#if COLLA_ONLYCORE + #define COLLA_NOTHREADS 1 + #define COLLA_NOSOCKETS 1 + #define COLLA_NOHTTP 1 + #define COLLA_NOSERVER 1 +#endif -#include "colla/arena.c" -#include "colla/base64.c" -#include "colla/cthreads.c" -#include "colla/file.c" -#include "colla/format.c" -#include "colla/http.c" -#include "colla/ini.c" -#include "colla/json.c" -#include "colla/server.c" -#include "colla/socket.c" -#include "colla/str.c" -#include "colla/strstream.c" -#include "colla/tracelog.c" -#include "colla/utf8.c" -#include "colla/vmem.c" +#if COLLA_NOSOCKETS + #undef COLLA_NOHTTP + #undef COLLA_NOSERVER + #define COLLA_NOHTTP 1 + #define COLLA_NOSERVER 1 +#endif + +#include "src/arena.c" +#include "src/base64.c" +#include "src/file.c" +#include "src/format.c" +#include "src/ini.c" +#include "src/json.c" +#include "src/str.c" +#include "src/strstream.c" +#include "src/tracelog.c" +#include "src/utf8.c" +#include "src/vmem.c" +#include "src/xml.c" +#include "src/hot_reload.c" + +#if !COLLA_NOTHREADS +#include "src/cthreads.c" +#endif + +#if !COLLA_NOSOCKETS +#include "src/socket.c" +#endif + +#if !COLLA_NOHTTP +#include "src/http.c" +#endif + +#if !COLLA_NOSERVER +#include "src/server.c" +#endif diff --git a/colla/arena.c b/colla/arena.c index 14a1982..fdf09da 100644 --- a/colla/arena.c +++ b/colla/arena.c @@ -44,7 +44,10 @@ void arenaCleanup(arena_t *arena) { case ARENA_STATIC: break; } - memset(arena, 0, sizeof(arena_t)); + arena->start = NULL; + arena->current = NULL; + arena->end = NULL; + arena->type = 0; } arena_t arenaScratch(arena_t *arena) { @@ -104,12 +107,6 @@ void *arenaAlloc(const arena_alloc_desc_t *desc) { byte *ptr = arena->current; arena->current += total; - if (desc->flags & ALLOC_NOZERO) return ptr; - - memset(ptr, 0, total); - - return ptr; - return desc->flags & ALLOC_NOZERO ? ptr : memset(ptr, 0, total); } diff --git a/colla/arena.h b/colla/arena.h index fdd1511..829cf6f 100644 --- a/colla/arena.h +++ b/colla/arena.h @@ -2,7 +2,11 @@ #include "collatypes.h" +#ifdef __TINYC__ +#define alignof __alignof__ +#else #define alignof _Alignof +#endif typedef enum { ARENA_VIRTUAL, diff --git a/colla/base64.h b/colla/base64.h index de4ef10..71c4b03 100644 --- a/colla/base64.h +++ b/colla/base64.h @@ -1,7 +1,6 @@ #pragma once #include "collatypes.h" -#include "str.h" typedef struct arena_t arena_t; diff --git a/colla/bits.h b/colla/bits.h new file mode 100644 index 0000000..30b44c8 --- /dev/null +++ b/colla/bits.h @@ -0,0 +1,34 @@ +#pragma once + +#include "collatypes.h" + +uint32 bitsCtz(uint32 v); + +// == INLINE IMPLEMENTATION ============================================================== + +#if COLLA_MSVC +#define BITS_WIN 1 +#define BITS_LIN 0 +#include +#elif COLLA_GCC || COLLA_CLANG || COLLA_EMC +#define BITS_WIN 0 +#define BITS_LIN 1 +#else +#error "bits header not supported on this compiler" +#endif + +#include "tracelog.h" + +inline uint32 bitsCtz(uint32 v) { +#if BITS_LIN + return v ? __builtin_ctz(v) : 0; +#elif BITS_WIN + uint32 trailing = 0; + return _BitScanForward(&trailing, v) ? trailing : 0; +#else + return 0; +#endif +} + +#undef BITS_WIN +#undef BITS_LIN diff --git a/colla/colladefines.h b/colla/colladefines.h index 7e30b7a..a9d58e6 100644 --- a/colla/colladefines.h +++ b/colla/colladefines.h @@ -1,6 +1,7 @@ #pragma once #define arrlen(a) (sizeof(a) / sizeof((a)[0])) +#define for_each(it, list) for (typeof(list) it = list; it; it = it->next) #if defined(_DEBUG) || !defined(NDEBUG) #define COLLA_DEBUG 1 @@ -15,18 +16,28 @@ #define COLLA_WIN 1 #define COLLA_OSX 0 #define COLLA_LIN 0 +#define COLLA_EMC 0 -#elif defined(__APPLE__) +#elif defined(__EMSCRIPTEN__) #define COLLA_WIN 0 -#define COLLA_OSX 1 +#define COLLA_OSX 0 #define COLLA_LIN 0 +#define COLLA_EMC 1 #elif defined(__linux__) #define COLLA_WIN 0 #define COLLA_OSX 0 #define COLLA_LIN 1 +#define COLLA_EMC 0 + +#elif defined(__APPLE__) + +#define COLLA_WIN 0 +#define COLLA_OSX 1 +#define COLLA_LIN 0 +#define COLLA_EMC 0 #endif diff --git a/colla/cthreads.c b/colla/cthreads.c index 92227f6..9208231 100644 --- a/colla/cthreads.c +++ b/colla/cthreads.c @@ -9,15 +9,7 @@ typedef struct { #if COLLA_WIN #define WIN32_LEAN_AND_MEAN -#include -#include -#include - -#undef INFINITE -#undef WAIT_FAILED -// couple of defines to avoid including windows.h -#define INFINITE 0xFFFFFFFF // Infinite timeout -#define WAIT_FAILED ((DWORD)0xFFFFFFFF) +#include // == THREAD =========================================== @@ -60,6 +52,9 @@ int thrCurrentId(void) { } int thrGetId(cthread_t ctx) { +#if COLLA_TCC + return 0; +#endif return GetThreadId((HANDLE)ctx); } @@ -108,6 +103,7 @@ bool mtxUnlock(cmutex_t ctx) { return true; } +#if !COLLA_NO_CONDITION_VAR // == CONDITION VARIABLE =============================== #include "tracelog.h" @@ -138,6 +134,8 @@ void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds) { SleepConditionVariableCS((CONDITION_VARIABLE *)cond, (CRITICAL_SECTION *)mtx, milliseconds); } +#endif + #else #include #include @@ -236,6 +234,8 @@ bool mtxUnlock(cmutex_t ctx) { return pthread_mutex_unlock((pthread_mutex_t *)ctx) == 0; } +#if !COLLA_NO_CONDITION_VAR + // == CONDITION VARIABLE =============================== condvar_t condInit(void) { @@ -277,3 +277,5 @@ void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds) { } #endif + +#endif diff --git a/colla/cthreads.h b/colla/cthreads.h index e7e6a01..9e8ecd3 100644 --- a/colla/cthreads.h +++ b/colla/cthreads.h @@ -2,6 +2,12 @@ #include "collatypes.h" +#if COLLA_TCC +#define COLLA_NO_CONDITION_VAR 1 +#endif + +typedef struct arena_t arena_t; + // == THREAD =========================================== typedef uintptr_t cthread_t; @@ -32,6 +38,7 @@ bool mtxLock(cmutex_t ctx); bool mtxTryLock(cmutex_t ctx); bool mtxUnlock(cmutex_t ctx); +#if !COLLA_NO_CONDITION_VAR // == CONDITION VARIABLE =============================== typedef uintptr_t condvar_t; @@ -46,3 +53,5 @@ void condWakeAll(condvar_t cond); void condWait(condvar_t cond, cmutex_t mtx); void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds); + +#endif \ No newline at end of file diff --git a/colla/file.c b/colla/file.c index 1c7391b..7171935 100644 --- a/colla/file.c +++ b/colla/file.c @@ -8,8 +8,7 @@ #if COLLA_WIN #define WIN32_LEAN_AND_MEAN -#include -#include +#include #undef FILE_BEGIN #undef FILE_CURRENT @@ -19,6 +18,10 @@ #define FILE_CURRENT 1 #define FILE_END 2 +#if COLLA_TCC +#include "tcc/colla_tcc.h" +#endif + static DWORD file__mode_to_access(filemode_e mode) { if (mode & FILE_APPEND) return FILE_APPEND_DATA; @@ -38,22 +41,70 @@ bool fileExists(const char *name) { return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES; } -file_t fileOpen(arena_t scratch, strview_t name, filemode_e mode) { +TCHAR *fileGetFullPath(arena_t *arena, strview_t filename) { TCHAR long_path_prefix[] = TEXT("\\\\?\\"); const usize prefix_len = arrlen(long_path_prefix) - 1; - TCHAR *rel_path = strvToTChar(&scratch, name); + uint8 tempbuf[4096]; + arena_t scratch = arenaMake(ARENA_STATIC, sizeof(tempbuf), tempbuf); + + TCHAR *rel_path = strvToTChar(&scratch, filename); DWORD pathlen = GetFullPathName(rel_path, 0, NULL, NULL); - TCHAR *full_path = alloc(&scratch, TCHAR, pathlen + prefix_len + 1); + TCHAR *full_path = alloc(arena, TCHAR, pathlen + prefix_len + 1); memcpy(full_path, long_path_prefix, prefix_len * sizeof(TCHAR)); GetFullPathName(rel_path, pathlen + 1, full_path + prefix_len, NULL); + return full_path; +} + +strview_t fileGetFilename(strview_t path) { + usize last_lin = strvRFind(path, '/', 0); + usize last_win = strvRFind(path, '\\', 0); + last_lin = last_lin != SIZE_MAX ? last_lin : 0; + last_win = last_win != SIZE_MAX ? last_win : 0; + usize last = max(last_lin, last_win); + return strvSub(path, last ? last + 1 : last, SIZE_MAX); +} + +strview_t fileGetExtension(strview_t path) { + usize ext_pos = strvRFind(path, '.', 0); + return strvSub(path, ext_pos, SIZE_MAX); +} + +void fileSplitPath(strview_t path, strview_t *dir, strview_t *name, strview_t *ext) { + usize dir_lin = strvRFind(path, '/', 0); + usize dir_win = strvRFind(path, '\\', 0); + dir_lin = dir_lin != STR_NONE ? dir_lin : 0; + dir_win = dir_win != STR_NONE ? dir_win : 0; + usize dir_pos = max(dir_lin, dir_win); + + usize ext_pos = strvRFind(path, '.', 0); + + if (dir) { + *dir = strvSub(path, 0, dir_pos); + } + if (name) { + *name = strvSub(path, dir_pos ? dir_pos + 1 : dir_pos, ext_pos); + } + if (ext) { + *ext = strvSub(path, ext_pos, SIZE_MAX); + } +} + +bool fileDelete(arena_t scratch, strview_t filename) { + wchar_t *wfname = strvToWChar(&scratch, filename, NULL); + return DeleteFileW(wfname); +} + +file_t fileOpen(arena_t scratch, strview_t name, filemode_e mode) { + TCHAR *full_path = fileGetFullPath(&scratch, name); + HANDLE handle = CreateFile( full_path, file__mode_to_access(mode), - 0, + FILE_SHARE_READ, NULL, file__mode_to_creation(mode), FILE_ATTRIBUTE_NORMAL, @@ -78,14 +129,14 @@ bool fileIsValid(file_t ctx) { usize fileRead(file_t ctx, void *buf, usize len) { if (!fileIsValid(ctx)) return 0; DWORD read = 0; - ReadFile((HANDLE)ctx.handle, buf, len, &read, NULL); + ReadFile((HANDLE)ctx.handle, buf, (DWORD)len, &read, NULL); return (usize)read; } usize fileWrite(file_t ctx, const void *buf, usize len) { if (!fileIsValid(ctx)) return 0; DWORD written = 0; - WriteFile((HANDLE)ctx.handle, buf, len, &written, NULL); + WriteFile((HANDLE)ctx.handle, buf, (DWORD)len, &written, NULL); return (usize)written; } @@ -160,7 +211,9 @@ void fileClose(file_t ctx) { } bool fileIsValid(file_t ctx) { - return (FILE *)ctx.handle != NULL; + bool is_valid = (FILE *)ctx.handle != NULL; + if (!is_valid) warn("file not valid"); + return is_valid; } usize fileRead(file_t ctx, void *buf, usize len) { @@ -180,7 +233,7 @@ bool fileSeekEnd(file_t ctx) { void fileRewind(file_t ctx) { if (!fileIsValid(ctx)) return; - return fseek((FILE *)ctx.handle, 0, SEEK_SET) == 0; + fseek((FILE *)ctx.handle, 0, SEEK_SET); } usize fileTell(file_t ctx) { @@ -199,6 +252,7 @@ usize fileSize(file_t ctx) { uint64 fileGetTimeFP(file_t ctx) { #if COLLA_LIN + return 0; #else fatal("fileGetTime not implemented yet outside of linux and windows"); return 0; @@ -228,8 +282,15 @@ bool filePrintfv(arena_t scratch, file_t ctx, const char *fmt, va_list args) { return fileWrite(ctx, string.buf, string.len) == string.len; } -buffer_t fileReadWhole(arena_t *arena, arena_t scratch, strview_t name) { - return fileReadWholeFP(arena, fileOpen(scratch, name, FILE_READ)); +buffer_t fileReadWhole(arena_t *arena, strview_t name) { + file_t fp = fileOpen(*arena, name, FILE_READ); + if (!fileIsValid(fp)) { + err("could not open file: %v", name); + return (buffer_t){0}; + } + buffer_t out = fileReadWholeFP(arena, fp); + fileClose(fp); + return out; } buffer_t fileReadWholeFP(arena_t *arena, file_t ctx) { @@ -249,8 +310,11 @@ buffer_t fileReadWholeFP(arena_t *arena, file_t ctx) { return out; } -str_t fileReadWholeStr(arena_t *arena, arena_t scratch, strview_t name) { - return fileReadWholeStrFP(arena, fileOpen(scratch, name, FILE_READ)); +str_t fileReadWholeStr(arena_t *arena, strview_t name) { + file_t fp = fileOpen(*arena, name, FILE_READ); + str_t out = fileReadWholeStrFP(arena, fp); + fileClose(fp); + return out; } str_t fileReadWholeStrFP(arena_t *arena, file_t ctx) { @@ -263,7 +327,7 @@ str_t fileReadWholeStrFP(arena_t *arena, file_t ctx) { usize read = fileRead(ctx, out.buf, out.len); if (read != out.len) { - err("fileReadWholeFP: fileRead failed, should be %zu but is %zu", out.len, read); + err("fileReadWholeStrFP: fileRead failed, should be %zu but is %zu", out.len, read); arenaPop(arena, out.len + 1); return (str_t){0}; } @@ -282,7 +346,15 @@ bool fileWriteWhole(arena_t scratch, strview_t name, const void *buf, usize len) } uint64 fileGetTime(arena_t scratch, strview_t name) { - return fileGetTimeFP(fileOpen(scratch, name, FILE_READ)); + file_t fp = fileOpen(scratch, name, FILE_READ); + uint64 result = fileGetTimeFP(fp); + fileClose(fp); + return result; +} + +bool fileHasChanged(arena_t scratch, strview_t name, uint64 last_timestamp) { + uint64 timestamp = fileGetTime(scratch, name); + return timestamp > last_timestamp; } #include "warnings/colla_warn_end.h" diff --git a/colla/file.h b/colla/file.h index 6e34167..c6ca20d 100644 --- a/colla/file.h +++ b/colla/file.h @@ -17,6 +17,11 @@ typedef struct { } file_t; bool fileExists(const char *name); +TCHAR *fileGetFullPath(arena_t *arena, strview_t filename); +strview_t fileGetFilename(strview_t path); +strview_t fileGetExtension(strview_t path); +void fileSplitPath(strview_t path, strview_t *dir, strview_t *name, strview_t *ext); +bool fileDelete(arena_t scratch, strview_t filename); file_t fileOpen(arena_t scratch, strview_t name, filemode_e mode); void fileClose(file_t ctx); @@ -37,13 +42,14 @@ void fileRewind(file_t ctx); usize fileTell(file_t ctx); usize fileSize(file_t ctx); -buffer_t fileReadWhole(arena_t *arena, arena_t scratch, strview_t name); +buffer_t fileReadWhole(arena_t *arena, strview_t name); buffer_t fileReadWholeFP(arena_t *arena, file_t ctx); -str_t fileReadWholeStr(arena_t *arena, arena_t scratch, strview_t name); +str_t fileReadWholeStr(arena_t *arena, strview_t name); str_t fileReadWholeStrFP(arena_t *arena, file_t ctx); bool fileWriteWhole(arena_t scratch, strview_t name, const void *buf, usize len); uint64 fileGetTime(arena_t scratch, strview_t name); -uint64 fileGetTimeFP(file_t ctx); \ No newline at end of file +uint64 fileGetTimeFP(file_t ctx); +bool fileHasChanged(arena_t scratch, strview_t name, uint64 last_timestamp); \ No newline at end of file diff --git a/colla/format.c b/colla/format.c index a45a8f6..9ab29d1 100644 --- a/colla/format.c +++ b/colla/format.c @@ -8,6 +8,7 @@ #include "arena.h" static char *fmt__stb_callback(const char *buf, void *ud, int len) { + (void)len; printf("%s", buf); return (char *)ud; } @@ -21,7 +22,7 @@ int fmtPrint(const char *fmt, ...) { } int fmtPrintv(const char *fmt, va_list args) { - char buffer[STB_SPRINTF_MIN]; + char buffer[STB_SPRINTF_MIN] = {0}; return stb_vsprintfcb(fmt__stb_callback, buffer, buffer, fmt, args); } diff --git a/colla/format.h b/colla/format.h index ade889a..4d566ad 100644 --- a/colla/format.h +++ b/colla/format.h @@ -2,6 +2,8 @@ #include +#include "collatypes.h" + typedef struct arena_t arena_t; int fmtPrint(const char *fmt, ...); diff --git a/colla/hot_reload.c b/colla/hot_reload.c new file mode 100644 index 0000000..9c11b30 --- /dev/null +++ b/colla/hot_reload.c @@ -0,0 +1,200 @@ +#include "hot_reload.h" + +#include "arena.h" +#include "file.h" +#include "tracelog.h" + +// todo linux support? +#if COLLA_WIN +#define WIN32_LEAN_AND_MEAN +#include +#else +// patch stuff up for cross platform for now, the actual program should not really call anything for now +#define HMODULE void* +#endif + +typedef int (*hr_f)(hr_t *ctx); + +typedef struct { + arena_t arena; + str_t path; + uint64 last_timestamp; + HMODULE handle; + hr_f hr_init; + hr_f hr_loop; + hr_f hr_close; +} hr_internal_t; + +static bool hr__file_copy(arena_t scratch, strview_t src, strview_t dst) { + buffer_t srcbuf = fileReadWhole(&scratch, src); + if (srcbuf.data == NULL || srcbuf.len == 0) { + err("fileReadWhole(%v) returned an empty buffer", src); + return false; + } + if (!fileWriteWhole(scratch, dst, srcbuf.data, srcbuf.len)) { + err("fileWriteWhole failed"); + return false; + } + return true; +} + +static bool hr__reload(hr_t *ctx) { +#ifdef HR_DISABLE + return true; +#endif + + hr_internal_t *hr = ctx->p; + arena_t scratch = hr->arena; + + if (!fileExists(hr->path.buf)) { + err("dll file %v does not exist anymore!", hr->path); + return false; + } + + uint64 now = fileGetTime(scratch, strv(hr->path)); + if (now <= hr->last_timestamp) { + return false; + } + + ctx->version = ctx->last_working_version + 1; + + // can't copy the dll directly, make a temporary one based on the version + strview_t dir, name, ext; + fileSplitPath(strv(hr->path), &dir, &name, &ext); + str_t dll = strFmt(&scratch, "%v/%v-%d%v", dir, name, ctx->version, ext); + + if (!hr__file_copy(scratch, strv(hr->path), strv(dll))) { + err("failed to copy %v to %v", hr->path, dll); + return false; + } + + info("loading library: %v", dll); + + if (hr->handle) { + FreeLibrary(hr->handle); + } + + hr->handle = LoadLibraryA(dll.buf); + if (!hr->handle) { + err("couldn't load %v: %u", dll, GetLastError()); + return true; + } + + hr->hr_init = (hr_f)GetProcAddress(hr->handle, "hr_init"); + DWORD init_err = GetLastError(); + hr->hr_loop = (hr_f)GetProcAddress(hr->handle, "hr_loop"); + DWORD loop_err = GetLastError(); + hr->hr_close = (hr_f)GetProcAddress(hr->handle, "hr_close"); + DWORD close_err = GetLastError(); + + if (!hr->hr_init) { + err("couldn't load address for hr_init: %u", init_err); + goto error; + } + + if (!hr->hr_loop) { + err("couldn't load address for hr_loop: %u", loop_err); + goto error; + } + + if (!hr->hr_close) { + err("couldn't load address for hr_close: %u", close_err); + goto error; + } + + info("Reloaded, version: %d", ctx->version); + hr->last_timestamp = now; + ctx->last_working_version = ctx->version; + + hr->hr_init(ctx); + + return true; + +error: + if (hr->handle) FreeLibrary(hr->handle); + hr->handle = NULL; + hr->hr_init = hr->hr_loop = hr->hr_close = NULL; + return false; +} + +bool hrOpen(hr_t *ctx, strview_t path) { +#ifdef HR_DISABLE + cr_init(ctx); + return true; +#endif + arena_t arena = arenaMake(ARENA_VIRTUAL, MB(1)); + + str_t path_copy = str(&arena, path); + + if (!fileExists(path_copy.buf)) { + err("dll file: %v does not exist", path); + arenaCleanup(&arena); + return false; + } + + hr_internal_t *hr = alloc(&arena, hr_internal_t); + hr->arena = arena; + hr->path = path_copy; + + ctx->p = hr; + ctx->last_working_version = 0; + + return hr__reload(ctx); +} + +void hrClose(hr_t *ctx, bool clean_temp_files) { +#ifdef HR_DISABLE + hr_close(ctx); + return; +#endif + + hr_internal_t *hr = ctx->p; + if (hr->hr_close) { + hr->hr_close(ctx); + } + + if (hr->handle) { + FreeLibrary(hr->handle); + } + + hr->handle = NULL; + hr->hr_init = hr->hr_loop = hr->hr_close = NULL; + + if (clean_temp_files) { + arena_t scratch = hr->arena; + + strview_t dir, name, ext; + fileSplitPath(strv(hr->path), &dir, &name, &ext); + + for (int i = 0; i < ctx->last_working_version; ++i) { + str_t fname = strFmt(&scratch, "%v/%v-%d%v", dir, name, i + 1, ext); + if (!fileDelete(scratch, strv(fname))) { + err("couldn't delete %v: %d", fname, GetLastError()); + } + } + } + + arena_t arena = hr->arena; + arenaCleanup(&arena); + + ctx->p = NULL; +} + +int hrStep(hr_t *ctx) { +#ifdef CR_DISABLE + hr_loop(ctx); + return 0; +#endif + hr_internal_t *hr = ctx->p; + + int result = -1; + if (hr->hr_loop) { + result = hr->hr_loop(ctx); + } + return result; +} + +bool hrReload(hr_t *ctx) { + return hr__reload(ctx); +} + diff --git a/colla/hot_reload.h b/colla/hot_reload.h new file mode 100644 index 0000000..3c03f88 --- /dev/null +++ b/colla/hot_reload.h @@ -0,0 +1,31 @@ +#pragma once + +// api functions: +// int hr_init(hr_t *ctx) +// int hr_loop(hr_t *ctx) +// int hr_close(hr_t *ctx) + +// you can turn off hot reloading and run the program +// "as normal" by defining +// HR_DISABLE + +#include "str.h" + +#if COLLA_WIN +#define HR_EXPORT __declspec(dllexport) +#else +// todo linux support? +#define HR_EXPORT +#endif + +typedef struct hr_t { + void *p; + void *userdata; + int version; + int last_working_version; +} hr_t; + +bool hrOpen(hr_t *ctx, strview_t path); +void hrClose(hr_t *ctx, bool clean_temp_files); +int hrStep(hr_t *ctx); +bool hrReload(hr_t *ctx); diff --git a/colla/http.c b/colla/http.c index 28c3a5e..2a08cd8 100644 --- a/colla/http.c +++ b/colla/http.c @@ -2,7 +2,11 @@ #include "warnings/colla_warn_beg.h" +#include +#include + #include "arena.h" +#include "str.h" #include "strstream.h" #include "format.h" #include "socket.h" @@ -14,7 +18,9 @@ #endif #include - #include + #if !COLLA_TCC + #include + #endif #endif static const TCHAR *https__get_method_str(http_method_e method); @@ -93,7 +99,7 @@ http_req_t httpParseReq(arena_t *arena, strview_t request) { req.body = strvTrim(istrGetViewLen(&in, SIZE_MAX)); - strview_t methods[] = { strv("GET"), strv("POST"), strv("HEAD"), strv("PUT"), strv("DELETE") }; + strview_t methods[5] = { strv("GET"), strv("POST"), strv("HEAD"), strv("PUT"), strv("DELETE") }; usize methods_count = arrlen(methods); for (usize i = 0; i < methods_count; ++i) { @@ -254,6 +260,49 @@ str_t httpMakeUrlSafe(arena_t *arena, strview_t string) { return out; } +str_t httpDecodeUrlSafe(arena_t *arena, strview_t string) { + usize final_len = string.len; + + for (usize i = 0; i < string.len; ++i) { + if (string.buf[i] == '%') { + final_len -= 2; + i += 2; + } + } + + assert(final_len <= string.len); + + str_t out = { + .buf = alloc(arena, char, final_len + 1), + .len = final_len + }; + + usize k = 0; + + for (usize i = 0; i < string.len; ++i) { + if (string.buf[i] == '%') { + // skip % + ++i; + + unsigned int ch = 0; + int result = sscanf(string.buf + i, "%02X", &ch); + if (result != 1 || ch > UINT8_MAX) { + err("malformed url at %zu (%s)", i, string.buf + i); + return (str_t){0}; + } + out.buf[k++] = (char)ch; + + // skip first char of hex + ++i; + } + else { + out.buf[k++] = string.buf[i]; + } + } + + return out; +} + http_url_t httpSplitUrl(strview_t url) { http_url_t out = {0}; @@ -326,7 +375,7 @@ http_res_t httpRequest(http_request_desc_t *request) { assert(url.host.len < arrlen(hostname)); memcpy(hostname, url.host.buf, url.host.len); - const int DEFAULT_HTTP_PORT = 80; + const uint16 DEFAULT_HTTP_PORT = 80; if (!skConnect(sock, hostname, DEFAULT_HTTP_PORT)) { err("Couldn't connect to host %s: %s", hostname, skGetErrorString()); goto error; @@ -338,7 +387,7 @@ http_res_t httpRequest(http_request_desc_t *request) { goto error; } - if (skSend(sock, reqstr.buf, (int)reqstr.len) == -1) { + if (skSend(sock, reqstr.buf, (int)reqstr.len) == SOCKET_ERROR) { err("couldn't send request to socket: %s", skGetErrorString()); goto error; } @@ -348,7 +397,7 @@ http_res_t httpRequest(http_request_desc_t *request) { int read = 0; do { read = skReceive(sock, buffer, arrlen(buffer)); - if (read == -1) { + if (read == SOCKET_ERROR) { err("couldn't get the data from the server: %s", skGetErrorString()); goto error; } @@ -374,7 +423,7 @@ error: #if COLLA_WIN buffer_t httpsRequest(http_request_desc_t *req) { - HINTERNET internet = InternetOpen( + HINTERNET internet = InternetOpenA( TEXT("COLLA"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, @@ -446,7 +495,7 @@ buffer_t httpsRequest(http_request_desc_t *req) { HttpAddRequestHeadersA( request, header_str.buf, - header_str.len, + (DWORD)header_str.len, 0 ); } @@ -456,7 +505,7 @@ buffer_t httpsRequest(http_request_desc_t *req) { NULL, 0, (void *)req->body.buf, - req->body.len + (DWORD)req->body.len ); if (!request_sent) { fatal("call to HttpSendRequest failed: %u", GetLastError()); diff --git a/colla/http.h b/colla/http.h index dc2fbba..4c4d210 100644 --- a/colla/http.h +++ b/colla/http.h @@ -57,6 +57,7 @@ void httpSetHeader(http_header_t *headers, strview_t key, strview_t value); strview_t httpGetHeader(http_header_t *headers, strview_t key); str_t httpMakeUrlSafe(arena_t *arena, strview_t string); +str_t httpDecodeUrlSafe(arena_t *arena, strview_t string); typedef struct { strview_t host; diff --git a/colla/ini.c b/colla/ini.c index f96e08e..e0e7ecc 100644 --- a/colla/ini.c +++ b/colla/ini.c @@ -29,6 +29,10 @@ ini_t iniParseStr(arena_t *arena, strview_t str, const iniopts_t *options) { return out; } +bool iniIsValid(ini_t *ctx) { + return ctx && !strvIsEmpty(ctx->text); +} + initable_t *iniGetTable(ini_t *ctx, strview_t name) { initable_t *t = ctx ? ctx->tables : NULL; while (t) { @@ -161,9 +165,10 @@ static void ini__add_value(arena_t *arena, initable_t *table, instream_t *in, in strview_t key = strvTrim(istrGetView(in, opts->key_value_divider)); istrSkip(in, 1); - strview_t value = strvTrim(istrGetView(in, '\n')); + strview_t value = strvTrim(istrGetViewEither(in, strv("\n#;"))); istrSkip(in, 1); inivalue_t *newval = NULL; + if (opts->merge_duplicate_keys) { newval = table->values; @@ -213,6 +218,8 @@ static void ini__add_table(arena_t *arena, ini_t *ctx, instream_t *in, iniopts_t if (!table) { table = alloc(arena, initable_t); + table->name = name; + if (!ctx->tables) { ctx->tables = table; } @@ -243,6 +250,11 @@ static void ini__add_table(arena_t *arena, ini_t *ctx, instream_t *in, iniopts_t static void ini__parse(arena_t *arena, ini_t *ini, const iniopts_t *options) { iniopts_t opts = ini__get_options(options); + initable_t *root = alloc(arena, initable_t); + root->name = INI_ROOT; + ini->tables = root; + ini->tail = root; + instream_t in = istrInitLen(ini->text.buf, ini->text.len); while (!istrIsFinished(in)) { diff --git a/colla/ini.h b/colla/ini.h index 7349cf0..47ce76d 100644 --- a/colla/ini.h +++ b/colla/ini.h @@ -35,6 +35,8 @@ ini_t iniParse(arena_t *arena, strview_t filename, const iniopts_t *options); ini_t iniParseFile(arena_t *arena, file_t file, const iniopts_t *options); ini_t iniParseStr(arena_t *arena, strview_t str, const iniopts_t *options); +bool iniIsValid(ini_t *ctx); + #define INI_ROOT strv("__ROOT__") initable_t *iniGetTable(ini_t *ctx, strview_t name); diff --git a/colla/json.c b/colla/json.c index d08cb46..acbceaa 100644 --- a/colla/json.c +++ b/colla/json.c @@ -5,40 +5,27 @@ #include #include "strstream.h" +#include "file.h" #include "tracelog.h" -#define json__ensure(c) \ - if (istrGet(in) != (c)) { \ - istrRewindN(in, 1); \ - fatal("wrong character at %zu, should be " #c " but is %c", istrTell(*in), istrPeek(in));\ +// #define json__logv() warn("%s:%d", __FILE__, __LINE__) +#define json__logv() +#define json__ensure(c) json__check_char(in, c) + +static bool json__check_char(instream_t *in, char c) { + if (istrGet(in) == c) { + return true; } - -str_t json__read_whole(arena_t *arena, const char *filename) { - str_t out = {0}; - FILE *fp = fopen(filename, "rb"); - if (!fp) { - err("could not open %s", filename); - return (str_t){0}; - } - - fseek(fp, 0, SEEK_END); - long len = ftell(fp); - fseek(fp, 0, SEEK_SET); - - out.buf = alloc(arena, char, len + 1); - out.len = len; - - fread(out.buf, 1, len, fp); - - fclose(fp); - - return out; + istrRewindN(in, 1); + err("wrong character at %zu, should be '%c' but is 0x%02x '%c'", istrTell(*in), c, istrPeek(in), istrPeek(in)); + json__logv(); + return false; } -jsonval_t *json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags); -jsonval_t *json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags); +static bool json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out); +static bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out); -bool json__is_value_finished(instream_t *in) { +static bool json__is_value_finished(instream_t *in) { usize old_pos = istrTell(*in); istrSkipWhitespace(in); @@ -53,30 +40,46 @@ bool json__is_value_finished(instream_t *in) { return false; } -void json__parse_null(instream_t *in) { +static bool json__parse_null(instream_t *in) { strview_t null_view = istrGetViewLen(in, 4); + bool is_valid = true; if (!strvEquals(null_view, strv("null"))) { - fatal("should be null but is: (%.*s) at %zu", null_view.len, null_view.buf, istrTell(*in)); + err("should be null but is: (%.*s) at %zu", null_view.len, null_view.buf, istrTell(*in)); + is_valid = false; } if (!json__is_value_finished(in)) { - fatal("null, should be finished, but isn't at %zu", istrTell(*in)); + err("null, should be finished, but isn't at %zu", istrTell(*in)); + is_valid = false; } + + if (!is_valid) json__logv(); + + return is_valid; } -jsonval_t *json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags) { - json__ensure('['); +static bool json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) { + jsonval_t *head = NULL; + + if (!json__ensure('[')) { + json__logv(); + goto fail; + } istrSkipWhitespace(in); // if it is an empty array if (istrPeek(in) == ']') { istrSkip(in, 1); - return NULL; + goto success; } - jsonval_t *head = json__parse_value(arena, in, flags); + if (!json__parse_value(arena, in, flags, &head)) { + json__logv(); + goto fail; + } + jsonval_t *cur = head; while (true) { @@ -88,11 +91,22 @@ jsonval_t *json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags) { istrSkipWhitespace(in); // trailing comma - if (!(flags & JSON_NO_TRAILING_COMMAS) && istrPeek(in) == ']') { - return head; + if (istrPeek(in) == ']') { + if (flags & JSON_NO_TRAILING_COMMAS) { + err("trailing comma in array at at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur); + json__logv(); + goto fail; + } + else { + continue; + } } - jsonval_t *next = json__parse_value(arena, in, flags); + jsonval_t *next = NULL; + if (!json__parse_value(arena, in, flags, &next)) { + json__logv(); + goto fail; + } cur->next = next; next->prev = cur; cur = next; @@ -100,17 +114,27 @@ jsonval_t *json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags) } default: istrRewindN(in, 1); - fatal("unknown char after array at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur); + err("unknown char after array at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur); + json__logv(); + goto fail; } } - return NULL; +success: + *out = head; + return true; +fail: + *out = NULL; + return false; } -str_t json__parse_string(arena_t *arena, instream_t *in, jsonflags_e flags) { - istrSkipWhitespace(in); +static bool json__parse_string(arena_t *arena, instream_t *in, str_t *out) { + istrSkipWhitespace(in); - json__ensure('"'); + if (!json__ensure('"')) { + json__logv(); + goto fail; + } const char *from = in->cur; @@ -122,61 +146,83 @@ str_t json__parse_string(arena_t *arena, instream_t *in, jsonflags_e flags) { usize len = in->cur - from; - str_t out = str(arena, from, len); + *out = str(arena, from, len); - json__ensure('"'); - - return out; -} - -double json__parse_number(instream_t *in, jsonflags_e flags) { - double value = 0.0; - istrGetDouble(in, &value); - return value; -} - -bool json__parse_bool(instream_t *in, jsonflags_e flags) { - size_t remaining = istrRemaining(*in); - if (remaining >= 4 && memcmp(in->cur, "true", 4) == 0) { - istrSkip(in, 4); - return true; + if (!json__ensure('"')) { + json__logv(); + goto fail; } - if (remaining >= 5 && memcmp(in->cur, "false", 5) == 0) { - istrSkip(in, 5); - return false; - } - fatal("unknown boolean at %zu: %.10s", istrTell(*in), in->cur); + +success: + return true; +fail: + *out = (str_t){0}; return false; } -jsonval_t *json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags) { - json__ensure('{'); +static bool json__parse_number(instream_t *in, double *out) { + return istrGetDouble(in, out); +} + +static bool json__parse_bool(instream_t *in, bool *out) { + size_t remaining = istrRemaining(*in); + if (remaining >= 4 && memcmp(in->cur, "true", 4) == 0) { + istrSkip(in, 4); + *out = true; + } + else if (remaining >= 5 && memcmp(in->cur, "false", 5) == 0) { + istrSkip(in, 5); + *out = false; + } + else { + err("unknown boolean at %zu: %.10s", istrTell(*in), in->cur); + json__logv(); + return false; + } + + return true; +} + +static bool json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) { + if (!json__ensure('{')) { + json__logv(); + goto fail; + } istrSkipWhitespace(in); // if it is an empty object if (istrPeek(in) == '}') { istrSkip(in, 1); - return NULL; + *out = NULL; + return true; } - jsonval_t *head = json__parse_pair(arena, in, flags); + jsonval_t *head = NULL; + if (!json__parse_pair(arena, in, flags, &head)) { + json__logv(); + goto fail; + } jsonval_t *cur = head; while (true) { istrSkipWhitespace(in); switch (istrGet(in)) { case '}': - return head; + goto success; case ',': { istrSkipWhitespace(in); // trailing commas if (!(flags & JSON_NO_TRAILING_COMMAS) && istrPeek(in) == '}') { - return head; + goto success; } - jsonval_t *next = json__parse_pair(arena, in, flags); + jsonval_t *next = NULL; + if (!json__parse_pair(arena, in, flags, &next)) { + json__logv(); + goto fail; + } cur->next = next; next->prev = cur; cur = next; @@ -184,83 +230,136 @@ jsonval_t *json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags) { } default: istrRewindN(in, 1); - fatal("unknown char after object at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur); + err("unknown char after object at %zu: (%c)(%d)", istrTell(*in), *in->cur, *in->cur); + json__logv(); + goto fail; } } - return head; +success: + *out = head; + return true; +fail: + *out = NULL; + return false; } -jsonval_t *json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags) { - str_t key = json__parse_string(arena, in, flags); +static bool json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) { + str_t key = {0}; + if (!json__parse_string(arena, in, &key)) { + json__logv(); + goto fail; + } // skip preamble istrSkipWhitespace(in); - json__ensure(':'); + if (!json__ensure(':')) { + json__logv(); + goto fail; + } - jsonval_t *out = json__parse_value(arena, in, flags); - out->key = key; - return out; + if (!json__parse_value(arena, in, flags, out)) { + json__logv(); + goto fail; + } + + (*out)->key = key; + return true; + +fail: + *out = NULL; + return false; } -jsonval_t *json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags) { - jsonval_t *out = alloc(arena, jsonval_t); +static bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) { + jsonval_t *val = alloc(arena, jsonval_t); istrSkipWhitespace(in); switch (istrPeek(in)) { // object case '{': - out->object = json__parse_obj(arena, in, flags); - out->type = JSON_OBJECT; + if (!json__parse_obj(arena, in, flags, &val->object)) { + json__logv(); + goto fail; + } + val->type = JSON_OBJECT; break; // array case '[': - out->array = json__parse_array(arena, in, flags); - out->type = JSON_ARRAY; + if (!json__parse_array(arena, in, flags, &val->array)) { + json__logv(); + goto fail; + } + val->type = JSON_ARRAY; break; // string case '"': - out->string = json__parse_string(arena, in, flags); - out->type = JSON_STRING; + if (!json__parse_string(arena, in, &val->string)) { + json__logv(); + goto fail; + } + val->type = JSON_STRING; break; // boolean case 't': // fallthrough case 'f': - out->boolean = json__parse_bool(in, flags); - out->type = JSON_BOOL; + if (!json__parse_bool(in, &val->boolean)) { + json__logv(); + goto fail; + } + val->type = JSON_BOOL; break; // null case 'n': - json__parse_null(in); - out->type = JSON_NULL; + if (!json__parse_null(in)) { + json__logv(); + goto fail; + } + val->type = JSON_NULL; break; // comment case '/': - fatal("TODO comments"); + err("TODO comments"); break; // number default: - out->number = json__parse_number(in, flags); - out->type = JSON_NUMBER; + if (!json__parse_number(in, &val->number)) { + json__logv(); + goto fail; + } + val->type = JSON_NUMBER; break; } - return out; + *out = val; + return true; +fail: + *out = NULL; + return false; } -json_t jsonParse(arena_t *arena, arena_t scratch, const char *filename, jsonflags_e flags) { - str_t data = json__read_whole(&scratch, filename); +json_t jsonParse(arena_t *arena, arena_t scratch, strview_t filename, jsonflags_e flags) { + str_t data = fileReadWholeStr(&scratch, filename); + return NULL; json_t json = jsonParseStr(arena, strv(data), flags); return json; } json_t jsonParseStr(arena_t *arena, strview_t jsonstr, jsonflags_e flags) { + arena_t before = *arena; + jsonval_t *root = alloc(arena, jsonval_t); root->type = JSON_OBJECT; instream_t in = istrInitLen(jsonstr.buf, jsonstr.len); - root->object = json__parse_obj(arena, &in, flags); + + if (!json__parse_obj(arena, &in, flags, &root->object)) { + // reset arena + *arena = before; + json__logv(); + return NULL; + } return root; } diff --git a/colla/json.h b/colla/json.h index 2b55231..762e53a 100644 --- a/colla/json.h +++ b/colla/json.h @@ -38,10 +38,10 @@ typedef struct jsonval_t { typedef jsonval_t *json_t; -json_t jsonParse(arena_t *arena, arena_t scratch, const char *filename, jsonflags_e flags); +json_t jsonParse(arena_t *arena, arena_t scratch, strview_t filename, jsonflags_e flags); json_t jsonParseStr(arena_t *arena, strview_t jsonstr, jsonflags_e flags); jsonval_t *jsonGet(jsonval_t *node, strview_t key); -#define json_for(name, arr) for (jsonval_t *name = arr->array; name; name = name->next) #define json_check(val, js_type) ((val) && (val)->type == js_type) +#define json_for(name, arr) for (jsonval_t *name = json_check(arr, JSON_ARRAY) ? arr->array : NULL; name; name = name->next) diff --git a/colla/server.c b/colla/server.c index e9a9838..3fa277c 100644 --- a/colla/server.c +++ b/colla/server.c @@ -7,6 +7,39 @@ #include "strstream.h" #include "arena.h" +#if COLLA_NOHTTP +const char *httpGetStatusString(int status) { + switch (status) { + case 200: return "OK"; + case 201: return "CREATED"; + case 202: return "ACCEPTED"; + case 204: return "NO CONTENT"; + case 205: return "RESET CONTENT"; + case 206: return "PARTIAL CONTENT"; + + case 300: return "MULTIPLE CHOICES"; + case 301: return "MOVED PERMANENTLY"; + case 302: return "MOVED TEMPORARILY"; + case 304: return "NOT MODIFIED"; + + case 400: return "BAD REQUEST"; + case 401: return "UNAUTHORIZED"; + case 403: return "FORBIDDEN"; + case 404: return "NOT FOUND"; + case 407: return "RANGE NOT SATISFIABLE"; + + case 500: return "INTERNAL SERVER_ERROR"; + case 501: return "NOT IMPLEMENTED"; + case 502: return "BAD GATEWAY"; + case 503: return "SERVICE NOT AVAILABLE"; + case 504: return "GATEWAY TIMEOUT"; + case 505: return "VERSION NOT SUPPORTED"; + } + + return "UNKNOWN"; +} +#endif + #define SERVER_BUFSZ 4096 typedef enum { @@ -15,6 +48,7 @@ typedef enum { PARSE_REQ_VERSION, PARSE_REQ_FIELDS, PARSE_REQ_FINISHED, + PARSE_REQ_FAILED, } server__req_status_e; typedef struct { @@ -36,6 +70,7 @@ typedef struct server_t { socket_t socket; server__route_t *routes; server__route_t *routes_default; + socket_t current_client; bool should_stop; } server_t; @@ -68,11 +103,11 @@ bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SER ctx->req.method = HTTP_POST; } else { - fatal("unknown method: (%.*s)", method.len, method.buf); + err("unknown method: (%.*s)", method.len, method.buf); + ctx->status = PARSE_REQ_FAILED; + break; } - info("parsed method: %.*s", method.len, method.buf); - ctx->status = PARSE_REQ_PAGE; } // fallthrough @@ -88,8 +123,6 @@ bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SER ctx->req.page = str(arena, page); - info("parsed page: %.*s", page.len, page.buf); - ctx->status = PARSE_REQ_VERSION; } // fallthrough @@ -104,11 +137,15 @@ bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SER } if (version.len < 8) { - fatal("version too short: (%.*s)", version.len, version.buf); + err("version too short: (%.*s)", version.len, version.buf); + ctx->status = PARSE_REQ_FAILED; + break; } - if (strvEquals(strvSub(version, 0, 4), strv("HTTP"))) { - fatal("version does not start with HTTP: (%.4s)", version.buf); + if (!strvEquals(strvSub(version, 0, 4), strv("HTTP"))) { + err("version does not start with HTTP: (%.4s)", version.buf); + ctx->status = PARSE_REQ_FAILED; + break; } // skip HTTP @@ -118,7 +155,9 @@ bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SER int scanned = sscanf(version.buf, "/%hhu.%hhu", &major, &minor); if (scanned != 2) { - fatal("could not scan both major and minor from version: (%.*s)", version.len, version.buf); + err("could not scan both major and minor from version: (%.*s)", version.len, version.buf); + ctx->status = PARSE_REQ_FAILED; + break; } ctx->req.version_major = major; @@ -144,7 +183,9 @@ bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SER strview_t key = istrGetView(&field_in, ':'); if (istrGet(&field_in) != ':') { - fatal("field does not have ':' (%.*s)", field.len, field.buf); + err("field does not have ':' (%.*s)", field.len, field.buf); + ctx->status = PARSE_REQ_FAILED; + break; } istrSkipWhitespace(&field_in); @@ -202,7 +243,7 @@ bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SER memmove(ctx->fullbuf, ctx->fullbuf + istrTell(in), ctx->prevbuf_len); - return ctx->status == PARSE_REQ_FINISHED; + return ctx->status >= PARSE_REQ_FINISHED; } void server__parse_req_url(arena_t *arena, server_req_t *req) { @@ -306,6 +347,8 @@ void serverRouteDefault(arena_t *arena, server_t *server, server_route_f cb, voi void serverStart(arena_t scratch, server_t *server) { usize start = arenaTell(&scratch); + info("Server started!"); + while (!server->should_stop) { socket_t client = skAccept(server->socket); if (!skIsValid(client)) { @@ -313,8 +356,6 @@ void serverStart(arena_t scratch, server_t *server) { continue; } - info("received connection: %zu", client); - arenaRewind(&scratch, start); server__req_ctx_t req_ctx = {0}; @@ -323,7 +364,7 @@ void serverStart(arena_t scratch, server_t *server) { int read = 0; do { read = skReceive(client, buffer, sizeof(buffer)); - if(read == -1) { + if(read == SOCKET_ERROR) { fatal("couldn't get the data from the server: %s", skGetErrorString()); } if (server__parse_chunk(&scratch, &req_ctx, buffer, read)) { @@ -331,7 +372,10 @@ void serverStart(arena_t scratch, server_t *server) { } } while(read != 0); - info("parsed request"); + if (req_ctx.status == PARSE_REQ_FAILED || req_ctx.status == PARSE_REQ_BEGIN) { + err("failed to parse request!"); + goto end_connection; + } server_req_t req = req_ctx.req; @@ -349,9 +393,16 @@ void serverStart(arena_t scratch, server_t *server) { route = server->routes_default; } + server->current_client = client; str_t response = route->fn(scratch, server, &req, route->userdata); - skSend(client, response.buf, response.len); + if (!skIsValid(server->current_client)) { + continue; + } + + skSend(client, response.buf, (int)response.len); + +end_connection: skClose(client); } @@ -369,14 +420,23 @@ str_t serverMakeResponse(arena_t *arena, int status_code, strview_t content_type arena, "HTTP/1.1 %d %s\r\n" - "Content-Type: %.*s\r\n" + "Content-Type: %v\r\n" "\r\n" - "%.*s", + "%v", status_code, httpGetStatusString(status_code), - content_type.len, content_type.buf, - body.len, body.buf + content_type, + body ); } +socket_t serverGetClient(server_t *server) { + return server->current_client; +} + +void serverSetClient(server_t *server, socket_t client) { + server->current_client = client; +} + + #undef SERVER_BUFSZ \ No newline at end of file diff --git a/colla/server.h b/colla/server.h index c5df090..28aca5e 100644 --- a/colla/server.h +++ b/colla/server.h @@ -26,11 +26,12 @@ typedef struct { typedef str_t (*server_route_f)(arena_t scratch, server_t *server, server_req_t *req, void *userdata); - server_t *serverSetup(arena_t *arena, uint16 port); void serverRoute(arena_t *arena, server_t *server, strview_t page, server_route_f cb, void *userdata); void serverRouteDefault(arena_t *arena, server_t *server, server_route_f cb, void *userdata); void serverStart(arena_t scratch, server_t *server); void serverStop(server_t *server); -str_t serverMakeResponse(arena_t *arena, int status_code, strview_t content_type, strview_t body); \ No newline at end of file +str_t serverMakeResponse(arena_t *arena, int status_code, strview_t content_type, strview_t body); +socket_t serverGetClient(server_t *server); +void serverSetClient(server_t *server, socket_t client); \ No newline at end of file diff --git a/colla/socket.c b/colla/socket.c index 8eeb630..dd67242 100644 --- a/colla/socket.c +++ b/colla/socket.c @@ -5,7 +5,8 @@ #endif #if COLLA_WIN -#include + +typedef int socklen_t; bool skInit(void) { WSADATA w; @@ -18,7 +19,7 @@ bool skCleanup(void) { } bool skClose(socket_t sock) { - return closesocket(sock) != -1; + return closesocket(sock) != SOCKET_ERROR; } int skPoll(skpoll_t *to_poll, int num_to_poll, int timeout) { @@ -31,7 +32,6 @@ int skGetError(void) { #else -#include #include #include #include @@ -48,7 +48,7 @@ bool skCleanup(void) { } bool skClose(socket_t sock) { - return close(sock) != -1; + return close(sock) != SOCKET_ERROR; } int skPoll(skpoll_t *to_poll, int num_to_poll, int timeout) { @@ -90,7 +90,7 @@ socket_t skOpenPro(int af, int type, int protocol) { } bool skIsValid(socket_t sock) { - return sock != (socket_t)-1; + return sock != SOCKET_ERROR; } skaddrin_t skAddrinInit(const char *ip, uint16_t port) { @@ -108,7 +108,7 @@ bool skBind(socket_t sock, const char *ip, uint16_t port) { } bool skBindPro(socket_t sock, const skaddr_t *name, int namelen) { - return bind(sock, name, namelen) != -1; + return bind(sock, name, namelen) != SOCKET_ERROR; } bool skListen(socket_t sock) { @@ -116,7 +116,7 @@ bool skListen(socket_t sock) { } bool skListenPro(socket_t sock, int backlog) { - return listen(sock, backlog) != -1; + return listen(sock, backlog) != SOCKET_ERROR; } socket_t skAccept(socket_t sock) { @@ -126,7 +126,7 @@ socket_t skAccept(socket_t sock) { } socket_t skAcceptPro(socket_t sock, skaddr_t *addr, int *addrlen) { - return accept(sock, addr, addrlen); + return accept(sock, addr, (socklen_t *)addrlen); } bool skConnect(socket_t sock, const char *server, unsigned short server_port) { @@ -144,7 +144,7 @@ bool skConnect(socket_t sock, const char *server, unsigned short server_port) { } bool skConnectPro(socket_t sock, const skaddr_t *name, int namelen) { - return connect(sock, name, namelen) != -1; + return connect(sock, name, namelen) != SOCKET_ERROR; } int skSend(socket_t sock, const void *buf, int len) { @@ -177,7 +177,7 @@ int skReceiveFrom(socket_t sock, void *buf, int len, skaddrin_t *from) { } int skReceiveFromPro(socket_t sock, void *buf, int len, int flags, skaddr_t *from, int *fromlen) { - return recvfrom(sock, buf, len, flags, from, fromlen); + return recvfrom(sock, buf, len, flags, from, (socklen_t *)fromlen); } // put this at the end of file to not make everything else unreadable diff --git a/colla/socket.h b/colla/socket.h index c0cbfdc..5681629 100644 --- a/colla/socket.h +++ b/colla/socket.h @@ -2,10 +2,14 @@ #include "collatypes.h" -#if COLLA_WIN +#if COLLA_TCC +#include "tcc/colla_tcc.h" +#elif COLLA_WIN +#define _WINSOCK_DEPRECATED_NO_WARNINGS #include -#else +#elif COLLA_LIN || COLLA_OSX #include +#include #endif typedef uintptr_t socket_t; @@ -13,6 +17,8 @@ typedef struct sockaddr skaddr_t; typedef struct sockaddr_in skaddrin_t; typedef struct pollfd skpoll_t; +#define SOCKET_ERROR (-1) + typedef enum { SOCK_TCP, SOCK_UDP, diff --git a/colla/str.c b/colla/str.c index c781aa9..68909c1 100644 --- a/colla/str.c +++ b/colla/str.c @@ -7,7 +7,18 @@ #include "tracelog.h" #if COLLA_WIN -#include + +#define WIN32_LEAN_AND_MEAN +#include + +#else + +#include + +#endif + +#if COLLA_TCC +#include "tcc/colla_tcc.h" #endif // == STR_T ======================================================== @@ -44,13 +55,14 @@ str_t strFmt(arena_t *arena, const char *fmt, ...) { str_t strFmtv(arena_t *arena, const char *fmt, va_list args) { va_list vcopy; va_copy(vcopy, args); + // stb_vsnprintf returns the length + null_term int len = fmtBufferv(NULL, 0, fmt, vcopy); va_end(vcopy); char *buffer = alloc(arena, char, len + 1); fmtBufferv(buffer, len + 1, fmt, args); - return (str_t){ .buf = buffer, .len = (usize)len }; + return (str_t) { .buf = buffer, .len = (usize)len }; } str_t strFromWChar(arena_t *arena, const wchar_t *src, usize srclen) { @@ -87,6 +99,8 @@ str_t strFromWChar(arena_t *arena, const wchar_t *src, usize srclen) { NULL, NULL ); + out.len = outlen; + #elif COLLA_LIN fatal("strFromWChar not implemented yet!"); #endif @@ -101,7 +115,7 @@ bool strEquals(str_t a, str_t b) { int strCompare(str_t a, str_t b) { return a.len == b.len ? memcmp(a.buf, b.buf, a.len) : - a.len - (int)b.len; + (int)(a.len - b.len); } str_t strDup(arena_t *arena, str_t src) { @@ -190,7 +204,7 @@ bool strvEquals(strview_t a, strview_t b) { int strvCompare(strview_t a, strview_t b) { return a.len == b.len ? memcmp(a.buf, b.buf, a.len) : - a.len - (int)b.len; + (int)(a.len - b.len); } wchar_t *strvToWChar(arena_t *arena, strview_t ctx, usize *outlen) { @@ -273,7 +287,7 @@ strview_t strvTrimLeft(strview_t ctx) { strview_t out = ctx; for (usize i = 0; i < ctx.len; ++i) { char c = ctx.buf[i]; - if (c != ' ' || c < '\t' || c > '\r') { + if (c != ' ' && (c < '\t' || c > '\r')) { break; } out.buf++; @@ -286,7 +300,7 @@ strview_t strvTrimRight(strview_t ctx) { strview_t out = ctx; for (isize i = ctx.len - 1; i >= 0; --i) { char c = ctx.buf[i]; - if (c != ' ' || c < '\t' || c > '\r') { + if (c != ' ' && (c < '\t' || c > '\r')) { break; } out.len--; @@ -348,7 +362,7 @@ usize strvFind(strview_t ctx, char c, usize from) { usize strvFindView(strview_t ctx, strview_t view, usize from) { if (ctx.len < view.len) return STR_NONE; usize end = ctx.len - view.len; - for (usize i = 0; i < end; ++i) { + for (usize i = from; i < end; ++i) { if (memcmp(ctx.buf + i, view.buf, view.len) == 0) { return i; } @@ -370,7 +384,7 @@ usize strvRFind(strview_t ctx, char c, usize from_right) { usize strvRFindView(strview_t ctx, strview_t view, usize from_right) { if (from_right > ctx.len) from_right = ctx.len; isize end = (isize)(ctx.len - from_right); - if (end < view.len) return STR_NONE; + if (end < (isize)view.len) return STR_NONE; for (isize i = end - view.len; i >= 0; --i) { if (memcmp(ctx.buf + i, view.buf, view.len) == 0) { return (usize)i; diff --git a/colla/strstream.c b/colla/strstream.c index b2fcd19..6b5ec58 100644 --- a/colla/strstream.c +++ b/colla/strstream.c @@ -33,19 +33,13 @@ instream_t istrInitLen(const char *str, usize len) { } char istrGet(instream_t *ctx) { - return *ctx->cur++; + return ctx && ctx->cur ? *ctx->cur++ : '\0'; } void istrIgnore(instream_t *ctx, char delim) { for (; !istrIsFinished(*ctx) && *ctx->cur != delim; ++ctx->cur) { } - - //usize position = ctx->cur - ctx->start; - //usize i; - //for(i = position; - // i < ctx->size && *ctx->cur != delim; - // ++i, ++ctx->cur); } void istrIgnoreAndSkip(instream_t *ctx, char delim) { @@ -54,30 +48,33 @@ void istrIgnoreAndSkip(instream_t *ctx, char delim) { } char istrPeek(instream_t *ctx) { - return *ctx->cur; + return ctx && ctx->cur ? *ctx->cur : '\0'; } char istrPeekNext(instream_t *ctx) { + if (!ctx || !ctx->cur) return '\0'; usize offset = (ctx->cur - ctx->start) + 1; return offset > ctx->size ? '\0' : *(ctx->cur + 1); } void istrSkip(instream_t *ctx, usize n) { + if (!ctx || !ctx->cur) return; usize remaining = ctx->size - (ctx->cur - ctx->start); if(n > remaining) { - warn("skipping more then remaining: %zu -> %zu", n, remaining); return; } ctx->cur += n; } void istrSkipWhitespace(instream_t *ctx) { + if (!ctx || !ctx->cur) return; while (*ctx->cur && isspace(*ctx->cur)) { ++ctx->cur; } } void istrRead(instream_t *ctx, char *buf, usize len) { + if (!ctx || !ctx->cur) return; usize remaining = ctx->size - (ctx->cur - ctx->start); if(len > remaining) { warn("istrRead: trying to read len %zu from remaining %zu", len, remaining); @@ -88,6 +85,7 @@ void istrRead(instream_t *ctx, char *buf, usize len) { } usize istrReadMax(instream_t *ctx, char *buf, usize len) { + if (!ctx || !ctx->cur) return 0; usize remaining = ctx->size - (ctx->cur - ctx->start); len = remaining < len ? remaining : len; memcpy(buf, ctx->cur, len); @@ -100,24 +98,26 @@ void istrRewind(instream_t *ctx) { } void istrRewindN(instream_t *ctx, usize amount) { + if (!ctx || !ctx->cur) return; usize remaining = ctx->size - (ctx->cur - ctx->start); if (amount > remaining) amount = remaining; ctx->cur -= amount; } usize istrTell(instream_t ctx) { - return ctx.cur - ctx.start; + return ctx.cur ? ctx.cur - ctx.start : 0; } usize istrRemaining(instream_t ctx) { - return ctx.size - (ctx.cur - ctx.start); + return ctx.cur ? ctx.size - (ctx.cur - ctx.start) : 0; } bool istrIsFinished(instream_t ctx) { - return (usize)(ctx.cur - ctx.start) >= ctx.size; + return ctx.cur ? (usize)(ctx.cur - ctx.start) >= ctx.size : true; } bool istrGetBool(instream_t *ctx, bool *val) { + if (!ctx || !ctx->cur) return false; usize remaining = ctx->size - (ctx->cur - ctx->start); if(strncmp(ctx->cur, "true", remaining) == 0) { *val = true; @@ -131,6 +131,7 @@ bool istrGetBool(instream_t *ctx, bool *val) { } bool istrGetU8(instream_t *ctx, uint8 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = (uint8) strtoul(ctx->cur, &end, 0); @@ -148,6 +149,7 @@ bool istrGetU8(instream_t *ctx, uint8 *val) { } bool istrGetU16(instream_t *ctx, uint16 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = (uint16) strtoul(ctx->cur, &end, 0); @@ -165,6 +167,7 @@ bool istrGetU16(instream_t *ctx, uint16 *val) { } bool istrGetU32(instream_t *ctx, uint32 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = (uint32) strtoul(ctx->cur, &end, 0); @@ -182,6 +185,7 @@ bool istrGetU32(instream_t *ctx, uint32 *val) { } bool istrGetU64(instream_t *ctx, uint64 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = strtoull(ctx->cur, &end, 0); @@ -199,6 +203,7 @@ bool istrGetU64(instream_t *ctx, uint64 *val) { } bool istrGetI8(instream_t *ctx, int8 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = (int8) strtol(ctx->cur, &end, 0); @@ -216,6 +221,7 @@ bool istrGetI8(instream_t *ctx, int8 *val) { } bool istrGetI16(instream_t *ctx, int16 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = (int16) strtol(ctx->cur, &end, 0); @@ -233,6 +239,7 @@ bool istrGetI16(instream_t *ctx, int16 *val) { } bool istrGetI32(instream_t *ctx, int32 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = (int32) strtol(ctx->cur, &end, 0); @@ -250,6 +257,7 @@ bool istrGetI32(instream_t *ctx, int32 *val) { } bool istrGetI64(instream_t *ctx, int64 *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = strtoll(ctx->cur, &end, 0); @@ -267,6 +275,7 @@ bool istrGetI64(instream_t *ctx, int64 *val) { } bool istrGetFloat(instream_t *ctx, float *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = strtof(ctx->cur, &end); @@ -284,6 +293,7 @@ bool istrGetFloat(instream_t *ctx, float *val) { } bool istrGetDouble(instream_t *ctx, double *val) { + if (!ctx || !ctx->cur) return false; char *end = NULL; *val = strtod(ctx->cur, &end); @@ -301,6 +311,7 @@ bool istrGetDouble(instream_t *ctx, double *val) { } str_t istrGetStr(arena_t *arena, instream_t *ctx, char delim) { + if (!ctx || !ctx->cur) return (str_t){0}; const char *from = ctx->cur; istrIgnore(ctx, delim); // if it didn't actually find it, it just reached the end of the string @@ -317,6 +328,7 @@ str_t istrGetStr(arena_t *arena, instream_t *ctx, char delim) { } usize istrGetBuf(instream_t *ctx, char *buf, usize buflen) { + if (!ctx || !ctx->cur) return 0; usize remaining = ctx->size - (ctx->cur - ctx->start); buflen -= 1; buflen = remaining < buflen ? remaining : buflen; @@ -327,13 +339,25 @@ usize istrGetBuf(instream_t *ctx, char *buf, usize buflen) { } strview_t istrGetView(instream_t *ctx, char delim) { + if (!ctx || !ctx->cur) return (strview_t){0}; const char *from = ctx->cur; istrIgnore(ctx, delim); usize len = ctx->cur - from; return strvInitLen(from, len); } +strview_t istrGetViewEither(instream_t *ctx, strview_t chars) { + if (!ctx || !ctx->cur) return (strview_t){0}; + const char *from = ctx->cur; + for (; !istrIsFinished(*ctx) && !strvContains(chars, *ctx->cur); ++ctx->cur) { + + } + usize len = ctx->cur - from; + return strvInitLen(from, len); +} + strview_t istrGetViewLen(instream_t *ctx, usize len) { + if (!ctx || !ctx->cur) return (strview_t){0}; const char *from = ctx->cur; istrSkip(ctx, len); usize buflen = ctx->cur - from; @@ -343,7 +367,8 @@ strview_t istrGetViewLen(instream_t *ctx, usize len) { /* == OUTPUT STREAM =========================================== */ void ostr__remove_null(outstream_t *o) { - if (ostrTell(o)) { + usize len = ostrTell(o); + if (len && o->beg[len - 1] == '\0') { arenaPop(o->arena, 1); } } @@ -364,20 +389,23 @@ usize ostrTell(outstream_t *ctx) { } char ostrBack(outstream_t *ctx) { - return arenaTell(ctx->arena) ? *ctx->arena->current : '\0'; + usize len = ostrTell(ctx); + return len ? ctx->beg[len - 1] : '\0'; } str_t ostrAsStr(outstream_t *ctx) { + bool is_null_terminated = ostrBack(ctx) == '\0' && false; return (str_t){ .buf = ctx->beg, - .len = ostrTell(ctx) + .len = ostrTell(ctx) - is_null_terminated }; } strview_t ostrAsView(outstream_t *ctx) { + bool is_null_terminated = ostrBack(ctx) == '\0'; return (strview_t){ .buf = ctx->beg, - .len = ostrTell(ctx) + .len = ostrTell(ctx) - is_null_terminated }; } @@ -392,6 +420,8 @@ void ostrPrintfV(outstream_t *ctx, const char *fmt, va_list args) { if (!ctx->arena) return; ostr__remove_null(ctx); strFmtv(ctx->arena, fmt, args); + // remove null termination + arenaPop(ctx->arena, 1); } void ostrPutc(outstream_t *ctx, char c) { @@ -423,4 +453,177 @@ void ostrAppendNum(outstream_t *ctx, double val) { ostrPrintf(ctx, "%g", val); } +/* == OUT BYTE STREAM ========================================= */ + +obytestream_t obstrInit(arena_t *exclusive_arena) { + return (obytestream_t){ + .beg = exclusive_arena ? exclusive_arena->current : NULL, + .arena = exclusive_arena, + }; +} + +void obstrClear(obytestream_t *ctx) { + if (ctx->arena) { + ctx->arena->current = ctx->beg; + } +} + +usize obstrTell(obytestream_t *ctx) { + return ctx->arena ? ctx->arena->current - ctx->beg : 0; +} + +buffer_t obstrAsBuf(obytestream_t *ctx) { + return (buffer_t){ .data = ctx->beg, .len = obstrTell(ctx) }; +} + +void obstrWrite(obytestream_t *ctx, const void *buf, usize buflen) { + uint8 *dst = alloc(ctx->arena, uint8, buflen); + memcpy(dst, buf, buflen); +} + +void obstrPuts(obytestream_t *ctx, strview_t str) { + obstrWrite(ctx, str.buf, str.len); +} + +void obstrAppendU8(obytestream_t *ctx, uint8 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +void obstrAppendU16(obytestream_t *ctx, uint16 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +void obstrAppendU32(obytestream_t *ctx, uint32 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +void obstrAppendU64(obytestream_t *ctx, uint64 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +void obstrAppendI8(obytestream_t *ctx, int8 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +void obstrAppendI16(obytestream_t *ctx, int16 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +void obstrAppendI32(obytestream_t *ctx, int32 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +void obstrAppendI64(obytestream_t *ctx, int64 value) { + obstrWrite(ctx, &value, sizeof(value)); +} + +/* == IN BYTE STREAM ========================================== */ + +ibytestream_t ibstrInit(const void *buf, usize len) { + return (ibytestream_t) { + .cur = buf, + .start = buf, + .size = len, + }; +} + +usize ibstrRemaining(ibytestream_t *ctx) { + return ctx->size - (ctx->cur - ctx->start); +} + +usize ibstrTell(ibytestream_t *ctx) { + return ctx->cur ? ctx->cur - ctx->start : 0; +} + +usize ibstrRead(ibytestream_t *ctx, void *buf, usize buflen) { + if (!ctx->cur) return 0; + usize remaining = ibstrRemaining(ctx); + if (buflen > remaining) buflen = remaining; + memcpy(buf, ctx->cur, buflen); + ctx->cur += buflen; + return buflen; +} + +uint8 ibstrGetU8(ibytestream_t *ctx) { + uint8 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +uint16 ibstrGetU16(ibytestream_t *ctx) { + uint16 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +uint32 ibstrGetU32(ibytestream_t *ctx) { + uint32 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +uint64 ibstrGetU64(ibytestream_t *ctx) { + uint64 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +int8 ibstrGetI8(ibytestream_t *ctx) { + int8 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +int16 ibstrGetI16(ibytestream_t *ctx) { + int16 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +int32 ibstrGetI32(ibytestream_t *ctx) { + int32 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +int64 ibstrGetI64(ibytestream_t *ctx) { + int64 value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +float ibstrGetFloat(ibytestream_t *ctx) { + float value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +double ibstrGetDouble(ibytestream_t *ctx) { + double value = 0; + usize read = ibstrRead(ctx, &value, sizeof(value)); + return read == sizeof(value) ? value : 0; +} + +strview_t ibstrGetView(ibytestream_t *ctx, usize lensize) { + uint64 len = 0; + usize read = ibstrRead(ctx, &len, lensize); + if (read != lensize) { + warn("couldn't read %zu bytes, instead read %zu for string", lensize, read); + return (strview_t){0}; + } + usize remaining = ibstrRemaining(ctx); + if (len > remaining) { + warn("trying to read a string of length %zu, but only %zu bytes remaining", len, remaining); + len = remaining; + } + + const char *str = (const char *)ctx->cur; + ctx->cur += len; + + return (strview_t){ + .buf = str, + .len = len, + }; +} + #include "warnings/colla_warn_end.h" \ No newline at end of file diff --git a/colla/strstream.h b/colla/strstream.h index 19f09bb..f4585d2 100644 --- a/colla/strstream.h +++ b/colla/strstream.h @@ -35,7 +35,7 @@ void istrIgnore(instream_t *ctx, char delim); void istrIgnoreAndSkip(instream_t *ctx, char delim); // skip n characters void istrSkip(instream_t *ctx, usize n); -// skips whitespace (' ', '\n', '\t', '\r') +// skips whitespace (' ', '\\n', '\\t', '\\r') void istrSkipWhitespace(instream_t *ctx); // read len bytes into buffer, the buffer will not be null terminated void istrRead(instream_t *ctx, char *buf, usize len); @@ -68,6 +68,7 @@ str_t istrGetStr(arena_t *arena, instream_t *ctx, char delim); // get a string of maximum size len, the string is not allocated by the function and will be null terminated usize istrGetBuf(instream_t *ctx, char *buf, usize buflen); strview_t istrGetView(instream_t *ctx, char delim); +strview_t istrGetViewEither(instream_t *ctx, strview_t chars); strview_t istrGetViewLen(instream_t *ctx, usize len); /* == OUTPUT STREAM =========================================== */ @@ -96,6 +97,59 @@ void ostrAppendUInt(outstream_t *ctx, uint64 val); void ostrAppendInt(outstream_t *ctx, int64 val); void ostrAppendNum(outstream_t *ctx, double val); +/* == OUT BYTE STREAM ========================================= */ + +typedef struct { + uint8 *beg; + arena_t *arena; +} obytestream_t; + +obytestream_t obstrInit(arena_t *exclusive_arena); +void obstrClear(obytestream_t *ctx); + +usize obstrTell(obytestream_t *ctx); +buffer_t obstrAsBuf(obytestream_t *ctx); + +void obstrWrite(obytestream_t *ctx, const void *buf, usize buflen); +void obstrPuts(obytestream_t *ctx, strview_t str); +void obstrAppendU8(obytestream_t *ctx, uint8 value); +void obstrAppendU16(obytestream_t *ctx, uint16 value); +void obstrAppendU32(obytestream_t *ctx, uint32 value); +void obstrAppendU64(obytestream_t *ctx, uint64 value); +void obstrAppendI8(obytestream_t *ctx, int8 value); +void obstrAppendI16(obytestream_t *ctx, int16 value); +void obstrAppendI32(obytestream_t *ctx, int32 value); +void obstrAppendI64(obytestream_t *ctx, int64 value); + +/* == IN BYTE STREAM ========================================== */ + +typedef struct { + const uint8 *start; + const uint8 *cur; + usize size; +} ibytestream_t; + +ibytestream_t ibstrInit(const void *buf, usize len); + +usize ibstrRemaining(ibytestream_t *ctx); +usize ibstrTell(ibytestream_t *ctx); +usize ibstrRead(ibytestream_t *ctx, void *buf, usize buflen); + +uint8 ibstrGetU8(ibytestream_t *ctx); +uint16 ibstrGetU16(ibytestream_t *ctx); +uint32 ibstrGetU32(ibytestream_t *ctx); +uint64 ibstrGetU64(ibytestream_t *ctx); +int8 ibstrGetI8(ibytestream_t *ctx); +int16 ibstrGetI16(ibytestream_t *ctx); +int32 ibstrGetI32(ibytestream_t *ctx); +int64 ibstrGetI64(ibytestream_t *ctx); +float ibstrGetFloat(ibytestream_t *ctx); +double ibstrGetDouble(ibytestream_t *ctx); + +// reads a string, before reads bytes for the length (e.g. sizeof(uint32)) +// then reads sizeof(char) * strlen +strview_t ibstrGetView(ibytestream_t *ctx, usize lensize); + #ifdef __cplusplus } // extern "C" #endif diff --git a/colla/tcc/colla.def b/colla/tcc/colla.def new file mode 100644 index 0000000..4f47962 --- /dev/null +++ b/colla/tcc/colla.def @@ -0,0 +1,12 @@ +LIBRARY + +EXPORTS +GetThreadId +InitializeConditionVariable +WakeConditionVariable +WakeAllConditionVariable +SleepConditionVariableCS +InternetOpen +InternetConnect +HttpOpenRequest +HttpSendRequest \ No newline at end of file diff --git a/colla/tcc/colla_tcc.h b/colla/tcc/colla_tcc.h new file mode 100644 index 0000000..e1e2fc0 --- /dev/null +++ b/colla/tcc/colla_tcc.h @@ -0,0 +1,300 @@ +#pragma once + +#if COLLA_TCC || 1 + +#include + +//// FILE.H //////////////////////////////////////////////////////////////////////////////////// + +static BOOL tcc_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize) { + LARGE_INTEGER file_size = {0}; + file_size.LowPart = GetFileSize(hFile, &file_size.HighPart); + if (lpFileSize) *lpFileSize = file_size; + return file_size.LowPart != INVALID_FILE_SIZE; +} + +#define GetFileSizeEx tcc_GetFileSizeEx + +//////////////////////////////////////////////////////////////////////////////////////////////// + +//// STR.H ///////////////////////////////////////////////////////////////////////////////////// + +#define CP_UTF8 65001 +extern int __stdcall WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); +extern int __stdcall MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); + +//////////////////////////////////////////////////////////////////////////////////////////////// + +//// SOCKET.H ////////////////////////////////////////////////////////////////////////////////// + +#define WSADESCRIPTION_LEN 256 +#define WSASYS_STATUS_LEN 128 +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define AF_INET 2 +#define INADDR_ANY (ULONG)0x00000000 + +#define WSA_IO_PENDING (ERROR_IO_PENDING) +#define WSA_IO_INCOMPLETE (ERROR_IO_INCOMPLETE) +#define WSA_INVALID_HANDLE (ERROR_INVALID_HANDLE) +#define WSA_INVALID_PARAMETER (ERROR_INVALID_PARAMETER) +#define WSA_NOT_ENOUGH_MEMORY (ERROR_NOT_ENOUGH_MEMORY) +#define WSA_OPERATION_ABORTED (ERROR_OPERATION_ABORTED) +#define WSA_INVALID_EVENT ((WSAEVENT)NULL) +#define WSA_MAXIMUM_WAIT_EVENTS (MAXIMUM_WAIT_OBJECTS) +#define WSA_WAIT_FAILED (WAIT_FAILED) +#define WSA_WAIT_EVENT_0 (WAIT_OBJECT_0) +#define WSA_WAIT_IO_COMPLETION (WAIT_IO_COMPLETION) +#define WSA_WAIT_TIMEOUT (WAIT_TIMEOUT) +#define WSA_INFINITE (INFINITE) +#define WSAEINTR 10004L +#define WSAEBADF 10009L +#define WSAEACCES 10013L +#define WSAEFAULT 10014L +#define WSAEINVAL 10022L +#define WSAEMFILE 10024L +#define WSAEWOULDBLOCK 10035L +#define WSAEINPROGRESS 10036L +#define WSAEALREADY 10037L +#define WSAENOTSOCK 10038L +#define WSAEDESTADDRREQ 10039L +#define WSAEMSGSIZE 10040L +#define WSAEPROTOTYPE 10041L +#define WSAENOPROTOOPT 10042L +#define WSAEPROTONOSUPPORT 10043L +#define WSAESOCKTNOSUPPORT 10044L +#define WSAEOPNOTSUPP 10045L +#define WSAEPFNOSUPPORT 10046L +#define WSAEAFNOSUPPORT 10047L +#define WSAEADDRINUSE 10048L +#define WSAEADDRNOTAVAIL 10049L +#define WSAENETDOWN 10050L +#define WSAENETUNREACH 10051L +#define WSAENETRESET 10052L +#define WSAECONNABORTED 10053L +#define WSAECONNRESET 10054L +#define WSAENOBUFS 10055L +#define WSAEISCONN 10056L +#define WSAENOTCONN 10057L +#define WSAESHUTDOWN 10058L +#define WSAETOOMANYREFS 10059L +#define WSAETIMEDOUT 10060L +#define WSAECONNREFUSED 10061L +#define WSAELOOP 10062L +#define WSAENAMETOOLONG 10063L +#define WSAEHOSTDOWN 10064L +#define WSAEHOSTUNREACH 10065L +#define WSAENOTEMPTY 10066L +#define WSAEPROCLIM 10067L +#define WSAEUSERS 10068L +#define WSAEDQUOT 10069L +#define WSAESTALE 10070L +#define WSAEREMOTE 10071L +#define WSASYSNOTREADY 10091L +#define WSAVERNOTSUPPORTED 10092L +#define WSANOTINITIALISED 10093L +#define WSAEDISCON 10101L +#define WSAENOMORE 10102L +#define WSAECANCELLED 10103L +#define WSAEINVALIDPROCTABLE 10104L +#define WSAEINVALIDPROVIDER 10105L +#define WSAEPROVIDERFAILEDINIT 10106L +#define WSASYSCALLFAILURE 10107L +#define WSASERVICE_NOT_FOUND 10108L +#define WSATYPE_NOT_FOUND 10109L +#define WSA_E_NO_MORE 10110L +#define WSA_E_CANCELLED 10111L +#define WSAEREFUSED 10112L +#define WSAHOST_NOT_FOUND 11001L +#define WSATRY_AGAIN 11002L +#define WSANO_RECOVERY 11003L +#define WSANO_DATA 11004L +#define WSA_QOS_RECEIVERS 11005L +#define WSA_QOS_SENDERS 11006L +#define WSA_QOS_NO_SENDERS 11007L +#define WSA_QOS_NO_RECEIVERS 11008L +#define WSA_QOS_REQUEST_CONFIRMED 11009L +#define WSA_QOS_ADMISSION_FAILURE 11010L +#define WSA_QOS_POLICY_FAILURE 11011L +#define WSA_QOS_BAD_STYLE 11012L +#define WSA_QOS_BAD_OBJECT 11013L +#define WSA_QOS_TRAFFIC_CTRL_ERROR 11014L +#define WSA_QOS_GENERIC_ERROR 11015L +#define WSA_QOS_ESERVICETYPE 11016L +#define WSA_QOS_EFLOWSPEC 11017L +#define WSA_QOS_EPROVSPECBUF 11018L +#define WSA_QOS_EFILTERSTYLE 11019L +#define WSA_QOS_EFILTERTYPE 11020L +#define WSA_QOS_EFILTERCOUNT 11021L +#define WSA_QOS_EOBJLENGTH 11022L +#define WSA_QOS_EFLOWCOUNT 11023L +#define WSA_QOS_EUNKOWNPSOBJ 11024L +#define WSA_QOS_EPOLICYOBJ 11025L +#define WSA_QOS_EFLOWDESC 11026L +#define WSA_QOS_EPSFLOWSPEC 11027L +#define WSA_QOS_EPSFILTERSPEC 11028L +#define WSA_QOS_ESDMODEOBJ 11029L +#define WSA_QOS_ESHAPERATEOBJ 11030L +#define WSA_QOS_RESERVED_PETYPE 11031L +#define WSA_SECURE_HOST_NOT_FOUND 11032L +#define WSA_IPSEC_NAME_POLICY_ERROR 11033L + +typedef UINT_PTR SOCKET; + +typedef struct WSAData { + WORD wVersion; + WORD wHighVersion; +#ifdef _WIN64 + unsigned short iMaxSockets; + unsigned short iMaxUdpDg; + char FAR * lpVendorInfo; + char szDescription[WSADESCRIPTION_LEN+1]; + char szSystemStatus[WSASYS_STATUS_LEN+1]; +#else + char szDescription[WSADESCRIPTION_LEN+1]; + char szSystemStatus[WSASYS_STATUS_LEN+1]; + unsigned short iMaxSockets; + unsigned short iMaxUdpDg; + char FAR * lpVendorInfo; +#endif +} WSADATA, *LPWSADATA; + +typedef struct pollfd { + SOCKET fd; + SHORT events; + SHORT revents; +} WSAPOLLFD, *LPWSAPOLLFD; + +struct tcc__protoent { + char *p_name; + char **p_aliases; + short p_proto; /* protocol # */ +}; + +typedef struct in_addr { + union { + struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { USHORT s_w1,s_w2; } S_un_w; + ULONG S_addr; + } S_un; +#define s_addr S_un.S_addr /* can be used for most tcp & ip code */ +#define s_host S_un.S_un_b.s_b2 // host on imp +#define s_net S_un.S_un_b.s_b1 // network +#define s_imp S_un.S_un_w.s_w2 // imp +#define s_impno S_un.S_un_b.s_b4 // imp # +#define s_lh S_un.S_un_b.s_b3 // logical host +} IN_ADDR; + +typedef struct sockaddr_in { +#if(_WIN32_WINNT < 0x0600) + short sin_family; +#else //(_WIN32_WINNT < 0x0600) + ADDRESS_FAMILY sin_family; +#endif //(_WIN32_WINNT < 0x0600) + + USHORT sin_port; + IN_ADDR sin_addr; + CHAR sin_zero[8]; +} SOCKADDR_IN; + +struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + short h_addrtype; /* host address type */ + short h_length; /* length of address */ + char **h_addr_list; /* list of addresses */ +#define h_addr h_addr_list[0] /* address, for backward compat */ +}; + +typedef struct sockaddr { + +#if (_WIN32_WINNT < 0x0600) + USHORT sa_family; +#else + ADDRESS_FAMILY sa_family; // Address family. +#endif //(_WIN32_WINNT < 0x0600) + + CHAR sa_data[14]; // Up to 14 bytes of direct address. +} SOCKADDR; + +#define protoent tcc__protoent + +extern int __stdcall WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); +extern int __stdcall WSACleanup(void); +extern int __stdcall closesocket(SOCKET s); +extern int __stdcall WSAPoll(LPWSAPOLLFD fdArray, ULONG fds, INT timeout); +extern int __stdcall WSAGetLastError(void); +extern struct protoent *__stdcall getprotobyname(const char *name); +extern SOCKET __stdcall socket(int af, int type, int protocol); +extern USHORT __stdcall htons(USHORT hostshort); +extern unsigned long __stdcall inet_addr(const char *cp); +extern int __stdcall bind(SOCKET s, const struct sockaddr *name, int namelen); +extern int __stdcall listen(SOCKET s, int backlog); +extern SOCKET __stdcall accept(SOCKET s, struct sockaddr *addr, int *addrlen); +extern struct hostent *__stdcall gethostbyname(const char *name); + +extern char *__stdcall inet_ntoa(struct in_addr in); +extern int __stdcall connect(SOCKET s, const struct sockaddr *name, int namelen); +extern int __stdcall send(SOCKET s, const char *buf, int len, int flags); +extern int __stdcall sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen); +extern int __stdcall recv(SOCKET s, char *buf, int len, int flags); +extern int __stdcall recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); + +//////////////////////////////////////////////////////////////////////////////////////////////// + +// for the next few, we need to use colla.def, so it doesn't work with tcc -run +// because of this, let's just put them behind the defines + +#if !COLLA_NOTHREADS + +typedef struct CONDITION_VARIABLE { + PVOID Ptr; +} CONDITION_VARIABLE, *PCONDITION_VARIABLE; + +extern void __stdcall InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable);; +extern void __stdcall WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable); +extern void __stdcall WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable); +extern BOOL __stdcall SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds); + +#endif + +#if !COLLA_NOHTTP + +#define INTERNET_OPEN_TYPE_PRECONFIG 0 +#define INTERNET_DEFAULT_HTTPS_PORT 443 +#define INTERNET_SERVICE_HTTP 3 +#define INTERNET_FLAG_SECURE 0x00800000 + + +typedef LPVOID HINTERNET; +typedef WORD INTERNET_PORT; + +#if UNICODE + #define InternetOpen InternetOpenW + #define InternetConnect InternetConnectW + #define HttpOpenRequest HttpOpenRequestW + #define HttpSendRequest HttpSendRequestW +#else + #define InternetOpen InternetOpenA + #define InternetConnect InternetConnectA + #define HttpOpenRequest HttpOpenRequestA + #define HttpSendRequest HttpSendRequestA +#endif + +extern HINTERNET __stdcall InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType, LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags); +extern HINTERNET __stdcall InternetConnectW(HINTERNET hInternet, LPCWSTR lpszServerName, INTERNET_PORT nServerPort, LPCWSTR lpszUserName, LPCWSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext); +extern HINTERNET __stdcall HttpOpenRequestW(HINTERNET hConnect, LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion, LPCWSTR lpszReferrer, LPCWSTR *lplpszAcceptTypes, DWORD dwFlags, DWORD_PTR dwContext); +extern BOOL __stdcall HttpSendRequestW(HINTERNET hRequest, LPCWSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength); + +extern HINTERNET __stdcall InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags); +extern HINTERNET __stdcall InternetConnectA(HINTERNET hInternet, LPCSTR lpszServerName, INTERNET_PORT nServerPort, LPCSTR lpszUserName, LPCSTR lpszPassword, DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext); +extern HINTERNET __stdcall HttpOpenRequestA(HINTERNET hConnect, LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion, LPCSTR lpszReferrer, LPCSTR *lplpszAcceptTypes, DWORD dwFlags, DWORD_PTR dwContext); +extern BOOL __stdcall HttpSendRequestA(HINTERNET hRequest, LPCSTR lpszHeaders, DWORD dwHeadersLength, LPVOID lpOptional, DWORD dwOptionalLength); + +extern BOOL __stdcall HttpAddRequestHeadersA(HINTERNET hRequest, LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwModifiers); +extern BOOL __stdcall InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead); +extern BOOL __stdcall InternetCloseHandle(HINTERNET hInternet); + +#endif + +#endif \ No newline at end of file diff --git a/colla/tracelog.c b/colla/tracelog.c index 71ab15b..f7830d6 100644 --- a/colla/tracelog.c +++ b/colla/tracelog.c @@ -11,8 +11,12 @@ #pragma warning(disable:4996) // _CRT_SECURE_NO_WARNINGS. #endif - #include + #include +#if COLLA_CMT_LIB + #pragma comment(lib, "User32") +#endif + // avoid including windows.h #ifndef STD_OUTPUT_HANDLE @@ -22,29 +26,27 @@ #define CP_UTF8 65001 #endif - typedef unsigned short WORD; - typedef unsigned long DWORD; - typedef unsigned int UINT; - typedef int BOOL; - WINBASEAPI HANDLE WINAPI GetStdHandle(DWORD nStdHandle); - WINBASEAPI BOOL WINAPI SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes); - WINBASEAPI BOOL WINAPI SetConsoleOutputCP(UINT wCodePageID); - - #ifndef TLOG_VS - #define TLOG_WIN32_NO_VS - #ifndef TLOG_NO_COLOURS - #define TLOG_NO_COLOURS - #endif + #ifndef TLOG_NO_COLOURS + #define TLOG_NO_COLOURS #endif #endif -#ifdef TLOG_VS - #if COLLA_WIN - #error "can't use TLOG_VS if not on windows" - #endif +#if COLLA_EMC + #define TLOG_NO_COLOURS #endif -#ifdef TLOG_NO_COLOURS +#if COLLA_EMC + #define COLOUR_BLACK "" + #define COLOUR_RED "" + #define COLOUR_GREEN "" + #define COLOUR_YELLOW "" + #define COLOUR_BLUE "" + #define COLOUR_MAGENTA "" + #define COLOUR_CYAN "" + #define COLOUR_WHITE "" + #define COLOUR_RESET "" + #define COLOUR_BOLD "" +#elif defined(TLOG_NO_COLOURS) #define COLOUR_BLACK "" #define COLOUR_RED "" #define COLOUR_GREEN "" @@ -72,7 +74,7 @@ bool use_newline = true; -#ifdef TLOG_WIN32_NO_VS +#if COLLA_WIN static void setLevelColour(int level) { WORD attribute = 15; switch (level) { @@ -86,11 +88,23 @@ static void setLevelColour(int level) { HANDLE hc = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hc, attribute); } +#else +static void setLevelColour(int level) { + switch (level) { + case LogDebug: printf(COLOUR_BLUE); break; + case LogInfo: printf(COLOUR_GREEN); break; + case LogWarning: printf(COLOUR_YELLOW); break; + case LogError: printf(COLOUR_RED); break; + case LogFatal: printf(COLOUR_MAGENTA); break; + default: printf(COLOUR_RESET); break; + } +} #endif void traceLog(int level, const char *fmt, ...) { va_list args; - va_start(args, fmt); traceLogVaList(level, fmt, args); + va_start(args, fmt); + traceLogVaList(level, fmt, args); va_end(args); } @@ -105,55 +119,82 @@ void traceLogVaList(int level, const char *fmt, va_list args) { mtxLock(g_mtx); #endif - char buffer[MAX_TRACELOG_MSG_LENGTH]; - memset(buffer, 0, sizeof(buffer)); - - const char *beg; + const char *beg = ""; switch (level) { - case LogTrace: beg = COLOUR_BOLD COLOUR_WHITE "[TRACE]: " COLOUR_RESET; break; - case LogDebug: beg = COLOUR_BOLD COLOUR_BLUE "[DEBUG]: " COLOUR_RESET; break; - case LogInfo: beg = COLOUR_BOLD COLOUR_GREEN "[INFO]: " COLOUR_RESET; break; - case LogWarning: beg = COLOUR_BOLD COLOUR_YELLOW "[WARNING]: " COLOUR_RESET; break; - case LogError: beg = COLOUR_BOLD COLOUR_RED "[ERROR]: " COLOUR_RESET; break; - case LogFatal: beg = COLOUR_BOLD COLOUR_RED "[FATAL]: " COLOUR_RESET; break; - default: beg = ""; break; + case LogTrace: beg = "[TRACE" ; break; + case LogDebug: beg = "[DEBUG" ; break; + case LogInfo: beg = "[INFO" ; break; + case LogWarning: beg = "[WARNING" ; break; + case LogError: beg = "[ERROR" ; break; + case LogFatal: beg = "[FATAL" ; break; + default: break; } - size_t offset = 0; - -#ifndef TLOG_WIN32_NO_VS - offset = strlen(beg); - strncpy(buffer, beg, sizeof(buffer)); -#endif - - fmtBufferv(buffer + offset, sizeof(buffer) - offset, fmt, args); - -#if defined(TLOG_VS) - OutputDebugStringA(buffer); - if(use_newline) OutputDebugStringA("\n"); -#elif defined(TLOG_WIN32_NO_VS) +#if COLLA_WIN SetConsoleOutputCP(CP_UTF8); +#endif setLevelColour(level); printf("%s", beg); + +#if GAME_CLIENT + putchar(':'); + traceSetColour(COL_CYAN); + printf("CLIENT"); +#elif GAME_HOST + putchar(':'); + traceSetColour(COL_MAGENTA); + printf("HOST"); +#endif + + setLevelColour(level); + printf("]: "); + // set back to white setLevelColour(LogTrace); - printf("%s", buffer); - if(use_newline) puts(""); + // vprintf(fmt, args); + fmtPrintv(fmt, args); + + if(use_newline) { +#if COLLA_EMC + puts("
"); #else - printf("%s", buffer); - if(use_newline) puts(""); + puts(""); #endif + } #ifndef TLOG_DONT_EXIT_ON_FATAL if (level == LogFatal) { + +#ifndef TLOG_NO_MSGBOX + +#if COLLA_WIN + char errbuff[1024]; + fmtBufferv(errbuff, sizeof(errbuff), fmt, args); + + char captionbuf[] = +#if GAME_HOST + "Fatal Host Error"; +#elif GAME_CLIENT + "Fatal Client Error"; +#else + "Fatal Error"; +#endif + MessageBoxA( + NULL, + errbuff, captionbuf, + MB_ICONERROR | MB_OK + ); +#endif + +#endif + fflush(stdout); abort(); - exit(1); } #endif - #ifdef TLOG_MUTEX +#ifdef TLOG_MUTEX mtxUnlock(g_mtx); - #endif +#endif } void traceUseNewline(bool newline) { @@ -161,18 +202,18 @@ void traceUseNewline(bool newline) { } void traceSetColour(colour_e colour) { -#ifdef TLOG_WIN32_NO_VS +#if COLLA_WIN SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colour); #else switch (colour) { - case COL_RESET: printf(RESET); break; - case COL_BLACK: printf(BLACK); break; - case COL_BLUE: printf(BLUE); break; - case COL_GREEN: printf(GREEN); break; - case COL_CYAN: printf(CYAN); break; - case COL_RED: printf(RED); break; - case COL_MAGENTA: printf(MAGENTA); break; - case COL_YELLOW: printf(YELLOW); break; + case COL_RESET: printf(COLOUR_RESET); break; + case COL_BLACK: printf(COLOUR_BLACK); break; + case COL_BLUE: printf(COLOUR_BLUE); break; + case COL_GREEN: printf(COLOUR_GREEN); break; + case COL_CYAN: printf(COLOUR_CYAN); break; + case COL_RED: printf(COLOUR_RED); break; + case COL_MAGENTA: printf(COLOUR_MAGENTA); break; + case COL_YELLOW: printf(COLOUR_YELLOW); break; } #endif } \ No newline at end of file diff --git a/colla/utf8.c b/colla/utf8.c index 4a18df1..8e4390e 100644 --- a/colla/utf8.c +++ b/colla/utf8.c @@ -9,7 +9,7 @@ static const uint8 masks[] = { 0x01 // 0000-0001 }; -struct { +static struct { uint8 mask; uint8 result; int octets; diff --git a/colla/vmem.c b/colla/vmem.c index fb19a71..264790e 100644 --- a/colla/vmem.c +++ b/colla/vmem.c @@ -72,20 +72,21 @@ static void vmem__update_page_size(void) { vmem__page_size = info.dwPageSize; } -#elif COLLA_LIN +// #elif COLLA_LIN +#else #include #include #include #include -struct vmem__header { +typedef struct { usize len; -}; +} vmem__header; void *vmemInit(usize size, usize *out_padded_size) { size += sizeof(vmem__header); - usize alloc_size = padToPage(size); + usize alloc_size = vmemPadToPage(size); vmem__header *header = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -93,8 +94,8 @@ void *vmemInit(usize size, usize *out_padded_size) { fatal("could not map %zu memory: %s", size, strerror(errno)); } - if (padded_size) { - *padded_size = alloc_size; + if (out_padded_size) { + *out_padded_size = alloc_size; } header->len = alloc_size; @@ -115,8 +116,8 @@ bool vmemRelease(void *base_ptr) { bool vmemCommit(void *ptr, usize num_of_pages) { // mmap doesn't need a commit step - (VOID)ptr; - (VOID)num_of_pages; + (void)ptr; + (void)num_of_pages; return true; } @@ -127,7 +128,7 @@ static void vmem__update_page_size(void) { fatal("could not get page size: %s", strerror(errno)); } - page_size = (usize)lin_page_size; + vmem__page_size = (usize)lin_page_size; } #endif \ No newline at end of file diff --git a/colla/xml.c b/colla/xml.c new file mode 100644 index 0000000..b39cba2 --- /dev/null +++ b/colla/xml.c @@ -0,0 +1,145 @@ +#include "xml.h" + +#include "file.h" +#include "strstream.h" +#include "tracelog.h" + +static xmltag_t *xml__parse_tag(arena_t *arena, instream_t *in); + +xml_t xmlParse(arena_t *arena, strview_t filename) { + return xmlParseStr(arena, fileReadWholeStr(arena, filename)); +} + +xml_t xmlParseStr(arena_t *arena, str_t xmlstr) { + xml_t out = { + .text = xmlstr, + .root = alloc(arena, xmltag_t), + }; + return out; + + + instream_t in = istrInitLen(xmlstr.buf, xmlstr.len); + + while (!istrIsFinished(in)) { + xmltag_t *tag = xml__parse_tag(arena, &in); + + if (out.tail) out.tail->next = tag; + else out.root->child = tag; + + out.tail = tag; + } + + return out; +} + +xmltag_t *xmlGetTag(xmltag_t *parent, strview_t key, bool recursive) { + xmltag_t *t = parent ? parent->child : NULL; + while (t) { + if (strvEquals(key, t->key)) { + return t; + } + if (recursive && t->child) { + xmltag_t *out = xmlGetTag(t, key, recursive); + if (out) { + return out; + } + } + t = t->next; + } + return NULL; +} + +strview_t xmlGetAttribute(xmltag_t *tag, strview_t key) { + xmlattr_t *a = tag ? tag->attributes : NULL; + while (a) { + if (strvEquals(key, a->key)) { + return a->value; + } + a = a->next; + } + return (strview_t){0}; +} + +// == PRIVATE FUNCTIONS ======================================================================== + +static xmlattr_t *xml__parse_attr(arena_t *arena, instream_t *in) { + if (istrPeek(in) != ' ') { + return NULL; + } + + strview_t key = strvTrim(istrGetView(in, '=')); + istrSkip(in, 2); // skip = and " + strview_t val = strvTrim(istrGetView(in, '"')); + istrSkip(in, 1); // skip " + + if (strvIsEmpty(key) || strvIsEmpty(val)) { + warn("key or value empty"); + return NULL; + } + + xmlattr_t *attr = alloc(arena, xmlattr_t); + attr->key = key; + attr->value = val; + return attr; +} + +static xmltag_t *xml__parse_tag(arena_t *arena, instream_t *in) { + istrSkipWhitespace(in); + + // we're either parsing the body, or we have finished the object + if (istrPeek(in) != '<' || istrPeekNext(in) == '/') { + return NULL; + } + + istrSkip(in, 1); // skip < + + // meta tag, we don't care about these + if (istrPeek(in) == '?') { + istrIgnoreAndSkip(in, '\n'); + return NULL; + } + + xmltag_t *tag = alloc(arena, xmltag_t); + + tag->key = strvTrim(istrGetViewEither(in, strv(" >"))); + + xmlattr_t *attr = xml__parse_attr(arena, in); + while (attr) { + attr->next = tag->attributes; + tag->attributes = attr; + attr = xml__parse_attr(arena, in); + } + + // this tag does not have children, return + if (istrPeek(in) == '/') { + istrSkip(in, 2); // skip / and > + return tag; + } + + istrSkip(in, 1); // skip > + + xmltag_t *child = xml__parse_tag(arena, in); + while (child) { + if (tag->tail) { + tag->tail->next = child; + tag->tail = child; + } + else { + tag->child = tag->tail = child; + } + child = xml__parse_tag(arena, in); + } + + // parse content + istrSkipWhitespace(in); + tag->content = istrGetView(in, '<'); + + // closing tag + istrSkip(in, 2); // skip < and / + strview_t closing = strvTrim(istrGetView(in, '>')); + if (!strvEquals(tag->key, closing)) { + warn("opening and closing tags are different!: (%v) != (%v)", tag->key, closing); + } + istrSkip(in, 1); // skip > + return tag; +} diff --git a/colla/xml.h b/colla/xml.h new file mode 100644 index 0000000..573ec33 --- /dev/null +++ b/colla/xml.h @@ -0,0 +1,31 @@ +#pragma once + +#include "str.h" +#include "arena.h" + +typedef struct xmlattr_t { + strview_t key; + strview_t value; + struct xmlattr_t *next; +} xmlattr_t; + +typedef struct xmltag_t { + strview_t key; + xmlattr_t *attributes; + strview_t content; + struct xmltag_t *child; + struct xmltag_t *tail; + struct xmltag_t *next; +} xmltag_t; + +typedef struct { + str_t text; + xmltag_t *root; + xmltag_t *tail; +} xml_t; + +xml_t xmlParse(arena_t *arena, strview_t filename); +xml_t xmlParseStr(arena_t *arena, str_t xmlstr); + +xmltag_t *xmlGetTag(xmltag_t *parent, strview_t key, bool recursive); +strview_t xmlGetAttribute(xmltag_t *tag, strview_t key); \ No newline at end of file