]> git.leonardobizzoni.com Git - CBuild/commitdiff
CBuild refactor
authorLeonardoBizzoni <leo2002714@gmail.com>
Thu, 2 Apr 2026 15:19:10 +0000 (17:19 +0200)
committerLeonardoBizzoni <leo2002714@gmail.com>
Thu, 2 Apr 2026 15:19:10 +0000 (17:19 +0200)
cbuild.h
extra/cbuild_codegen.h
extra/cbuild_linagen.h
extra/cbuild_tests.h

index 90f5db913a2f787e424f232a8ce8248e1a4e84ff..3de54bc239a6fa0c1b80d6ef4f40f5ecbb2196d0 100644 (file)
--- a/cbuild.h
+++ b/cbuild.h
@@ -7,6 +7,17 @@
 #  define COMPILER_CLANG 1
 #elif defined(_MSC_VER)
 #  define COMPILER_CL 1
+#  if defined(_M_IX86)
+#    define ARCH_X86_32 1
+#  elif defined(_M_AMD64)
+#    define ARCH_X86_64 1
+#  elif defined(_M_ARM)
+#    define ARCH_ARM32 1
+#  elif defined(_M_ARM64)
+#    define ARCH_ARM64 1
+#  else
+#    error Unsupported platform
+#  endif
 #else
 #  error Unsupported compiler
 #endif
 #  error Unknown operating system
 #endif
 
+#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
+#  define ARCH_X86_64 1
+#elif defined(i386) || defined(__i386) || defined(__i386__)
+#  define ARCH_X86_32 1
+#elif defined(__aarch64__)
+#  define ARCH_ARM64 1
+#elif defined(__arm__)
+#  define ARCH_ARM32 1
+#else
+#  error Unknown architecture
+#endif
+
 #if !defined(COMPILER_GCC)
 #  define COMPILER_GCC 0
 #endif
 #  define OS_NONE 0
 #endif
 
+#ifdef alignof
+#  undef alignof
+#endif
+#if COMPILER_GCC
+#  define alignof(TYPE) __alignof__(TYPE)
+#elif COMPILER_CLANG || COMPILER_CL
+#  define alignof(TYPE) __alignof(TYPE)
+#else
+#  error undefined alignof macro
+#endif
+
 #define internal static
 
-#define BOOL_DEFINED 1
-#if __STDC_VERSION__ >= 199901L
-#  include <stdbool.h>
-#else
-   typedef enum {false, true} bool;
+#if !OS_WINDOWS && !defined(_GNU_SOURCE)
+#  define _GNU_SOURCE
 #endif
 
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <assert.h>
-
-#if OS_WINDOWS
-#  if COMPILER_CL
-#    define CC "cl"
-#  else
-#    define CC "x86_64-w64-mingw32-gcc"
-#  endif
-#else
-#  define CC "cc"
-#endif
-
 #if OS_WINDOWS
 #  define WIN32_LEAN_AND_MEAN
 #  include <windows.h>
 #  undef stdin
 #  undef stdout
 #  undef stderr
-#  define _cb_platform_mkdir(Path) _mkdir((Path));
-#  define MAX_ENVVAR 32767
-#  define CB_PROC_INVALID INVALID_HANDLE_VALUE
-#  define CB_HANDLE_INVALID INVALID_HANDLE_VALUE
+#endif
 
-   typedef HANDLE CB_Handle;
-   typedef HANDLE CB_ProcHandle;
+typedef float     f32;
+typedef double    f64;
+typedef uint8_t   b8;
+typedef uint32_t  b32;
+typedef int8_t    s8;
+typedef int16_t   s16;
+typedef int32_t   s32;
+typedef int64_t   s64;
+typedef uint8_t   u8;
+typedef uint16_t  u16;
+typedef uint32_t  u32;
+typedef uint64_t  u64;
+typedef uintptr_t usize;
+typedef intptr_t  ssize;
+
+typedef u64 time64;
+typedef u64 unix_time;
+
+#define false 0
+#define true  1
+
+#define UNIX_MINUTE    60
+#define UNIX_HOUR      3600
+#define UNIX_DAY       86400
+#define UNIX_WEEK      604800
+#define UNIX_YEAR      31536000
+#define UNIX_LEAP_YEAR 31622400
+
+#if COMPILER_CL
+#  define CC "cl"
+#elif COMPILER_GCC
+#  define CC "gcc"
+#elif COMPILER_CLANG
+#  define CC "clang"
 #else
-#  include <unistd.h>
-#  include <fcntl.h>
-#  include <sys/stat.h>
-#  include <sys/wait.h>
-#  include <sys/stat.h>
-#  define _cb_platform_mkdir(Path) mkdir((Path), S_IRWXU | (S_IRGRP | S_IXGRP) | (S_IROTH | S_IXOTH));
-#  define CB_PROC_INVALID -1
-#  define CB_HANDLE_INVALID -1
-   typedef int32_t CB_Handle;
-   typedef pid_t CB_ProcHandle;
+#  define CC "cc"
 #endif
 
 #define ANSI_COLOR_RED     "\x1b[31m"
 #define ANSI_COLOR_CYAN    "\x1b[36m"
 #define ANSI_COLOR_RESET   "\x1b[0m"
 
+#define cb_memcopy(Destination, Source, Size) memcpy((Destination), (Source), (u64)(Size))
+#define cb_memzero(Destination, Size)         memset((Destination), 0, (u64)(Size))
+
+#define cb_arrlength(Arr) (sizeof((Arr)) / sizeof(*(Arr)))
+
+// ================================================================================
+// Static arena
+#define CB_ARENA_SIZE 4 * 1024 * 1024
+
+static struct {
+  u8 data[CB_ARENA_SIZE];
+  s64 head;
+} cb_arena = {0};
+
+typedef struct CB_Scratch CB_Scratch;
+struct CB_Scratch {
+  s64 restore_point;
+};
+
+#define cb_arena_push(type) cb_arena_push_many(type, 1)
+#define cb_arena_push_many(type, count) ((type*)_cb_arena_push(sizeof(type) * (u64)(count), alignof(type)))
+#define cb_arena_push_explicit(bytes, alignment) (_cb_arena_push((bytes), (alignment)))
+
+#define cb_scratch_begin() ((CB_Scratch) { .restore_point = cb_arena.head })
+#define cb_scratch_end(scratch) cb_arena_pop(cb_arena.head - (scratch).restore_point)
+
+static void cb_arena_pop(s64 bytes);
+internal void* _cb_arena_push(u64 bytes, u64 alignment);
+
+static inline u64 cb_align_forward(u64 x, u64 align);
+
+// ================================================================================
+// Compact time format
+static time64 cb_time64_from_unix(unix_time timestamp);
+#if OS_WINDOWS
+static time64 time64_from_systemtime(const SYSTEMTIME *in);
+#endif
+
+// ================================================================================
+// Strings & Arrays
+typedef struct CB_StringView CB_StringView;
+struct CB_StringView {
+  u8 *data;
+  int64_t length;
+};
+
+typedef struct CB_StringBuilder_Node CB_StringBuilder_Node;
+struct CB_StringBuilder_Node {
+  struct CB_StringBuilder_Node *prev;
+  struct CB_StringBuilder_Node *next;
+  CB_StringView value;
+};
+
+typedef struct CB_StringBuilder CB_StringBuilder;
+struct CB_StringBuilder {
+  struct CB_StringBuilder_Node *first;
+  struct CB_StringBuilder_Node *last;
+  int32_t total_length;
+  int32_t node_count;
+};
+
+typedef struct CB_Array CB_Array;
+struct CB_Array {
+  s64 length;
+  void *data;
+};
+
+typedef uint8_t CB_LogLevel;
+enum {
+  CB_LogLevel_None,
+  CB_LogLevel_Info,
+  CB_LogLevel_Warn,
+  CB_LogLevel_Error,
+  CB_LogLevel_COUNT,
+};
+
+#define cb_strlit(cstr) (CB_StringView) { .data = (cstr), .length = cb_arrlength(cstr) - 1 }
+#define cb_strinit(cstr) { .data = (cstr), .length = cb_arrlength(cstr) - 1 }
+#define cb_view_expand(view) (s32)(view).length, (view).data
+
+#define cb_array(CArrayLitteral) (CB_Array) { .data = (CArrayLitteral), .length = cb_arrlength((CArrayLitteral)) }
+
+#ifndef CB_DYN_DEFAULT_CAPACITY
+#  define CB_DYN_DEFAULT_CAPACITY 8
+#endif
+
+#define cb_dyn_reserve(Dynarr, HowMany) cb_dyn_reserve_custom((Dynarr), (HowMany), values, count, capacity)
+#define cb_dyn_free(Dynarr) cb_dyn_free_custom((Dynarr), values, count)
+#define cb_dyn_push(Dynarr, Node) cb_dyn_push_custom((Dynarr), (Node), values, count, capacity)
+#define cb_dyn_append(Dynarr, Array, Size) cb_dyn_append_custom((Dynarr), (Array), (Size), values, count, capacity)
+
+#define cb_stack_push(Head, Nodeptr) cb_ull_push_front_custom((Head), (Head), (Nodeptr), next)
+#define cb_stack_pop(Head) (Head ? (Head = Head->next) : 0)
+
+#define cb_ull_push_front_custom(Head, Last, Nodeptr, Next)     \
+  (!(Head) ? (Head) = (Nodeptr), (Last) = (Nodeptr)             \
+   : ((Nodeptr)->Next = (Head), (Head) = (Nodeptr)))
+
+#define cb_queue_push(Head, Last, Nodeptr) cb_ull_push_back_custom((Head), (Last), (Nodeptr), next)
+#define cb_queue_pop(Head) (Head ? (Head = Head->next) : 0)
+
+#define cb_ull_push_back_custom(Head, Last, Nodeptr, Next)      \
+  (!(Head) ? (Head) = (Last) = (Nodeptr)                        \
+   : ((Last) ? ((Last)->Next = (Nodeptr), (Last) = (Nodeptr))   \
+      : ((Head)->Next = (Last) = (Nodeptr))))
+
+#define cb_bll_push_front(Head, Last, Nodeptr) cb_bll_push_front_custom(Head, Last, Nodeptr, next, prev)
+#define cb_bll_push_back(Head, Last, Nodeptr) cb_bll_push_back_custom(Head, Last, Nodeptr, next, prev)
+
+#define cb_bll_push_front_custom(Head, Last, Nodeptr, Next, Prev)       \
+  (!(Head) ? (Head) = (Last) = (Nodeptr)                                \
+   : ((Nodeptr)->Next = (Head), (Head)->Prev = (Nodeptr),               \
+      (Head) = (Nodeptr)))
+
+#define cb_bll_push_back_custom(DLLNodeHead, DLLNodeLast, NodeToInsertptr, Next, Prev)  \
+  (!DLLNodeHead                                                                         \
+   ? DLLNodeHead = DLLNodeLast = NodeToInsertptr                                        \
+   : (DLLNodeLast->Next = NodeToInsertptr,                                              \
+      NodeToInsertptr->Prev = DLLNodeLast, DLLNodeLast = NodeToInsertptr))
+
+#define cb_bll_remove(Head, Last, Nodeptr)              \
+  ((Head) == (Last) && (Head) == (Nodeptr)              \
+   ? (Head) = (Last) = 0                                \
+   : ((Last) == (Nodeptr)                               \
+      ? ((Last) = (Last)->prev, (Last)->next = 0)       \
+      : ((Head) == (Nodeptr)                            \
+         ? ((Head) = (Head)->next, (Head)->prev = 0)    \
+         : ((Nodeptr)->prev->next = (Nodeptr)->next,    \
+            (Nodeptr)->next->prev = (Nodeptr)->prev))))
+
+#define cb_bll_pop(Head, Last) cb_bll_pop_back(Head, Last)
+
+#define cb_bll_pop_back(Head, Last)                             \
+  (!(Last)                                                      \
+   ? 0                                                          \
+   : (!(Last)->prev ? (Head) = (Last) = 0                       \
+      : ((Last)->prev->next = 0, (Last) = (Last)->prev)))
+
+#define cb_bll_pop_front(Head, Last)                            \
+  (!(Head)                                                      \
+   ? 0                                                          \
+   : (!(Head)->next ? (Head) = (Last) = 0                       \
+      : ((Head)->next->prev = 0, (Head) = (Head)->next)))
+
+#define cb_dyn_free_custom(Dynarr, Values, Count)       \
+  do {                                                  \
+    free((Dynarr)->Values);                             \
+    (Dynarr)->Count = 0;                                \
+  } while (0)
+
+#define cb_dyn_reserve_custom(Dynarr, HowMany, Values, Count, Capacity)                 \
+  do {                                                                                  \
+    if (!(Dynarr)->Capacity) {                                                          \
+      (Dynarr)->Capacity = CB_DYN_DEFAULT_CAPACITY;                                     \
+    }                                                                                   \
+    while ((HowMany) > (Dynarr)->Capacity) {                                            \
+      (Dynarr)->Capacity *= 2;                                                          \
+    }                                                                                   \
+    (Dynarr)->Values = realloc((Dynarr)->Values,                                        \
+                               (Dynarr)->Capacity * sizeof((Dynarr)->Values[0]));       \
+    cb_assert((Dynarr)->Values);                                                        \
+  } while(0)
+
+#define cb_dyn_push_custom(Dynarr, Node, Values, Count, Capacity)       \
+  do {                                                                  \
+    cb_dyn_reserve_custom((Dynarr), (Dynarr)->Count + 1,                \
+                          Values, Count, Capacity);                     \
+    (Dynarr)->Values[(Dynarr)->Count++] = (Node);                       \
+  } while(0)
+
+#define cb_dyn_append_custom(Dynarr, Array, Size, Values, Count, Capacity)      \
+  do {                                                                          \
+    cb_dyn_reserve((Dynarr), (Dynarr)->Count + (Size));                         \
+    memcpy((Dynarr)->Values + (Dynarr)->Count, (Array),                         \
+           (Size) * sizeof((Dynarr)->Values[0]));                               \
+    (Dynarr)->Count += (Size);                                                  \
+  } while(0)
+
+static void cb_print(CB_LogLevel level, const char *fmt, ...);
+
+static char* cb_format(const char *format, ...);
+static char* cb_cstr_from_view(CB_StringView view);
+
+static CB_StringView cb_view_from_cstr(const char *cstr);
+static b8 cb_view_starts_with(const char *string, CB_StringView sv);
+static void cb_view_trim(CB_StringView *sv);
+static CB_StringBuilder cb_view_split(CB_StringView sv, char delim);
+
+static void cb_sb_append(CB_StringBuilder *sb, CB_StringView sv);
+static void cb_sb_concat(CB_StringBuilder *sb, const CB_StringBuilder *other);
+static CB_StringView cb_sb_join_with_ch(CB_StringBuilder sb, char ch);
+
+static b8 cb_cli_contains(int32_t argc, char **argv, const char *target);
+
+internal char* cb_format_va(const char *format, va_list args);
+
+// ================================================================================
+// File-system interaction
+#if !OS_WINDOWS
+#  include <unistd.h>
+#  include <fcntl.h>
+#  include <dirent.h>
+#  include <sys/stat.h>
+#  include <sys/mman.h>
+#endif
+
+typedef u8 CB_FileType;
+enum {
+  CB_FileType_Unknown = 0,
+  CB_FileType_Device_Disk,
+  CB_FileType_Device_Terminal,
+  CB_FileType_Directory,
+  CB_FileType_Pipe,
+  CB_FileType_Link,
+  CB_FileType_Socket,
+  CB_FileType_Regular,
+  CB_FileType_COUNT,
+};
+
+typedef u8 CB_FilePermission;
+enum {
+  CB_FilePermission_Unknown = 0,
+  CB_FilePermission_Read    = 1 << 0,
+  CB_FilePermission_Write   = 1 << 1,
+  CB_FilePermission_Execute = 1 << 2,
+};
+
+typedef u8 CB_AccessFlag;
+enum {
+  CB_AccessFlag_Read       = 1 << 0,
+  CB_AccessFlag_Write      = 1 << 1,
+  CB_AccessFlag_Append     = 1 << 2,
+};
+
+typedef struct CB_FileProperties CB_FileProperties;
+struct CB_FileProperties {
+  s64 size;
+  time64 time_last_access;
+  time64 time_last_modification;
+  time64 time_last_status_change;
+  CB_FileType type;
+  union {
+    CB_FilePermission values[3];
+    struct {
+      CB_FilePermission user;
+      CB_FilePermission group;
+      CB_FilePermission other;
+    };
+  } permission;
+};
+
+typedef struct CB_FileInfo CB_FileInfo;
+struct CB_FileInfo {
+  s64 size;
+  char *path;
+  CB_FileType type;
+};
+
+typedef struct CB_FileIterator_Handle CB_FileIterator_Handle;
+#if OS_WINDOWS
+struct CB_FileIterator_Handle {
+  const char *path;
+};
+#else
+struct CB_FileIterator_Handle {
+  DIR *dir;
+  const char *path;
+  struct dirent *dir_entry;
+};
+#endif
+
+typedef struct CB_FileIterator CB_FileIterator;
+struct CB_FileIterator {
+  CB_FileType filter_allowed;
+  CB_FileIterator_Handle handle;
+};
+
+#if OS_WINDOWS
+#  define CB_FILEHANDLE_INVALID INVALID_HANDLE_VALUE
+   typedef HANDLE CB_FileHandle;
+#else
+#  define CB_FILEHANDLE_INVALID -1
+   typedef int32_t CB_FileHandle;
+#endif
+
+static CB_FileProperties cb_file_properties(CB_FileHandle hfile);
+static CB_StringView cb_file_read(CB_FileHandle hfile, s64 size, s64 offset);
+static u8* cb_file_memory_map(CB_FileHandle hfile, s64 size, s64 offset);
+static void cb_file_write_at_pointer(CB_FileHandle hfile, CB_StringView content);
+static void cb_file_write_at_offset(CB_FileHandle hfile, CB_StringView content, s64 offset);
+static b8 cb_file_pointer_set_position_absolute(CB_FileHandle hfile, s64 position);
+static b8 cb_file_pointer_set_position_relative(CB_FileHandle hfile, s64 offset);
+static b8 cb_file_flush(CB_FileHandle hfile);
+static b8 cb_file_set_size(CB_FileHandle hfile, s64 new_size);
+static void cb_file_memory_unmap(u8 *file_map, s64 size);
+static void cb_file_close(CB_FileHandle hfile);
+
+static CB_FileHandle cb_filesystem_open(CB_StringView path, CB_AccessFlag flags);
+static CB_FileHandle cb_filesystem_open_temp(CB_StringView *path);
+static CB_FileProperties cb_filesystem_properties(CB_StringView path);
+static void cb_filesystem_delete(CB_StringView path);
+static b8 cb_filesystem_exists(CB_StringView path);
+static b8 cb_filesystem_rename(CB_StringView old, CB_StringView new);
+static b8 cb_filesystem_copy(CB_StringView path_source, CB_StringView path_dest, s64 bytes_to_copy, s64 offset_source, s64 offset_dest);
+static b8 cb_filesystem_link_create_symbolic(CB_StringView realpath, CB_StringView linkpath);
+static b8 cb_filesystem_link_create_physical(CB_StringView realpath, CB_StringView linkpath);
+static CB_StringView cb_filesystem_link_read(CB_StringView linkpath);
+static b8 cb_filesystem_directory_create(CB_StringView path);
+static b8 cb_filesystem_directory_copy(CB_StringView path, CB_StringView new);
+static b8 cb_filesystem_directory_delete(CB_StringView path, b8 recursive);
+static CB_StringView cb_filesystem_filename_from_path(CB_StringView path);
+static CB_StringView cb_filesystem_path_canonicalize(CB_StringView path);
+
+static void cb_filesystem_iter_begin(CB_FileIterator *it, const char *rootpath);
+static b8 cb_filesystem_iter_next(CB_FileIterator *it, CB_FileInfo *output);
+static void cb_filesystem_iter_end(CB_FileIterator *it);
+
+#if !OS_WINDOWS
+internal CB_FileProperties cb_fileproperties_from_stat(struct stat *file_stat);
+#endif
+
+// ================================================================================
+// Custom assertion handler
+// TODO(lb): rename to `expect` and move to "test" extra
 #ifndef _assert_break
 #  if OS_WINDOWS
 #    define _assert_break() __debugbreak()
@@ -140,264 +504,183 @@ internal cb_assertion_handler_fn cb_assertion_handler = cb_assertion_break;
     }                                                  \
   } while (0)
 
-struct CB_PathList {
-  const char **values;
-  size_t count;
-  size_t capacity;
-};
-typedef struct CB_PathList CB_Cmd;
-
-struct CB_Cmd_RunArgs {
-  CB_Handle stdin;
-  CB_Handle stdout;
-  CB_Handle stderr;
-  bool async;
-  bool reset;
-};
+// ================================================================================
+// Processes
+#if OS_WINDOWS
+#  define CB_PROC_INVALID INVALID_HANDLE_VALUE
+   typedef HANDLE CB_ProcHandle;
+#else
+#  include <sys/wait.h>
+#  include <sys/stat.h>
+#  define CB_PROC_INVALID -1
+   typedef pid_t CB_ProcHandle;
+#endif
 
-typedef struct {
+typedef struct CB_Process CB_Process;
+struct CB_Process {
   CB_ProcHandle handle;
   int32_t status_code;
-} CB_Process;
+};
 
-typedef struct {
+typedef struct CB_ProcessList CB_ProcessList;
+struct CB_ProcessList {
   CB_Process *values;
   size_t count;
   size_t capacity;
-} CB_ProcessList;
-
-typedef uint8_t CB_AccessFlag;
-enum {
-  CB_AccessFlag_Read       = 1 << 0,
-  CB_AccessFlag_Write      = 1 << 1,
-  CB_AccessFlag_Append     = 1 << 2,
 };
 
-typedef uint8_t CB_LogLevel;
-enum {
-  CB_LogLevel_None,
-  CB_LogLevel_Info,
-  CB_LogLevel_Warn,
-  CB_LogLevel_Error,
-  CB_LogLevel_COUNT,
-};
-
-typedef uint8_t CB_FileType;
-enum {
-  CB_FileType_Device_Block = 1 << 0,
-  CB_FileType_Device_Char  = 1 << 1,
-  CB_FileType_Directory    = 1 << 2,
-  CB_FileType_Pipe         = 1 << 3,
-  CB_FileType_Link         = 1 << 4,
-  CB_FileType_Socket       = 1 << 5,
-  CB_FileType_Regular      = 1 << 6,
-};
-
-#if OS_WINDOWS
-#else
-#  include <dirent.h>
-  struct CB_FileIterator_Handle {
-    const char *path;
-    DIR *dir;
-    struct dirent *dir_entry;
-  };
-#endif
-
-typedef struct {
-  CB_FileType filter_allowed;
-  struct CB_FileIterator_Handle handle;
-} CB_FileIterator;
-
-typedef struct {
-  size_t size;
-  char *path;
-  CB_FileType type;
-} CB_FileInfo;
+#define cb_proclist_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
 
-typedef struct CB_StringView {
-  const char *data;
-  ssize_t length;
-} CB_StringView;
+static void cb_process_wait(CB_Process *handle);
+static void cb_proclist_wait(CB_ProcessList *procs);
 
-struct CB_StringBuilder_Node {
-  struct CB_StringBuilder_Node *prev;
-  struct CB_StringBuilder_Node *next;
-  CB_StringView value;
+// ================================================================================
+// Commands
+typedef struct CB_Cmd CB_Cmd;
+struct CB_Cmd {
+  const char **values;
+  u64 count;
+  u64 capacity;
 };
 
-typedef struct CB_StringBuilder {
-  struct CB_StringBuilder_Node *first;
-  struct CB_StringBuilder_Node *last;
-  int32_t total_length;
-  int32_t node_count;
-} CB_StringBuilder;
-
-#ifndef CB_DYN_DEFAULT_CAPACITY
-#  define CB_DYN_DEFAULT_CAPACITY 8
-#endif
-
-#if COMPILER_CL
-#  define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) CC, "/Fe:", (Exe_name), (Builder_src), "/nologo"
-#else
-#  define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) CC, "-o", (Exe_name), (Builder_src)
-#endif
-#define CB_CMD_REBUILD_SELF_WITH_OPTION(Exe_name, Builder_src, ...) CB_CMD_REBUILD_SELF(Exe_name, Builder_src), __VA_ARGS__
+typedef struct CB_Cmd_RunArgs CB_Cmd_RunArgs;
+struct CB_Cmd_RunArgs {
+  CB_FileHandle stream_in;
+  CB_FileHandle stream_out;
+  CB_FileHandle stream_err;
+  b8 async;
+  b8 reset;
+};
 
-#define cb_strlit(cstr) (CB_StringView) { .data = (cstr), .length = cb_arrlength(cstr) - 1 }
-#define cb_strinit(cstr) { .data = (cstr), .length = cb_arrlength(cstr) - 1 }
-#define cb_arrlength(Arr) (sizeof((Arr)) / sizeof(*(Arr)))
-#define cb_dyn_reserve(Dynarr, HowMany) cb_dyn_reserve_custom((Dynarr), (HowMany), values, count, capacity)
-#define cb_dyn_free(Dynarr) cb_dyn_free_custom((Dynarr), values, count)
-#define cb_dyn_push(Dynarr, Node) cb_dyn_push_custom((Dynarr), (Node), values, count, capacity)
-#define cb_dyn_append(Dynarr, Array, Size) cb_dyn_append_custom((Dynarr), (Array), (Size), values, count, capacity)
 #define cb_cmd_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
 #define cb_cmd_append_dyn(Dynarr, Values, Count) cb_dyn_append((Dynarr), (Values), (Count));
 #define cb_cmd_append(Dynarr, ...) cb_dyn_append((Dynarr), ((const char*[]){__VA_ARGS__}), (sizeof((const char*[]){__VA_ARGS__}) / sizeof(char*)))
-#define cb_rebuild_self(argc, argv) _cb_rebuild(argc, argv, __FILE__, NULL)
-#define cb_rebuild_self_with(argc, argv, ...) _cb_rebuild(argc, argv, __FILE__, __VA_ARGS__, NULL)
-#define cb_is_outdated(OutputFile, ...) _cb_is_outdated((OutputFile), __VA_ARGS__, 0)
-#define cb_proclist_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
-#define cb_cmd_exec(Cmd)                \
-  _cb_cmd_run((Cmd),                    \
-              (struct CB_Cmd_RunArgs) { \
-                .async = false,         \
-                .reset = true,          \
+
+#define cb_cmd_exec(Cmd)                        \
+  _cb_cmd_run((Cmd),                            \
+              (CB_Cmd_RunArgs) {                \
+                .async = false,                 \
+                .reset = true,                  \
               })
+
 #define cb_cmd_exec_with_option(Cmd, ...)       \
   _cb_cmd_run((Cmd),                            \
-              (struct CB_Cmd_RunArgs) {         \
+              (CB_Cmd_RunArgs) {                \
                 .async = false,                 \
                 .reset = true,                  \
                 __VA_ARGS__                     \
               })
 
-#define cb_dyn_free_custom(Dynarr, Values, Count) \
-  do {                                            \
-    free((Dynarr)->Values);                       \
-    (Dynarr)->Count = 0;                          \
-  } while (0)
-#define cb_dyn_reserve_custom(Dynarr, HowMany, Values, Count, Capacity)           \
-  do {                                                                            \
-    if (!(Dynarr)->Capacity) {                                                    \
-      (Dynarr)->Capacity = CB_DYN_DEFAULT_CAPACITY;                               \
-    }                                                                             \
-    while ((HowMany) > (Dynarr)->Capacity) {                                      \
-      (Dynarr)->Capacity *= 2;                                                    \
-    }                                                                             \
-    (Dynarr)->Values = realloc((Dynarr)->Values,                                  \
-                               (Dynarr)->Capacity * sizeof((Dynarr)->Values[0])); \
-    cb_assert((Dynarr)->Values);                                                  \
-  } while(0)
-#define cb_dyn_push_custom(Dynarr, Node, Values, Count, Capacity) \
-  do {                                                            \
-    cb_dyn_reserve_custom((Dynarr), (Dynarr)->Count + 1,          \
-                          Values, Count, Capacity);               \
-    (Dynarr)->Values[(Dynarr)->Count++] = (Node);                 \
-  } while(0)
-#define cb_dyn_append_custom(Dynarr, Array, Size, Values, Count, Capacity) \
-  do {                                                                     \
-    cb_dyn_reserve((Dynarr), (Dynarr)->Count + (Size));                    \
-    memcpy((Dynarr)->Values + (Dynarr)->Count, (Array),                    \
-           (Size) * sizeof((Dynarr)->Values[0]));                          \
-    (Dynarr)->Count += (Size);                                             \
-  } while(0)
+static void cb_cmd_print(const CB_Cmd *cmd);
+static void cb_cmd_clear(CB_Cmd *cmd);
+internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args);
 
-#define stack_push(Head, Nodeptr) ull_push_front_custom((Head), (Head), (Nodeptr), next)
-#define stack_pop(Head) (Head ? (Head = Head->next) : 0)
-#define ull_push_front_custom(Head, Last, Nodeptr, Next)                \
-  (!(Head) ? (Head) = (Nodeptr), (Last) = (Nodeptr)                     \
-           : ((Nodeptr)->Next = (Head), (Head) = (Nodeptr)))
+static void cb_rebuild_and_restart(s32 argc, char **argv, CB_Array sources, CB_Array build_options);
 
-#define queue_push(Head, Last, Nodeptr) ull_push_back_custom((Head), (Last), (Nodeptr), next)
-#define queue_pop(Head) (Head ? (Head = Head->next) : 0)
-#define ull_push_back_custom(Head, Last, Nodeptr, Next)                 \
-  (!(Head) ? (Head) = (Last) = (Nodeptr)                                \
-           : ((Last) ? ((Last)->Next = (Nodeptr), (Last) = (Nodeptr))   \
-                     : ((Head)->Next = (Last) = (Nodeptr))))
-
-#define bll_push_front(Head, Last, Nodeptr) bll_push_front_custom(Head, Last, Nodeptr, next, prev)
-#define bll_push_back(Head, Last, Nodeptr) bll_push_back_custom(Head, Last, Nodeptr, next, prev)
-#define bll_push_front_custom(Head, Last, Nodeptr, Next, Prev)          \
-  (!(Head) ? (Head) = (Last) = (Nodeptr)                                \
-           : ((Nodeptr)->Next = (Head), (Head)->Prev = (Nodeptr),       \
-              (Head) = (Nodeptr)))
-#define bll_push_back_custom(DLLNodeHead, DLLNodeLast, NodeToInsertptr, Next, Prev) \
-  (!DLLNodeHead                                                                     \
-       ? DLLNodeHead = DLLNodeLast = NodeToInsertptr                                \
-       : (DLLNodeLast->Next = NodeToInsertptr,                                      \
-          NodeToInsertptr->Prev = DLLNodeLast, DLLNodeLast = NodeToInsertptr))
-#define bll_remove(Head, Last, Nodeptr)                                 \
-  ((Head) == (Last) && (Head) == (Nodeptr)                              \
-       ? (Head) = (Last) = 0                                            \
-       : ((Last) == (Nodeptr)                                           \
-              ? ((Last) = (Last)->prev, (Last)->next = 0)               \
-              : ((Head) == (Nodeptr)                                    \
-                     ? ((Head) = (Head)->next, (Head)->prev = 0)        \
-                     : ((Nodeptr)->prev->next = (Nodeptr)->next,        \
-                        (Nodeptr)->next->prev = (Nodeptr)->prev))))
-#define bll_pop(Head, Last) bll_pop_back(Head, Last)
-#define bll_pop_back(Head, Last)                                            \
-  (!(Last)                                                                  \
-       ? 0                                                                  \
-       : (!(Last)->prev ? (Head) = (Last) = 0                               \
-                        : ((Last)->prev->next = 0, (Last) = (Last)->prev)))
-#define bll_pop_front(Head, Last)                                           \
-  (!(Head)                                                                  \
-       ? 0                                                                  \
-       : (!(Head)->next ? (Head) = (Last) = 0                               \
-                        : ((Head)->next->prev = 0, (Head) = (Head)->next)))
-
-#define cb_file_write(Handle, Content) _Generic((Content),      \
-    char *: cb_file_write_cstr,                                 \
-    const char *: cb_file_write_cstr,                           \
-    CB_StringView: cb_file_write_sv                             \
-  )((Handle), (Content))
+// ==============================================================================
+// Environment variables
+#if OS_WINDOWS
+#  define MAX_ENVVAR 32767
+#endif
 
-static char* cb_format(const char *format, ...);
-static void cb_print(CB_LogLevel level, const char *fmt, ...);
 static char* cb_getenv(const char *varname);
-static bool cb_setenv(const char *varname, const char *value);
-static void cb_cmd_print(const CB_Cmd *cmd);
-static void cb_process_wait(CB_Process *handle);
-static void cb_proclist_wait(CB_ProcessList *procs);
-static bool cb_dir_create(const char *path);
-static void cb_dir_delete(const char *path);
-static CB_Handle cb_file_open(const char *path, CB_AccessFlag permission);
-static void cb_file_close(CB_Handle fd);
-static CB_StringView cb_file_read(CB_Handle fd);
-static void cb_file_write_cstr(CB_Handle fd, const char *cstr);
-static void cb_file_write_sv(CB_Handle fd, CB_StringView sv);
-static void cb_file_delete(const char *path);
-static bool cb_file_rename(const char *path, const char *to);
-static bool cb_file_exists(const char *path);
-static bool cb_symlink_create(const char *target, const char *linkpath);
-static bool cb_cli_contains(int32_t argc, char **argv, const char *target);
-static bool cb_view_starts_with(const char *string, CB_StringView sv);
-static void cb_view_trim(CB_StringView *sv);
-static CB_StringBuilder cb_view_split(CB_StringView sv, char delim);
-static void cb_sb_append(CB_StringBuilder *sb, CB_StringView sv);
-static void cb_sb_concat(CB_StringBuilder *sb, const CB_StringBuilder *other);
-
-internal void _cb_file_write_buffer(CB_Handle fd, const char *buffer, size_t size);
-internal char* cb_format_va(const char *format, va_list args);
-internal bool _cb_need_rebuild(const char *output_path, struct CB_PathList sources);
-internal void _cb_rebuild(int argc, char **argv, const char *cb_src, ...);
-internal bool _cb_is_outdated(const char *output, ...);
-internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args);
-internal size_t _last_occurance_of(const char *string, char ch);
-
+static b8 cb_setenv(const char *varname, const char *value);
 
 // ==============================================================================
 // Implementation
-static char* cb_format(const char *format, ...) {
-  va_list args = {0};
-  va_start(args, format);
-  char *res = cb_format_va(format, args);
-  va_end(args);
+
+// ================================================================================
+// Static arena impl
+static inline u64 cb_align_forward(u64 x, u64 align)
+{
+  assert(align > 0);
+  assert((align & (align - 1)) == 0);
+  u64 mod = x & (align - 1);
+  return (mod ? x = x + align - mod : x);
+}
+
+static void cb_arena_pop(s64 bytes)
+{
+  assert(bytes >= 0);
+  if ((u64)bytes == (u64)-1) {
+    cb_arena.head = 0;
+  } else {
+    cb_arena.head -= bytes;
+  }
+}
+
+internal void* _cb_arena_push(u64 bytes, u64 alignment)
+{
+  u64 res = cb_align_forward((u64)cb_arena.data + (u32)cb_arena.head, alignment);
+  assert(res >= (u64)cb_arena.data);
+  s32 offset = (s32)(res - ((u64)cb_arena.data + (u32)cb_arena.head));
+  assert(offset >= 0 && (u64)offset <= alignment);
+  cb_arena.head += bytes + (u32)offset;
+  assert(cb_arena.head < CB_ARENA_SIZE);
+  return (void*)res;
+}
+
+// ================================================================================
+// Compact time format
+static time64 cb_time64_from_unix(unix_time timestamp)
+{
+  static const u8 daysXmonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+  time64 res = 1ULL << 63;
+  s32 year = 1970;
+  b8 is_leap_year = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
+  for (u64 secondsXyear = is_leap_year ? UNIX_LEAP_YEAR : UNIX_YEAR;
+       timestamp >= secondsXyear;
+       ++year, timestamp -= secondsXyear,
+               secondsXyear = is_leap_year ? UNIX_LEAP_YEAR : UNIX_YEAR);
+
+  res |= (u64)((s64)year & ~(1 << 27)) << 36;
+  u64 month = 1;
+  for (;;) {
+    u8 days = daysXmonth[month - 1];
+    if (month == 2 && is_leap_year) { ++days; }
+    u64 secondsXmonth = days * UNIX_DAY;
+    if (timestamp < secondsXmonth) { break; }
+    timestamp -= secondsXmonth;
+    ++month;
+  }
+  res |= month << 32;
+  res |= (timestamp / UNIX_DAY + 1) << 27;
+  timestamp %= UNIX_DAY;
+  res |= (timestamp / UNIX_HOUR) << 22;
+  timestamp %= UNIX_HOUR;
+  res |= (timestamp / UNIX_MINUTE) << 16;
+  timestamp %= UNIX_MINUTE;
+  res |= timestamp << 10;
   return res;
 }
 
-static void cb_print(CB_LogLevel level, const char *fmt, ...) {
+#if OS_WINDOWS
+static time64 time64_from_systemtime(const SYSTEMTIME *in)
+{
+  assert(in);
+  assert(in->wMilliseconds <= 999);
+  assert(in->wSecond <= 60);
+  assert(in->wMinute <= 60);
+  assert(in->wHour <= 23);
+  assert(in->wDay <= 31);
+  assert(in->wMonth >= 1 && in->wMonth <= 12);
+  s16 year = (s16)in->wYear;
+  time64 res = (year >= 0 ? 1ULL << 63 : 0);
+  res |= (u64)((year >= 0 ? year : -year) & ~(1 << 27)) << 36;
+  res |= (u64)(in->wMonth) << 32;
+  res |= (u64)(in->wDay) << 27;
+  res |= (u64)(in->wHour) << 22;
+  res |= (u64)(in->wMinute) << 16;
+  res |= (u64)(in->wSecond) << 10;
+  res |= (u64)in->wMilliseconds;
+  return res;
+}
+#endif
+
+// ================================================================================
+// Strings & Arrays
+static void cb_print(CB_LogLevel level, const char *fmt, ...)
+{
   cb_assert(fmt != NULL);
   cb_assert(level >= CB_LogLevel_None && level <= CB_LogLevel_COUNT);
   switch (level) {
@@ -421,234 +704,589 @@ static void cb_print(CB_LogLevel level, const char *fmt, ...) {
   va_end(args);
 }
 
-static char* cb_getenv(const char *varname) {
-  cb_assert(varname != NULL);
+static char* cb_format(const char *format, ...)
+{
+  va_list args = {0};
+  va_start(args, format);
+  char *res = cb_format_va(format, args);
+  va_end(args);
+  return res;
+}
+
+static char* cb_cstr_from_view(CB_StringView view)
+{
+  assert(view.length > 0);
+  assert(view.data != NULL);
+  char *res = cb_arena_push_many(char, view.length + 1);
+  cb_memcopy(res, view.data, view.length);
+  res[view.length] = '\0';
+  return res;
+}
+
+static CB_StringView cb_view_from_cstr(const char *cstr)
+{
+  CB_StringView res = {0};
+  res.data = (u8*)cstr;
+  if (cstr != NULL) {
+    const char *start = cstr;
+    for (; *cstr; ++cstr);
+    res.length = cstr - start;
+  }
+  return res;
+}
+
+static b8 cb_view_starts_with(const char *string, CB_StringView sv)
+{
+  if (string == NULL || sv.length == 0 || sv.data == NULL) { return false; }
+  for (int32_t i = 0; i < sv.length; ++i) {
+    if (string[i] == 0 || string[i] != sv.data[i]) { return false; }
+  }
+  return true;
+}
+
+static void cb_view_trim(CB_StringView *sv)
+{
+  cb_assert(sv != NULL);
+  if (sv->length <= 0) { return; }
+  cb_assert(sv->data != NULL);
+  cb_assert(sv->length > 0);
+
+  for (; sv->data[0] == ' ' || sv->data[0] == '\n' || sv->data[0] == '\r' || sv->data[0] == '\t';
+       sv->length -= 1, sv->data += 1) {}
+  for (; sv->data[sv->length - 1] == ' ' || sv->data[sv->length - 1] == '\n' ||
+         sv->data[sv->length - 1] == '\r' || sv->data[sv->length - 1] == '\t';
+       sv->length -= 1) {}
+}
+
+static CB_StringBuilder cb_view_split(CB_StringView sv, char delim)
+{
+  CB_StringBuilder res = {0};
+  if (sv.data == NULL || sv.length == 0) { return res; }
+  ssize_t prev = 0;
+  for (ssize_t i = 0; i < sv.length;) {
+    if (sv.data[i] == delim) {
+      if (prev != i) {
+        cb_sb_append(&res, (CB_StringView) {
+          .data = sv.data + prev,
+          .length = (int64_t)(i - prev),
+        });
+      }
+      do {
+        prev = ++i;
+      } while (sv.data[i] == delim);
+    } else {
+      ++i;
+    }
+  }
+  if (prev != sv.length) {
+    cb_sb_append(&res, (CB_StringView) {
+      .data = sv.data + prev,
+      .length = (int64_t)(sv.length - prev),
+    });
+  }
+  cb_assert(res.node_count > 0);
+  return res;
+}
+
+static void cb_sb_append(CB_StringBuilder *sb, CB_StringView sv)
+{
+  cb_assert(sb != NULL);
+  if (sv.length == 0 || sv.data == NULL) { return; }
+  CB_StringBuilder_Node *node = cb_arena_push(CB_StringBuilder_Node);
+  cb_assert(node != NULL);
+  memset(node, 0, sizeof *node);
+  node->value = sv;
+  cb_bll_push_back(sb->first, sb->last, node);
+  sb->total_length += sv.length;
+  sb->node_count += 1;
+}
+
+static void cb_sb_concat(CB_StringBuilder *sb, const CB_StringBuilder *other)
+{
+  cb_assert(sb != NULL);
+  cb_assert(sb->node_count >= 0);
+  cb_assert(sb->total_length >= 0);
+  cb_assert(other != NULL);
+  cb_assert(other->node_count >= 0);
+  cb_assert(other->total_length >= 0);
+  sb->node_count += other->node_count;
+  sb->total_length += other->total_length;
+  cb_bll_push_back(sb->first, sb->last, other->first);
+  sb->last = other->last;
+}
+
+static CB_StringView cb_sb_join_with_ch(CB_StringBuilder sb, char ch)
+{
+  CB_StringView sv = {0};
+  sv.data = malloc((uint32_t)(sb.total_length + sb.node_count));
+  sv.length = sb.total_length + sb.node_count - 1;
+  int32_t offset = 0;
+  for (struct CB_StringBuilder_Node *curr = sb.first; curr;) {
+    memcpy((char*)(sv.data + offset), curr->value.data, (uint32_t)curr->value.length);
+    offset += curr->value.length;
+    ((char*)sv.data)[offset++] = ch;
+    struct CB_StringBuilder_Node *next = curr->next;
+    free(curr);
+    curr = next;
+  }
+  ((char*)sv.data)[sv.length] = 0;
+  return sv;
+}
+
+static b8 cb_cli_contains(int32_t argc, char **argv, const char *target)
+{
+  for (int32_t i = 0; i < argc; ++i) {
+    if (!strcmp(argv[i], target)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+internal char* cb_format_va(const char *format, va_list args)
+{
+  va_list args2;
+  va_copy(args2, args);
+  size_t needed_bytes = (size_t)vsnprintf(0, 0, format, args2) + 1;
+  va_end(args2);
+
+  char *res = cb_arena_push_many(char, needed_bytes);
+  (void)vsnprintf(res, needed_bytes, format, args);
+  return res;
+}
+
+// ================================================================================
+// File-system interaction
+static CB_FileProperties cb_file_properties(CB_FileHandle hfile)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
 #if OS_WINDOWS
-  char *res = malloc(MAX_ENVVAR);
-  if (!GetEnvironmentVariableA(varname, res, MAX_ENVVAR)) {
-    free(res);
-    res = 0;
+  CB_FileProperties properties = {0};
+
+  BY_HANDLE_FILE_INFORMATION file_info = {0};
+  BOOL get_info_res = GetFileInformationByHandle(hfile, &file_info);
+  assert(get_info_res == TRUE);
+
+  properties.size = (((s64)file_info.nFileSizeHigh << 32) | file_info.nFileSizeLow);
+  assert(properties.size >= 0);
+
+  if (file_info.dwFileAttributes & FILE_ATTRIBUTE_NORMAL || file_info.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
+    properties.type = OS_FileType_Regular;
+  } else if (file_info.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) {
+    properties.type = OS_FileType_Device_Block;
+  } else if (file_info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+    properties.type = OS_FileType_Link;
+  } else if (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+    properties.type = OS_FileType_Directory;
+  } else {
+    unreachable();
   }
+
+  assert((((u64)file_info.ftLastAccessTime.dwHighDateTime << 32) | file_info.ftLastAccessTime.dwLowDateTime) <= 0x8000000000000000);
+  assert((((u64)file_info.ftLastWriteTime.dwHighDateTime << 32) | file_info.ftLastWriteTime.dwLowDateTime) <= 0x8000000000000000);
+  SYSTEMTIME last_access_time = {0}, last_write_time = {0};
+  res = FileTimeToSystemTime(&file_info.ftLastAccessTime, &last_access_time);
+  assert(res == TRUE);
+  res = FileTimeToSystemTime(&file_info.ftLastWriteTime, &last_write_time);
+  assert(res == TRUE);
+
+  properties.time_last_access = time64_from_systemtime(&last_access_time);
+  properties.time_last_status_change = time64_from_systemtime(&last_write_time);
+  properties.time_last_modification = properties.time_last_status_change;
+
+  return properties;
+#else
+  struct stat file_stat = {0};
+  s32 stat_res = fstat(hfile, &file_stat);
+  assert(stat_res == 0);
+  return cb_fileproperties_from_stat(&file_stat);
+#endif
+}
+
+static CB_StringView cb_file_read(CB_FileHandle hfile, s64 size, s64 offset)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+  assert(size > 0);
+  assert(offset >= 0);
+  assert(offset < size);
+
+  CB_StringView result = {0};
+  u8 *buffer = cb_arena_push_many(u8, size);
+
+#if OS_WINDOWS
+  // TODO(lb): win32
+#else
+  s64 bytes_read = pread(hfile, buffer, (u64)size, offset);
+  assert(bytes_read != -1);
+  cb_arena_pop(size - bytes_read);
+  result.data = (u8*)buffer;
+  result.length = bytes_read;
+  return result;
+#endif
+}
+
+static u8* cb_file_memory_map(CB_FileHandle hfile, s64 size, s64 offset)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+  assert(size > 0);
+  assert(offset >= 0);
+  assert(offset < size);
+
+#if OS_WINDOWS
+  // TODO(lb): win32
+#else
+  u8 *res = mmap(NULL, (u64)size, O_RDWR, MAP_SHARED, hfile, offset);
+  assert(res != MAP_FAILED);
   return res;
+#endif
+}
+
+static void cb_file_write_at_pointer(CB_FileHandle hfile, CB_StringView content)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+  assert(content.length > 0);
+  assert(content.data != NULL);
+
+#if OS_WINDOWS
+  // TODO(lb): win32
 #else
-  return getenv(varname);
+  CB_Scratch scratch = cb_scratch_begin();
+  s64 bytes_wrote = write(hfile, cb_cstr_from_view(content), (u64)content.length);
+  cb_scratch_end(scratch);
+  assert(bytes_wrote == content.length);
+#endif
+}
+
+static void cb_file_write_at_offset(CB_FileHandle hfile, CB_StringView content, s64 offset)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+  assert(content.length > 0);
+  assert(content.data != NULL);
+  assert(offset >= 0);
+
+#if OS_WINDOWS
+  // TODO(lb): win32
+#else
+  CB_Scratch scratch = cb_scratch_begin();
+  s64 bytes_wrote = pwrite(hfile, cb_cstr_from_view(content), (u64)content.length, offset);
+  cb_scratch_end(scratch);
+  assert(bytes_wrote <= content.length);
+#endif
+}
+
+static b8 cb_file_pointer_set_position_absolute(CB_FileHandle hfile, s64 position)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+  assert(position > 0);
+#if OS_WINDOWS
+  // TODO(lb): win32
+#else
+  off_t res = lseek(hfile, position, SEEK_SET);
+  return res == 0;
+#endif
+}
+
+static b8 cb_file_pointer_set_position_relative(CB_FileHandle hfile, s64 offset)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+#if OS_WINDOWS
+  // TODO(lb): win32
+#else
+  off_t res = lseek(hfile, offset, SEEK_CUR);
+  return res == 0;
 #endif
 }
 
-static bool cb_setenv(const char *varname, const char *value) {
-  cb_assert(varname != NULL);
-  cb_assert(value != NULL);
+static b8 cb_file_flush(CB_FileHandle hfile)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
 #if OS_WINDOWS
-  return SetEnvironmentVariableA(varname, value);
+  // TODO(lb): win32
 #else
-  return !setenv(varname, value, true);
+  s32 res = fsync(hfile);
+  return res == 0;
 #endif
 }
 
-static void cb_cmd_print(const CB_Cmd *cmd) {
-  cb_assert(cmd != NULL);
-  cb_print(CB_LogLevel_Info, "Command: `");
-  for (size_t i = 0; i < cmd->count; ++i) {
-    printf("%s ", cmd->values[i]);
-  }
-  printf("\b`\n");
+static b8 cb_file_set_size(CB_FileHandle hfile, s64 new_size)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+  assert(new_size >= 0);
+#if OS_WINDOWS
+  // TODO(lb): win32
+#else
+  s32 res = ftruncate(hfile, new_size);
+  return res == 0;
+#endif
 }
 
-static void cb_process_wait(CB_Process *proc) {
-  cb_assert(proc != NULL);
-  if (proc->handle == CB_PROC_INVALID) {
-    cb_print(CB_LogLevel_Error, "Waiting on invalid process handle\n");
-    cb_assert(false);
-  }
-
+static void cb_file_memory_unmap(u8 *file_map, s64 size)
+{
+  assert(file_map != NULL);
+  assert(size > 0);
 #if OS_WINDOWS
-  WaitForSingleObject(proc->handle, INFINITE);
-  GetExitCodeProcess(proc->handle, (LPDWORD)&proc->status_code);
-  CloseHandle(proc->handle);
+  // TODO(lb): win32
 #else
-  int32_t status = 0;
-  int32_t wait_res = waitpid(proc->handle, &status, 0);
-  cb_assert(wait_res == proc->handle);
-  if (WIFEXITED(status)) {
-    proc->status_code = WEXITSTATUS(status);
-  } else if (WIFSIGNALED(status)) {
-    proc->status_code = WTERMSIG(status);
-  } else {
-    proc->status_code = 0;
-  }
+  s32 res = munmap(file_map, (u64)size);
+  assert(res == 0);
 #endif
 }
 
-static void cb_proclist_wait(CB_ProcessList *procs) {
-  cb_assert(procs != NULL);
-  if (procs->count == 0) { return; }
-  for (size_t i = 0; i < procs->count; ++i) {
-    cb_process_wait(&procs->values[i]);
-  }
-  cb_dyn_free(procs);
+static void cb_file_close(CB_FileHandle hfile)
+{
+  assert(hfile != CB_FILEHANDLE_INVALID);
+#if OS_WINDOWS
+  // TODO(lb): win32
+#else
+  s32 res = close(hfile);
+  assert(res == 0);
+#endif
 }
 
-static CB_Handle cb_file_open(const char *path, CB_AccessFlag permission) {
-  cb_assert(path != NULL);
+static CB_FileHandle cb_filesystem_open(CB_StringView path, CB_AccessFlag flags)
+{
+  assert(path.length > 0);
+  assert(path.data != NULL);
 
 #if OS_WINDOWS
-  SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), 0, 0};
-  DWORD access_flags = 0;
-  DWORD share_mode = 0;
-  DWORD creation_disposition = OPEN_EXISTING;
-
-  if(permission & CB_AccessFlag_Read) { access_flags |= GENERIC_READ; }
-  if(permission & CB_AccessFlag_Write) {
-    access_flags |= GENERIC_WRITE;
-    creation_disposition = CREATE_ALWAYS;
-  }
-  if(permission & CB_AccessFlag_Append) {
-    access_flags |= FILE_APPEND_DATA;
-    creation_disposition = OPEN_ALWAYS;
-  }
-  return CreateFileA(path, access_flags,
-                     share_mode, &security_attributes,
-                     creation_disposition,
-                     FILE_ATTRIBUTE_NORMAL, 0);
+  // TODO(lb): win32
 #else
-  int32_t flags = O_CREAT;
-  if ((permission & CB_AccessFlag_Read) && ((permission & CB_AccessFlag_Write) ||
-                                            (permission & CB_AccessFlag_Append))) {
-    flags |= O_RDWR;
+  s32 mode = O_CREAT;
+  if ((flags & CB_AccessFlag_Read) && ((flags & CB_AccessFlag_Write) || (flags & CB_AccessFlag_Append))) {
+    mode |= O_RDWR;
   } else {
-    if (permission & CB_AccessFlag_Read) {
-      flags |= O_RDONLY;
-    } else if ((permission & CB_AccessFlag_Write) ||
-               (permission & CB_AccessFlag_Append)) {
-      flags |= O_WRONLY;
+    if (flags & CB_AccessFlag_Read) {
+      mode |= O_RDONLY;
+    } else if ((flags & CB_AccessFlag_Write) || (flags & CB_AccessFlag_Append)) {
+      mode |= O_WRONLY;
     }
   }
-  if (permission & CB_AccessFlag_Append) { flags |= O_APPEND; }
-  int32_t res =  open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-  if (res == -1) { return CB_HANDLE_INVALID; }
-  return (CB_Handle)res;
+  if (flags & CB_AccessFlag_Append) { mode |= O_APPEND; }
+  CB_Scratch scratch = cb_scratch_begin();
+  int32_t res = open(cb_cstr_from_view(path), mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+  cb_scratch_end(scratch);
+  if (res == -1) { return CB_FILEHANDLE_INVALID; }
+  return (CB_FileHandle)res;
 #endif
 }
 
-static void cb_file_close(CB_Handle fd) {
-  if (fd == CB_HANDLE_INVALID) {
-    cb_print(CB_LogLevel_Error, "Closing invalid handle\n");
-    cb_assert(false);
-  }
-
+static CB_FileHandle cb_filesystem_open_temp(CB_StringView *path)
+{
+  assert(path != NULL);
 #if OS_WINDOWS
-  FlushFileBuffers(fd);
-  CloseHandle(fd);
+  // TODO(lb): win32
 #else
-  close(fd);
+  path->length = cb_arrlength("/tmp/XXXXXX");
+  char *bytes = cb_arena_push_many(char, path->length);
+  cb_memcopy(bytes, "/tmp/XXXXXX", path->length);
+  path->data = (u8*)(bytes);
+  CB_FileHandle res = mkstemp(bytes);
+  assert(res != -1);
+  return res;
 #endif
 }
 
-static CB_StringView cb_file_read(CB_Handle fd) {
-  if (fd == CB_HANDLE_INVALID) {
-    cb_print(CB_LogLevel_Warn, "Reading from invalid handle\n");
-    return (CB_StringView) {0};
-  }
+static CB_FileProperties cb_filesystem_properties(CB_StringView path)
+{
+  assert(path.length > 0);
+  assert(path.data != NULL);
 
 #if OS_WINDOWS
-  LARGE_INTEGER file_size = {0};
-  GetFileSizeEx(fd, &file_size);
-
-  LONGLONG size = file_size.QuadPart;
-  LONGLONG total_read = 0;
-  char *buffer = malloc((size_t)(size + 1));
-  ReadFile(fd, buffer, (DWORD)size, NULL, NULL);
-  buffer[size] = 0;
-  return (CB_StringView) {
-    .data = buffer,
-    .length = size,
-  };
+  // TODO(lb): get file properties from path on win32
+  CB_FileProperties res = {0};
+  return res;
 #else
   struct stat file_stat = {0};
-  if (fstat(fd, &file_stat) == -1 || file_stat.st_size <= 0) { return (CB_StringView) {0}; }
-  char *res = malloc(file_stat.st_size + 1);
-  if (pread(fd, res, file_stat.st_size, 0) == -1) { return (CB_StringView) {0}; }
-  res[file_stat.st_size] = 0;
-  return (CB_StringView) {
-    .data = res,
-    .length = file_stat.st_size,
-  };
+  CB_Scratch scratch = cb_scratch_begin();
+  s32 stat_res = stat(cb_cstr_from_view(path), &file_stat);
+  cb_scratch_end(scratch);
+  assert(stat_res == 0);
+  return cb_fileproperties_from_stat(&file_stat);
 #endif
 }
 
-static void cb_file_write_sv(CB_Handle fd, CB_StringView sv) {
-  _cb_file_write_buffer(fd, sv.data, sv.length);
+static void cb_filesystem_delete(CB_StringView path)
+{
+  assert(path.length > 0);
+  assert(path.data != NULL);
+#if OS_WINDOWS
+  // TODO(lb): implement file delete on win32
+#else
+  CB_Scratch scratch = cb_scratch_begin();
+  unlink(cb_cstr_from_view(path));
+  cb_scratch_end(scratch);
+#endif
 }
 
-static void cb_file_write_cstr(CB_Handle fd, const char *buffer) {
-  if (fd == CB_HANDLE_INVALID) {
-    cb_print(CB_LogLevel_Error, "Writing to invalid handle\n");
-    assert(false);
-  }
-  size_t buffsize = strlen(buffer);
-  _cb_file_write_buffer(fd, buffer, buffsize);
+static b8 cb_filesystem_exists(CB_StringView path)
+{
+#if OS_WINDOWS
+  // TODO(lb): win32
+#  define F_OK 0
+#  define access _access
+#endif
+  CB_Scratch scratch = cb_scratch_begin();
+  b8 res = (access(cb_cstr_from_view(path), F_OK) == 0);
+  cb_scratch_end(scratch);
+  return res;
 }
 
-static bool cb_dir_create(const char *path) {
-  cb_assert(path != NULL);
-  int32_t mkdir_res = _cb_platform_mkdir(path);
-  if (mkdir_res < 0 && errno == ENOENT) {
-    size_t parent_end = _last_occurance_of(path, '/');
-    if (!parent_end) { return false; }
-    char *parent = (char *)malloc(parent_end + 1);
-    memcpy(parent, path, parent_end);
-    parent[parent_end] = 0;
-    cb_dir_create(parent);
-    free(parent);
-    _cb_platform_mkdir(path);
-  }
-  return !mkdir_res;
+static b8 cb_filesystem_rename(CB_StringView old, CB_StringView new)
+{
+#if OS_WINDOWS
+  // TODO(lb): implement file rename on win32
+#else
+  CB_Scratch scratch = cb_scratch_begin();
+  char *old_cstr = cb_cstr_from_view(old);
+  char *new_cstr = cb_cstr_from_view(new);
+  b8 res = (rename(old_cstr, new_cstr) == 0);
+  cb_scratch_end(scratch);
+  return res;
+#endif
 }
 
-static void cb_dir_delete(const char *path) {
+static b8 cb_filesystem_copy(CB_StringView path_source, CB_StringView path_dest, s64 bytes_to_copy, s64 offset_source, s64 offset_dest)
+{
+  assert(bytes_to_copy > 0);
+  assert(offset_source >= 0);
+  assert(offset_dest >= 0);
+  assert(cb_filesystem_exists(path_source));
+  if (!cb_filesystem_exists(path_dest)) { assert(offset_dest == 0); }
 #if OS_WINDOWS
-  _rmdir(path);
+  // TODO(lb): win32
 #else
-  rmdir(path);
+  CB_Scratch scratch = cb_scratch_begin();
+  s32 src = open(cb_cstr_from_view(path_source), O_RDONLY, 0);
+  s32 dest = open(cb_cstr_from_view(path_dest), O_WRONLY | O_CREAT, 0644);
+  cb_scratch_end(scratch);
+  assert(src != -1);
+  assert(dest != -1);
+
+  s64 bytes_copied = copy_file_range(src, &offset_source, dest, &offset_dest, (u64)bytes_to_copy, 0);
+  assert(bytes_copied != -1);
+
+  struct stat stat_src = {0};
+  s32 res_fstat = fstat(src, &stat_src);
+  assert(res_fstat == 0);
+  fchmod(dest, stat_src.st_mode & 0777);
+
+  fdatasync(dest);
+  close(src);
+  close(dest);
+  return bytes_copied == bytes_to_copy;
 #endif
 }
 
-static void cb_file_delete(const char *path) {
+static b8 cb_filesystem_link_create_symbolic(CB_StringView realpath, CB_StringView linkpath)
+{
+  assert(cb_filesystem_exists(realpath));
+  assert(!cb_filesystem_exists(linkpath));
 #if OS_WINDOWS
-  _unlink(path);
+  // TODO(lb): create symlink on win32
+  return false;
 #else
-  unlink(path);
+  CB_Scratch scratch = cb_scratch_begin();
+  s32 res = symlink(cb_cstr_from_view(realpath), cb_cstr_from_view(linkpath));
+  cb_scratch_end(scratch);
+  return res == 0;
 #endif
 }
 
-static bool cb_file_rename(const char *path, const char *to) {
+static b8 cb_filesystem_link_create_physical(CB_StringView realpath, CB_StringView linkpath)
+{
+  assert(cb_filesystem_properties(realpath).type != CB_FileType_Directory);
+  assert(!cb_filesystem_exists(linkpath));
 #if OS_WINDOWS
-  return MoveFileExA(path, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
+  // TODO(lb): win32
 #else
-  return !rename(path, to);
+  CB_Scratch scratch = cb_scratch_begin();
+  s32 res = link(cb_cstr_from_view(realpath), cb_cstr_from_view(linkpath));
+  cb_scratch_end(scratch);
+  return res == 0;
 #endif
 }
 
-static bool cb_file_exists(const char *path) {
+static CB_StringView cb_filesystem_link_read(CB_StringView linkpath)
+{
+  assert(cb_filesystem_exists(linkpath));
+  assert(cb_filesystem_properties(linkpath).type == CB_FileType_Link);
+  CB_StringView res = {0};
 #if OS_WINDOWS
-#  define F_OK 0
-#  define access _access
+  // TODO(lb): win32
+#else
+  s64 approx_length = 1024;
+  char *buffer = cb_arena_push_many(char, approx_length);
+  CB_Scratch scratch = cb_scratch_begin();
+  s64 length = readlink(cb_cstr_from_view(linkpath), buffer, (u64)approx_length);
+  assert(length > 0);
+  cb_scratch_end(scratch);
+  cb_arena_pop(approx_length - length);
+  res.length = length;
+  res.data = (u8*)buffer;
 #endif
-  return access(path, F_OK) == 0;
+  return res;
 }
 
-static bool cb_symlink_create(const char *target, const char *linkpath) {
-  cb_assert(target != NULL);
-  cb_assert(linkpath != NULL);
-
+static b8 cb_filesystem_directory_create(CB_StringView path)
+{
+  assert(!cb_filesystem_exists(path));
 #if OS_WINDOWS
+  // TODO(lb): win32
 #else
-  int32_t res = symlink(target, linkpath);
+  CB_Scratch scratch = cb_scratch_begin();
+  s32 res = mkdir(cb_cstr_from_view(path), (S_IRWXU) | (S_IRWXG) | (S_IROTH | S_IXOTH));
+  cb_scratch_end(scratch);
   return res == 0;
 #endif
 }
 
-static void cb_file_iter_begin(CB_FileIterator *it, const char *rootpath) {
+static b8 cb_filesystem_directory_copy(CB_StringView dirpath, CB_StringView new)
+{
+  assert(cb_filesystem_exists(dirpath));
+  assert(cb_filesystem_properties(dirpath).type == CB_FileType_Directory);
+  assert(!cb_filesystem_exists(new));
+  // TODO(lb): directory copy
+  return false;
+}
+
+static b8 cb_filesystem_directory_delete(CB_StringView path, b8 recursive)
+{
+  assert(cb_filesystem_exists(path));
+  assert(cb_filesystem_properties(path).type == CB_FileType_Directory);
+  // TODO(lb): directory delete
+  return false;
+}
+
+static CB_StringView cb_filesystem_filename_from_path(CB_StringView path)
+{
+  assert(cb_filesystem_exists(path));
+#if OS_WINDOWS
+    // TODO(lb): win32
+#else
+  const char *fullname = basename(cb_cstr_from_view(path));
+  assert(fullname != NULL);
+  CB_StringView res = cb_view_from_cstr(fullname);
+  return res;
+#endif
+}
+
+static CB_StringView cb_filesystem_path_canonicalize(CB_StringView path)
+{
+  assert(cb_filesystem_exists(path));
+#if OS_WINDOWS
+    // TODO(lb): win32
+#else
+  CB_Scratch scratch = cb_scratch_begin();
+  char *cstr_fullpath = realpath(cb_cstr_from_view(path), NULL);
+  assert(cstr_fullpath != NULL);
+  cb_scratch_end(scratch);
+  CB_StringView res = cb_view_from_cstr(cstr_fullpath);
+  return res;
+#endif
+}
+
+static void cb_filesystem_iter_begin(CB_FileIterator *it, const char *rootpath)
+{
   memset(&it->handle, 0, sizeof it->handle);
 #if OS_WINDOWS
   // TODO(lb): file iteration on win32
@@ -658,12 +1296,14 @@ static void cb_file_iter_begin(CB_FileIterator *it, const char *rootpath) {
 #endif
 }
 
-static bool cb_file_iter_next(CB_FileIterator *it, CB_FileInfo *output) {
+static b8 cb_filesystem_iter_next(CB_FileIterator *it, CB_FileInfo *output)
+{
   static const char *const currdir = ".";
   static const char *const parentdir = "..";
 
 #if OS_WINDOWS
   // TODO(lb): file iteration on win32
+  return false;
 #else
   struct dirent *entry = NULL;
   char *filepath = NULL;
@@ -680,9 +1320,9 @@ static bool cb_file_iter_next(CB_FileIterator *it, CB_FileInfo *output) {
     if (!filename) { return false; }
 
     int32_t filepath_size = snprintf(NULL, 0, "%s/%s", it->handle.path, filename) + 1;
-    filepath = malloc(filepath_size);
+    filepath = malloc((uint32_t)filepath_size);
     if (!filepath) { return false; }
-    snprintf(filepath, filepath_size, "%s/%s", it->handle.path, filename);
+    snprintf(filepath, (uint32_t)filepath_size, "%s/%s", it->handle.path, filename);
 
     struct stat file_stat = {0};
     if (stat(filepath, &file_stat) != 0) {
@@ -692,13 +1332,13 @@ static bool cb_file_iter_next(CB_FileIterator *it, CB_FileInfo *output) {
 
     CB_FileType filetype = 0;
     switch (file_stat.st_mode & S_IFMT) {
-    case S_IFBLK:  filetype = CB_FileType_Device_Block; break;
-    case S_IFCHR:  filetype = CB_FileType_Device_Char;  break;
-    case S_IFDIR:  filetype = CB_FileType_Directory;    break;
-    case S_IFIFO:  filetype = CB_FileType_Pipe;         break;
-    case S_IFLNK:  filetype = CB_FileType_Link;         break;
-    case S_IFSOCK: filetype = CB_FileType_Socket;       break;
-    case S_IFREG:  filetype = CB_FileType_Regular;      break;
+    case S_IFBLK:  filetype = CB_FileType_Device_Disk;          break;
+    case S_IFCHR:  filetype = CB_FileType_Device_Terminal;      break;
+    case S_IFDIR:  filetype = CB_FileType_Directory;            break;
+    case S_IFIFO:  filetype = CB_FileType_Pipe;                 break;
+    case S_IFLNK:  filetype = CB_FileType_Link;                 break;
+    case S_IFSOCK: filetype = CB_FileType_Socket;               break;
+    case S_IFREG:  filetype = CB_FileType_Regular;              break;
     }
 
     if (!(it->filter_allowed & filetype)) {
@@ -709,7 +1349,7 @@ static bool cb_file_iter_next(CB_FileIterator *it, CB_FileInfo *output) {
       } else {
         output->path = filepath;
         output->type = filetype;
-        output->size = file_stat.st_size;
+        output->size = (uint32_t)file_stat.st_size;
       }
       return true;
     }
@@ -718,7 +1358,8 @@ static bool cb_file_iter_next(CB_FileIterator *it, CB_FileInfo *output) {
 #endif
 }
 
-static void cb_file_iter_end(CB_FileIterator *it) {
+static void cb_filesystem_iter_end(CB_FileIterator *it)
+{
 #if OS_WINDOWS
   // TODO(lb): file iteration on win32
 #else
@@ -726,162 +1367,98 @@ static void cb_file_iter_end(CB_FileIterator *it) {
 #endif
 }
 
-static bool cb_cli_contains(int32_t argc, char **argv, const char *target) {
-  for (int32_t i = 0; i < argc; ++i) {
-    if (!strcmp(argv[i], target)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-static bool cb_view_starts_with(const char *string, CB_StringView sv) {
-  if (string == NULL || sv.length == 0 || sv.data == NULL) { return false; }
-  for (int32_t i = 0; i < sv.length; ++i) {
-    if (string[i] == 0 || string[i] != sv.data[i]) { return false; }
-  }
-  return true;
-}
-
-static void cb_view_trim(CB_StringView *sv) {
-  cb_assert(sv != NULL);
-  if (sv->length <= 0) { return; }
-  cb_assert(sv->data != NULL);
-  cb_assert(sv->length > 0);
-
-  for (; sv->data[0] == ' ' || sv->data[0] == '\n' || sv->data[0] == '\r' || sv->data[0] == '\t';
-       sv->length -= 1, sv->data += 1) {}
-  for (; sv->data[sv->length - 1] == ' ' || sv->data[sv->length - 1] == '\n' ||
-         sv->data[sv->length - 1] == '\r' || sv->data[sv->length - 1] == '\t';
-       sv->length -= 1) {}
-}
-
-static CB_StringBuilder cb_view_split(CB_StringView sv, char delim) {
-  CB_StringBuilder res = {0};
-  if (sv.data == NULL || sv.length == 0) { return res; }
-  size_t prev = 0;
-  for (size_t i = 0; i < sv.length;) {
-    if (sv.data[i] == delim) {
-      if (prev != i) {
-        cb_sb_append(&res, (CB_StringView) {
-          .data = sv.data + prev,
-          .length = i - prev,
-        });
-      }
-      do {
-        prev = ++i;
-      } while (sv.data[i] == delim);
-    } else {
-      ++i;
-    }
-  }
-  if (prev != sv.length) {
-    cb_sb_append(&res, (CB_StringView) {
-      .data = sv.data + prev,
-      .length = sv.length - prev,
-    });
+#if !OS_WINDOWS
+internal CB_FileProperties cb_fileproperties_from_stat(struct stat *file_stat)
+{
+  CB_FileProperties res = {0};
+  res.size = file_stat->st_size;
+  res.time_last_access = cb_time64_from_unix((unix_time)file_stat->st_atime);
+  res.time_last_modification = cb_time64_from_unix((unix_time)file_stat->st_mtime);
+  res.time_last_status_change = cb_time64_from_unix((unix_time)file_stat->st_ctime);
+  switch (file_stat->st_mode & S_IFMT) {
+  case S_IFBLK:  res.type = CB_FileType_Device_Disk;         break;
+  case S_IFCHR:  res.type = CB_FileType_Device_Terminal;     break;
+  case S_IFDIR:  res.type = CB_FileType_Directory;           break;
+  case S_IFIFO:  res.type = CB_FileType_Pipe;                break;
+  case S_IFLNK:  res.type = CB_FileType_Link;                break;
+  case S_IFSOCK: res.type = CB_FileType_Socket;              break;
+  case S_IFREG:  res.type = CB_FileType_Regular;             break;
   }
-  cb_assert(res.node_count > 0);
-  return res;
-}
-
-static CB_StringView cb_view_from_cstr(const char *cstr) {
-  CB_StringView res = {0};
-  res.data = cstr;
-  res.length = strlen(cstr);
+  res.permission.user = CB_FilePermission_Unknown;
+  if (file_stat->st_mode & S_IRUSR) { res.permission.user |= CB_FilePermission_Read; }
+  if (file_stat->st_mode & S_IWUSR) { res.permission.user |= CB_FilePermission_Write; }
+  if (file_stat->st_mode & S_IXUSR) { res.permission.user |= CB_FilePermission_Execute; }
+  res.permission.group = CB_FilePermission_Unknown;
+  if (file_stat->st_mode & S_IRGRP) { res.permission.group |= CB_FilePermission_Read; }
+  if (file_stat->st_mode & S_IWGRP) { res.permission.group |= CB_FilePermission_Write; }
+  if (file_stat->st_mode & S_IXGRP) { res.permission.group |= CB_FilePermission_Execute; }
+  res.permission.other = CB_FilePermission_Unknown;
+  if (file_stat->st_mode & S_IROTH) { res.permission.other |= CB_FilePermission_Read; }
+  if (file_stat->st_mode & S_IWOTH) { res.permission.other |= CB_FilePermission_Write; }
+  if (file_stat->st_mode & S_IXOTH) { res.permission.other |= CB_FilePermission_Execute; }
   return res;
 }
+#endif
 
-static void cb_sb_append(CB_StringBuilder *sb, CB_StringView sv) {
-  cb_assert(sb != NULL);
-  if (sv.length == 0 || sv.data == NULL) { return; }
-  struct CB_StringBuilder_Node *node = malloc(sizeof *node);
-  cb_assert(node != NULL);
-  memset(node, 0, sizeof *node);
-  node->value = sv;
-  bll_push_back(sb->first, sb->last, node);
-  sb->total_length += sv.length;
-  sb->node_count += 1;
-}
-
-static void cb_sb_concat(CB_StringBuilder *sb, const CB_StringBuilder *other) {
-  cb_assert(sb != NULL);
-  cb_assert(sb->node_count >= 0);
-  cb_assert(sb->total_length >= 0);
-  cb_assert(other != NULL);
-  cb_assert(other->node_count >= 0);
-  cb_assert(other->total_length >= 0);
-  sb->node_count += other->node_count;
-  sb->total_length += other->total_length;
-  bll_push_back(sb->first, sb->last, other->first);
-  sb->last = other->last;
-}
-
-static CB_StringView cb_sb_join_with_ch(CB_StringBuilder sb, char ch) {
-  CB_StringView sv = {0};
-  sv.data = malloc(sb.total_length + sb.node_count);
-  sv.length = sb.total_length + sb.node_count - 1;
-  int32_t offset = 0;
-  for (struct CB_StringBuilder_Node *curr = sb.first; curr;) {
-    memcpy((char*)(sv.data + offset), curr->value.data, curr->value.length);
-    offset += curr->value.length;
-    ((char*)sv.data)[offset++] = ch;
-    struct CB_StringBuilder_Node *next = curr->next;
-    free(curr);
-    curr = next;
+// ================================================================================
+// Processes
+static void cb_process_wait(CB_Process *proc)
+{
+  cb_assert(proc != NULL);
+  if (proc->handle == CB_PROC_INVALID) {
+    cb_print(CB_LogLevel_Error, "Waiting on invalid process handle\n");
+    cb_assert(false);
   }
-  ((char*)sv.data)[sv.length] = 0;
-  return sv;
-}
 
-internal void _cb_file_write_buffer(CB_Handle fd, const char *buffer, size_t size) {
-  cb_assert(fd != CB_HANDLE_INVALID);
-  cb_assert(buffer != NULL);
-  cb_assert(size > 0);
-  while (!buffer[size - 1]) { size -= 1; }
 #if OS_WINDOWS
-  uint64_t to_write = buffsize;
-  uint64_t total_write = 0;
-  char *ptr = buffer;
-  for(;total_write < to_write;) {
-    uint64_t amount64 = to_write - total_write;;
-    uint32_t amount32 = amount64 > 0xFFFFFFFF ? 0xFFFFFFFF : (uint32_t)amount64;
-    DWORD bytes_written = 0;
-    WriteFile(fd, ptr + total_write, amount32, &bytes_written, 0);
-    total_write += bytes_written;
-    if(bytes_written != amount32) { break; }
-  }
+  WaitForSingleObject(proc->handle, INFINITE);
+  GetExitCodeProcess(proc->handle, (LPDWORD)&proc->status_code);
+  CloseHandle(proc->handle);
 #else
-  write(fd, buffer, size);
+  int32_t status = 0;
+  int32_t wait_res = waitpid(proc->handle, &status, 0);
+  cb_assert(wait_res == proc->handle);
+  if (WIFEXITED(status)) {
+    proc->status_code = WEXITSTATUS(status);
+  } else if (WIFSIGNALED(status)) {
+    proc->status_code = WTERMSIG(status);
+  } else {
+    proc->status_code = 0;
+  }
 #endif
 }
 
-internal bool _cb_is_outdated(const char *output, ...) {
-  struct CB_PathList sources = {0};
-  va_list args;
-  va_start(args, output);
-  for (;;) {
-    char *path = va_arg(args, char*);
-    if (!path) { break; }
-    cb_dyn_push(&sources, path);
+static void cb_proclist_wait(CB_ProcessList *procs)
+{
+  cb_assert(procs != NULL);
+  if (procs->count == 0) { return; }
+  for (size_t i = 0; i < procs->count; ++i) {
+    cb_process_wait(&procs->values[i]);
   }
-  va_end(args);
-  return _cb_need_rebuild(output, sources);
+  cb_dyn_free(procs);
 }
 
+// ================================================================================
+// Commands
+static void cb_cmd_print(const CB_Cmd *cmd)
+{
+  cb_assert(cmd != NULL);
+  cb_print(CB_LogLevel_Info, "Command: `");
+  for (size_t i = 0; i < cmd->count; ++i) {
+    printf("%s ", cmd->values[i]);
+  }
+  printf("\b`\n");
+}
 
-internal char* cb_format_va(const char *format, va_list args) {
-  va_list args2;
-  va_copy(args2, args);
-  size_t needed_bytes = (size_t)vsnprintf(0, 0, format, args2) + 1;
-  va_end(args2);
-
-  char *res = malloc(needed_bytes);
-  (void)vsnprintf(res, needed_bytes, format, args);
-  return res;
+static void cb_cmd_clear(CB_Cmd *cmd)
+{
+  free(cmd->values);
+  cmd->capacity = cmd->count = 0;
+  cmd->values = NULL;
 }
 
-internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args) {
+internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args)
+{
   CB_Process res = {0};
 
 #if OS_WINDOWS
@@ -897,9 +1474,9 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args) {
   STARTUPINFOA si = {0};
   si.cb = sizeof(si);
   si.dwFlags = STARTF_USESTDHANDLES;
-  si.hStdInput  = args.stdin  ? args.stdin  : GetStdHandle(STD_INPUT_HANDLE);
-  si.hStdOutput = args.stdout ? args.stdout : GetStdHandle(STD_OUTPUT_HANDLE);
-  si.hStdError  = args.stderr ? args.stderr : GetStdHandle(STD_ERROR_HANDLE);
+  si.hStdInput  = args.stream_in  ? args.stream_in  : GetStdHandle(STD_INPUT_HANDLE);
+  si.hStdOutput = args.stream_out ? args.stream_out : GetStdHandle(STD_OUTPUT_HANDLE);
+  si.hStdError  = args.stream_err ? args.stream_err : GetStdHandle(STD_ERROR_HANDLE);
 
   PROCESS_INFORMATION pi = {0};
   if (!CreateProcessA(0, cmdline, 0, 0, TRUE, 0, 0, 0, &si, &pi)) {
@@ -924,11 +1501,11 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args) {
              cmd->values[0], errno, strerror(errno));
     exit(-1);
   } else if (!res.handle) {
-    if (args.stdout) { dup2(args.stdout, STDOUT_FILENO); }
-    if (args.stderr) { dup2(args.stderr, STDERR_FILENO); }
-    if (args.stdin)  {
-      lseek(args.stdin, 0, SEEK_SET);
-      dup2(args.stdin, STDIN_FILENO);
+    if (args.stream_out) { dup2(args.stream_out, STDOUT_FILENO); }
+    if (args.stream_err) { dup2(args.stream_err, STDERR_FILENO); }
+    if (args.stream_in)  {
+      lseek(args.stream_in, 0, SEEK_SET);
+      dup2(args.stream_in, STDIN_FILENO);
     }
 
     CB_Cmd _cmd = {0};
@@ -953,118 +1530,98 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args) {
   return res;
 }
 
-internal void _cb_rebuild(int argc, char **argv, const char *builder_src, ...) {
-  cb_assert(argc >= 1);
-  char *exe_name = argv[0];
-
-  struct CB_PathList sources = {0};
-  cb_dyn_push(&sources, builder_src);
-  va_list args;
-  va_start(args, builder_src);
-  for (;;) {
-    char *path = va_arg(args, char*);
-    if (!path) { break; }
-    cb_dyn_push(&sources, path);
-  }
-  va_end(args);
-
-  if (!_cb_need_rebuild(exe_name, sources)) {
-    cb_dyn_free(&sources);
-    return;
+static void cb_rebuild_and_restart(s32 argc, char **argv, CB_Array sources, CB_Array build_options)
+{
+  assert(argc >= 1);
+  assert(argv != NULL);
+  assert(sources.length >= 1);
+  assert(sources.data != NULL);
+  assert(build_options.length >= 0);
+  if (build_options.length > 0) { assert(build_options.data != NULL); }
+
+  b8 should_rebuild = false;
+  CB_StringView executable = cb_view_from_cstr(argv[0]);
+  cb_view_trim(&executable);
+  time64 last_edit_executable = cb_filesystem_properties(executable).time_last_modification;
+  CB_StringView *str_sources = sources.data;
+  for (s64 i = 0; i < sources.length; ++i) {
+    time64 last_edit_source = cb_filesystem_properties(str_sources[i]).time_last_modification;
+    if (last_edit_executable < last_edit_source) {
+      should_rebuild = true;
+      break;
+    }
   }
-  cb_print(CB_LogLevel_Info, "rebuilding %s\n", exe_name);
+  if (!should_rebuild) { return; }
 
 #if OS_WINDOWS
-  char *exe_name_old = cb_format("%s.old", exe_name);
-  if (!cb_file_rename(exe_name, exe_name_old)) {
-    cb_print(CB_LogLevel_Info, "File rename failed: %s -> %s\n",
-             exe_name, exe_name_old);
-    exit(-1);
+  {
+    CB_Scratch scratch = cb_scratch_begin();
+    CB_StringView executable_old = cb_view_from_cstr(cb_format("%.*s.old", executable));
+    if (!cb_file_rename(executable, executable_old)) {
+      cb_print(CB_LogLevel_Info, "File rename failed: %.*s -> %.*s\n", cb_view_expand(executable), cb_view_expand(executable_old));
+      exit(-1);
+    }
+    cb_scratch_end(scratch);
   }
 #endif
 
   CB_Cmd cmd = {0};
-  cb_cmd_append(&cmd, CB_CMD_REBUILD_SELF(exe_name, builder_src));
-  cb_print(CB_LogLevel_Info, "running: `");
-  for (size_t i = 0; i < cmd.count; ++i) {
-    printf("%s ", cmd.values[i]);
+  cb_cmd_push(&cmd, CC);
+#if COMPILER_CL
+  cb_cmd_append(&cmd, "/std:c11", "/nologo");
+  cb_cmd_push(&cmd, cb_format("/Fe:%.*s", cb_view_expand(executable)));
+  cb_cmd_push(&cmd, cb_format("%.*s", cb_view_expand(str_sources[0])));
+#else
+  cb_cmd_push(&cmd, "-o");
+  cb_cmd_push(&cmd, cb_format("%.*s", cb_view_expand(executable)));
+  cb_cmd_push(&cmd, cb_format("%.*s", cb_view_expand(str_sources[0])));
+#endif
+  CB_StringView *str_build_options = build_options.data;
+  for (s64 i = 0; i < build_options.length; ++i) {
+    cb_cmd_push(&cmd, cb_format("%.*s", cb_view_expand(str_build_options[i])));
   }
-  printf("\b`\n");
-  CB_Process recompiler = cb_cmd_exec(&cmd);
-  if (recompiler.status_code) {
+
+  cb_cmd_print(&cmd);
+  CB_Process compilation_proc = cb_cmd_exec(&cmd);
+  if (compilation_proc.status_code != 0) {
 #if OS_WINDOWS
-    cb_file_rename(exe_name_old, exe_name);
+    cb_file_rename(executable_old, executable);
 #endif
     exit(-1);
   }
 
-  cb_cmd_push(&cmd, exe_name);
+  cb_cmd_push(&cmd, cb_format("%.*s", cb_view_expand(executable)));
   cb_cmd_append_dyn(&cmd, argv, (size_t)argc);
   (void)cb_cmd_exec(&cmd);
   exit(0);
 }
 
-internal bool _cb_need_rebuild(const char *output_path, struct CB_PathList sources) {
+// ==============================================================================
+// Environment variables
+static char* cb_getenv(const char *varname)
+{
+  cb_assert(varname != NULL);
 #if OS_WINDOWS
-  FILETIME output_mtime_large = {0};
-  HANDLE output_handle = CreateFileA(output_path, GENERIC_READ,
-                                     FILE_SHARE_READ, 0, OPEN_EXISTING,
-                                     FILE_ATTRIBUTE_NORMAL, 0);
-  if (output_handle == INVALID_HANDLE_VALUE ||
-      !GetFileTime(output_handle, 0, 0, &output_mtime_large)) {
-    CloseHandle(output_handle);
-    return true;
-  }
-  CloseHandle(output_handle);
-
-  ULARGE_INTEGER output_mtime = {0};
-  output_mtime.LowPart = output_mtime_large.dwLowDateTime;
-  output_mtime.HighPart = output_mtime_large.dwHighDateTime;
-
-  for (size_t i = 0; i < sources.count; ++i) {
-    FILETIME source_mtime_large = {0};
-    HANDLE source_handle = CreateFileA(sources.values[i], GENERIC_READ,
-                                       FILE_SHARE_READ, 0, OPEN_EXISTING,
-                                       FILE_ATTRIBUTE_NORMAL, 0);
-    if (source_handle == INVALID_HANDLE_VALUE) { return true; }
-    if (!GetFileTime(source_handle, 0, 0, &source_mtime_large)) {
-      CloseHandle(output_handle);
-      return true;
-    }
-    CloseHandle(output_handle);
-
-    ULARGE_INTEGER source_mtime = {0};
-    source_mtime.LowPart = source_mtime_large.dwLowDateTime;
-    source_mtime.HighPart = source_mtime_large.dwHighDateTime;
-    if (output_mtime.QuadPart < source_mtime.QuadPart) {
-      return true;
-    }
+  char *res = cb_arena_push_many(char, MAX_ENVVAR);
+  if (!GetEnvironmentVariableA(varname, res, MAX_ENVVAR)) {
+    cb_arena_pop(sizeof(char) * MAX_ENVVAR);
+    res = 0;
   }
-  return false;
+  return res;
 #else
-  struct stat output_stat = {};
-  if (stat(output_path, &output_stat) < 0) { return true; }
-  for (size_t i = 0; i < sources.count; ++i) {
-    struct stat source_stat = {};
-    if (stat(sources.values[i], &source_stat) < 0) {
-    rebuild_failure:
-      cb_print(CB_LogLevel_Error,
-               "`%s` modification time unreadable: %s\n",
-               sources.values[i], strerror(errno));
-      exit(-1);
-    }
-    if (output_stat.st_mtime < source_stat.st_mtime) { return true; }
-  }
-  return false;
+  return getenv(varname);
 #endif
 }
 
-internal size_t _last_occurance_of(const char *string, char ch) {
-  const char *res = string;
-  for (const char *curr = string; curr && *curr; ++curr) {
-    if (*curr == ch) { res = curr; }
-  }
-  return (size_t)(res - string);
+static b8 cb_setenv(const char *varname, const char *value)
+{
+  cb_assert(varname != NULL);
+  cb_assert(value != NULL);
+#if OS_WINDOWS
+  return SetEnvironmentVariableA(varname, value);
+#else
+  return !setenv(varname, value, true);
+#endif
 }
 
 #endif
index f7d3e73a3e7bd7ee85468c7136bfe8a769d7acc2..c1fae9ecf8faaf92cb395553043ac7d1c48bc14a 100644 (file)
@@ -13,25 +13,23 @@ typedef struct {
   } signature;
 } CB_Generator;
 
-static void cb_gen_write(CB_Generator *gen, char *filepath, bool append_mode);
+static void cb_gen_write(CB_Generator *gen, CB_StringView path, b8 append_mode);
 
 static void cb_gen_push(CB_Generator *gen, char *string);
 static void cb_gen_push_func_begin(CB_Generator *gen, char *signature_no_args);
 static void cb_gen_push_func_arg(CB_Generator *gen, char *argument);
-static void cb_gen_push_func_end(CB_Generator *gen, bool implementation);
+static void cb_gen_push_func_end(CB_Generator *gen, b8 implementation);
 
 // ======================================================================
 // Implementations
-static void cb_gen_write(CB_Generator *gen, char *filepath, bool append_mode) {
-  CB_Handle file = cb_file_open(filepath, append_mode
-                                          ? CB_AccessFlag_Append
-                                          : CB_AccessFlag_Write);
+static void cb_gen_write(CB_Generator *gen, CB_StringView path, b8 append_mode) {
+  CB_FileHandle file = cb_filesystem_open(path, append_mode ? CB_AccessFlag_Append : CB_AccessFlag_Write);
   char *full_string = malloc(gen->total_length + 1);
   memset(full_string, 0, gen->total_length + 1);
   for (size_t i = 0; i < gen->count; ++i) {
     strcat(full_string, gen->values[i]);
   }
-  cb_file_write(file, full_string);
+  cb_file_write_at_pointer(file, cb_view_from_cstr(full_string));
   free(full_string);
   cb_file_close(file);
   cb_dyn_free(gen);
@@ -72,7 +70,7 @@ static void cb_gen_push_func_arg(CB_Generator *gen, char *argument) {
 #define cb_gen_push_func_end(GeneratorPtr, Implementation) \
   _cb_gen_push_func_end((GeneratorPtr), (Implementation), __FILE__, __LINE__)
 
-static void _cb_gen_push_func_end(CB_Generator *gen, bool implementation,
+static void _cb_gen_push_func_end(CB_Generator *gen, b8 implementation,
                                  const char *file, int32_t line) {
   if (!gen->signature.has_args) {
     cb_gen_push(gen, "void");
index cf6f1f0bbe0c9b8ce00ef60d3b24e5304caf9918..e70b9d89c02a5dca21f6d4633c7caf9b02a3350e 100644 (file)
 
 internal void
 cb_linagen_defun_vecn_element_wise(CB_Generator *gen, char *type,
-                                   int32_t n, bool implementation,
+                                   int32_t n, b8 implementation,
                                    char *func_name, char op);
 
 internal void
 cb_linagen_defun_matnn_element_wise(CB_Generator *gen, char *type,
-                                    int32_t n, bool implementation,
+                                    int32_t n, b8 implementation,
                                     char *func_name, char op);
 
 internal char char_toupper(char ch);
@@ -31,74 +31,74 @@ static void
 cb_linagen_typedef_matnn(CB_Generator *gen, char *type, int32_t n);
 
 static inline void
-cb_linagen_defun_vecn(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_vecn_add(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_add(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_vecn_sub(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_sub(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_vecn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_vecn_hadamard_div(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_hadamard_div(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_vecn_equal(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_equal(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_vecn_near(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_near(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_normalize(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_normalize(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_dot(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_dot(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_magnitude(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_magnitude(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_magnitude64(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_magnitude64(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_distance(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_distance(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_vecn_distance2(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_vecn_distance2(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 
 static inline void
-cb_linagen_defun_vec3_cross(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_vec3_cross(CB_Generator *gen, char *type, b8 implementation);
 
 static void
-cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_matnn_add(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_add(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_matnn_sub(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_sub(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_matnn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static inline void
-cb_linagen_defun_matnn_hadamard_div(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_hadamard_div(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 static void
-cb_linagen_defun_matnn_identity(CB_Generator *gen, char *type, int32_t n, bool implementation);
+cb_linagen_defun_matnn_identity(CB_Generator *gen, char *type, int32_t n, b8 implementation);
 
 static void
-cb_linagen_defun_mat4_transform_translate_by(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_transform_translate_by(CB_Generator *gen, char *type, b8 implementation);
 static void
-cb_linagen_defun_mat4_transform_translate_to(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_transform_translate_to(CB_Generator *gen, char *type, b8 implementation);
 static void
-cb_linagen_defun_mat4_transform_rotate(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_transform_rotate(CB_Generator *gen, char *type, b8 implementation);
 static void
-cb_linagen_defun_mat4_transform_rotate_rad(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_transform_rotate_rad(CB_Generator *gen, char *type, b8 implementation);
 static void
-cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *type, b8 implementation);
 static void
-cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *type, b8 implementation);
 static void
-cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *type, b8 implementation);
 static void
-cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *type, bool implementation);
+cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *type, b8 implementation);
 
 // ======================================================================
 // Implementations
@@ -111,7 +111,7 @@ cb_linagen_typedef_vecn(CB_Generator *gen, char *type, int32_t n, ...) {
   cb_gen_push(gen, cb_format("typedef union {"
                              "\n  %s values[%d];"
                              "\n  struct {", type, n));
-  bool found_last_named_field = false;
+  b8 found_last_named_field = false;
   for (int32_t i = 0; i < n; ++i) {
     char *prefix = " ";
     if (i % 4 == 0) {
@@ -162,7 +162,7 @@ cb_linagen_typedef_matnn(CB_Generator *gen, char *type, int32_t n) {
 }
 
 static inline void
-cb_linagen_defun_vecn(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Vec%d%s vec%d%s",
@@ -182,33 +182,33 @@ cb_linagen_defun_vecn(CB_Generator *gen, char *type, int32_t n, bool implementat
 }
 
 static inline void
-cb_linagen_defun_vecn_add(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_add(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_vecn_element_wise(gen, type, n, implementation, "add", '+');
 }
 
 static inline void
-cb_linagen_defun_vecn_sub(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_sub(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_vecn_element_wise(gen, type, n, implementation, "sub", '-');
 }
 
 static inline void
-cb_linagen_defun_vecn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_vecn_element_wise(gen, type, n, implementation,
                                      "hadamard_prod", '*');
 }
 
 static inline void
-cb_linagen_defun_vecn_hadamard_div(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_hadamard_div(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_vecn_element_wise(gen, type, n, implementation,
                                      "hadamard_div", '/');
 }
 
 static inline void
-cb_linagen_defun_vecn_equal(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_equal(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
-  cb_gen_push_func_begin(gen, cb_format("linagen_fn bool vec%d%s_equal", n, type));
+  cb_gen_push_func_begin(gen, cb_format("linagen_fn b8 vec%d%s_equal", n, type));
     cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v1", n, suffix));
     cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v2", n, suffix));
   cb_gen_push_func_end(gen, implementation);
@@ -223,11 +223,11 @@ cb_linagen_defun_vecn_equal(CB_Generator *gen, char *type, int32_t n, bool imple
 }
 
 static inline void
-cb_linagen_defun_vecn_near(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_near(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
-  cb_gen_push_func_begin(gen, cb_format("linagen_fn bool vec%d%s_near",
+  cb_gen_push_func_begin(gen, cb_format("linagen_fn b8 vec%d%s_near",
                                         n, type));
     cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v1", n, suffix));
     cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v2", n, suffix));
@@ -244,7 +244,7 @@ cb_linagen_defun_vecn_near(CB_Generator *gen, char *type, int32_t n, bool implem
 }
 
 static inline void
-cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -278,7 +278,7 @@ cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, bool implem
 }
 
 static void
-cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -310,7 +310,7 @@ cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, bool imple
 }
 
 static void
-cb_linagen_defun_vecn_normalize(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_normalize(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -351,7 +351,7 @@ cb_linagen_defun_vecn_normalize(CB_Generator *gen, char *type, int32_t n, bool i
 }
 
 static void
-cb_linagen_defun_vecn_dot(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_dot(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -370,7 +370,7 @@ cb_linagen_defun_vecn_dot(CB_Generator *gen, char *type, int32_t n, bool impleme
 }
 
 static void
-cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -417,7 +417,7 @@ cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, bool implem
 }
 
 static void
-cb_linagen_defun_vecn_magnitude(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_magnitude(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -437,7 +437,7 @@ cb_linagen_defun_vecn_magnitude(CB_Generator *gen, char *type, int32_t n, bool i
 }
 
 static void
-cb_linagen_defun_vecn_magnitude64(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_magnitude64(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -457,7 +457,7 @@ cb_linagen_defun_vecn_magnitude64(CB_Generator *gen, char *type, int32_t n, bool
 }
 
 static void
-cb_linagen_defun_vecn_distance(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_distance(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -483,7 +483,7 @@ cb_linagen_defun_vecn_distance(CB_Generator *gen, char *type, int32_t n, bool im
 }
 
 static void
-cb_linagen_defun_vecn_distance2(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_vecn_distance2(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn float vec%d%s_distance2",
@@ -507,7 +507,7 @@ cb_linagen_defun_vecn_distance2(CB_Generator *gen, char *type, int32_t n, bool i
 }
 
 static inline void
-cb_linagen_defun_vec3_cross(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_vec3_cross(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Vec3%s vec3%s_cross", suffix, type));
@@ -525,7 +525,7 @@ cb_linagen_defun_vec3_cross(CB_Generator *gen, char *type, bool implementation)
 }
 
 static void
-cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -563,29 +563,29 @@ cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, bool impl
 }
 
 static inline void
-cb_linagen_defun_matnn_add(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_add(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_matnn_element_wise(gen, type, n, implementation, "add", '+');
 }
 
 static inline void
-cb_linagen_defun_matnn_sub(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_sub(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_matnn_element_wise(gen, type, n, implementation, "sub", '-');
 }
 
 static inline void
-cb_linagen_defun_matnn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_hadamard_prod(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_matnn_element_wise(gen, type, n, implementation,
                                       "hadamard_prod", '*');
 }
 
 static inline void
-cb_linagen_defun_matnn_hadamard_div(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_hadamard_div(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   cb_linagen_defun_matnn_element_wise(gen, type, n, implementation,
                                       "hadamard_div", '/');
 }
 
 static void
-cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -637,7 +637,7 @@ cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, bool implem
 }
 
 static void
-cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -685,7 +685,7 @@ cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, bool imple
 }
 
 static void
-cb_linagen_defun_matnn_identity(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+cb_linagen_defun_matnn_identity(CB_Generator *gen, char *type, int32_t n, b8 implementation) {
   assert(n > 1);
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
@@ -715,7 +715,7 @@ cb_linagen_defun_matnn_identity(CB_Generator *gen, char *type, int32_t n, bool i
 }
 
 static void
-cb_linagen_defun_mat4_transform_translate_by(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_transform_translate_by(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_transform_translate_by", suffix, type));
@@ -733,7 +733,7 @@ cb_linagen_defun_mat4_transform_translate_by(CB_Generator *gen, char *type, bool
 }
 
 static void
-cb_linagen_defun_mat4_transform_translate_to(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_transform_translate_to(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_transform_translate_to", suffix, type));
@@ -751,7 +751,7 @@ cb_linagen_defun_mat4_transform_translate_to(CB_Generator *gen, char *type, bool
 }
 
 static void
-cb_linagen_defun_mat4_transform_rotate(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_transform_rotate(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_transform_rotate", suffix, type));
@@ -787,7 +787,7 @@ cb_linagen_defun_mat4_transform_rotate(CB_Generator *gen, char *type, bool imple
 }
 
 static void
-cb_linagen_defun_mat4_transform_rotate_rad(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_transform_rotate_rad(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_transform_rotate_rad", suffix, type));
@@ -821,7 +821,7 @@ cb_linagen_defun_mat4_transform_rotate_rad(CB_Generator *gen, char *type, bool i
 }
 
 static void
-cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_transform_scale", suffix, type));
@@ -854,7 +854,7 @@ cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *type, bool implem
 }
 
 static void
-cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_projection_orthographic", suffix, type));
@@ -877,7 +877,7 @@ cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *type, boo
 }
 
 static void
-cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_projection_perspective", suffix, type));
@@ -900,7 +900,7 @@ cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *type, bool
 }
 
 static void
-cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *type, bool implementation) {
+cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *type, b8 implementation) {
   char *suffix = strdup(type);
   *suffix = char_toupper(*type);
   cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_projection_perspective_rad", suffix, type));
@@ -924,7 +924,7 @@ cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *type,
 
 internal void
 cb_linagen_defun_vecn_element_wise(CB_Generator *gen, char *type,
-                                   int32_t n, bool implementation,
+                                   int32_t n, b8 implementation,
                                    char *func_name, char op) {
   assert(n > 1);
   char *suffix = strdup(type);
@@ -962,7 +962,7 @@ cb_linagen_defun_vecn_element_wise(CB_Generator *gen, char *type,
 
 internal void
 cb_linagen_defun_matnn_element_wise(CB_Generator *gen, char *type,
-                                    int32_t n, bool implementation,
+                                    int32_t n, b8 implementation,
                                     char *func_name, char op) {
   assert(n > 1);
   char *suffix = strdup(type);
index f79e0e7a70cef756dd849cc13bac222fcf2ad837..3213174c560e36b73681a1ced9b09faacab7ca54 100644 (file)
@@ -40,9 +40,7 @@ typedef void cb_w32_test_func_init(void);
 static void cb_test_run(void);
 
 internal void cb_test_register(struct CB_Test test);
-internal void cb_assertion_test(char *condition,
-                                char *file,
-                                int32_t line);
+internal void cb_assertion_test(char *condition, char *file, int32_t line);
 
 
 // ======================================================================