浏览代码

Little Work :)

Vinicius Teshima 3 月之前
父节点
当前提交
1c7d7b1a00
共有 4 个文件被更改,包括 774 次插入323 次删除
  1. 75 51
      Makefile
  2. 25 0
      build.sh
  3. 308 217
      first.c
  4. 366 55
      src/lib.h

+ 75 - 51
Makefile

@@ -1,52 +1,76 @@
-SRCS=$(shell find ./src -name '*.c' -type f)
-OBJS=$(patsubst %.c, %.o, ${SRCS})
-DEPS=$(patsubst %.c, %.d, ${SRCS})
-HDRS=$(shell find ./src -name '*.h' -type f)
+.PHONY: all
+all:
+	@echo 'Running build.sh'
+	@./build.sh
+	@ctags --recurse
 
-CFLAGS+=-ansi -m64 -Og -ggdb \
--pedantic -Wall -Wextra -Wshadow \
--Wcast-align -Wunused -Wconversion -Wmisleading-indentation \
--Wdouble-promotion -Wformat=2 -Wbad-function-cast \
--Wmissing-declarations \
--Wmissing-prototypes -Wnested-externs -Werror
-
-LDFLAGS+= -fsanitize=address
-
-ifneq ($(findstring -static, ${LDFLAGS}), )
-	LDFLAGS:=$(filter-out -fsanitize=address, ${LDFLAGS})
-	_PKGT_STATIC:=--static
-endif
-
-BIN := $(subst src/,,$(patsubst %.c, %, ${SRCS}))
-
-sdl3: CFLAGS+=$(shell pkg-config ${_PKG_STATIC} --cflags sdl3)
-sdl3: LDFLAGS+=$(shell pkg-config ${_PKG_STATIC} --libs sdl3)
-
-rl: CFLAGS+=-I./raylib-5.5_linux_amd64/include -std=c99
-rl: LDFLAGS+=-L./raylib-5.5_linux_amd64/lib -lraylib -Wl,-rpath=./raylib-5.5_linux_amd64/lib
-
-raw_x11: CFLAGS+=$(shell pkg-config ${_PKG_STATIC} --cflags x11)
-raw_x11: LDFLAGS+=$(shell pkg-config ${_PKG_STATIC} --libs x11)
-
-cad: CFLAGS+=$(shell pkg-config ${_PKG_STATIC} --cflags x11)
-cad: LDFLAGS+=$(shell pkg-config ${_PKG_STATIC} --libs x11)
-
-all: ${BIN}
-
-${BIN}: %: src/%.o
-	@echo -n "[LD] "
-	gcc ${LDFLAGS} ${_LDFLAGS_$@} $^ -o $@
-
-%.d: %.c
-	gcc ${CFLAGS} -MM $^ > $@
-
-include ${DEPS}
-
-tags: ${SRCS} ${HDRS}
-	ctags --kinds-all=* --totals=yes $^
-
-clean:
-	rm -fv ${BIN} ${OBJS}
-
-fullclean: clean
-	rm -fv ${DEPS}
+#SRCS=$(shell find ./src -name '*.c' -type f)
+#OBJS=$(patsubst %.c, %.o, ${SRCS})
+#DEPS=$(patsubst %.c, %.d, ${SRCS})
+#HDRS=$(shell find ./src -name '*.h' -type f)
+#PRECOMPS=$(patsubst %.h, %.I, ${HDRS})
+#
+#CFLAGS+=-ansi -m64 -Og -ggdb \
+#-pedantic -Wall -Wextra -Wshadow \
+#-Wcast-align -Wunused -Wconversion -Wmisleading-indentation \
+#-Wdouble-promotion -Wformat=2 -Wbad-function-cast \
+#-Wmissing-declarations \
+#-Wmissing-prototypes -Wnested-externs -Werror
+#
+#LDFLAGS+=-fsanitize=address
+#
+#ifneq ($(findstring -static, ${LDFLAGS}), )
+#	LDFLAGS:=$(filter-out -fsanitize=address, ${LDFLAGS})
+#	_PKGT_STATIC:=--static
+#endif
+#
+#BIN := $(subst src/,,$(patsubst %.c, %, ${SRCS}))
+#
+##tt: tt.o
+#
+#a.out: first.o src/lib.h
+#	@echo -n "[LD] "
+#	gcc ${LDFLAGS} ${_LDFLAGS_$@} $^ -o $@
+#
+#first.o: src/lib.h
+#
+#sdl3: CFLAGS+=$(shell pkg-config ${_PKG_STATIC} --cflags sdl3)
+#sdl3: LDFLAGS+=$(shell pkg-config ${_PKG_STATIC} --libs sdl3)
+#
+#rl: CFLAGS+=-I./raylib-5.5_linux_amd64/include -std=c99
+#rl: LDFLAGS+=-L./raylib-5.5_linux_amd64/lib -lraylib -Wl,-rpath=./raylib-5.5_linux_amd64/lib
+#
+#ged: CFLAGS+=-I./raylib-5.5_linux_amd64/include -std=c89
+#ged: LDFLAGS+= -L./raylib-5.5_linux_amd64/lib -lraylib -Wl,-rpath=./raylib-5.5_linux_amd64/lib
+#
+#raw_x11: CFLAGS+=$(shell pkg-config ${_PKG_STATIC} --cflags x11)
+#raw_x11: LDFLAGS+=$(shell pkg-config ${_PKG_STATIC} --libs x11)
+#
+#cad: CFLAGS+=$(shell pkg-config ${_PKG_STATIC} --cflags x11)
+#cad: LDFLAGS+=$(shell pkg-config ${_PKG_STATIC} --libs x11)
+#
+#gui: CFLAGS+=-I./raylib-5.5_linux_amd64/include -std=c99
+#gui: LDFLAGS+=-L./raylib-5.5_linux_amd64/lib -lraylib -Wl,-rpath=./raylib-5.5_linux_amd64/lib
+#
+#all: tags ${BIN}
+#
+#${BIN}: %: src/%.o
+#	@echo -n "[LD] "
+#	gcc ${LDFLAGS} ${_LDFLAGS_$@} $^ -o $@
+#
+#%.d: %.c
+#	gcc ${CFLAGS} -MM $^ > $@
+#
+#%.I: %.h
+#	gcc ${CFLAGS} -DIMP -E $^ -o $@
+#
+#include ${DEPS}
+#
+#tags: ${SRCS} ${HDRS} ${PRECOMPS}
+#	@ctags -D IMP --kinds-all=* $^
+#
+#clean:
+#	rm -fv ${BIN} ${OBJS}
+#
+#fullclean: clean
+#	rm -fv ${DEPS}

+ 25 - 0
build.sh

@@ -0,0 +1,25 @@
+#!/bin/sh
+
+CFLAGS='-std=gnu99 -m64 -Og -ggdb
+-pedantic -Wall -Wextra -Wshadow
+-Wcast-align -Wunused -Wconversion -Wmisleading-indentation
+-Wdouble-promotion -Wformat=2 -Wbad-function-cast
+-Wmissing-declarations
+-Wmissing-prototypes -Wnested-externs -Werror'
+
+LDFLAGS='-fsanitize=address'
+
+find ./ -name '*.c' -printf '%T@ %p\n' \
+        | sort -r | head -n1 | cut -d' ' -f2 |  while read file
+do
+        out_file="$(basename "$file" | cut -d'.' -f1)"
+        echo "------------------------------------"
+        echo "Compiling file '${file}' into '${out_file}'"
+        extras=''
+        grep -q -e 'raylib.h' "$file"\
+            && extras="${extra} -I./raylib-5.5_linux_amd64/include
+                                -L./raylib-5.5_linux_amd64/lib -lraylib
+                                -Wl,-rpath=./raylib-5.5_linux_amd64/lib"
+        gcc $CFLAGS $LDFLAGS $extras "$file" -o "$out_file"
+        echo "------------------------------------"
+done

+ 308 - 217
first.c

@@ -1,6 +1,8 @@
 #include <stdio.h>
 #include <stdbool.h>
 
+#include <sys/wait.h>
+
 /* #define STR_SIZE_LIMIT 65536 */
 #define STR_SIZE_LIMIT 5458264
 
@@ -14,81 +16,13 @@
 #define WANT_PATH
 #define WANT_ENV
 #define WANT_SLOP
+#define WANT_CMD
 #include "./src/lib.h"
 
 #include <stdlib.h>
 #include <stdio.h>
 
-bool tkn_expect(struct tokenizer *tkn, enum token_type type,
-                struct token *out_tk, enum err *out_err);
-bool tkn_expect_id(struct tokenizer *tkn, const char *cstr,
-                   struct token *out_tk, enum err *out_err);
-bool tkn_parse_function(struct tokenizer *tkn);
-
-bool run_function(struct str str);
-
-bool tkn_parse_pp_directive(struct tokenizer *tkn, struct token *out_tk,
-                            enum err *out_err);
-
-bool tkn_parse_decl(struct tokenizer *tkn, struct token *out_tk,
-                    enum err *out_err);
-
-bool is_space(char c);
-bool
-is_space(char c)
-{
-    return (c == ' ') || (c == '\r') || (c == '\n')
-           || (c == '\t') || (c == '\v');
-}
-
-enum c_token_type {
-    _TT = TK_LAST,
-    TK_PP_DEF,
-    TK_PP_INC
-};
-
-struct token_pp_inc {
-    enum c_token_type type;
-    u64 loc_start;
-    u64 loc_end;
-
-    struct str string;
-};
-
-struct token_pp_def {
-    enum token_type type;
-    u64 loc_start;
-    u64 loc_end;
-
-    struct str name;
-    struct str val;
-};
-
-struct token_var_decl {
-    enum token_type type;
-    u64 loc_start;
-    u64 loc_end;
-
-    struct str _type;
-    struct str name;
-    struct str init;
-};
-
-struct token_func_decl {
-    enum token_type type;
-    u64 loc_start;
-    u64 loc_end;
-
-    struct str _type;
-    struct str name;
-};
-
-struct ctx {
-    void *t;
-};
-
-bool skip_token(struct tokenizer *tkn, char c);
-
+/*
 # define DIE(f, e) \
     (f); \
     if ( (e) != ERR_OK ) {\
@@ -102,95 +36,133 @@ bool skip_token(struct tokenizer *tkn, char c);
     printf("PRE: `" #f "`\n"); \
     f; \
     printf("POST: `" #f "`\n");
+*/
 
 int
 main(int argc, char *argv[])
 {
     enum err err = ERR_OK;
 
-    u8 *file = NULL;
-    u64 file_size = 0;
-    struct str s = {0};
-    struct str ss = {0};
-    struct str_tokenizer stkn = {0};
+    struct cmd cmd = {0};
 
-    u64 *index = NULL;
-    u64 i = 0;
-    u64 counter = 0;
 
-    void *tmp = NULL;
-    struct key_bytes *kb = NULL;
+    cmd = cmd_create_ns("/bin/echo", &err);
+
+    cmd_append_arg_ns(&cmd, "This is a test of execve", &err);
 
-    struct map m = {0};
+    cmd_exec(&cmd, &err);
 
-    m = DIE(map_create(sizeof(u64), 64, &err), err);
+    cmd_destroy(&cmd, &err);
 
-    file = DIE(slop_file_slurp("./t8.shakespeare.txt", &file_size, &err), err);
+    if ( err != ERR_OK ) {
+        cmd_destroy(&cmd, NULL);
+        fprintf(stderr, "Failed cmd: %s\n", err_to_name[err]);
+        return 1;
+    }
 
-    s = DIE(str_from_cstr((char*)file, file_size, &err), err);
+    /*
+    bool was_rebuild = false;
+    struct dyn_arr dirs = {0};
+    size_t i = 0;
 
-    stkn = str_tokenize_func(s, is_space);
+    was_rebuild = build_go_rebuild_yourself(__FILE__, &err);
+    if ( was_rebuild == true ) {
+        return 0;
+    }
 
-    ss = str_tokenizer_next(&stkn);
-    for ( ; ss.size != ((u64) -1); (ss = str_tokenizer_next(&stkn), ++i)) {
-        /*
-        if ( i > 10 ) {
-            break;
-        }
-        */
-
-        /* printf("i -> %ld\n", i); */
-        tmp = map_get(&m, (void*)ss.data, ss.size, &err);
-        if ( err == ERR_NOT_FOUND ) {
-            err = ERR_OK;
-            counter = 1;
-            /* printf("-> `%.*s` | `%ld`\n", (int)ss.size, ss.data, counter); */
-            DIE(map_add(&m, (void*)ss.data, ss.size, &counter, &err), err);
-            continue;
-        }
-        counter = *(u64*) tmp;
+    dirs = dir_list_with_ext("./src", ".c", &err);
 
-        ++counter;
-        DIE(map_set(&m, (void*)ss.data, ss.size, &counter, &err), err);
+    free(dirs);
+    */
 
-        /* printf("-> `%.*s` | `%ld`\n", (int)ss.size, ss.data, counter); */
-    }
+    UNUSED(argc);
+    UNUSED(argv);
+    return 0;
+}
 
-    i = 0;
-    MAP_FOR_EACH(&m, index, kb) {
-        struct map_item *mi = MAP_GET_INDEX(&m, *index);
+bool skip_token(struct tokenizer *tkn, char c);
 
-        if ( i > 10 ) break;
+enum c_token_type {
+    _TT = TK_LAST,
+    TK_PP_DEF,
+    TK_PP_INC
+};
 
-        printf("%.*s -> %ld\n", (int) kb->size, kb->key , *(u64*)mi->item);
+struct pp_def {
+    struct str name;
+    struct str val;
+};
 
-        ++i;
+# define PP_DEF_DESTROY(Var) \
+    if ( (Var) != NULL ) { \
+        STR_DESTROY((Var)->name); \
+        STR_DESTROY((Var)->val); \
+        LIB_FREE((Var)); \
     }
 
-    /*
-    MAP_FOR_EACH_INDEXS(&m, index) {
-        struct map_item *mi = MAP_GET_INDEX(&m, *index);
-        printf("%ld -> Value: %ld\n", *index, *(u64*)mi->item);
+struct var_decl {
+    bool is_ptr;
+    struct str type;
+    struct str name;
+};
+
+# define VAR_DECL_DESTROY(Var) \
+    if ( (Var) != NULL ) { \
+        STR_DESTROY((Var)->type); \
+        STR_DESTROY((Var)->name); \
     }
 
-    kb = m.keys.data;
-    while ( kb->size > 0 ) {
-        printf("-->> %.*s\n", (int) kb->size, kb->key);
-        kb = CAST(struct key_bytes *, CAST(u8*, kb->key) + kb->size);
+struct func_decl {
+    struct str ret_type;
+    struct str name;
+    struct {
+        struct var_decl *data;
+        u64 size;
+        u64 cap;
+    } args;
+};
+
+void func_decl_destroy(struct func_decl *fd);
+void
+func_decl_destroy(struct func_decl *fd)
+{
+    u64 i = 0;
+    if ( fd == NULL ) return;
+    STR_DESTROY(fd->ret_type);
+    STR_DESTROY(fd->name);
+
+    for ( i = 0; i < fd->args.size; ++i ) {
+        STR_DESTROY(fd->args.data[i].type);
+        STR_DESTROY(fd->args.data[i].name);
     }
-    */
+    LIB_FREE(fd->args.data);
 
-    DIE(map_destroy(&m, &err), err);
+    LIB_FREE(fd);
+}
 
-    free(file);
+bool tkn_expect(struct tokenizer *tkn, enum token_type type,
+                struct token *out_tk, enum err *out_err);
+bool tkn_expect_id(struct tokenizer *tkn, const char *cstr,
+                   struct token *out_tk, enum err *out_err);
+bool tkn_parse_function(struct tokenizer *tkn);
 
-    UNUSED(argc);
-    UNUSED(argv);
-    return 0;
-exit_err:
-    if ( file != NULL ) free(file);
-    map_destroy(&m, NULL);
-    return 1;
+bool run_function(struct str str);
+
+bool tkn_parse_pp_directive(struct tokenizer *tkn, struct token *out_tk,
+                            enum err *out_err);
+
+bool tkn_parse_var(struct tokenizer *tkn, struct token *out_tk,
+                   enum err *out_err);
+
+bool tkn_parse_decl(struct tokenizer *tkn, struct token *out_tk,
+                    enum err *out_err);
+
+bool is_space(char c);
+bool
+is_space(char c)
+{
+    return (c == ' ') || (c == '\r') || (c == '\n')
+           || (c == '\t') || (c == '\v');
 }
 
 int main2(int argc, char *argv[]);
@@ -217,8 +189,6 @@ main2(int argc, char *argv[])
     tkn_opts.skip_token = skip_token;
     tkn = tokenizer_create(code, src_path, &tkn_opts, &err);
 
-    tkn.edata = &tk_da;
-
     tk = tokenizer_next_token(&tkn, &err);
     do {
         if ( err != ERR_OK ) {
@@ -228,11 +198,18 @@ main2(int argc, char *argv[])
         }
         switch ( tk.type ) {
         case TK_POUND: {
-            struct token pp_tk = {0};
-            if ( ! tkn_parse_pp_directive(&tkn, &pp_tk, &err) ) goto error_exit;
+            if ( ! tkn_parse_pp_directive(&tkn, NULL, &err) ) goto error_exit;
             } break;
         case TK_ID: if ( ! tkn_parse_decl(&tkn, NULL, &err) ) goto error_exit;
         case TK_NL: break;
+        case TK_SLASH: {
+            if ( ! tkn_expect(&tkn, TK_ASTERISK, NULL, &err) ) goto error_exit;
+
+            while ( tk.type != TK_ASTERISK )
+                tk = tokenizer_next_token(&tkn, &err);
+
+            if ( ! tkn_expect(&tkn, TK_SLASH, NULL, &err) ) goto error_exit;
+            } break;
         default: fprintf(stderr, "%s ERROR: Invalid Token `%s`\n",
                          tokenizer_token_loc_temp(&tkn, &tk, TLF_VIM, NULL),
                          token_to_cstr(tk.type));
@@ -298,7 +275,6 @@ tkn_expect_id(struct tokenizer *tkn, const char *cstr, struct token *out_tk,
 
     if ( tokenizer_is_next_id(tkn, str, out_tk, &err) == false ) {
         struct token tk = {0};
-        struct token_wstr *tk_ws = NULL;
         tk = tokenizer_next_token(tkn, &err);
         if ( err != ERR_OK ) {
             fprintf(stderr, "Failed to get next token: %s\n",
@@ -312,10 +288,9 @@ tkn_expect_id(struct tokenizer *tkn, const char *cstr, struct token *out_tk,
                     token_to_cstr(tk.type));
             return false;
         }
-        tk_ws = (struct token_wstr *)&tk;
         fprintf(stderr, "%s ERROR: Got wrong id, expected: %s, got: %.*s\n",
                 tokenizer_token_loc_temp(tkn, &tk, TLF_VIM, NULL),
-                cstr, (int) tk_ws->string.size, tk_ws->string.data);
+                cstr, (int) tk.string.size, tk.string.data);
         return false;
     }
 
@@ -326,10 +301,7 @@ bool
 tkn_parse_pp_directive(struct tokenizer *tkn, struct token *out_tk,
                        enum err *out_err)
 {
-    static char buf[1024] = {0};
-    static char buf2[1024] = {0};
     struct token tk = {0};
-    struct token_wstr *tk_ws = NULL;
     enum err err = ERR_OK;
     enum err *perr = &err;
 
@@ -353,61 +325,68 @@ tkn_parse_pp_directive(struct tokenizer *tkn, struct token *out_tk,
         return false;
     }
 
-    tk_ws = (struct token_wstr *) &tk;
-
-    if ( str_eq_cstr(tk_ws->string, "define", 6) == true ) {
-        struct token_pp_def tk_def = {0};
+    if ( str_eq_cstr(tk.string, "define", 6) == true ) {
+        struct token tk_def = {0};
+        struct pp_def *def = NULL;
 
-        if ( ! tkn_expect(tkn, TK_ID, &tk, perr) ) return false;
-        tk_ws = (struct token_wstr *) &tk;
-
-        tk_def.loc_start = tk_ws->loc_start;
+        def = malloc(sizeof(struct pp_def));
+        if ( def == NULL ) {
+            *perr = ERR_FAILED_ALLOC;
+            goto def_exit_err;
+        }
+        memset(def, 0, sizeof(struct pp_def));
 
-        memcpy(buf, tk_ws->string.data, tk_ws->string.size);
+        if ( ! tkn_expect(tkn, TK_ID, &tk, perr) ) goto def_exit_err;
+        tk_def.loc_start = tk.loc_start;
 
-        tk_def.name.data = buf;
-        tk_def.name.size = tk_ws->string.size;
+        def->name = str_dup(tk.string);
 
         tk = tokenizer_next_token(tkn, &err);
         if ( err != ERR_OK ) {
             fprintf(stderr, "Failed to get next token: %s\n",
                     err_to_name[err]);
-            return false;
+            goto def_exit_err;
         }
 
         switch ( tk.type ) {
         case TK_ID:
         case TK_STR_LIT:
         case TK_NUM_LIT:
-            tk_ws = (struct token_wstr *) &tk;
             break;
-        case TK_NL: goto define_wout_value;
+        case TK_NL: goto def_wout_value;
         default:
             fprintf(stderr,
                     "%s Got wrong token, expected:"
                     " TK_ID/TK_STR_LIT/TK_NUM_LIT, got: %s\n",
                     tokenizer_token_loc_temp(tkn, &tk, TLF_VIM, NULL),
                     token_to_cstr(tk.type));
-            return false;
+            goto def_exit_err;
         }
 
-        memcpy(buf2, tk_ws->string.data, tk_ws->string.size);
+        def->val = str_dup(tk.string);
+
+        tk_def.loc_end = tk.loc_end;
 
-        tk_def.val.data = buf2;
-        tk_def.val.size = tk_ws->string.size;
+        if ( ! tkn_expect(tkn, TK_NL, NULL, perr) ) goto def_exit_err;
+def_wout_value:
 
-        tk_def.loc_end = tk_ws->loc_end;
+        tk_def.extra = def;
 
-        if ( ! tkn_expect(tkn, TK_NL, NULL, perr) ) return false;
-define_wout_value:
+        if ( out_tk != NULL ) {
+            *out_tk = tk_def;
+        } else {
+            PP_DEF_DESTROY(def);
+        }
 
-        LIB_SET_IF_NOT_NULL(out_tk, *(struct token *) &tk_def);
         return true;
+def_exit_err:
+        PP_DEF_DESTROY(def);
+        return false;
     }
 
-    if ( str_eq_cstr(tk_ws->string, "include", 7) == true ) {
-        struct token_pp_inc tk_inc = {0};
-        tk_inc.type = TK_PP_INC;
+    if ( str_eq_cstr(tk.string, "include", 7) == true ) {
+        struct token tk_inc = {0};
+        tk_inc.type = (enum token_type) TK_PP_INC;
 
         tk = tokenizer_next_token(tkn, &err);
         if ( err != ERR_OK ) {
@@ -417,72 +396,143 @@ define_wout_value:
         }
 
         if ( tk.type == TK_STR_LIT ) {
-            tk_ws = (struct token_wstr *) &tk;
-            tk_inc.loc_start = tk_ws->loc_start;
-            tk_inc.loc_end = tk_ws->loc_end;
-
-            memcpy(buf, tk_ws->string.data, tk_ws->string.size);
-            tk_inc.string.data = buf;
-            tk_inc.string.size = tk_ws->string.size;
-            goto include_str_lit;
+            tk_inc.loc_start = tk.loc_start;
+            tk_inc.loc_end = tk.loc_end;
+
+            tk_inc.string = str_dup(tk.string);
+            goto inc_str_lit;
         } else if ( tk.type != TK_L_ANG_BRACKET) {
             fprintf(stderr,
                     "%s Got wrong token, expected:"
                     " TK_ID/TK_STR_LIT/TK_NUM_LIT, got: %s\n",
                     tokenizer_token_loc_temp(tkn, &tk, TLF_VIM, NULL),
                     token_to_cstr(tk.type));
-            return false;
+            goto inc_exit_err;
         }
 
-        if ( ! tkn_expect(tkn, TK_ID, &tk, perr) ) return false;
-        tk_ws = (struct token_wstr *) &tk;
+        if ( ! tkn_expect(tkn, TK_ID, &tk, perr) ) goto inc_exit_err;
+        tk_inc.loc_start = tk.loc_start;
+        tk_inc.loc_end = tk.loc_end;
 
-        tk_inc.loc_start = tk_ws->loc_start;
-        tk_inc.loc_end = tk_ws->loc_end;
+        tk_inc.string = str_dup(tk.string);
 
-        memcpy(buf, tk_ws->string.data, tk_ws->string.size);
+        if ( ! tkn_expect(tkn, TK_DOT, NULL, perr) ) goto inc_exit_err;
+        if ( ! tkn_expect_id(tkn, "h", NULL, perr) ) goto inc_exit_err;
 
-        tk_inc.string.data = buf;
-        tk_inc.string.size = tk_ws->string.size;
+        if ( ! tkn_expect(tkn, TK_R_ANG_BRACKET, NULL, perr) )
+            goto inc_exit_err;
+        if ( ! tkn_expect(tkn, TK_NL, NULL, perr) ) goto inc_exit_err;
 
-        if ( ! tkn_expect(tkn, TK_DOT, NULL, perr) ) return false;
-        if ( ! tkn_expect_id(tkn, "h", NULL, perr) ) return false;
-
-        if ( ! tkn_expect(tkn, TK_R_ANG_BRACKET, NULL, perr) ) return false;
-        if ( ! tkn_expect(tkn, TK_NL, NULL, perr) ) return false;
-
-include_str_lit:
-        LIB_SET_IF_NOT_NULL(out_tk, *(struct token *) &tk_inc);
+inc_str_lit:
+        if ( out_tk != NULL ) {
+            *out_tk = tk_inc;
+        } else {
+            STR_DESTROY(tk_inc.string);
+        }
         return true;
+inc_exit_err:
+        STR_DESTROY(tk_inc.string);
+        return false;
     }
 
-    if ( str_eq_cstr(tk_ws->string, "if", 2) == true ) {
+    if ( str_eq_cstr(tk.string, "if", 2) == true ) {
         return true;
     }
 
-    if ( str_eq_cstr(tk_ws->string, "ifdef", 2) == true ) {
+    if ( str_eq_cstr(tk.string, "ifdef", 2) == true ) {
         return true;
     }
 
-    if ( str_eq_cstr(tk_ws->string, "ifndef", 2) == true ) {
+    if ( str_eq_cstr(tk.string, "ifndef", 2) == true ) {
         return true;
     }
 
     *perr = -1;
     fprintf(stderr, "%s ERROR: Invalid Pre-Compiler directive `%.*s`\n",
             tokenizer_token_loc_temp(tkn, &tk, TLF_VIM, NULL),
-            (int) tk_ws->string.size, tk_ws->string.data);
+            (int) tk.string.size, tk.string.data);
+    return false;
+}
+
+bool
+tkn_parse_var(struct tokenizer *tkn, struct token *out_tk, enum err *out_err)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    struct token tk = {0};
+
+    struct var_decl *vd = NULL;
+
+    struct {
+        char *data;
+        u64 size;
+        u64 cap;
+    } type = {0};
+
+    LIB_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, false);
+    LIB_ARG_MUST_NOT_BE_NULL(tkn, out_err, false);
+
+    if ( out_err != NULL ) {
+        perr = out_err;
+    }
+
+    vd = malloc(sizeof(*vd));
+    if ( vd == NULL ) {
+        *perr = ERR_FAILED_ALLOC;
+        goto exit_err;
+    }
+    memset(vd, 0, sizeof(*vd));
+
+    if ( ! tkn_expect(tkn, TK_ID, &tk, perr) ) goto exit_err;
+    DA_APPEND_DATA(type, tk.string.data, tk.string.size, perr);
+    DA_APPEND(type, ' ', perr);
+
+    while ( true ) {
+        /* bool is_name = false;*/
+        /*enum token_type ntt = tokenizer_peek_token_type(tkn, NULL);*/
+
+        /*is_name = ( ntt == TK_COMMA ) || ( ntt == TK_SEMICOLON )
+                  || ( ntt == TK_ASTERISK ) || ( ntt == TK_EQUAL );*/
+
+        tk = tokenizer_next_token(tkn, NULL);
+        switch ( tk.type ) {
+        case TK_ID: break;
+        case TK_ASTERISK: break;
+        case TK_COMMA: break;
+        case TK_SEMICOLON: break;
+        default:
+            fprintf(stderr,
+                    "%s Got wrong token, expected:"
+                    " TK_ID/TK_ASTERISK/TK_COMMA/TK_SEMICOLON, got: %s\n",
+                    tokenizer_token_loc_temp(tkn, &tk, TLF_VIM, NULL),
+                    token_to_cstr(tk.type));
+            goto exit_err;
+        }
+    }
+
+    TODO("Rest of var decl!");
+
+    if ( ! tkn_expect(tkn, TK_ID, &tk, perr) ) goto exit_err;
+    vd->name = str_dup(tk.string);
+    if ( ! tkn_expect(tkn, TK_COMMA, NULL, perr) ) goto exit_err;
+
+    if ( out_tk != NULL ) {
+        *out_tk = tk;
+    }
+
+exit_err:
+    LIB_FREE(vd);
+    LIB_FREE(type.data);
+    if ( *perr == ERR_OK ) *perr = ERR_GENERAL_ERROR;
     return false;
 }
 
 bool
 tkn_parse_decl(struct tokenizer *tkn, struct token *out_tk, enum err *out_err)
 {
-    /* static char buf[1024] = {0}; */
-    /* static char buf2[1024] = {0}; */
     struct token tk = {0};
-    struct token_wstr *tk_ws = NULL;
-    struct token_wstr tk_type = {0};
+    struct token tk_type = {0};
     enum err err = ERR_OK;
     enum err *perr = &err;
 
@@ -493,13 +543,70 @@ tkn_parse_decl(struct tokenizer *tkn, struct token *out_tk, enum err *out_err)
         perr = out_err;
     }
 
-    tk_type = *(struct token_wstr *) &tkn->last;
+    tk_type = tkn->last;
 
     if ( ! tkn_expect(tkn, TK_ID, &tk, perr) ) return false;
 
     switch ( tokenizer_next_token_type(tkn, perr) ) {
     case TK_L_BRACES: {
-        TODO("Implement function declaration");
+        struct token tk_fd = {0};
+        struct func_decl *fd = NULL;
+
+        fd = malloc(sizeof(struct func_decl));
+        if ( fd == NULL ) {
+            *out_err = ERR_FAILED_ALLOC;
+            goto func_decl_exit_err;
+        }
+        memset(fd, 0, sizeof(*fd));
+
+        fd->args.cap = 32;
+        fd->args.data = malloc(sizeof(*fd->args.data) * fd->args.cap);
+        if ( fd->args.data == NULL ) {
+            *out_err = ERR_FAILED_ALLOC;
+            goto func_decl_exit_err;
+        }
+        memset(fd->args.data, 0, sizeof(*fd->args.data) * fd->args.cap);
+
+        tk_fd.loc_start = tk_type.loc_start;
+
+        fd->ret_type = str_dup(tk_type.string);
+        fd->name = str_dup(tk.string);
+
+        while ( true ) {
+            struct token tk_vd = {0};
+            struct var_decl *vd = NULL;
+
+            tkn_parse_var(tkn, &tk_vd, perr);
+            if ( *perr != ERR_OK ) goto func_decl_exit_err;
+
+            vd = tk_vd.extra;
+
+            DA_APPEND(fd->args, *vd, perr);
+        };
+
+
+        if ( out_tk != NULL ) {
+            *out_tk = tk_fd;
+        } else {
+            func_decl_destroy(fd);
+        }
+
+        return true;
+func_decl_exit_err:
+        func_decl_destroy(fd);
+        return false;
+        /*
+struct func_decl {
+    struct str ret_type;
+    struct str name;
+    struct {
+        struct str *data;
+        u64 size;
+        u64 cap;
+    } args;
+};
+*/
+
         } break;
     case TK_INVALID:
         fprintf(stderr, "Failed to get next token: %s\n", err_to_name[*perr]);
@@ -533,10 +640,9 @@ tkn_parse_decl(struct tokenizer *tkn, struct token *out_tk, enum err *out_err)
     UNUSED(out_tk);
 
     *perr = ERR_OK;
-    tk_ws = (struct token_wstr *) &tkn->last;
     fprintf(stderr, "%s ERROR: Invalid declaration id `%.*s`\n",
             tokenizer_token_loc_temp(tkn, &tkn->last, TLF_VIM, NULL),
-            (int) tk_ws->string.size, tk_ws->string.data);
+            (int) tk.string.size, tk.string.data);
     return false;
 }
 
@@ -544,20 +650,5 @@ tkn_parse_decl(struct tokenizer *tkn, struct token *out_tk, enum err *out_err)
 int
 main(void)
 {
-    enum err err = ERR_OK;
-    bool was_rebuild = false;
-    struct dyn_arr dirs = {0};
-    size_t i = 0;
-
-    was_rebuild = build_go_rebuild_yourself(__FILE__, &err);
-    if ( was_rebuild == true ) {
-        return 0;
-    }
-
-    dirs = dir_list_with_ext("./src", ".c", &err);
-
-    free(dirs);
-
-    return 0;
 }
 */

+ 366 - 55
src/lib.h

@@ -48,6 +48,7 @@
 
 enum err {
     ERR_OK = 0,
+    ERR_GENERAL_ERROR,
     ERR_NOT_INT,
     ERR_TOO_BIG,
     ERR_NULL_ARG,
@@ -58,6 +59,8 @@ enum err {
     ERR_FAILED_CLOSE,
     ERR_FAILED_READ,
     ERR_FAILED_WRITE,
+    ERR_FAILED_FORK,
+    ERR_FAILED_WAITPID,
     ERR_WROTE_WRONG_AMOUNT,
     ERR_FAILED_ALLOC,
     ERR_FAILED_REALLOC,
@@ -68,11 +71,13 @@ enum err {
     ERR_PATH_INVALID,
     ERR_PATH_FILE_EMPTY,
     ERR_PATH_FILE_FAILED_SEEK,
+    ERR_CMD_NON_ZERO_EXIT_CODE,
     ERR_SGFX_POS_OUTSIDE_CANVAS
 };
 
 const char *err_to_name[] = {
     "ERR_OK",
+    "ERR_GENERAL_ERROR",
     "ERR_NOT_INT",
     "ERR_TOO_BIG",
     "ERR_NULL_ARG",
@@ -83,15 +88,19 @@ const char *err_to_name[] = {
     "ERR_FAILED_CLOSE",
     "ERR_FAILED_READ",
     "ERR_FAILED_WRITE",
+    "ERR_FAILED_FORK",
+    "ERR_FAILED_WAITPID",
     "ERR_WROTE_WRONG_AMOUNT",
     "ERR_FAILED_ALLOC",
     "ERR_FAILED_REALLOC",
     "ERR_MKDIR_FAILED",
     "ERR_FAILED_HASHING",
+    "ERR_STR_EMPTY",
     "ERR_PATH_EMPTY",
     "ERR_PATH_INVALID",
     "ERR_PATH_FILE_EMPTY",
     "ERR_PATH_FILE_FAILED_SEEK",
+    "ERR_CMD_NON_ZERO_EXIT_CODE",
     "ERR_SGFX_POS_OUTSIDE_CANVAS",
 };
 
@@ -120,6 +129,16 @@ void *memcpy(void *dest, const void *src, u64 n);
 void *memset(void *s, int c, u64 n);
 char *getenv(const char *name);
 void abort(void);
+void exit(int exit_code);
+
+int execve(const char *path, char *const argv[],
+           char *const envp[]);
+
+
+int fork(void);
+# ifndef WIFEXITED
+#  error "Must import <sys/wait.h> before lib.h!"
+# endif
 
 /* ----------------------------- START LIB DEF ----------------------------- */
 
@@ -184,6 +203,22 @@ void abort(void);
 
 # define CAST(Type, Var) ((Type) (Var))
 
+# define LIB_FREE lib_free
+
+void lib_free(const void *ptr);
+
+# if defined(IMP) || defined(IMP_LIB)
+
+void
+lib_free(const void *ptr)
+{
+    if ( ptr == NULL ) return;
+
+    free((void *)ptr);
+}
+
+# endif /* defined(IMP) || defined(IMP_LIB) */
+
 /* ------------------------------ END LIB DEF ------------------------------ */
 
 /* ----------------------------- START VEC DEF ----------------------------- */
@@ -220,8 +255,8 @@ _VEC2_STRUCT_DEF(u8);
 
 # if defined(WANT_CSTR) || defined(WANT_ALL)
 
-u64 cstr_len_max(const char *cstr, u64 max,
-            bool *out_hit_max, enum err *out_err);
+u64 cstr_len_max(const char *cstr, u64 max, bool *out_hit_max,
+                 enum err *out_err);
 
 # endif /* defined(WANT_CSTR) || defined(WANT_ALL) */
 
@@ -235,6 +270,14 @@ u64 cstr_len_max(const char *cstr, u64 max,
 #  define STR_SIZE_LIMIT 1028
 # endif /* STR_SIZE_LIMIT */
 
+# define STR_DESTROY(Str) \
+    if ( (Str).should_be_freed == true ) LIB_FREE((Str).data)
+
+# define DA2STR(Da, Str) \
+    (Str).data = (Da).data; \
+    (Str).size = (Da).size; \
+    (Str).should_be_freed = true;
+
 struct str {
     const char *data;
     u64 size;
@@ -522,6 +565,16 @@ dyn_arr_destroy(struct dyn_arr *da, enum err *out_err)
 
 /* ---------------------------- START DA DEF ------------------------------- */
 
+# define DA_FOREACH(Da, Var) \
+    for ( typeof((Da).data) (Var) = (Da).data; \
+          (Var) < ((Da).data + (Da).size); \
+          ++(Var) )
+
+# define DA_FOREACH_INDEX(Da, VarI) \
+    for ( typeof((Da).size) (VarI) = 0; \
+          (VarI) < (Da).size; \
+          ++(VarI) )
+
 # define DA_APPEND(da, val, err_var) \
     do { \
         if ( err_var != NULL ) { \
@@ -547,11 +600,44 @@ dyn_arr_destroy(struct dyn_arr *da, enum err *out_err)
                 LIB_SET_IF_NOT_NULL((err_var), ERR_FAILED_ALLOC); \
                 break; \
             } \
+            memset((da).data, 0, sizeof(*(da).data) * (da).cap); \
         } \
         (da).data[(da).size++] = (val); \
         LIB_SET_IF_NOT_NULL((err_var), ERR_OK); \
     } while(0)
 
+# define DA_APPEND_DATA(Da, Val, ItemNum, ErrVar) \
+    do { \
+        if ( (ErrVar) != NULL ) { \
+            if ( *(ErrVar) != ERR_OK ) { \
+                break; \
+            } \
+        } \
+        if ( (Da).size + (ItemNum) >= (Da).cap ) { \
+            (Da).cap += (ItemNum); \
+            (Da).cap *= 2; \
+            (Da).data = realloc((Da).data, (Da).cap); \
+            if ( (Da).data == NULL ) { \
+                LIB_SET_IF_NOT_NULL((ErrVar), ERR_FAILED_REALLOC); \
+                break; \
+            } \
+        } \
+        if ( (Da).data == NULL ) { \
+            if ( (Da).cap == 0 ) { \
+                (Da).cap = (ItemNum) * 2; \
+                (Da).size = 0; \
+            } \
+            (Da).data = malloc(sizeof(*(Da).data) * (Da).cap); \
+            if ( (Da).data == NULL ) { \
+                LIB_SET_IF_NOT_NULL((ErrVar), ERR_FAILED_ALLOC); \
+                break; \
+            } \
+        } \
+        memcpy((Da).data, (Val), sizeof(*(Da).data) * (ItemNum)); \
+        (Da).size += ItemNum; \
+        LIB_SET_IF_NOT_NULL((ErrVar), ERR_OK); \
+    } while(0)
+
 # define DAV_APPEND(Da, Val, ValSize, ErrVar) \
     do { \
         if ( ErrVar != NULL ) { \
@@ -1271,16 +1357,9 @@ struct token {
     enum token_type type;
     u64 loc_start;
     u64 loc_end;
-
-    u8 _[64];
-};
-
-struct token_wstr {
-    enum token_type type;
-    u64 loc_start;
-    u64 loc_end;
-
     struct str string;
+
+    void *extra; /* This is not used by the lib */
 };
 
 struct tokenizer {
@@ -1301,9 +1380,13 @@ struct tokenizer tokenizer_create(struct str code, struct path src,
                                   struct tokenizer_options *opts,
                                   enum err *out_err);
 
+/* In normal use this function does not fail! */
 struct token tokenizer_next_token(struct tokenizer *tkn, enum err *out_err);
 enum token_type tokenizer_next_token_type(struct tokenizer *tkn,
                                           enum err *out_err);
+struct token tokenizer_peek_token(struct tokenizer *tkn, enum err *out_err);
+enum token_type tokenizer_peek_token_type(struct tokenizer *tkn,
+                                          enum err *out_err);
 bool tokenizer_is_next(struct tokenizer *tkn, enum token_type type,
                        struct token *out_tk, enum err *out_err);
 bool tokenizer_is_next_id(struct tokenizer *tkn, struct str string,
@@ -1389,10 +1472,8 @@ tokenizer_next_token(struct tokenizer *tkn, enum err *out_err)
     }
 
     if ( tkn->opts.is_id_start(tkn, c) == true ) {
-        struct token_wstr tk_ws = {0};
-
-        tk_ws.type = TK_ID;
-        tk_ws.loc_start = tkn->i-1;
+        tk.type = TK_ID;
+        tk.loc_start = tkn->i-1;
 
         do {
             buf.data[buf.size++] = c;
@@ -1400,23 +1481,19 @@ tokenizer_next_token(struct tokenizer *tkn, enum err *out_err)
         } while ( tkn->opts.is_id(tkn, c) == true );
         --tkn->i;
 
-        tk_ws.string.data = buf.data;
-        tk_ws.string.size = buf.size;
+        tk.string.data = buf.data;
+        tk.string.size = buf.size;
 
-        if ( tkn->opts.is_num_lit(tkn, tk_ws.string) == true ) {
-            tk_ws.type = TK_NUM_LIT;
+        if ( tkn->opts.is_num_lit(tkn, tk.string) == true ) {
+            tk.type = TK_NUM_LIT;
         }
-        tk_ws.loc_end = tkn->i-1;
-
-        tk = *(struct token *)&tk_ws;
+        tk.loc_end = tkn->i-1;
         goto exit;
     }
 
     if ( tkn->opts.is_digit(tkn, c) == true ) {
-        struct token_wstr tk_ws = {0};
-
-        tk_ws.type = TK_NUM_LIT;
-        tk_ws.loc_start = tkn->i-1;
+        tk.type = TK_NUM_LIT;
+        tk.loc_start = tkn->i-1;
 
         do {
             buf.data[buf.size++] = c;
@@ -1424,19 +1501,15 @@ tokenizer_next_token(struct tokenizer *tkn, enum err *out_err)
         } while ( tkn->opts.is_digit(tkn, c) == true );
         --tkn->i;
 
-        tk_ws.string.data = buf.data;
-        tk_ws.string.size = buf.size;
-        tk_ws.loc_end = tkn->i-1;
-
-        tk = *(struct token *)&tk_ws;
+        tk.string.data = buf.data;
+        tk.string.size = buf.size;
+        tk.loc_end = tkn->i-1;
         goto exit;
     }
 
     if ( tkn->opts.is_str_lit_start(tkn, c) == true ) {
-        struct token_wstr tk_ws = {0};
-
-        tk_ws.type = TK_STR_LIT;
-        tk_ws.loc_start = tkn->i-1;
+        tk.type = TK_STR_LIT;
+        tk.loc_start = tkn->i-1;
 
         do {
             buf.data[buf.size++] = c;
@@ -1444,11 +1517,9 @@ tokenizer_next_token(struct tokenizer *tkn, enum err *out_err)
         } while ( tkn->opts.is_str_lit_end(tkn, c) == false );
         buf.data[buf.size++] = c;
 
-        tk_ws.string.data = buf.data;
-        tk_ws.string.size = buf.size - 1;
-        tk_ws.loc_end = tkn->i-1;
-
-        tk = *(struct token *)&tk_ws;
+        tk.string.data = buf.data;
+        tk.string.size = buf.size - 1;
+        tk.loc_end = tkn->i-1;
         goto exit;
     }
 
@@ -1491,6 +1562,46 @@ tokenizer_next_token_type(struct tokenizer *tkn, enum err *out_err)
     return tk.type;
 }
 
+struct token
+tokenizer_peek_token(struct tokenizer *tkn, enum err *out_err)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    u64 last_i = 0;
+    struct token empty = {0};
+    struct token tk = {0};
+
+    LIB_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, empty);
+    LIB_ARG_MUST_NOT_BE_NULL(tkn, out_err, empty);
+
+    if ( out_err != NULL ) {
+        perr = out_err;
+    }
+
+    last_i = tkn->i;
+    tk = tokenizer_next_token(tkn, perr);
+    tkn->i = last_i;
+
+    return tk;
+}
+
+enum token_type
+tokenizer_peek_token_type(struct tokenizer *tkn, enum err *out_err)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    struct token tk = {0};
+
+    LIB_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, TK_INVALID);
+    LIB_ARG_MUST_NOT_BE_NULL(tkn, out_err, TK_INVALID);
+
+    tk = tokenizer_peek_token(tkn, perr);
+
+    return tk.type;
+}
+
 bool
 tokenizer_is_next(struct tokenizer *tkn, enum token_type type,
                   struct token *out_tk, enum err *out_err)
@@ -1506,11 +1617,6 @@ tokenizer_is_next(struct tokenizer *tkn, enum token_type type,
     pi = tkn->i;
 
     tk = tokenizer_next_token(tkn, &err);
-    if ( err != ERR_OK ) {
-        tkn->i = pi;
-        LIB_SET_IF_NOT_NULL(out_err, err);
-        return false;
-    }
 
     if ( tk.type != type ) {
         tkn->i = pi;
@@ -1528,7 +1634,6 @@ tokenizer_is_next_id(struct tokenizer *tkn, struct str string,
 {
     enum err err = ERR_OK;
     struct token tk = {0};
-    struct token_wstr *tk_ws = NULL;
     u64 pi = 0;
 
     LIB_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, false);
@@ -1540,19 +1645,13 @@ tokenizer_is_next_id(struct tokenizer *tkn, struct str string,
     pi = tkn->i;
 
     tk = tokenizer_next_token(tkn, &err);
-    if ( err != ERR_OK ) {
-        LIB_SET_IF_NOT_NULL(out_err, err);
-        return false;
-    }
 
     if ( tk.type != TK_ID ) {
         tkn->i = pi;
         return false;
     }
 
-    tk_ws = (struct token_wstr *)&tk;
-
-    if ( str_eq_str(tk_ws->string, string) == false ) {
+    if ( str_eq_str(tk.string, string) == false ) {
         tkn->i = pi;
         return false;
     }
@@ -1835,6 +1934,219 @@ tokenizer_is_str_lit(struct tokenizer *tkn, struct str str)
 
 /* -------------------------- END TOKENIZER DEF ---------------------------- */
 
+/* ---------------------------- START CMD DEF ------------------------------ */
+
+# if defined(WANT_CMD) || defined(WANT_ALL)
+
+struct cmd {
+    char *exe;
+    struct {
+        char **data;
+        u64 size;
+        u64 cap;
+    } args;
+};
+
+#  define cmd_create_ns(Exe, OutErr) \
+    cmd_create( \
+        (struct str) { \
+            .data=(Exe), \
+            .size=cstr_len_max((Exe), STR_SIZE_LIMIT, NULL, (OutErr)) \
+        }, \
+        (OutErr) \
+    )
+struct cmd cmd_create(struct str exe, enum err *_r_ out_err);
+
+#  define cmd_set_exe_ns(Cmd, Exe, OutErr) \
+    cmd_set_exe( \
+        (Cmd), \
+        (struct str) { \
+            .data=(Exe), \
+            .size=cstr_len_max((Exe), STR_SIZE_LIMIT, NULL, (OutErr)) \
+        }, \
+        (OutErr) \
+    )
+enum err cmd_set_exe(struct cmd *_r_ cmd, struct str exe,
+                     enum err *_r_ out_err);
+
+#  define cmd_append_arg_ns(Cmd, Arg, OutErr) \
+    cmd_append_arg( \
+        (Cmd), \
+        (struct str) { \
+            .data=(Arg), \
+            .size=cstr_len_max((Arg), STR_SIZE_LIMIT, NULL, (OutErr)) \
+        }, \
+        (OutErr) \
+    )
+enum err cmd_append_arg(struct cmd *_r_ cmd, struct str arg,
+                        enum err *_r_ out_err);
+
+enum err cmd_exec(struct cmd *_r_ cmd, enum err *_r_ out_err);
+
+enum err cmd_destroy(struct cmd *cmd, enum err *out_err);
+
+#  if defined(IMP_CMD) || defined(IMP)
+
+struct cmd
+cmd_create(struct str exe, enum err *_r_ out_err)
+{
+    struct cmd empty = {0};
+    struct cmd cmd = {0};
+
+    LIB_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, empty);
+    LIB_ARG_MUST_NOT_BE_NULL(exe.data, out_err, empty);
+
+    cmd.exe = malloc(exe.size+1);
+    if ( cmd.exe == NULL ) {
+        LIB_SET_IF_NOT_NULL(out_err, ERR_FAILED_ALLOC);
+        goto exit_err;
+    }
+    cmd.exe[exe.size] = 0;
+    memcpy(cmd.exe, exe.data, exe.size);
+
+    cmd.args.cap = 32;
+    cmd.args.data = malloc(sizeof(*cmd.args.data) * cmd.args.cap);
+    if ( cmd.args.data == NULL ) {
+        LIB_SET_IF_NOT_NULL(out_err, ERR_FAILED_ALLOC);
+        goto exit_err;
+    }
+
+    DA_APPEND(cmd.args, cmd.exe, out_err);
+
+    LIB_SET_IF_NOT_NULL(out_err, ERR_OK);
+    return cmd;
+exit_err:
+    LIB_FREE(cmd.exe);
+    LIB_FREE(cmd.args.data);
+    return (struct cmd){0};
+}
+
+enum err
+cmd_set_exe(struct cmd *_r_ cmd, struct str exe, enum err *_r_ out_err)
+{
+    LIB_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd, out_err);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(exe.data, out_err);
+
+    if ( cmd->exe != NULL ) {
+        LIB_FREE(cmd->exe);
+    }
+
+    cmd->exe = malloc(exe.size+1);
+    if ( cmd->exe == NULL ) {
+        LIB_SET_IF_NOT_NULL(out_err, ERR_FAILED_ALLOC);
+        return ERR_FAILED_ALLOC;
+    }
+    cmd->exe[exe.size] = 0;
+    memcpy(cmd->exe, exe.data, exe.size);
+
+    cmd->args.data[0] = cmd->exe;
+
+    LIB_SET_IF_NOT_NULL(out_err, ERR_OK);
+    return ERR_OK;
+}
+
+enum err
+cmd_append_arg(struct cmd *_r_ cmd, struct str arg, enum err *_r_ out_err)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    char *t = NULL;
+
+    LIB_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd, out_err);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd->args.data, out_err);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(arg.data, out_err);
+
+    if ( out_err != NULL ) {
+        perr = out_err;
+    }
+
+    t = malloc(arg.size + 1);
+    if ( t == NULL ) {
+        LIB_SET_IF_NOT_NULL(out_err, ERR_FAILED_ALLOC);
+        return ERR_FAILED_ALLOC;
+    }
+    t[arg.size] = 0;
+    memcpy(t, arg.data, arg.size);
+
+    DA_APPEND(cmd->args, t, perr);
+
+    return *perr;
+}
+
+enum err
+cmd_exec(struct cmd *_r_ cmd, enum err *_r_ out_err)
+{
+    i32 pid = -1;
+    i32 status = 0;
+
+    LIB_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd, out_err);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd->exe, out_err);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd->args.data, out_err);
+
+    cmd->args.data[cmd->args.size] = NULL;
+
+    pid = fork();
+    if ( pid < 0 ) {
+        LIB_SET_IF_NOT_NULL(out_err, ERR_FAILED_FORK);
+        return ERR_FAILED_FORK;
+    }
+
+    if ( pid == 0 ) {
+        /*
+        printf("exe=`%s`\n", cmd->exe);
+        DA_FOREACH(cmd->args, it) {
+            printf("arg=`%s`\n", *it);
+        }
+        */
+        execve(cmd->exe, cmd->args.data, NULL);
+        exit(1);
+    }
+
+    if ( waitpid(pid, &status, 0) < 0 ) {
+        LIB_SET_IF_NOT_NULL(out_err, ERR_FAILED_WAITPID);
+        return ERR_FAILED_WAITPID;
+    }
+
+    if ( WIFEXITED(status) == true ) {
+        if ( WEXITSTATUS(status) != 0 ) {
+            LIB_SET_IF_NOT_NULL(out_err, ERR_CMD_NON_ZERO_EXIT_CODE);
+            return ERR_CMD_NON_ZERO_EXIT_CODE;
+        }
+    }
+
+    LIB_SET_IF_NOT_NULL(out_err, ERR_OK);
+    return ERR_OK;
+}
+
+enum err
+cmd_destroy(struct cmd *cmd, enum err *out_err)
+{
+    LIB_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd, out_err);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd->exe, out_err);
+    LIB_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(cmd->args.data, out_err);
+
+    LIB_FREE(cmd->exe);
+    DA_FOREACH(cmd->args, it) {
+        if ( *it == cmd->exe ) continue;
+        LIB_FREE(*it);
+    }
+    LIB_FREE(cmd->args.data);
+
+    LIB_SET_IF_NOT_NULL(out_err, ERR_OK);
+    return ERR_OK;
+}
+
+#  endif /* defined(IMP_CMD) || defined(IMP) */
+
+# endif /* defined(WANT_CMD) || defined(WANT_ALL) */
+
+/* ----------------------------- END CMD DEF ------------------------------- */
+
 /* ---------------------------- START SGFX DEF ----------------------------- */
 
 # if defined(WANT_SGFX) || defined(WANT_ALL)
@@ -2178,8 +2490,7 @@ _str_memcpy(void *dest, const void *src, u64 n)
 #  if defined(WANT_CSTR) || defined(WANT_ALL)
 
 u64
-cstr_len_max(const char *cstr, u64 max,
-        bool *out_hit_max, enum err *out_err)
+cstr_len_max(const char *cstr, u64 max, bool *out_hit_max, enum err *out_err)
 {
     u64 ret = 0;