Files
gebs/gebs.h
2025-06-18 10:37:45 +02:00

1014 lines
32 KiB
C

/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org/>
*/
/* QUICKSTART
// Put into mybuild.c
// Building: cc -o mybuild mybuild.c
// Rebuilding: ./mybuild # will auto-recompile
#define GEBS_NO_PREFIX // strips the GEBS_ and gebs_ prefixes (optional)
#define GEBS_IMPLEMENTATION // include function definitions, not just declarations
#include "../gebs.h" // include gebs
int main(int argc, char ** argv)
{
// A gebs executable can recompile itself. We must supply a compiler commandline
// for recompiling. This means we can also put additional flags here.
rebuild_self(argc, argv, "cc", "-o", "self_rebuild", __FILE__);
// create a build directory
if (!exists1("build")) {
mkdir1("build");
}
// Create a dependecy:
//
// build/myapp
// (target)
// |
// / \
// / \
// / \
// myapp.c gebs.h
//
// This means that if files myapp.c or gebs.h change, build/myapp will be recompiled.
RULE("build/myapp", "myapp.c", "gebs.h") {
// commandline for building target build/myapp
// We can also check the error code of CMD(), but this is just an example.
CMD("cc", "-ggdb", "-o", "build/myapp", "myapp.c");
}
return 0;
}
*/
#ifndef GEBS_H_
#define GEBS_H_
#if !(defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)))
# error "gebs works only on posix platforms"
#endif
// ----------------------------------------------------------------------------
// Includes
// ----------------------------------------------------------------------------
#include <stdbool.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
// ----------------------------------------------------------------------------
// Types
// ----------------------------------------------------------------------------
#define nil NULL
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long long vlong;
#define discard __attribute__((unused))
// ----------------------------------------------------------------------------
// Parsing commandline arguments
// ----------------------------------------------------------------------------
#define GEBS_SHIFT_CHECK(argc) \
(*(argc) <= 0 ? (GEBS_LOGE("Expected more arguments\n"), exit(EXIT_FAILURE)) : (0))
#define GEBS_SHIFT(argc, argv) \
(GEBS_SHIFT_CHECK((argc)), --(*(argc)), *(*(argv))++)
// ----------------------------------------------------------------------------
// Defer macro
// ----------------------------------------------------------------------------
#define defer defer__2(__COUNTER__)
#define defer__2(X) defer__3(X)
#define defer__3(X) defer__4(defer__id##X)
#define defer__4(ID) auto void ID##func(char (*)[]); __attribute__((cleanup(ID##func))) char ID##var[0]; void ID##func(char (*ID##param)[])
// ----------------------------------------------------------------------------
// Logging
// ----------------------------------------------------------------------------
#define GEBS_LOGI(fmt, ...) fprintf(stdout, "Info: "fmt, ##__VA_ARGS__)
#define GEBS_LOGE(fmt, ...) fprintf(stdout, "Error: "fmt, ##__VA_ARGS__)
#define GEBS_LOGD(fmt, ...) fprintf(stdout, "Debug: "fmt, ##__VA_ARGS__)
#define GEBS_LOGW(fmt, ...) fprintf(stdout, "Warning: "fmt, ##__VA_ARGS__)
#ifdef GEBS_NO_FS_OPERATION_LOGGING
# define GEBS_LOGO(fmt, ...)
#else
# define GEBS_LOGO(fmt, ...) fprintf(stdout, "Info: "fmt, ##__VA_ARGS__)
#endif
// ----------------------------------------------------------------------------
// Allocators
// ----------------------------------------------------------------------------
typedef struct {
void *(*malloc)(void *self, size_t size);
void (*free)(void *self, void *memory);
void *(*realloc)(void *self, void *memory, size_t prev_size, size_t new_size);
} Gebs_Allocator;
// Wrapper macros
#define gebs_malloc(alloc, size) ((alloc)->malloc((void *)(alloc), (size)))
#define gebs_free(alloc, memory) ((alloc)->free((void *)(alloc), (memory)))
#define gebs_realloc(alloc, memory, prev_size, new_size) \
((alloc)->realloc((void *)(alloc), (memory), (prev_size), (new_size)))
extern Gebs_Allocator gebs_default_allocator;
void *gebs_default_allocator_malloc(void *self, size_t size);
void gebs_default_allocator_free(void *self, void *memory);
void *gebs_default_allocator_realloc(void *self, void *memory,
size_t prev_size, size_t new_size);
typedef struct Gebs_Arena_Chunk {
struct Gebs_Arena_Chunk *next;
size_t size;
size_t capacity;
uintptr_t memory[];
} Gebs_Arena_Chunk;
typedef struct Gebs_Arena {
void *(*malloc)(void *self, size_t size);
void (*free)(void *self, void *memory);
void *(*realloc)(void *self, void *memory, size_t prev_size, size_t new_size);
Gebs_Arena_Chunk *begin;
Gebs_Arena_Chunk *end;
} Gebs_Arena;
void *gebs_arena_malloc(void *self, size_t size);
void gebs_arena_free(void *self, void *memory);
void *gebs_arena_realloc(void *self, void *memory, size_t prev_size, size_t new_size);
void gebs_arena_reset(Gebs_Arena *a);
void gebs_arena_destroy(Gebs_Arena *a);
Gebs_Arena_Chunk *gebs_arena_create_chunk(size_t capacity);
void gebs_arena_destroy_chunk(Gebs_Arena_Chunk *chunk);
#define gebs_arena_get() \
({ \
Gebs_Arena __arena = {0}; \
__arena.malloc = &gebs_arena_malloc; \
__arena.realloc = &gebs_arena_realloc; \
__arena.free = &gebs_arena_free; \
__arena; \
})
#ifndef GEBS_ARENA_CHUNK_CAPACITY
# define GEBS_ARENA_CHUNK_CAPACITY (8*1024)
#endif
// ----------------------------------------------------------------------------
// Dynamic list
// ----------------------------------------------------------------------------
#define gebs_list_append_alloc(alloc, list, item) \
do { \
if ((list)->items == nil) { \
(list)->capacity = 1; \
(list)->items = gebs_malloc((alloc), \
sizeof(*(list)->items) * (list)->capacity); \
} else { \
if ((list)->count == (list)->capacity) { \
size_t __prev_capacity = (list)->capacity; \
(list)->capacity *= 2; \
(list)->items = gebs_realloc((alloc), (list)->items, \
sizeof(*(list)->items) * __prev_capacity, \
sizeof(*(list)->items) * (list)->capacity); \
} \
} \
(list)->items[(list)->count++] = (item); \
} while(0)
#define gebs_list_append(list, item) \
gebs_list_append_alloc(&gebs_default_allocator, (list), (item))
#define gebs_list_free_alloc(alloc, list) \
do { \
if ((list)->items != nil) { \
gebs_free((alloc), (list)->items); \
(list)->items = nil; \
} \
(list)->count = 0; \
} while(0)
#define gebs_list_free(list) gebs_list_free_alloc(&gebs_default_allocator, (list))
// List of null-terminated strings
typedef struct {
char **items;
size_t count, capacity;
} Gebs_NString_List;
#define gebs_nsl_join_alloc(alloc, nsl, sb, sep) \
do { \
for (size_t __i = 0; __i < (nsl)->count; __i++) { \
gebs_sb_append_nstr_alloc((alloc), (sb), (nsl)->items[__i]); \
if (__i != (nsl)->count - 1) { \
gebs_sb_append_nstr_alloc((alloc), (sb), (sep)); \
} \
} \
} while(0)
#define gebs_nsl_join(nsl, sb, sep) \
gebs_nsl_join_alloc(&gebs_default_allocator, (nsl), (sb), (sep))
typedef struct {
char *items;
size_t count, capacity;
} Gebs_String_Builder;
#define gebs_sb_append_char_alloc gebs_list_append_alloc
#define gebs_sb_free_alloc gebs_list_free_alloc
#define gebs_sb_finish_alloc(alloc, sb) \
gebs_sb_append_char_alloc((alloc), (sb), '\0')
#define gebs_sb_append_nstr_alloc(alloc, sb, nstr) \
do { \
char *__p = (nstr); \
for (size_t __i = 0; __p[__i]; __i++) { \
gebs_sb_append_char_alloc((alloc), (sb), __p[__i]); \
} \
} while(0)
#define gebs_sb_append_char(sb, chr) \
gebs_sb_append_char_alloc(&gebs_default_allocator, (sb), (chr))
#define gebs_sb_free(sb) \
gebs_sb_free_alloc(&gebs_default_allocator, (sb))
#define gebs_sb_finish(sb) \
gebs_sb_finish_alloc(&gebs_default_allocator, (sb))
#define gebs_sb_append_nstr(sb, nstr) \
gebs_sb_append_nstr_alloc(&gebs_default_allocator, (sb), (nstr))
#define gebs_sb_read_file_alloc(alloc, sb, path) \
({ \
bool __ret = false; \
FILE *__f = fopen((path), "r"); \
if (__f != nil) { \
__ret = true; \
fseek(__f, 0L, SEEK_END); \
size_t __size = ftell(__f); \
rewind(__f); \
char *__buf = gebs_malloc((alloc), __size); \
fread(__buf, __size, 1, __f); \
fclose(__f); \
for (size_t __i = 0; __i < __size; __i++) { \
gebs_sb_append_char_alloc((alloc), (sb), __buf[__i]); \
} \
gebs_free((alloc), __buf); \
} \
__ret; \
})
#define gebs_sb_read_file(sb, path) \
gebs_sb_read_file_alloc(&default_allocator, (sb), (path))
// ----------------------------------------------------------------------------
// Filesystem operations
// ----------------------------------------------------------------------------
bool gebs_rename(const char *a, const char *b);
bool gebs_remove(const char *p);
bool gebs_mkdir(const char *p);
bool gebs_exists(const char *p);
// ----------------------------------------------------------------------------
// Commands
// ----------------------------------------------------------------------------
typedef Gebs_NString_List Gebs_Cmd;
#define gebs_cmd_append_alloc gebs_list_append_alloc
#define gebs_cmd_free_alloc gebs_list_free_alloc
#define gebs_cmd_append gebs_list_append
#define gebs_cmd_free gebs_list_free
#define gebs_cmd_run_alloc gebs_cmd_run_sync_alloc
#define gebs_cmd_fd_run_alloc gebs_cmd_run_sync_alloc
#define gebs_cmd_run(cmd) gebs_cmd_run_alloc(&gebs_default_allocator, (cmd))
#define gebs_cmd_fd_run(fd, cmd) gebs_cmd_fd_run_alloc(&gebs_default_allocator, fd, (cmd))
int gebs_cmd_run_sync_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd);
int gebs_cmd_fd_run_sync_alloc(Gebs_Allocator *alloc, int fd, Gebs_Cmd *cmd);
#define gebs_cmd_run_async(cmd) gebs_cmd_run_async_alloc(&gebs_default_allocator, (cmd))
pid_t gebs_cmd_run_async_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd);
#define GEBS_CMD(...) \
({ \
char *__args[] = { __VA_ARGS__ }; \
Gebs_Cmd __cmd = {0}; \
defer { gebs_cmd_free(&__cmd); } \
for (size_t __i = 0; __i < sizeof(__args)/sizeof(__args[0]); __i++) { \
gebs_cmd_append(&__cmd, __args[__i]); \
} \
gebs_cmd_run(&__cmd); \
})
int gebs_cmd_run_sync_collect_alloc(Gebs_Allocator *alloc,
Gebs_Cmd *cmd, Gebs_String_Builder *sb_out);
int gebs_cmd_fd_run_sync_collect_alloc(Gebs_Allocator *alloc, int fd,
Gebs_Cmd *cmd, Gebs_String_Builder *sb_out);
#define gebs_cmd_run_collect_alloc gebs_cmd_run_sync_collect_alloc
#define gebs_cmd_fd_run_collect_alloc gebs_cmd_fd_run_sync_collect_alloc
#define gebs_cmd_run_collect(cmd, out) \
gebs_cmd_run_collect_alloc(&gebs_default_allocator, (cmd), (out))
#define gebs_cmd_fd_run_collect(fd, cmd, out) \
gebs_cmd_fd_run_collect_alloc(&gebs_default_allocator, (fd), (cmd), (out))
// ----------------------------------------------------------------------------
// The build system
// ----------------------------------------------------------------------------
bool gebs_needs_rebuild(const char *out, const char *in);
void gebs_rebuild_self1_alloc(Gebs_Allocator *alloc, int argc, char ** argv,
int cmd_argc, char **cmd_argv, char *source);
#define gebs_rebuild_self(argc, argv, ...) \
do { \
const char *__argv[] = { __VA_ARGS__ }; \
gebs_rebuild_self1_alloc(&gebs_default_allocator, argc, argv, \
sizeof(__argv)/sizeof(__argv[0]), (char**)__argv, __FILE__); \
} while(0)
#define gebs_needs_rebuild_many(out, ...) \
({ \
const char *__deps[] = { __VA_ARGS__ }; \
bool __ok = false; \
if (sizeof(__deps)/sizeof(__deps[0]) == 0) { \
__ok = true; \
} \
for (size_t __i = 0; __i < sizeof(__deps)/sizeof(__deps[0]); __i++) { \
if (gebs_needs_rebuild((out), __deps[__i])) { \
__ok = true; \
break; \
} \
} \
__ok; \
})
#define gebs_make_compile_flags(...) \
do { \
if (gebs_needs_rebuild("compile_flags.txt", __FILE__)) { \
GEBS_LOGI("Creating compile_flags.txt\n"); \
const char *__flags[] = { __VA_ARGS__ }; \
FILE *__out = fopen("compile_flags.txt", "w"); \
char __nl = '\n'; \
if (__out != nil) { \
for (size_t __i = 0; __i < sizeof(__flags)/sizeof(__flags[0]); __i++) { \
const char *__flag = __flags[__i]; \
fwrite(__flag, strlen(__flag), 1, __out); \
fwrite(&__nl, 1, 1, __out); \
} \
fclose(__out); \
} \
} \
} while(0)
#define GEBS_RULE(target, ...) if (needs_rebuild_many(target, __VA_ARGS__))
// ----------------------------------------------------------------------------
// Scratch arena
// ----------------------------------------------------------------------------
extern Gebs_Arena gebs_scratch_arena;
void gebs_scratch_arena_reset(void);
char *gebs_fmt(const char *fmt, ...);
char *gebs_cwd(void);
#ifdef GEBS_IMPLEMENTATION
// ----------------------------------------------------------------------------
// Commands
// ----------------------------------------------------------------------------
int gebs_cmd_run_sync_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd)
{
Gebs_String_Builder sb = {0};
defer { gebs_sb_free_alloc(alloc, &sb); }
gebs_nsl_join_alloc(alloc, cmd, &sb, " ");
gebs_sb_finish_alloc(alloc, &sb);
GEBS_LOGI("cmd `%s`\n", sb.items);
// Clone the list
Gebs_Cmd new_cmd = {0};
defer { gebs_cmd_free_alloc(alloc, &new_cmd); }
for (size_t i = 0; i < cmd->count; i++) {
gebs_cmd_append_alloc(alloc, &new_cmd, cmd->items[i]);
}
gebs_cmd_append_alloc((alloc), &new_cmd, nil);
pid_t pid = vfork();
switch (pid) {
case -1:
GEBS_LOGE("Could not vfork\n");
return -1;
case 0:
execvp(new_cmd.items[0], (char * const *)new_cmd.items);
exit(EXIT_FAILURE);
default: {
int wstatus;
do {
if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) {
return -1;
}
if (WIFEXITED(wstatus)) {
return WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
GEBS_LOGI("%d killed by %d\n", pid, WTERMSIG(wstatus));
return -1;
} else if (WIFSTOPPED(wstatus)) {
GEBS_LOGI("%d stopped by %d\n", pid, WSTOPSIG(wstatus));
return -1;
}
} while(!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
}
}
}
int gebs_cmd_fd_run_sync_alloc(Gebs_Allocator *alloc, int fd, Gebs_Cmd *cmd)
{
Gebs_String_Builder sb = {0};
defer { gebs_sb_free_alloc(alloc, &sb); }
gebs_nsl_join_alloc(alloc, cmd, &sb, " ");
gebs_sb_finish_alloc(alloc, &sb);
GEBS_LOGI("cmd `%s`\n", sb.items);
// Clone the list
Gebs_Cmd new_cmd = {0};
defer { gebs_cmd_free_alloc(alloc, &new_cmd); }
for (size_t i = 0; i < cmd->count; i++) {
gebs_cmd_append_alloc(alloc, &new_cmd, cmd->items[i]);
}
gebs_cmd_append_alloc((alloc), &new_cmd, nil);
pid_t pid = vfork();
switch (pid) {
case -1:
GEBS_LOGE("Could not vfork\n");
return -1;
case 0: {
const char *const envp[] = {NULL};
fexecve(fd, (char * const *)new_cmd.items, (char * const *)envp);
exit(EXIT_FAILURE);
} break;
default: {
int wstatus;
do {
if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) {
return -1;
}
if (WIFEXITED(wstatus)) {
return WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
GEBS_LOGI("%d killed by %d\n", pid, WTERMSIG(wstatus));
return -1;
} else if (WIFSTOPPED(wstatus)) {
GEBS_LOGI("%d stopped by %d\n", pid, WSTOPSIG(wstatus));
return -1;
}
} while(!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
}
}
}
int gebs_cmd_run_sync_collect_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd, Gebs_String_Builder *sb_out)
{
Gebs_String_Builder sb = {0};
defer { gebs_sb_free_alloc(alloc, &sb); }
gebs_nsl_join_alloc(alloc, cmd, &sb, " ");
gebs_sb_finish_alloc(alloc, &sb);
GEBS_LOGI("cmd `%s`\n", sb.items);
// Clone the list
Gebs_Cmd new_cmd = {0};
defer { gebs_cmd_free_alloc(alloc, &new_cmd); }
for (size_t i = 0; i < cmd->count; i++) {
gebs_cmd_append_alloc(alloc, &new_cmd, cmd->items[i]);
}
gebs_cmd_append_alloc((alloc), &new_cmd, nil);
int pipe_ends[2];
pipe(pipe_ends);
pid_t pid = vfork();
switch (pid) {
case -1:
close(pipe_ends[0]);
close(pipe_ends[1]);
GEBS_LOGE("Could not vfork\n");
return -1;
case 0:
close(pipe_ends[0]);
dup2(pipe_ends[1], 1);
close(pipe_ends[1]);
execvp(new_cmd.items[0], (char * const *)new_cmd.items);
exit(EXIT_FAILURE);
default: {
close(pipe_ends[1]);
char c;
while (read(pipe_ends[0], &c, 1) > 0) {
gebs_sb_append_char_alloc(alloc, sb_out, c);
}
gebs_sb_finish_alloc(alloc, sb_out);
int wstatus;
do {
if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) {
return -1;
}
if (WIFEXITED(wstatus)) {
return WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
GEBS_LOGI("%d killed by %d\n", pid, WTERMSIG(wstatus));
return -1;
} else if (WIFSTOPPED(wstatus)) {
GEBS_LOGI("%d stopped by %d\n", pid, WSTOPSIG(wstatus));
return -1;
}
} while(!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
}
}
}
int gebs_cmd_fd_run_sync_collect_alloc(Gebs_Allocator *alloc, int fd, Gebs_Cmd *cmd, Gebs_String_Builder *sb_out)
{
Gebs_String_Builder sb = {0};
defer { gebs_sb_free_alloc(alloc, &sb); }
gebs_nsl_join_alloc(alloc, cmd, &sb, " ");
gebs_sb_finish_alloc(alloc, &sb);
GEBS_LOGI("cmd `%s`\n", sb.items);
// Clone the list
Gebs_Cmd new_cmd = {0};
defer { gebs_cmd_free_alloc(alloc, &new_cmd); }
for (size_t i = 0; i < cmd->count; i++) {
gebs_cmd_append_alloc(alloc, &new_cmd, cmd->items[i]);
}
gebs_cmd_append_alloc((alloc), &new_cmd, nil);
int pipe_ends[2];
pipe(pipe_ends);
pid_t pid = vfork();
switch (pid) {
case -1:
close(pipe_ends[0]);
close(pipe_ends[1]);
GEBS_LOGE("Could not vfork\n");
return -1;
case 0: {
close(pipe_ends[0]);
dup2(pipe_ends[1], 1);
close(pipe_ends[1]);
const char *const envp[] = {NULL};
fexecve(fd, (char *const *)new_cmd.items, (char *const *)envp);
exit(EXIT_FAILURE);
} break;
default: {
close(pipe_ends[1]);
char c;
while (read(pipe_ends[0], &c, 1) > 0) {
gebs_sb_append_char_alloc(alloc, sb_out, c);
}
gebs_sb_finish_alloc(alloc, sb_out);
int wstatus;
do {
if (waitpid(pid, &wstatus, WUNTRACED | WCONTINUED) == -1) {
return -1;
}
if (WIFEXITED(wstatus)) {
return WEXITSTATUS(wstatus);
} else if (WIFSIGNALED(wstatus)) {
GEBS_LOGI("%d killed by %d\n", pid, WTERMSIG(wstatus));
return -1;
} else if (WIFSTOPPED(wstatus)) {
GEBS_LOGI("%d stopped by %d\n", pid, WSTOPSIG(wstatus));
return -1;
}
} while(!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
}
}
}
pid_t gebs_cmd_run_async_alloc(Gebs_Allocator *alloc, Gebs_Cmd *cmd)
{
Gebs_String_Builder sb = {0};
defer { gebs_sb_free_alloc(alloc, &sb); }
gebs_nsl_join_alloc(alloc, cmd, &sb, " ");
gebs_sb_finish_alloc(alloc, &sb);
GEBS_LOGI("cmd `%s`\n", sb.items);
// Clone the list
Gebs_Cmd new_cmd = {0};
defer { gebs_cmd_free_alloc(alloc, &new_cmd); }
for (size_t i = 0; i < cmd->count; i++) {
gebs_cmd_append_alloc(alloc, &new_cmd, cmd->items[i]);
}
gebs_cmd_append_alloc((alloc), &new_cmd, nil);
pid_t pid = vfork();
switch (pid) {
case -1:
GEBS_LOGE("Could not vfork\n");
return -1;
case 0:
execvp(new_cmd.items[0], (char * const *)new_cmd.items);
exit(EXIT_FAILURE);
default:
return pid;
}
}
// ----------------------------------------------------------------------------
// Allocators
// ----------------------------------------------------------------------------
Gebs_Allocator gebs_default_allocator = {
.malloc = &gebs_default_allocator_malloc,
.free = &gebs_default_allocator_free,
.realloc = &gebs_default_allocator_realloc,
};
void *gebs_default_allocator_malloc(discard void *self, size_t size)
{
return malloc(size);
}
void gebs_default_allocator_free(discard void *self, void *memory)
{
free(memory);
}
void *gebs_default_allocator_realloc(discard void *self, void *memory,
discard size_t prev_size, size_t new_size)
{
void *p = realloc(memory, new_size);
return p;
}
Gebs_Arena_Chunk *gebs_arena_create_chunk(size_t capacity)
{
size_t size = sizeof(Gebs_Arena_Chunk) + sizeof(uintptr_t)*capacity;
Gebs_Arena_Chunk *chunk = (Gebs_Arena_Chunk *)malloc(size);
chunk->next = nil;
chunk->size = 0;
chunk->capacity = capacity;
return chunk;
}
void gebs_arena_destroy_chunk(Gebs_Arena_Chunk *chunk)
{
free(chunk);
}
void *gebs_arena_malloc(void *self, size_t size)
{
Gebs_Arena *a = (Gebs_Arena *)self;
size_t sz = (size + sizeof(uintptr_t) - 1)/sizeof(uintptr_t);
if (a->end == nil) {
size_t capacity = GEBS_ARENA_CHUNK_CAPACITY;
if (capacity < sz) {
capacity = sz;
}
a->end = gebs_arena_create_chunk(capacity);
a->begin = a->end;
}
while (a->end->size + sz > a->end->capacity && a->end->next != nil) {
a->end = a->end->next;
}
if (a->end->size + sz > a->end->capacity) {
size_t capacity = GEBS_ARENA_CHUNK_CAPACITY;
if (capacity < sz) {
capacity = sz;
}
a->end->next = gebs_arena_create_chunk(capacity);
a->end = a->end->next;
}
void *result = &a->end->memory[a->end->size];
a->end->size += sz;
return result;
}
void gebs_arena_free(discard void *self, discard void *memory)
{
}
void *gebs_arena_realloc(void *self, void *memory,
size_t prev_size, size_t new_size)
{
Gebs_Arena *a = (Gebs_Arena *)self;
if (new_size <= prev_size) {
return memory;
}
void *new_memory = gebs_arena_malloc(a, new_size);
for (size_t i = 0; i < prev_size; i++) {
((char *)new_memory)[i] = ((char *)memory)[i];
}
return new_memory;
}
void gebs_arena_reset(Gebs_Arena *a)
{
for (Gebs_Arena_Chunk *chunk = a->begin; chunk != nil; chunk = chunk->next) {
chunk->size = 0;
}
a->end = a->begin;
}
void gebs_arena_destroy(Gebs_Arena *a)
{
Gebs_Arena_Chunk *chunk = a->begin;
while (chunk) {
Gebs_Arena_Chunk *chunk1 = chunk;
chunk = chunk->next;
gebs_arena_destroy_chunk(chunk1);
}
a->begin = nil;
a->end = nil;
}
// ----------------------------------------------------------------------------
// Scratch arena
// ----------------------------------------------------------------------------
Gebs_Arena gebs_scratch_arena;
__attribute__((constructor))
void gebs_scratch_areana_init(void)
{
gebs_scratch_arena = gebs_arena_get();
}
__attribute__((destructor))
void gebs_scratch_areana_free(void)
{
gebs_arena_destroy(&gebs_scratch_arena);
}
char *gebs_fmt(const char *fmt, ...)
{
va_list list;
va_start(list, fmt);
va_list list_copy;
va_copy(list_copy, list);
size_t size = vsnprintf(nil, 0, fmt, list_copy);
va_end(list_copy);
char *buf = gebs_malloc(&gebs_scratch_arena, size+1);
vsprintf(buf, fmt, list);
va_end(list);
return buf;
}
void gebs_scratch_arena_reset(void)
{
gebs_arena_reset(&gebs_scratch_arena);
}
char *gebs_cwd(void)
{
char *cwd = gebs_malloc(&gebs_scratch_arena, PATH_MAX);
if (getcwd(cwd, PATH_MAX) == nil) {
return nil;
}
return cwd;
}
// ----------------------------------------------------------------------------
// Filesystem operations
// ----------------------------------------------------------------------------
bool gebs_mkdir(const char *p)
{
GEBS_LOGO("mkdir %s\n", p);
return mkdir(p, 0777) == 0;
}
bool gebs_exists(const char *p)
{
struct stat st;
return stat(p, &st) == 0;
}
bool gebs_rename(const char *a, const char *b)
{
GEBS_LOGO("rename %s -> %s\n", a, b);
return rename(a, b) == 0;
}
bool gebs_remove(const char *p)
{
GEBS_LOGO("remove %s\n", p);
return remove(p) == 0;
}
// ----------------------------------------------------------------------------
// The build system
// ----------------------------------------------------------------------------
bool gebs_needs_rebuild(const char *out, const char *in)
{
time_t out_mtime = 0;
time_t in_mtime = 0;
struct stat attr1 = {0};
if (stat(out, &attr1) == 0) { out_mtime = attr1.st_mtime; }
struct stat attr2 = {0};
if (stat(in, &attr2) == 0) { in_mtime = attr2.st_mtime; }
return in_mtime > out_mtime;
}
void gebs_rebuild_self1_alloc(Gebs_Allocator *alloc, int argc, char ** argv,
int cmd_argc, char **cmd_argv, char *source)
{
const char *binary_path = argv[0];
if (!gebs_needs_rebuild_many(binary_path, source, __FILE__)) {
return;
}
char old_binary_path[FILENAME_MAX];
snprintf(old_binary_path, sizeof(old_binary_path), "%s-1", binary_path);
gebs_rename(binary_path, old_binary_path);
Gebs_Cmd cmd = {0};
defer { gebs_cmd_free_alloc(alloc, &cmd); }
for (size_t i = 0; i < cmd_argc; i++) {
gebs_cmd_append_alloc(alloc, &cmd, cmd_argv[i]);
}
int code = gebs_cmd_run_alloc(alloc, &cmd);
if (code != EXIT_SUCCESS) {
gebs_rename(old_binary_path, binary_path);
exit(EXIT_FAILURE);
}
gebs_remove(old_binary_path);
cmd.count = 0;
gebs_cmd_append_alloc(alloc, &cmd, (char *)binary_path);
for (size_t i = 1; i < argc; i++) {
gebs_cmd_append_alloc(alloc, &cmd, argv[i]);
}
code = gebs_cmd_run_alloc(alloc, &cmd);
if (code != EXIT_SUCCESS) {
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
#endif // GEBS_IMPLEMENTATION
// ----------------------------------------------------------------------------
// Prefixes
// ----------------------------------------------------------------------------
#ifdef GEBS_NO_PREFIX
#define SHIFT_CHECK GEBS_SHIFT_CHECK
#define SHIFT GEBS_SHIFT
#define LOGI GEBS_LOGI
#define LOGE GEBS_LOGE
#define LOGD GEBS_LOGD
#define LOGW GEBS_LOGW
#define LOGO GEBS_LOGO
#define Allocator Gebs_Allocator
#define default_allocator gebs_default_allocator
#define Arena Gebs_Arena
#define arena_reset gebs_arena_reset
#define arena_destroy gebs_arena_destroy
#define arena_get gebs_arena_get
#define list_append_alloc gebs_list_append_alloc
#define list_append gebs_list_append
#define list_free_alloc gebs_list_free_alloc
#define list_free gebs_list_free
#define NString_List Gebs_NString_List
#define nsl_join_alloc gebs_nsl_join_alloc
#define nsl_join gebs_nsl_join
#define String_Builder Gebs_String_Builder
#define sb_append_char_alloc gebs_sb_append_char_alloc
#define sb_free_alloc gebs_sb_free_alloc
#define sb_finish_alloc gebs_sb_finish_alloc
#define sb_append_nstr_alloc gebs_sb_append_nstr_alloc
#define sb_append_char gebs_sb_append_char
#define sb_free gebs_sb_free
#define sb_finish gebs_sb_finish
#define sb_append_nstr gebs_sb_append_nstr
#define sb_read_file gebs_sb_read_file
#define rename1 gebs_rename
#define remove1 gebs_remove
#define mkdir1 gebs_mkdir
#define exists1 gebs_exists
#define Cmd Gebs_Cmd
#define cmd_append_alloc gebs_cmd_append_alloc
#define cmd_free_aloc gebs_cmd_free_alloc
#define cmd_append gebs_cmd_append
#define cmd_free gebs_cmd_free
#define cmd_run_alloc gebs_cmd_run_alloc
#define cmd_run gebs_cmd_run
#define cmd_run_sync_alloc gebs_cmd_run_sync_alloc
#define CMD GEBS_CMD
#define cmd_run_sync_collect_alloc gebs_cmd_run_sync_collect_alloc
#define cmd_run_collect_alloc gebs_cmd_run_collect_alloc
#define cmd_run_collect gebs_cmd_run_collect
#define cmd_fd_run_sync_collect_alloc gebs_cmd_fd_run_sync_collect_alloc
#define cmd_fd_run_collect_alloc gebs_cmd_fd_run_collect_alloc
#define cmd_fd_run_collect gebs_cmd_fd_run_collect
#define cmd_fd_run_sync_alloc gebs_cmd_fd_run_sync_alloc
#define cmd_fd_run_alloc gebs_cmd_fd_run_alloc
#define cmd_fd_run gebs_cmd_fd_run
#define cmd_run_async_alloc gebs_cmd_run_async_alloc
#define cmd_run_async gebs_cmd_run_async
#define needs_rebuild gebs_needs_rebuild
#define needs_rebuild_many gebs_needs_rebuild_many
#define rebuild_self gebs_rebuild_self
#define make_compile_flags gebs_make_compile_flags
#define RULE GEBS_RULE
#define scratch_arena gebs_scratch_arena
#define fmt gebs_fmt
#define scratch_arena_reset gebs_scratch_arena_reset
#endif
#endif // GEBS_H_