bug fixes and new features
added hot_reload.c for easy hot reloading added xml.c for xml parsing added support for tiny c compiler for mostly everything out of the box fuzzed xml/json/ini parsers
This commit is contained in:
parent
ae59f269c2
commit
82aee127b0
35 changed files with 1720 additions and 296 deletions
|
|
@ -1 +0,0 @@
|
||||||
zig cc -o test.exe main.c -lWs2_32 -lWininet -Wall -Wpedantic -Wno-newline-eof
|
|
||||||
58
build.c
58
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"
|
#if COLLA_NOSOCKETS
|
||||||
#include "colla/base64.c"
|
#undef COLLA_NOHTTP
|
||||||
#include "colla/cthreads.c"
|
#undef COLLA_NOSERVER
|
||||||
#include "colla/file.c"
|
#define COLLA_NOHTTP 1
|
||||||
#include "colla/format.c"
|
#define COLLA_NOSERVER 1
|
||||||
#include "colla/http.c"
|
#endif
|
||||||
#include "colla/ini.c"
|
|
||||||
#include "colla/json.c"
|
#include "src/arena.c"
|
||||||
#include "colla/server.c"
|
#include "src/base64.c"
|
||||||
#include "colla/socket.c"
|
#include "src/file.c"
|
||||||
#include "colla/str.c"
|
#include "src/format.c"
|
||||||
#include "colla/strstream.c"
|
#include "src/ini.c"
|
||||||
#include "colla/tracelog.c"
|
#include "src/json.c"
|
||||||
#include "colla/utf8.c"
|
#include "src/str.c"
|
||||||
#include "colla/vmem.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
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,10 @@ void arenaCleanup(arena_t *arena) {
|
||||||
case ARENA_STATIC: break;
|
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) {
|
arena_t arenaScratch(arena_t *arena) {
|
||||||
|
|
@ -104,12 +107,6 @@ void *arenaAlloc(const arena_alloc_desc_t *desc) {
|
||||||
byte *ptr = arena->current;
|
byte *ptr = arena->current;
|
||||||
arena->current += total;
|
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);
|
return desc->flags & ALLOC_NOZERO ? ptr : memset(ptr, 0, total);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
#include "collatypes.h"
|
#include "collatypes.h"
|
||||||
|
|
||||||
|
#ifdef __TINYC__
|
||||||
|
#define alignof __alignof__
|
||||||
|
#else
|
||||||
#define alignof _Alignof
|
#define alignof _Alignof
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ARENA_VIRTUAL,
|
ARENA_VIRTUAL,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "collatypes.h"
|
#include "collatypes.h"
|
||||||
#include "str.h"
|
|
||||||
|
|
||||||
typedef struct arena_t arena_t;
|
typedef struct arena_t arena_t;
|
||||||
|
|
||||||
|
|
|
||||||
34
colla/bits.h
Normal file
34
colla/bits.h
Normal file
|
|
@ -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 <intrin.h>
|
||||||
|
#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
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define arrlen(a) (sizeof(a) / sizeof((a)[0]))
|
#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)
|
#if defined(_DEBUG) || !defined(NDEBUG)
|
||||||
#define COLLA_DEBUG 1
|
#define COLLA_DEBUG 1
|
||||||
|
|
@ -15,18 +16,28 @@
|
||||||
#define COLLA_WIN 1
|
#define COLLA_WIN 1
|
||||||
#define COLLA_OSX 0
|
#define COLLA_OSX 0
|
||||||
#define COLLA_LIN 0
|
#define COLLA_LIN 0
|
||||||
|
#define COLLA_EMC 0
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__EMSCRIPTEN__)
|
||||||
|
|
||||||
#define COLLA_WIN 0
|
#define COLLA_WIN 0
|
||||||
#define COLLA_OSX 1
|
#define COLLA_OSX 0
|
||||||
#define COLLA_LIN 0
|
#define COLLA_LIN 0
|
||||||
|
#define COLLA_EMC 1
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
|
|
||||||
#define COLLA_WIN 0
|
#define COLLA_WIN 0
|
||||||
#define COLLA_OSX 0
|
#define COLLA_OSX 0
|
||||||
#define COLLA_LIN 1
|
#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
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,15 +9,7 @@ typedef struct {
|
||||||
|
|
||||||
#if COLLA_WIN
|
#if COLLA_WIN
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <handleapi.h>
|
#include <windows.h>
|
||||||
#include <processthreadsapi.h>
|
|
||||||
#include <synchapi.h>
|
|
||||||
|
|
||||||
#undef INFINITE
|
|
||||||
#undef WAIT_FAILED
|
|
||||||
// couple of defines to avoid including windows.h
|
|
||||||
#define INFINITE 0xFFFFFFFF // Infinite timeout
|
|
||||||
#define WAIT_FAILED ((DWORD)0xFFFFFFFF)
|
|
||||||
|
|
||||||
// == THREAD ===========================================
|
// == THREAD ===========================================
|
||||||
|
|
||||||
|
|
@ -60,6 +52,9 @@ int thrCurrentId(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int thrGetId(cthread_t ctx) {
|
int thrGetId(cthread_t ctx) {
|
||||||
|
#if COLLA_TCC
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
return GetThreadId((HANDLE)ctx);
|
return GetThreadId((HANDLE)ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,6 +103,7 @@ bool mtxUnlock(cmutex_t ctx) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !COLLA_NO_CONDITION_VAR
|
||||||
// == CONDITION VARIABLE ===============================
|
// == CONDITION VARIABLE ===============================
|
||||||
|
|
||||||
#include "tracelog.h"
|
#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);
|
SleepConditionVariableCS((CONDITION_VARIABLE *)cond, (CRITICAL_SECTION *)mtx, milliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -236,6 +234,8 @@ bool mtxUnlock(cmutex_t ctx) {
|
||||||
return pthread_mutex_unlock((pthread_mutex_t *)ctx) == 0;
|
return pthread_mutex_unlock((pthread_mutex_t *)ctx) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !COLLA_NO_CONDITION_VAR
|
||||||
|
|
||||||
// == CONDITION VARIABLE ===============================
|
// == CONDITION VARIABLE ===============================
|
||||||
|
|
||||||
condvar_t condInit(void) {
|
condvar_t condInit(void) {
|
||||||
|
|
@ -277,3 +277,5 @@ void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
#include "collatypes.h"
|
#include "collatypes.h"
|
||||||
|
|
||||||
|
#if COLLA_TCC
|
||||||
|
#define COLLA_NO_CONDITION_VAR 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct arena_t arena_t;
|
||||||
|
|
||||||
// == THREAD ===========================================
|
// == THREAD ===========================================
|
||||||
|
|
||||||
typedef uintptr_t cthread_t;
|
typedef uintptr_t cthread_t;
|
||||||
|
|
@ -32,6 +38,7 @@ bool mtxLock(cmutex_t ctx);
|
||||||
bool mtxTryLock(cmutex_t ctx);
|
bool mtxTryLock(cmutex_t ctx);
|
||||||
bool mtxUnlock(cmutex_t ctx);
|
bool mtxUnlock(cmutex_t ctx);
|
||||||
|
|
||||||
|
#if !COLLA_NO_CONDITION_VAR
|
||||||
// == CONDITION VARIABLE ===============================
|
// == CONDITION VARIABLE ===============================
|
||||||
|
|
||||||
typedef uintptr_t condvar_t;
|
typedef uintptr_t condvar_t;
|
||||||
|
|
@ -46,3 +53,5 @@ void condWakeAll(condvar_t cond);
|
||||||
|
|
||||||
void condWait(condvar_t cond, cmutex_t mtx);
|
void condWait(condvar_t cond, cmutex_t mtx);
|
||||||
void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds);
|
void condWaitTimed(condvar_t cond, cmutex_t mtx, int milliseconds);
|
||||||
|
|
||||||
|
#endif
|
||||||
104
colla/file.c
104
colla/file.c
|
|
@ -8,8 +8,7 @@
|
||||||
#if COLLA_WIN
|
#if COLLA_WIN
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <fileapi.h>
|
#include <windows.h>
|
||||||
#include <handleapi.h>
|
|
||||||
|
|
||||||
#undef FILE_BEGIN
|
#undef FILE_BEGIN
|
||||||
#undef FILE_CURRENT
|
#undef FILE_CURRENT
|
||||||
|
|
@ -19,6 +18,10 @@
|
||||||
#define FILE_CURRENT 1
|
#define FILE_CURRENT 1
|
||||||
#define FILE_END 2
|
#define FILE_END 2
|
||||||
|
|
||||||
|
#if COLLA_TCC
|
||||||
|
#include "tcc/colla_tcc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static DWORD file__mode_to_access(filemode_e mode) {
|
static DWORD file__mode_to_access(filemode_e mode) {
|
||||||
if (mode & FILE_APPEND) return FILE_APPEND_DATA;
|
if (mode & FILE_APPEND) return FILE_APPEND_DATA;
|
||||||
|
|
||||||
|
|
@ -38,22 +41,70 @@ bool fileExists(const char *name) {
|
||||||
return GetFileAttributesA(name) != INVALID_FILE_ATTRIBUTES;
|
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("\\\\?\\");
|
TCHAR long_path_prefix[] = TEXT("\\\\?\\");
|
||||||
const usize prefix_len = arrlen(long_path_prefix) - 1;
|
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);
|
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));
|
memcpy(full_path, long_path_prefix, prefix_len * sizeof(TCHAR));
|
||||||
|
|
||||||
GetFullPathName(rel_path, pathlen + 1, full_path + prefix_len, NULL);
|
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(
|
HANDLE handle = CreateFile(
|
||||||
full_path,
|
full_path,
|
||||||
file__mode_to_access(mode),
|
file__mode_to_access(mode),
|
||||||
0,
|
FILE_SHARE_READ,
|
||||||
NULL,
|
NULL,
|
||||||
file__mode_to_creation(mode),
|
file__mode_to_creation(mode),
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
|
@ -78,14 +129,14 @@ bool fileIsValid(file_t ctx) {
|
||||||
usize fileRead(file_t ctx, void *buf, usize len) {
|
usize fileRead(file_t ctx, void *buf, usize len) {
|
||||||
if (!fileIsValid(ctx)) return 0;
|
if (!fileIsValid(ctx)) return 0;
|
||||||
DWORD read = 0;
|
DWORD read = 0;
|
||||||
ReadFile((HANDLE)ctx.handle, buf, len, &read, NULL);
|
ReadFile((HANDLE)ctx.handle, buf, (DWORD)len, &read, NULL);
|
||||||
return (usize)read;
|
return (usize)read;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize fileWrite(file_t ctx, const void *buf, usize len) {
|
usize fileWrite(file_t ctx, const void *buf, usize len) {
|
||||||
if (!fileIsValid(ctx)) return 0;
|
if (!fileIsValid(ctx)) return 0;
|
||||||
DWORD written = 0;
|
DWORD written = 0;
|
||||||
WriteFile((HANDLE)ctx.handle, buf, len, &written, NULL);
|
WriteFile((HANDLE)ctx.handle, buf, (DWORD)len, &written, NULL);
|
||||||
return (usize)written;
|
return (usize)written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,7 +211,9 @@ void fileClose(file_t ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fileIsValid(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) {
|
usize fileRead(file_t ctx, void *buf, usize len) {
|
||||||
|
|
@ -180,7 +233,7 @@ bool fileSeekEnd(file_t ctx) {
|
||||||
|
|
||||||
void fileRewind(file_t ctx) {
|
void fileRewind(file_t ctx) {
|
||||||
if (!fileIsValid(ctx)) return;
|
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) {
|
usize fileTell(file_t ctx) {
|
||||||
|
|
@ -199,6 +252,7 @@ usize fileSize(file_t ctx) {
|
||||||
|
|
||||||
uint64 fileGetTimeFP(file_t ctx) {
|
uint64 fileGetTimeFP(file_t ctx) {
|
||||||
#if COLLA_LIN
|
#if COLLA_LIN
|
||||||
|
return 0;
|
||||||
#else
|
#else
|
||||||
fatal("fileGetTime not implemented yet outside of linux and windows");
|
fatal("fileGetTime not implemented yet outside of linux and windows");
|
||||||
return 0;
|
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;
|
return fileWrite(ctx, string.buf, string.len) == string.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_t fileReadWhole(arena_t *arena, arena_t scratch, strview_t name) {
|
buffer_t fileReadWhole(arena_t *arena, strview_t name) {
|
||||||
return fileReadWholeFP(arena, fileOpen(scratch, name, FILE_READ));
|
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) {
|
buffer_t fileReadWholeFP(arena_t *arena, file_t ctx) {
|
||||||
|
|
@ -249,8 +310,11 @@ buffer_t fileReadWholeFP(arena_t *arena, file_t ctx) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
str_t fileReadWholeStr(arena_t *arena, arena_t scratch, strview_t name) {
|
str_t fileReadWholeStr(arena_t *arena, strview_t name) {
|
||||||
return fileReadWholeStrFP(arena, fileOpen(scratch, name, FILE_READ));
|
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) {
|
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);
|
usize read = fileRead(ctx, out.buf, out.len);
|
||||||
|
|
||||||
if (read != 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);
|
arenaPop(arena, out.len + 1);
|
||||||
return (str_t){0};
|
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) {
|
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"
|
#include "warnings/colla_warn_end.h"
|
||||||
|
|
|
||||||
10
colla/file.h
10
colla/file.h
|
|
@ -17,6 +17,11 @@ typedef struct {
|
||||||
} file_t;
|
} file_t;
|
||||||
|
|
||||||
bool fileExists(const char *name);
|
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);
|
file_t fileOpen(arena_t scratch, strview_t name, filemode_e mode);
|
||||||
void fileClose(file_t ctx);
|
void fileClose(file_t ctx);
|
||||||
|
|
@ -37,13 +42,14 @@ void fileRewind(file_t ctx);
|
||||||
usize fileTell(file_t ctx);
|
usize fileTell(file_t ctx);
|
||||||
usize fileSize(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);
|
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);
|
str_t fileReadWholeStrFP(arena_t *arena, file_t ctx);
|
||||||
|
|
||||||
bool fileWriteWhole(arena_t scratch, strview_t name, const void *buf, usize len);
|
bool fileWriteWhole(arena_t scratch, strview_t name, const void *buf, usize len);
|
||||||
|
|
||||||
uint64 fileGetTime(arena_t scratch, strview_t name);
|
uint64 fileGetTime(arena_t scratch, strview_t name);
|
||||||
uint64 fileGetTimeFP(file_t ctx);
|
uint64 fileGetTimeFP(file_t ctx);
|
||||||
|
bool fileHasChanged(arena_t scratch, strview_t name, uint64 last_timestamp);
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
|
|
||||||
static char *fmt__stb_callback(const char *buf, void *ud, int len) {
|
static char *fmt__stb_callback(const char *buf, void *ud, int len) {
|
||||||
|
(void)len;
|
||||||
printf("%s", buf);
|
printf("%s", buf);
|
||||||
return (char *)ud;
|
return (char *)ud;
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +22,7 @@ int fmtPrint(const char *fmt, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int fmtPrintv(const char *fmt, va_list args) {
|
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);
|
return stb_vsprintfcb(fmt__stb_callback, buffer, buffer, fmt, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "collatypes.h"
|
||||||
|
|
||||||
typedef struct arena_t arena_t;
|
typedef struct arena_t arena_t;
|
||||||
|
|
||||||
int fmtPrint(const char *fmt, ...);
|
int fmtPrint(const char *fmt, ...);
|
||||||
|
|
|
||||||
200
colla/hot_reload.c
Normal file
200
colla/hot_reload.c
Normal file
|
|
@ -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 <windows.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
31
colla/hot_reload.h
Normal file
31
colla/hot_reload.h
Normal file
|
|
@ -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);
|
||||||
63
colla/http.c
63
colla/http.c
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
#include "warnings/colla_warn_beg.h"
|
#include "warnings/colla_warn_beg.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
|
#include "str.h"
|
||||||
#include "strstream.h"
|
#include "strstream.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
@ -14,8 +18,10 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#if !COLLA_TCC
|
||||||
#include <wininet.h>
|
#include <wininet.h>
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
static const TCHAR *https__get_method_str(http_method_e method);
|
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));
|
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);
|
usize methods_count = arrlen(methods);
|
||||||
|
|
||||||
for (usize i = 0; i < methods_count; ++i) {
|
for (usize i = 0; i < methods_count; ++i) {
|
||||||
|
|
@ -254,6 +260,49 @@ str_t httpMakeUrlSafe(arena_t *arena, strview_t string) {
|
||||||
return out;
|
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 httpSplitUrl(strview_t url) {
|
||||||
http_url_t out = {0};
|
http_url_t out = {0};
|
||||||
|
|
||||||
|
|
@ -326,7 +375,7 @@ http_res_t httpRequest(http_request_desc_t *request) {
|
||||||
assert(url.host.len < arrlen(hostname));
|
assert(url.host.len < arrlen(hostname));
|
||||||
memcpy(hostname, url.host.buf, url.host.len);
|
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)) {
|
if (!skConnect(sock, hostname, DEFAULT_HTTP_PORT)) {
|
||||||
err("Couldn't connect to host %s: %s", hostname, skGetErrorString());
|
err("Couldn't connect to host %s: %s", hostname, skGetErrorString());
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -338,7 +387,7 @@ http_res_t httpRequest(http_request_desc_t *request) {
|
||||||
goto error;
|
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());
|
err("couldn't send request to socket: %s", skGetErrorString());
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +397,7 @@ http_res_t httpRequest(http_request_desc_t *request) {
|
||||||
int read = 0;
|
int read = 0;
|
||||||
do {
|
do {
|
||||||
read = skReceive(sock, buffer, arrlen(buffer));
|
read = skReceive(sock, buffer, arrlen(buffer));
|
||||||
if (read == -1) {
|
if (read == SOCKET_ERROR) {
|
||||||
err("couldn't get the data from the server: %s", skGetErrorString());
|
err("couldn't get the data from the server: %s", skGetErrorString());
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
@ -374,7 +423,7 @@ error:
|
||||||
#if COLLA_WIN
|
#if COLLA_WIN
|
||||||
|
|
||||||
buffer_t httpsRequest(http_request_desc_t *req) {
|
buffer_t httpsRequest(http_request_desc_t *req) {
|
||||||
HINTERNET internet = InternetOpen(
|
HINTERNET internet = InternetOpenA(
|
||||||
TEXT("COLLA"),
|
TEXT("COLLA"),
|
||||||
INTERNET_OPEN_TYPE_PRECONFIG,
|
INTERNET_OPEN_TYPE_PRECONFIG,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
@ -446,7 +495,7 @@ buffer_t httpsRequest(http_request_desc_t *req) {
|
||||||
HttpAddRequestHeadersA(
|
HttpAddRequestHeadersA(
|
||||||
request,
|
request,
|
||||||
header_str.buf,
|
header_str.buf,
|
||||||
header_str.len,
|
(DWORD)header_str.len,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -456,7 +505,7 @@ buffer_t httpsRequest(http_request_desc_t *req) {
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
(void *)req->body.buf,
|
(void *)req->body.buf,
|
||||||
req->body.len
|
(DWORD)req->body.len
|
||||||
);
|
);
|
||||||
if (!request_sent) {
|
if (!request_sent) {
|
||||||
fatal("call to HttpSendRequest failed: %u", GetLastError());
|
fatal("call to HttpSendRequest failed: %u", GetLastError());
|
||||||
|
|
|
||||||
|
|
@ -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);
|
strview_t httpGetHeader(http_header_t *headers, strview_t key);
|
||||||
|
|
||||||
str_t httpMakeUrlSafe(arena_t *arena, strview_t string);
|
str_t httpMakeUrlSafe(arena_t *arena, strview_t string);
|
||||||
|
str_t httpDecodeUrlSafe(arena_t *arena, strview_t string);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
strview_t host;
|
strview_t host;
|
||||||
|
|
|
||||||
14
colla/ini.c
14
colla/ini.c
|
|
@ -29,6 +29,10 @@ ini_t iniParseStr(arena_t *arena, strview_t str, const iniopts_t *options) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool iniIsValid(ini_t *ctx) {
|
||||||
|
return ctx && !strvIsEmpty(ctx->text);
|
||||||
|
}
|
||||||
|
|
||||||
initable_t *iniGetTable(ini_t *ctx, strview_t name) {
|
initable_t *iniGetTable(ini_t *ctx, strview_t name) {
|
||||||
initable_t *t = ctx ? ctx->tables : NULL;
|
initable_t *t = ctx ? ctx->tables : NULL;
|
||||||
while (t) {
|
while (t) {
|
||||||
|
|
@ -161,10 +165,11 @@ 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));
|
strview_t key = strvTrim(istrGetView(in, opts->key_value_divider));
|
||||||
istrSkip(in, 1);
|
istrSkip(in, 1);
|
||||||
strview_t value = strvTrim(istrGetView(in, '\n'));
|
strview_t value = strvTrim(istrGetViewEither(in, strv("\n#;")));
|
||||||
istrSkip(in, 1);
|
istrSkip(in, 1);
|
||||||
inivalue_t *newval = NULL;
|
inivalue_t *newval = NULL;
|
||||||
|
|
||||||
|
|
||||||
if (opts->merge_duplicate_keys) {
|
if (opts->merge_duplicate_keys) {
|
||||||
newval = table->values;
|
newval = table->values;
|
||||||
while (newval) {
|
while (newval) {
|
||||||
|
|
@ -213,6 +218,8 @@ static void ini__add_table(arena_t *arena, ini_t *ctx, instream_t *in, iniopts_t
|
||||||
if (!table) {
|
if (!table) {
|
||||||
table = alloc(arena, initable_t);
|
table = alloc(arena, initable_t);
|
||||||
|
|
||||||
|
table->name = name;
|
||||||
|
|
||||||
if (!ctx->tables) {
|
if (!ctx->tables) {
|
||||||
ctx->tables = table;
|
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) {
|
static void ini__parse(arena_t *arena, ini_t *ini, const iniopts_t *options) {
|
||||||
iniopts_t opts = ini__get_options(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);
|
instream_t in = istrInitLen(ini->text.buf, ini->text.len);
|
||||||
|
|
||||||
while (!istrIsFinished(in)) {
|
while (!istrIsFinished(in)) {
|
||||||
|
|
|
||||||
|
|
@ -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 iniParseFile(arena_t *arena, file_t file, const iniopts_t *options);
|
||||||
ini_t iniParseStr(arena_t *arena, strview_t str, 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__")
|
#define INI_ROOT strv("__ROOT__")
|
||||||
|
|
||||||
initable_t *iniGetTable(ini_t *ctx, strview_t name);
|
initable_t *iniGetTable(ini_t *ctx, strview_t name);
|
||||||
|
|
|
||||||
283
colla/json.c
283
colla/json.c
|
|
@ -5,40 +5,27 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "strstream.h"
|
#include "strstream.h"
|
||||||
|
#include "file.h"
|
||||||
#include "tracelog.h"
|
#include "tracelog.h"
|
||||||
|
|
||||||
#define json__ensure(c) \
|
// #define json__logv() warn("%s:%d", __FILE__, __LINE__)
|
||||||
if (istrGet(in) != (c)) { \
|
#define json__logv()
|
||||||
istrRewindN(in, 1); \
|
#define json__ensure(c) json__check_char(in, c)
|
||||||
fatal("wrong character at %zu, should be " #c " but is %c", istrTell(*in), istrPeek(in));\
|
|
||||||
|
static bool json__check_char(instream_t *in, char c) {
|
||||||
|
if (istrGet(in) == c) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
str_t json__read_whole(arena_t *arena, const char *filename) {
|
static bool json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out);
|
||||||
str_t out = {0};
|
static bool json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out);
|
||||||
FILE *fp = fopen(filename, "rb");
|
|
||||||
if (!fp) {
|
|
||||||
err("could not open %s", filename);
|
|
||||||
return (str_t){0};
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
static bool json__is_value_finished(instream_t *in) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
bool json__is_value_finished(instream_t *in) {
|
|
||||||
usize old_pos = istrTell(*in);
|
usize old_pos = istrTell(*in);
|
||||||
|
|
||||||
istrSkipWhitespace(in);
|
istrSkipWhitespace(in);
|
||||||
|
|
@ -53,30 +40,46 @@ bool json__is_value_finished(instream_t *in) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void json__parse_null(instream_t *in) {
|
static bool json__parse_null(instream_t *in) {
|
||||||
strview_t null_view = istrGetViewLen(in, 4);
|
strview_t null_view = istrGetViewLen(in, 4);
|
||||||
|
bool is_valid = true;
|
||||||
|
|
||||||
if (!strvEquals(null_view, strv("null"))) {
|
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)) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonval_t *json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags) {
|
if (!is_valid) json__logv();
|
||||||
json__ensure('[');
|
|
||||||
|
return is_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
istrSkipWhitespace(in);
|
||||||
|
|
||||||
// if it is an empty array
|
// if it is an empty array
|
||||||
if (istrPeek(in) == ']') {
|
if (istrPeek(in) == ']') {
|
||||||
istrSkip(in, 1);
|
istrSkip(in, 1);
|
||||||
return NULL;
|
goto success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json__parse_value(arena, in, flags, &head)) {
|
||||||
|
json__logv();
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonval_t *head = json__parse_value(arena, in, flags);
|
|
||||||
jsonval_t *cur = head;
|
jsonval_t *cur = head;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
@ -88,11 +91,22 @@ jsonval_t *json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags)
|
||||||
{
|
{
|
||||||
istrSkipWhitespace(in);
|
istrSkipWhitespace(in);
|
||||||
// trailing comma
|
// trailing comma
|
||||||
if (!(flags & JSON_NO_TRAILING_COMMAS) && istrPeek(in) == ']') {
|
if (istrPeek(in) == ']') {
|
||||||
return head;
|
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;
|
cur->next = next;
|
||||||
next->prev = cur;
|
next->prev = cur;
|
||||||
cur = next;
|
cur = next;
|
||||||
|
|
@ -100,17 +114,27 @@ jsonval_t *json__parse_array(arena_t *arena, instream_t *in, jsonflags_e flags)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
istrRewindN(in, 1);
|
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) {
|
static bool json__parse_string(arena_t *arena, instream_t *in, str_t *out) {
|
||||||
istrSkipWhitespace(in);
|
istrSkipWhitespace(in);
|
||||||
|
|
||||||
json__ensure('"');
|
if (!json__ensure('"')) {
|
||||||
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
const char *from = in->cur;
|
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;
|
usize len = in->cur - from;
|
||||||
|
|
||||||
str_t out = str(arena, from, len);
|
*out = str(arena, from, len);
|
||||||
|
|
||||||
json__ensure('"');
|
if (!json__ensure('"')) {
|
||||||
|
json__logv();
|
||||||
return out;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
double json__parse_number(instream_t *in, jsonflags_e flags) {
|
success:
|
||||||
double value = 0.0;
|
return true;
|
||||||
istrGetDouble(in, &value);
|
fail:
|
||||||
return value;
|
*out = (str_t){0};
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool json__parse_bool(instream_t *in, jsonflags_e flags) {
|
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);
|
size_t remaining = istrRemaining(*in);
|
||||||
if (remaining >= 4 && memcmp(in->cur, "true", 4) == 0) {
|
if (remaining >= 4 && memcmp(in->cur, "true", 4) == 0) {
|
||||||
istrSkip(in, 4);
|
istrSkip(in, 4);
|
||||||
return true;
|
*out = true;
|
||||||
}
|
}
|
||||||
if (remaining >= 5 && memcmp(in->cur, "false", 5) == 0) {
|
else if (remaining >= 5 && memcmp(in->cur, "false", 5) == 0) {
|
||||||
istrSkip(in, 5);
|
istrSkip(in, 5);
|
||||||
return false;
|
*out = false;
|
||||||
}
|
}
|
||||||
fatal("unknown boolean at %zu: %.10s", istrTell(*in), in->cur);
|
else {
|
||||||
|
err("unknown boolean at %zu: %.10s", istrTell(*in), in->cur);
|
||||||
|
json__logv();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonval_t *json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags) {
|
return true;
|
||||||
json__ensure('{');
|
}
|
||||||
|
|
||||||
|
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);
|
istrSkipWhitespace(in);
|
||||||
|
|
||||||
// if it is an empty object
|
// if it is an empty object
|
||||||
if (istrPeek(in) == '}') {
|
if (istrPeek(in) == '}') {
|
||||||
istrSkip(in, 1);
|
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;
|
jsonval_t *cur = head;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
istrSkipWhitespace(in);
|
istrSkipWhitespace(in);
|
||||||
switch (istrGet(in)) {
|
switch (istrGet(in)) {
|
||||||
case '}':
|
case '}':
|
||||||
return head;
|
goto success;
|
||||||
case ',':
|
case ',':
|
||||||
{
|
{
|
||||||
istrSkipWhitespace(in);
|
istrSkipWhitespace(in);
|
||||||
// trailing commas
|
// trailing commas
|
||||||
if (!(flags & JSON_NO_TRAILING_COMMAS) && istrPeek(in) == '}') {
|
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;
|
cur->next = next;
|
||||||
next->prev = cur;
|
next->prev = cur;
|
||||||
cur = next;
|
cur = next;
|
||||||
|
|
@ -184,83 +230,136 @@ jsonval_t *json__parse_obj(arena_t *arena, instream_t *in, jsonflags_e flags) {
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
istrRewindN(in, 1);
|
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) {
|
static bool json__parse_pair(arena_t *arena, instream_t *in, jsonflags_e flags, jsonval_t **out) {
|
||||||
str_t key = json__parse_string(arena, in, flags);
|
str_t key = {0};
|
||||||
|
if (!json__parse_string(arena, in, &key)) {
|
||||||
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
// skip preamble
|
// skip preamble
|
||||||
istrSkipWhitespace(in);
|
istrSkipWhitespace(in);
|
||||||
json__ensure(':');
|
if (!json__ensure(':')) {
|
||||||
|
json__logv();
|
||||||
jsonval_t *out = json__parse_value(arena, in, flags);
|
goto fail;
|
||||||
out->key = key;
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonval_t *json__parse_value(arena_t *arena, instream_t *in, jsonflags_e flags) {
|
if (!json__parse_value(arena, in, flags, out)) {
|
||||||
jsonval_t *out = alloc(arena, jsonval_t);
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*out)->key = key;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
*out = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
istrSkipWhitespace(in);
|
||||||
|
|
||||||
switch (istrPeek(in)) {
|
switch (istrPeek(in)) {
|
||||||
// object
|
// object
|
||||||
case '{':
|
case '{':
|
||||||
out->object = json__parse_obj(arena, in, flags);
|
if (!json__parse_obj(arena, in, flags, &val->object)) {
|
||||||
out->type = JSON_OBJECT;
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
val->type = JSON_OBJECT;
|
||||||
break;
|
break;
|
||||||
// array
|
// array
|
||||||
case '[':
|
case '[':
|
||||||
out->array = json__parse_array(arena, in, flags);
|
if (!json__parse_array(arena, in, flags, &val->array)) {
|
||||||
out->type = JSON_ARRAY;
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
val->type = JSON_ARRAY;
|
||||||
break;
|
break;
|
||||||
// string
|
// string
|
||||||
case '"':
|
case '"':
|
||||||
out->string = json__parse_string(arena, in, flags);
|
if (!json__parse_string(arena, in, &val->string)) {
|
||||||
out->type = JSON_STRING;
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
val->type = JSON_STRING;
|
||||||
break;
|
break;
|
||||||
// boolean
|
// boolean
|
||||||
case 't': // fallthrough
|
case 't': // fallthrough
|
||||||
case 'f':
|
case 'f':
|
||||||
out->boolean = json__parse_bool(in, flags);
|
if (!json__parse_bool(in, &val->boolean)) {
|
||||||
out->type = JSON_BOOL;
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
val->type = JSON_BOOL;
|
||||||
break;
|
break;
|
||||||
// null
|
// null
|
||||||
case 'n':
|
case 'n':
|
||||||
json__parse_null(in);
|
if (!json__parse_null(in)) {
|
||||||
out->type = JSON_NULL;
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
val->type = JSON_NULL;
|
||||||
break;
|
break;
|
||||||
// comment
|
// comment
|
||||||
case '/':
|
case '/':
|
||||||
fatal("TODO comments");
|
err("TODO comments");
|
||||||
break;
|
break;
|
||||||
// number
|
// number
|
||||||
default:
|
default:
|
||||||
out->number = json__parse_number(in, flags);
|
if (!json__parse_number(in, &val->number)) {
|
||||||
out->type = JSON_NUMBER;
|
json__logv();
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
val->type = JSON_NUMBER;
|
||||||
break;
|
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) {
|
json_t jsonParse(arena_t *arena, arena_t scratch, strview_t filename, jsonflags_e flags) {
|
||||||
str_t data = json__read_whole(&scratch, filename);
|
str_t data = fileReadWholeStr(&scratch, filename);
|
||||||
|
return NULL;
|
||||||
json_t json = jsonParseStr(arena, strv(data), flags);
|
json_t json = jsonParseStr(arena, strv(data), flags);
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
json_t jsonParseStr(arena_t *arena, strview_t jsonstr, jsonflags_e flags) {
|
json_t jsonParseStr(arena_t *arena, strview_t jsonstr, jsonflags_e flags) {
|
||||||
|
arena_t before = *arena;
|
||||||
|
|
||||||
jsonval_t *root = alloc(arena, jsonval_t);
|
jsonval_t *root = alloc(arena, jsonval_t);
|
||||||
root->type = JSON_OBJECT;
|
root->type = JSON_OBJECT;
|
||||||
|
|
||||||
instream_t in = istrInitLen(jsonstr.buf, jsonstr.len);
|
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;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,10 @@ typedef struct jsonval_t {
|
||||||
|
|
||||||
typedef jsonval_t *json_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);
|
json_t jsonParseStr(arena_t *arena, strview_t jsonstr, jsonflags_e flags);
|
||||||
|
|
||||||
jsonval_t *jsonGet(jsonval_t *node, strview_t key);
|
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_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)
|
||||||
|
|
|
||||||
100
colla/server.c
100
colla/server.c
|
|
@ -7,6 +7,39 @@
|
||||||
#include "strstream.h"
|
#include "strstream.h"
|
||||||
#include "arena.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
|
#define SERVER_BUFSZ 4096
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
@ -15,6 +48,7 @@ typedef enum {
|
||||||
PARSE_REQ_VERSION,
|
PARSE_REQ_VERSION,
|
||||||
PARSE_REQ_FIELDS,
|
PARSE_REQ_FIELDS,
|
||||||
PARSE_REQ_FINISHED,
|
PARSE_REQ_FINISHED,
|
||||||
|
PARSE_REQ_FAILED,
|
||||||
} server__req_status_e;
|
} server__req_status_e;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -36,6 +70,7 @@ typedef struct server_t {
|
||||||
socket_t socket;
|
socket_t socket;
|
||||||
server__route_t *routes;
|
server__route_t *routes;
|
||||||
server__route_t *routes_default;
|
server__route_t *routes_default;
|
||||||
|
socket_t current_client;
|
||||||
bool should_stop;
|
bool should_stop;
|
||||||
} server_t;
|
} 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;
|
ctx->req.method = HTTP_POST;
|
||||||
}
|
}
|
||||||
else {
|
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;
|
ctx->status = PARSE_REQ_PAGE;
|
||||||
}
|
}
|
||||||
// fallthrough
|
// 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);
|
ctx->req.page = str(arena, page);
|
||||||
|
|
||||||
info("parsed page: %.*s", page.len, page.buf);
|
|
||||||
|
|
||||||
ctx->status = PARSE_REQ_VERSION;
|
ctx->status = PARSE_REQ_VERSION;
|
||||||
}
|
}
|
||||||
// fallthrough
|
// fallthrough
|
||||||
|
|
@ -104,11 +137,15 @@ bool server__parse_chunk(arena_t *arena, server__req_ctx_t *ctx, char buffer[SER
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version.len < 8) {
|
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"))) {
|
if (!strvEquals(strvSub(version, 0, 4), strv("HTTP"))) {
|
||||||
fatal("version does not start with HTTP: (%.4s)", version.buf);
|
err("version does not start with HTTP: (%.4s)", version.buf);
|
||||||
|
ctx->status = PARSE_REQ_FAILED;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip HTTP
|
// 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);
|
int scanned = sscanf(version.buf, "/%hhu.%hhu", &major, &minor);
|
||||||
|
|
||||||
if (scanned != 2) {
|
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;
|
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, ':');
|
strview_t key = istrGetView(&field_in, ':');
|
||||||
if (istrGet(&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);
|
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);
|
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) {
|
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) {
|
void serverStart(arena_t scratch, server_t *server) {
|
||||||
usize start = arenaTell(&scratch);
|
usize start = arenaTell(&scratch);
|
||||||
|
|
||||||
|
info("Server started!");
|
||||||
|
|
||||||
while (!server->should_stop) {
|
while (!server->should_stop) {
|
||||||
socket_t client = skAccept(server->socket);
|
socket_t client = skAccept(server->socket);
|
||||||
if (!skIsValid(client)) {
|
if (!skIsValid(client)) {
|
||||||
|
|
@ -313,8 +356,6 @@ void serverStart(arena_t scratch, server_t *server) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
info("received connection: %zu", client);
|
|
||||||
|
|
||||||
arenaRewind(&scratch, start);
|
arenaRewind(&scratch, start);
|
||||||
|
|
||||||
server__req_ctx_t req_ctx = {0};
|
server__req_ctx_t req_ctx = {0};
|
||||||
|
|
@ -323,7 +364,7 @@ void serverStart(arena_t scratch, server_t *server) {
|
||||||
int read = 0;
|
int read = 0;
|
||||||
do {
|
do {
|
||||||
read = skReceive(client, buffer, sizeof(buffer));
|
read = skReceive(client, buffer, sizeof(buffer));
|
||||||
if(read == -1) {
|
if(read == SOCKET_ERROR) {
|
||||||
fatal("couldn't get the data from the server: %s", skGetErrorString());
|
fatal("couldn't get the data from the server: %s", skGetErrorString());
|
||||||
}
|
}
|
||||||
if (server__parse_chunk(&scratch, &req_ctx, buffer, read)) {
|
if (server__parse_chunk(&scratch, &req_ctx, buffer, read)) {
|
||||||
|
|
@ -331,7 +372,10 @@ void serverStart(arena_t scratch, server_t *server) {
|
||||||
}
|
}
|
||||||
} while(read != 0);
|
} 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;
|
server_req_t req = req_ctx.req;
|
||||||
|
|
||||||
|
|
@ -349,9 +393,16 @@ void serverStart(arena_t scratch, server_t *server) {
|
||||||
route = server->routes_default;
|
route = server->routes_default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server->current_client = client;
|
||||||
str_t response = route->fn(scratch, server, &req, route->userdata);
|
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);
|
skClose(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,14 +420,23 @@ str_t serverMakeResponse(arena_t *arena, int status_code, strview_t content_type
|
||||||
arena,
|
arena,
|
||||||
|
|
||||||
"HTTP/1.1 %d %s\r\n"
|
"HTTP/1.1 %d %s\r\n"
|
||||||
"Content-Type: %.*s\r\n"
|
"Content-Type: %v\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"%.*s",
|
"%v",
|
||||||
|
|
||||||
status_code, httpGetStatusString(status_code),
|
status_code, httpGetStatusString(status_code),
|
||||||
content_type.len, content_type.buf,
|
content_type,
|
||||||
body.len, body.buf
|
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
|
#undef SERVER_BUFSZ
|
||||||
|
|
@ -26,7 +26,6 @@ typedef struct {
|
||||||
|
|
||||||
typedef str_t (*server_route_f)(arena_t scratch, server_t *server, server_req_t *req, void *userdata);
|
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);
|
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 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 serverRouteDefault(arena_t *arena, server_t *server, server_route_f cb, void *userdata);
|
||||||
|
|
@ -34,3 +33,5 @@ void serverStart(arena_t scratch, server_t *server);
|
||||||
void serverStop(server_t *server);
|
void serverStop(server_t *server);
|
||||||
|
|
||||||
str_t serverMakeResponse(arena_t *arena, int status_code, strview_t content_type, strview_t body);
|
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);
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if COLLA_WIN
|
#if COLLA_WIN
|
||||||
#include <winsock2.h>
|
|
||||||
|
typedef int socklen_t;
|
||||||
|
|
||||||
bool skInit(void) {
|
bool skInit(void) {
|
||||||
WSADATA w;
|
WSADATA w;
|
||||||
|
|
@ -18,7 +19,7 @@ bool skCleanup(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skClose(socket_t sock) {
|
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) {
|
int skPoll(skpoll_t *to_poll, int num_to_poll, int timeout) {
|
||||||
|
|
@ -31,7 +32,6 @@ int skGetError(void) {
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -48,7 +48,7 @@ bool skCleanup(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skClose(socket_t sock) {
|
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) {
|
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) {
|
bool skIsValid(socket_t sock) {
|
||||||
return sock != (socket_t)-1;
|
return sock != SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
skaddrin_t skAddrinInit(const char *ip, uint16_t port) {
|
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) {
|
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) {
|
bool skListen(socket_t sock) {
|
||||||
|
|
@ -116,7 +116,7 @@ bool skListen(socket_t sock) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skListenPro(socket_t sock, int backlog) {
|
bool skListenPro(socket_t sock, int backlog) {
|
||||||
return listen(sock, backlog) != -1;
|
return listen(sock, backlog) != SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t skAccept(socket_t sock) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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
|
// put this at the end of file to not make everything else unreadable
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@
|
||||||
|
|
||||||
#include "collatypes.h"
|
#include "collatypes.h"
|
||||||
|
|
||||||
#if COLLA_WIN
|
#if COLLA_TCC
|
||||||
|
#include "tcc/colla_tcc.h"
|
||||||
|
#elif COLLA_WIN
|
||||||
|
#define _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#else
|
#elif COLLA_LIN || COLLA_OSX
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef uintptr_t socket_t;
|
typedef uintptr_t socket_t;
|
||||||
|
|
@ -13,6 +17,8 @@ typedef struct sockaddr skaddr_t;
|
||||||
typedef struct sockaddr_in skaddrin_t;
|
typedef struct sockaddr_in skaddrin_t;
|
||||||
typedef struct pollfd skpoll_t;
|
typedef struct pollfd skpoll_t;
|
||||||
|
|
||||||
|
#define SOCKET_ERROR (-1)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SOCK_TCP,
|
SOCK_TCP,
|
||||||
SOCK_UDP,
|
SOCK_UDP,
|
||||||
|
|
|
||||||
28
colla/str.c
28
colla/str.c
|
|
@ -7,7 +7,18 @@
|
||||||
#include "tracelog.h"
|
#include "tracelog.h"
|
||||||
|
|
||||||
#if COLLA_WIN
|
#if COLLA_WIN
|
||||||
#include <stringapiset.h>
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if COLLA_TCC
|
||||||
|
#include "tcc/colla_tcc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// == STR_T ========================================================
|
// == STR_T ========================================================
|
||||||
|
|
@ -44,6 +55,7 @@ str_t strFmt(arena_t *arena, const char *fmt, ...) {
|
||||||
str_t strFmtv(arena_t *arena, const char *fmt, va_list args) {
|
str_t strFmtv(arena_t *arena, const char *fmt, va_list args) {
|
||||||
va_list vcopy;
|
va_list vcopy;
|
||||||
va_copy(vcopy, args);
|
va_copy(vcopy, args);
|
||||||
|
// stb_vsnprintf returns the length + null_term
|
||||||
int len = fmtBufferv(NULL, 0, fmt, vcopy);
|
int len = fmtBufferv(NULL, 0, fmt, vcopy);
|
||||||
va_end(vcopy);
|
va_end(vcopy);
|
||||||
|
|
||||||
|
|
@ -87,6 +99,8 @@ str_t strFromWChar(arena_t *arena, const wchar_t *src, usize srclen) {
|
||||||
NULL, NULL
|
NULL, NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
out.len = outlen;
|
||||||
|
|
||||||
#elif COLLA_LIN
|
#elif COLLA_LIN
|
||||||
fatal("strFromWChar not implemented yet!");
|
fatal("strFromWChar not implemented yet!");
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -101,7 +115,7 @@ bool strEquals(str_t a, str_t b) {
|
||||||
int strCompare(str_t a, str_t b) {
|
int strCompare(str_t a, str_t b) {
|
||||||
return a.len == b.len ?
|
return a.len == b.len ?
|
||||||
memcmp(a.buf, b.buf, a.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) {
|
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) {
|
int strvCompare(strview_t a, strview_t b) {
|
||||||
return a.len == b.len ?
|
return a.len == b.len ?
|
||||||
memcmp(a.buf, b.buf, a.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) {
|
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;
|
strview_t out = ctx;
|
||||||
for (usize i = 0; i < ctx.len; ++i) {
|
for (usize i = 0; i < ctx.len; ++i) {
|
||||||
char c = ctx.buf[i];
|
char c = ctx.buf[i];
|
||||||
if (c != ' ' || c < '\t' || c > '\r') {
|
if (c != ' ' && (c < '\t' || c > '\r')) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
out.buf++;
|
out.buf++;
|
||||||
|
|
@ -286,7 +300,7 @@ strview_t strvTrimRight(strview_t ctx) {
|
||||||
strview_t out = ctx;
|
strview_t out = ctx;
|
||||||
for (isize i = ctx.len - 1; i >= 0; --i) {
|
for (isize i = ctx.len - 1; i >= 0; --i) {
|
||||||
char c = ctx.buf[i];
|
char c = ctx.buf[i];
|
||||||
if (c != ' ' || c < '\t' || c > '\r') {
|
if (c != ' ' && (c < '\t' || c > '\r')) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
out.len--;
|
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) {
|
usize strvFindView(strview_t ctx, strview_t view, usize from) {
|
||||||
if (ctx.len < view.len) return STR_NONE;
|
if (ctx.len < view.len) return STR_NONE;
|
||||||
usize end = ctx.len - view.len;
|
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) {
|
if (memcmp(ctx.buf + i, view.buf, view.len) == 0) {
|
||||||
return i;
|
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) {
|
usize strvRFindView(strview_t ctx, strview_t view, usize from_right) {
|
||||||
if (from_right > ctx.len) from_right = ctx.len;
|
if (from_right > ctx.len) from_right = ctx.len;
|
||||||
isize end = (isize)(ctx.len - from_right);
|
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) {
|
for (isize i = end - view.len; i >= 0; --i) {
|
||||||
if (memcmp(ctx.buf + i, view.buf, view.len) == 0) {
|
if (memcmp(ctx.buf + i, view.buf, view.len) == 0) {
|
||||||
return (usize)i;
|
return (usize)i;
|
||||||
|
|
|
||||||
|
|
@ -33,19 +33,13 @@ instream_t istrInitLen(const char *str, usize len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char istrGet(instream_t *ctx) {
|
char istrGet(instream_t *ctx) {
|
||||||
return *ctx->cur++;
|
return ctx && ctx->cur ? *ctx->cur++ : '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void istrIgnore(instream_t *ctx, char delim) {
|
void istrIgnore(instream_t *ctx, char delim) {
|
||||||
for (; !istrIsFinished(*ctx) && *ctx->cur != delim; ++ctx->cur) {
|
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) {
|
void istrIgnoreAndSkip(instream_t *ctx, char delim) {
|
||||||
|
|
@ -54,30 +48,33 @@ void istrIgnoreAndSkip(instream_t *ctx, char delim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char istrPeek(instream_t *ctx) {
|
char istrPeek(instream_t *ctx) {
|
||||||
return *ctx->cur;
|
return ctx && ctx->cur ? *ctx->cur : '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
char istrPeekNext(instream_t *ctx) {
|
char istrPeekNext(instream_t *ctx) {
|
||||||
|
if (!ctx || !ctx->cur) return '\0';
|
||||||
usize offset = (ctx->cur - ctx->start) + 1;
|
usize offset = (ctx->cur - ctx->start) + 1;
|
||||||
return offset > ctx->size ? '\0' : *(ctx->cur + 1);
|
return offset > ctx->size ? '\0' : *(ctx->cur + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void istrSkip(instream_t *ctx, usize n) {
|
void istrSkip(instream_t *ctx, usize n) {
|
||||||
|
if (!ctx || !ctx->cur) return;
|
||||||
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
||||||
if(n > remaining) {
|
if(n > remaining) {
|
||||||
warn("skipping more then remaining: %zu -> %zu", n, remaining);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx->cur += n;
|
ctx->cur += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void istrSkipWhitespace(instream_t *ctx) {
|
void istrSkipWhitespace(instream_t *ctx) {
|
||||||
|
if (!ctx || !ctx->cur) return;
|
||||||
while (*ctx->cur && isspace(*ctx->cur)) {
|
while (*ctx->cur && isspace(*ctx->cur)) {
|
||||||
++ctx->cur;
|
++ctx->cur;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void istrRead(instream_t *ctx, char *buf, usize len) {
|
void istrRead(instream_t *ctx, char *buf, usize len) {
|
||||||
|
if (!ctx || !ctx->cur) return;
|
||||||
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
||||||
if(len > remaining) {
|
if(len > remaining) {
|
||||||
warn("istrRead: trying to read len %zu from remaining %zu", 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) {
|
usize istrReadMax(instream_t *ctx, char *buf, usize len) {
|
||||||
|
if (!ctx || !ctx->cur) return 0;
|
||||||
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
||||||
len = remaining < len ? remaining : len;
|
len = remaining < len ? remaining : len;
|
||||||
memcpy(buf, ctx->cur, len);
|
memcpy(buf, ctx->cur, len);
|
||||||
|
|
@ -100,24 +98,26 @@ void istrRewind(instream_t *ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void istrRewindN(instream_t *ctx, usize amount) {
|
void istrRewindN(instream_t *ctx, usize amount) {
|
||||||
|
if (!ctx || !ctx->cur) return;
|
||||||
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
||||||
if (amount > remaining) amount = remaining;
|
if (amount > remaining) amount = remaining;
|
||||||
ctx->cur -= amount;
|
ctx->cur -= amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize istrTell(instream_t ctx) {
|
usize istrTell(instream_t ctx) {
|
||||||
return ctx.cur - ctx.start;
|
return ctx.cur ? ctx.cur - ctx.start : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize istrRemaining(instream_t ctx) {
|
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) {
|
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) {
|
bool istrGetBool(instream_t *ctx, bool *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
||||||
if(strncmp(ctx->cur, "true", remaining) == 0) {
|
if(strncmp(ctx->cur, "true", remaining) == 0) {
|
||||||
*val = true;
|
*val = true;
|
||||||
|
|
@ -131,6 +131,7 @@ bool istrGetBool(instream_t *ctx, bool *val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool istrGetU8(instream_t *ctx, uint8 *val) {
|
bool istrGetU8(instream_t *ctx, uint8 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = (uint8) strtoul(ctx->cur, &end, 0);
|
*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) {
|
bool istrGetU16(instream_t *ctx, uint16 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = (uint16) strtoul(ctx->cur, &end, 0);
|
*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) {
|
bool istrGetU32(instream_t *ctx, uint32 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = (uint32) strtoul(ctx->cur, &end, 0);
|
*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) {
|
bool istrGetU64(instream_t *ctx, uint64 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = strtoull(ctx->cur, &end, 0);
|
*val = strtoull(ctx->cur, &end, 0);
|
||||||
|
|
||||||
|
|
@ -199,6 +203,7 @@ bool istrGetU64(instream_t *ctx, uint64 *val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool istrGetI8(instream_t *ctx, int8 *val) {
|
bool istrGetI8(instream_t *ctx, int8 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = (int8) strtol(ctx->cur, &end, 0);
|
*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) {
|
bool istrGetI16(instream_t *ctx, int16 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = (int16) strtol(ctx->cur, &end, 0);
|
*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) {
|
bool istrGetI32(instream_t *ctx, int32 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = (int32) strtol(ctx->cur, &end, 0);
|
*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) {
|
bool istrGetI64(instream_t *ctx, int64 *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = strtoll(ctx->cur, &end, 0);
|
*val = strtoll(ctx->cur, &end, 0);
|
||||||
|
|
||||||
|
|
@ -267,6 +275,7 @@ bool istrGetI64(instream_t *ctx, int64 *val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool istrGetFloat(instream_t *ctx, float *val) {
|
bool istrGetFloat(instream_t *ctx, float *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = strtof(ctx->cur, &end);
|
*val = strtof(ctx->cur, &end);
|
||||||
|
|
||||||
|
|
@ -284,6 +293,7 @@ bool istrGetFloat(instream_t *ctx, float *val) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool istrGetDouble(instream_t *ctx, double *val) {
|
bool istrGetDouble(instream_t *ctx, double *val) {
|
||||||
|
if (!ctx || !ctx->cur) return false;
|
||||||
char *end = NULL;
|
char *end = NULL;
|
||||||
*val = strtod(ctx->cur, &end);
|
*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) {
|
str_t istrGetStr(arena_t *arena, instream_t *ctx, char delim) {
|
||||||
|
if (!ctx || !ctx->cur) return (str_t){0};
|
||||||
const char *from = ctx->cur;
|
const char *from = ctx->cur;
|
||||||
istrIgnore(ctx, delim);
|
istrIgnore(ctx, delim);
|
||||||
// if it didn't actually find it, it just reached the end of the string
|
// 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) {
|
usize istrGetBuf(instream_t *ctx, char *buf, usize buflen) {
|
||||||
|
if (!ctx || !ctx->cur) return 0;
|
||||||
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
usize remaining = ctx->size - (ctx->cur - ctx->start);
|
||||||
buflen -= 1;
|
buflen -= 1;
|
||||||
buflen = remaining < buflen ? remaining : buflen;
|
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) {
|
strview_t istrGetView(instream_t *ctx, char delim) {
|
||||||
|
if (!ctx || !ctx->cur) return (strview_t){0};
|
||||||
const char *from = ctx->cur;
|
const char *from = ctx->cur;
|
||||||
istrIgnore(ctx, delim);
|
istrIgnore(ctx, delim);
|
||||||
usize len = ctx->cur - from;
|
usize len = ctx->cur - from;
|
||||||
return strvInitLen(from, len);
|
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) {
|
strview_t istrGetViewLen(instream_t *ctx, usize len) {
|
||||||
|
if (!ctx || !ctx->cur) return (strview_t){0};
|
||||||
const char *from = ctx->cur;
|
const char *from = ctx->cur;
|
||||||
istrSkip(ctx, len);
|
istrSkip(ctx, len);
|
||||||
usize buflen = ctx->cur - from;
|
usize buflen = ctx->cur - from;
|
||||||
|
|
@ -343,7 +367,8 @@ strview_t istrGetViewLen(instream_t *ctx, usize len) {
|
||||||
/* == OUTPUT STREAM =========================================== */
|
/* == OUTPUT STREAM =========================================== */
|
||||||
|
|
||||||
void ostr__remove_null(outstream_t *o) {
|
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);
|
arenaPop(o->arena, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -364,20 +389,23 @@ usize ostrTell(outstream_t *ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
char ostrBack(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) {
|
str_t ostrAsStr(outstream_t *ctx) {
|
||||||
|
bool is_null_terminated = ostrBack(ctx) == '\0' && false;
|
||||||
return (str_t){
|
return (str_t){
|
||||||
.buf = ctx->beg,
|
.buf = ctx->beg,
|
||||||
.len = ostrTell(ctx)
|
.len = ostrTell(ctx) - is_null_terminated
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
strview_t ostrAsView(outstream_t *ctx) {
|
strview_t ostrAsView(outstream_t *ctx) {
|
||||||
|
bool is_null_terminated = ostrBack(ctx) == '\0';
|
||||||
return (strview_t){
|
return (strview_t){
|
||||||
.buf = ctx->beg,
|
.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;
|
if (!ctx->arena) return;
|
||||||
ostr__remove_null(ctx);
|
ostr__remove_null(ctx);
|
||||||
strFmtv(ctx->arena, fmt, args);
|
strFmtv(ctx->arena, fmt, args);
|
||||||
|
// remove null termination
|
||||||
|
arenaPop(ctx->arena, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ostrPutc(outstream_t *ctx, char c) {
|
void ostrPutc(outstream_t *ctx, char c) {
|
||||||
|
|
@ -423,4 +453,177 @@ void ostrAppendNum(outstream_t *ctx, double val) {
|
||||||
ostrPrintf(ctx, "%g", 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"
|
#include "warnings/colla_warn_end.h"
|
||||||
|
|
@ -35,7 +35,7 @@ void istrIgnore(instream_t *ctx, char delim);
|
||||||
void istrIgnoreAndSkip(instream_t *ctx, char delim);
|
void istrIgnoreAndSkip(instream_t *ctx, char delim);
|
||||||
// skip n characters
|
// skip n characters
|
||||||
void istrSkip(instream_t *ctx, usize n);
|
void istrSkip(instream_t *ctx, usize n);
|
||||||
// skips whitespace (' ', '\n', '\t', '\r')
|
// skips whitespace (' ', '\\n', '\\t', '\\r')
|
||||||
void istrSkipWhitespace(instream_t *ctx);
|
void istrSkipWhitespace(instream_t *ctx);
|
||||||
// read len bytes into buffer, the buffer will not be null terminated
|
// read len bytes into buffer, the buffer will not be null terminated
|
||||||
void istrRead(instream_t *ctx, char *buf, usize len);
|
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
|
// 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);
|
usize istrGetBuf(instream_t *ctx, char *buf, usize buflen);
|
||||||
strview_t istrGetView(instream_t *ctx, char delim);
|
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);
|
strview_t istrGetViewLen(instream_t *ctx, usize len);
|
||||||
|
|
||||||
/* == OUTPUT STREAM =========================================== */
|
/* == OUTPUT STREAM =========================================== */
|
||||||
|
|
@ -96,6 +97,59 @@ void ostrAppendUInt(outstream_t *ctx, uint64 val);
|
||||||
void ostrAppendInt(outstream_t *ctx, int64 val);
|
void ostrAppendInt(outstream_t *ctx, int64 val);
|
||||||
void ostrAppendNum(outstream_t *ctx, double 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 <lensize> bytes for the length (e.g. sizeof(uint32))
|
||||||
|
// then reads sizeof(char) * strlen
|
||||||
|
strview_t ibstrGetView(ibytestream_t *ctx, usize lensize);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
12
colla/tcc/colla.def
Normal file
12
colla/tcc/colla.def
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
LIBRARY
|
||||||
|
|
||||||
|
EXPORTS
|
||||||
|
GetThreadId
|
||||||
|
InitializeConditionVariable
|
||||||
|
WakeConditionVariable
|
||||||
|
WakeAllConditionVariable
|
||||||
|
SleepConditionVariableCS
|
||||||
|
InternetOpen
|
||||||
|
InternetConnect
|
||||||
|
HttpOpenRequest
|
||||||
|
HttpSendRequest
|
||||||
300
colla/tcc/colla_tcc.h
Normal file
300
colla/tcc/colla_tcc.h
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if COLLA_TCC || 1
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
//// 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
|
||||||
157
colla/tracelog.c
157
colla/tracelog.c
|
|
@ -11,7 +11,11 @@
|
||||||
#pragma warning(disable:4996) // _CRT_SECURE_NO_WARNINGS.
|
#pragma warning(disable:4996) // _CRT_SECURE_NO_WARNINGS.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <handleapi.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#if COLLA_CMT_LIB
|
||||||
|
#pragma comment(lib, "User32")
|
||||||
|
#endif
|
||||||
|
|
||||||
// avoid including windows.h
|
// avoid including windows.h
|
||||||
|
|
||||||
|
|
@ -22,29 +26,27 @@
|
||||||
#define CP_UTF8 65001
|
#define CP_UTF8 65001
|
||||||
#endif
|
#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
|
#ifndef TLOG_NO_COLOURS
|
||||||
#define TLOG_NO_COLOURS
|
#define TLOG_NO_COLOURS
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if COLLA_EMC
|
||||||
|
#define TLOG_NO_COLOURS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TLOG_VS
|
#if COLLA_EMC
|
||||||
#if COLLA_WIN
|
#define COLOUR_BLACK "<span class=\"black\">"
|
||||||
#error "can't use TLOG_VS if not on windows"
|
#define COLOUR_RED "<span class=\"red\">"
|
||||||
#endif
|
#define COLOUR_GREEN "<span class=\"green\">"
|
||||||
#endif
|
#define COLOUR_YELLOW "<span class=\"yellow\">"
|
||||||
|
#define COLOUR_BLUE "<span class=\"blue\">"
|
||||||
#ifdef TLOG_NO_COLOURS
|
#define COLOUR_MAGENTA "<span class=\"magenta\">"
|
||||||
|
#define COLOUR_CYAN "<span class=\"cyan\">"
|
||||||
|
#define COLOUR_WHITE "<span class=\"white\">"
|
||||||
|
#define COLOUR_RESET "</span></b>"
|
||||||
|
#define COLOUR_BOLD "<b>"
|
||||||
|
#elif defined(TLOG_NO_COLOURS)
|
||||||
#define COLOUR_BLACK ""
|
#define COLOUR_BLACK ""
|
||||||
#define COLOUR_RED ""
|
#define COLOUR_RED ""
|
||||||
#define COLOUR_GREEN ""
|
#define COLOUR_GREEN ""
|
||||||
|
|
@ -72,7 +74,7 @@
|
||||||
|
|
||||||
bool use_newline = true;
|
bool use_newline = true;
|
||||||
|
|
||||||
#ifdef TLOG_WIN32_NO_VS
|
#if COLLA_WIN
|
||||||
static void setLevelColour(int level) {
|
static void setLevelColour(int level) {
|
||||||
WORD attribute = 15;
|
WORD attribute = 15;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
|
|
@ -86,11 +88,23 @@ static void setLevelColour(int level) {
|
||||||
HANDLE hc = GetStdHandle(STD_OUTPUT_HANDLE);
|
HANDLE hc = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
SetConsoleTextAttribute(hc, attribute);
|
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
|
#endif
|
||||||
|
|
||||||
void traceLog(int level, const char *fmt, ...) {
|
void traceLog(int level, const char *fmt, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt); traceLogVaList(level, fmt, args);
|
va_start(args, fmt);
|
||||||
|
traceLogVaList(level, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,49 +119,76 @@ void traceLogVaList(int level, const char *fmt, va_list args) {
|
||||||
mtxLock(g_mtx);
|
mtxLock(g_mtx);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char buffer[MAX_TRACELOG_MSG_LENGTH];
|
const char *beg = "";
|
||||||
memset(buffer, 0, sizeof(buffer));
|
|
||||||
|
|
||||||
const char *beg;
|
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case LogTrace: beg = COLOUR_BOLD COLOUR_WHITE "[TRACE]: " COLOUR_RESET; break;
|
case LogTrace: beg = "[TRACE" ; break;
|
||||||
case LogDebug: beg = COLOUR_BOLD COLOUR_BLUE "[DEBUG]: " COLOUR_RESET; break;
|
case LogDebug: beg = "[DEBUG" ; break;
|
||||||
case LogInfo: beg = COLOUR_BOLD COLOUR_GREEN "[INFO]: " COLOUR_RESET; break;
|
case LogInfo: beg = "[INFO" ; break;
|
||||||
case LogWarning: beg = COLOUR_BOLD COLOUR_YELLOW "[WARNING]: " COLOUR_RESET; break;
|
case LogWarning: beg = "[WARNING" ; break;
|
||||||
case LogError: beg = COLOUR_BOLD COLOUR_RED "[ERROR]: " COLOUR_RESET; break;
|
case LogError: beg = "[ERROR" ; break;
|
||||||
case LogFatal: beg = COLOUR_BOLD COLOUR_RED "[FATAL]: " COLOUR_RESET; break;
|
case LogFatal: beg = "[FATAL" ; break;
|
||||||
default: beg = ""; break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t offset = 0;
|
#if COLLA_WIN
|
||||||
|
|
||||||
#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)
|
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
|
#endif
|
||||||
setLevelColour(level);
|
setLevelColour(level);
|
||||||
printf("%s", beg);
|
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
|
// set back to white
|
||||||
setLevelColour(LogTrace);
|
setLevelColour(LogTrace);
|
||||||
printf("%s", buffer);
|
// vprintf(fmt, args);
|
||||||
if(use_newline) puts("");
|
fmtPrintv(fmt, args);
|
||||||
|
|
||||||
|
if(use_newline) {
|
||||||
|
#if COLLA_EMC
|
||||||
|
puts("<br>");
|
||||||
#else
|
#else
|
||||||
printf("%s", buffer);
|
puts("");
|
||||||
if(use_newline) puts("");
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef TLOG_DONT_EXIT_ON_FATAL
|
#ifndef TLOG_DONT_EXIT_ON_FATAL
|
||||||
if (level == LogFatal) {
|
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();
|
abort();
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -161,18 +202,18 @@ void traceUseNewline(bool newline) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void traceSetColour(colour_e colour) {
|
void traceSetColour(colour_e colour) {
|
||||||
#ifdef TLOG_WIN32_NO_VS
|
#if COLLA_WIN
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colour);
|
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colour);
|
||||||
#else
|
#else
|
||||||
switch (colour) {
|
switch (colour) {
|
||||||
case COL_RESET: printf(RESET); break;
|
case COL_RESET: printf(COLOUR_RESET); break;
|
||||||
case COL_BLACK: printf(BLACK); break;
|
case COL_BLACK: printf(COLOUR_BLACK); break;
|
||||||
case COL_BLUE: printf(BLUE); break;
|
case COL_BLUE: printf(COLOUR_BLUE); break;
|
||||||
case COL_GREEN: printf(GREEN); break;
|
case COL_GREEN: printf(COLOUR_GREEN); break;
|
||||||
case COL_CYAN: printf(CYAN); break;
|
case COL_CYAN: printf(COLOUR_CYAN); break;
|
||||||
case COL_RED: printf(RED); break;
|
case COL_RED: printf(COLOUR_RED); break;
|
||||||
case COL_MAGENTA: printf(MAGENTA); break;
|
case COL_MAGENTA: printf(COLOUR_MAGENTA); break;
|
||||||
case COL_YELLOW: printf(YELLOW); break;
|
case COL_YELLOW: printf(COLOUR_YELLOW); break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ static const uint8 masks[] = {
|
||||||
0x01 // 0000-0001
|
0x01 // 0000-0001
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
static struct {
|
||||||
uint8 mask;
|
uint8 mask;
|
||||||
uint8 result;
|
uint8 result;
|
||||||
int octets;
|
int octets;
|
||||||
|
|
|
||||||
19
colla/vmem.c
19
colla/vmem.c
|
|
@ -72,20 +72,21 @@ static void vmem__update_page_size(void) {
|
||||||
vmem__page_size = info.dwPageSize;
|
vmem__page_size = info.dwPageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif COLLA_LIN
|
// #elif COLLA_LIN
|
||||||
|
#else
|
||||||
|
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct vmem__header {
|
typedef struct {
|
||||||
usize len;
|
usize len;
|
||||||
};
|
} vmem__header;
|
||||||
|
|
||||||
void *vmemInit(usize size, usize *out_padded_size) {
|
void *vmemInit(usize size, usize *out_padded_size) {
|
||||||
size += sizeof(vmem__header);
|
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);
|
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));
|
fatal("could not map %zu memory: %s", size, strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (padded_size) {
|
if (out_padded_size) {
|
||||||
*padded_size = alloc_size;
|
*out_padded_size = alloc_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
header->len = alloc_size;
|
header->len = alloc_size;
|
||||||
|
|
@ -115,8 +116,8 @@ bool vmemRelease(void *base_ptr) {
|
||||||
|
|
||||||
bool vmemCommit(void *ptr, usize num_of_pages) {
|
bool vmemCommit(void *ptr, usize num_of_pages) {
|
||||||
// mmap doesn't need a commit step
|
// mmap doesn't need a commit step
|
||||||
(VOID)ptr;
|
(void)ptr;
|
||||||
(VOID)num_of_pages;
|
(void)num_of_pages;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +128,7 @@ static void vmem__update_page_size(void) {
|
||||||
fatal("could not get page size: %s", strerror(errno));
|
fatal("could not get page size: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
page_size = (usize)lin_page_size;
|
vmem__page_size = (usize)lin_page_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
145
colla/xml.c
Normal file
145
colla/xml.c
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
31
colla/xml.h
Normal file
31
colla/xml.h
Normal file
|
|
@ -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);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue