Vinicius Teshima 10 hodín pred
rodič
commit
1c49830696
1 zmenil súbory, kde vykonal 688 pridanie a 90 odobranie
  1. 688 90
      src/lib.h

+ 688 - 90
src/lib.h

@@ -2,34 +2,6 @@
 #ifndef LIB_H
 # define LIB_H
 
-# ifndef _STDINT_H
-#  define LIB_H_STDINT
-
-#  define intmax_t long int
-# endif /* _STDINT_H */
-
-# ifndef _UNISTD_H
-#  define LIB_H_UNISTD
-
-#  define R_OK 4
-#  define W_OK 2
-#  define X_OK 1
-#  define F_OK 0
-# endif /* _UNISTD_H */
-
-# ifndef NULL
-#  define LIB_H_NULL
-#  define NULL ((void *) 0)
-# endif /* NULL */
-
-# ifndef _STDBOOL_H
-#  define LIB_H_STDBOOL
-
-#  define bool unsigned char
-#  define true 1
-#  define false 0
-# endif /* _STDBOOL_H */
-
 /* --------------------------- START GLOBAL DEF ---------------------------- */
 
 # define ALLOC_FUNC void *(*alloc)(u64)
@@ -51,30 +23,45 @@ enum err {
     ERR_NOT_INT,
     ERR_TOO_BIG,
     ERR_NULL_ARG,
+    ERR_SIZE_ZERO,
     ERR_INVALID_ARG,
+    ERR_INVALID_SIZE,
+    ERR_INVALID_PPM_FORMAT,
     ERR_NOT_FOUND,
+    ERR_NO_MATCH,
+    ERR_AMBIGUOUS,
     ERR_INDEX_TOO_LARGE,
     ERR_FAILED_OPEN,
     ERR_FAILED_CLOSE,
     ERR_FAILED_READ,
     ERR_FAILED_WRITE,
+    ERR_FAILED_PIPE,
+    ERR_FAILED_LSEEK,
     ERR_FAILED_FORK,
     ERR_FAILED_WAITPID,
     ERR_FAILED_STAT,
     ERR_FAILED_OPENDIR,
     ERR_FAILED_READDIR,
+    ERR_FAILED_HASHING,
+    ERR_FAILED_ENCRYPTING,
+    ERR_FAILED_DECRYPTING,
     ERR_WROTE_WRONG_AMOUNT,
     ERR_FAILED_ALLOC,
     ERR_FAILED_REALLOC,
     ERR_MKDIR_FAILED,
-    ERR_FAILED_HASHING,
     ERR_STR_EMPTY,
+    ERR_FILE_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
+    ERR_SGFX_POS_OUTSIDE_CANVAS,
+    ERR_X11_FAILED_OPEN_DISPLAY,
+    ERR_X11_FAILED_CREATE_GC,
+    ERR_X11_FAILED_CREATE_IMAGE,
+    ERR_X11_FAILED_INIT_IMAGE,
+    ERR_X11_FAILED_GET_WIN_ATTR
 };
 
 const char *err2cstr(enum err err);
@@ -90,30 +77,45 @@ err2cstr(enum err err)
     case ERR_NOT_INT: return "ERR_NOT_INT";
     case ERR_TOO_BIG: return "ERR_TOO_BIG";
     case ERR_NULL_ARG: return "ERR_NULL_ARG";
+    case ERR_SIZE_ZERO: return "ERR_SIZE_ZERO";
     case ERR_INVALID_ARG: return "ERR_INVALID_ARG";
+    case ERR_INVALID_SIZE: return "ERR_INVALID_SIZE";
+    case ERR_INVALID_PPM_FORMAT: return "ERR_INVALID_PPM_FORMAT";
     case ERR_NOT_FOUND: return "ERR_NOT_FOUND";
+    case ERR_NO_MATCH: return "ERR_NO_MATCH";
+    case ERR_AMBIGUOUS: return "ERR_AMBIGUOUS";
     case ERR_INDEX_TOO_LARGE: return "ERR_INDEX_TOO_LARGE";
     case ERR_FAILED_OPEN: return "ERR_FAILED_OPEN";
     case ERR_FAILED_CLOSE: return "ERR_FAILED_CLOSE";
     case ERR_FAILED_READ: return "ERR_FAILED_READ";
     case ERR_FAILED_WRITE: return "ERR_FAILED_WRITE";
+    case ERR_FAILED_PIPE: return "ERR_FAILED_PIPE";
+    case ERR_FAILED_LSEEK: return "ERR_FAILED_LSEEK";
     case ERR_FAILED_FORK: return "ERR_FAILED_FORK";
     case ERR_FAILED_WAITPID: return "ERR_FAILED_WAITPID";
     case ERR_FAILED_STAT: return "ERR_FAILED_STAT";
     case ERR_FAILED_OPENDIR: return "ERR_FAILED_OPENDIR";
     case ERR_FAILED_READDIR: return "ERR_FAILED_READDIR";
+    case ERR_FAILED_HASHING: return "ERR_FAILED_HASHING";
+    case ERR_FAILED_ENCRYPTING: return "ERR_FAILED_ENCRYPTING";
+    case ERR_FAILED_DECRYPTING: return "ERR_FAILED_DECRYPTING";
     case ERR_WROTE_WRONG_AMOUNT: return "ERR_WROTE_WRONG_AMOUNT";
     case ERR_FAILED_ALLOC: return "ERR_FAILED_ALLOC";
     case ERR_FAILED_REALLOC: return "ERR_FAILED_REALLOC";
     case ERR_MKDIR_FAILED: return "ERR_MKDIR_FAILED";
-    case ERR_FAILED_HASHING: return "ERR_FAILED_HASHING";
     case ERR_STR_EMPTY: return "ERR_STR_EMPTY";
+    case ERR_FILE_EMPTY: return "ERR_FILE_EMPTY";
     case ERR_PATH_EMPTY: return "ERR_PATH_EMPTY";
     case ERR_PATH_INVALID: return "ERR_PATH_INVALID";
     case ERR_PATH_FILE_EMPTY: return "ERR_PATH_FILE_EMPTY";
     case ERR_PATH_FILE_FAILED_SEEK: return "ERR_PATH_FILE_FAILED_SEEK";
     case ERR_CMD_NON_ZERO_EXIT_CODE: return "ERR_CMD_NON_ZERO_EXIT_CODE";
     case ERR_SGFX_POS_OUTSIDE_CANVAS: return "ERR_SGFX_POS_OUTSIDE_CANVAS";
+    case ERR_X11_FAILED_OPEN_DISPLAY: return "ERR_X11_FAILED_OPEN_DISPLAY";
+    case ERR_X11_FAILED_CREATE_GC: return "ERR_X11_FAILED_CREATE_GC";
+    case ERR_X11_FAILED_CREATE_IMAGE: return "ERR_X11_FAILED_CREATE_IMAGE";
+    case ERR_X11_FAILED_INIT_IMAGE: return "ERR_X11_FAILED_INIT_IMAGE";
+    case ERR_X11_FAILED_GET_WIN_ATTR: return "ERR_X11_FAILED_GET_WIN_ATTR";
     default: return "UNKNOWN_ERR";
     }
 }
@@ -138,12 +140,29 @@ typedef unsigned char u8;
 
 /* ------------------------------ END INT DEF ------------------------------ */
 
+# ifndef LIB_DO_NOT_INCLUDE_IT
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <stdbool.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+# endif /* LIB_DO_NOT_INCLUDE_IT */
+
+# define _r_ __restrict__
+
 extern char **environ;
 
 void *malloc(u64 size);
 void *realloc(void *ptr, u64 new_size);
 void free(void *ptr);
-void *memcpy(void *dest, const void *src, u64 n);
+void *memcpy(void *_r_ dest, const void *_r_ src, u64 n);
+void *memmove(void *dest, const void *src, u64 n);
 void *memset(void *s, int c, u64 n);
 char *getenv(const char *name);
 void abort(void);
@@ -154,21 +173,6 @@ int execve(const char *path, char *const argv[], char *const envp[]);
 int execvpe(const char *path, char *const argv[], char *const envp[]);
 
 int fork(void);
-# ifndef WIFEXITED
-#  error "Must import <sys/wait.h> before lib.h!"
-# endif
-
-# if (! defined(_STDARG_H)) || (! defined(va_start))
-#  error "Must import <stdarg.h> before lib.h!"
-# endif
-
-# ifndef _DIRENT_H
-#  error "Must import <dirent.h> before lib.h!"
-# endif
-
-# ifndef _ERRNO_H
-#  error "Must import <errno.h> before lib.h!"
-# endif
 
 /* ----------------------------- START LIB DEF ----------------------------- */
 
@@ -227,27 +231,133 @@ int fork(void);
         return ret_val; \
     }
 
-# define _r_ __restrict__
-
 # define SIZEOF_MEMBER(Struct, Member) sizeof(((Struct *)0)->Member)
 
 # define CAST(Type, Var) ((Type) (Var))
 
-/*# define LIB_FREE lib_free*/
+# define LIB_CLOSE close
+# define LIB_LSEEK lseek
+# define LIB_READ read
+# define LIB_MALLOC malloc
+# define LIB_CALLOC calloc
+# define LIB_MEMCPY memcpy
+# define LIB_OPEN open
 # define LIB_FREE(Ptr) if ( (Ptr) != NULL ) { free((void *) (Ptr)); }
 
-void lib_free(const void *ptr);
 const char *temp_sprintf(const char *fmt, ...);
 
-# if defined(IMP) || defined(IMP_LIB)
+#define COALESCE(...) \
+    _COALESCE_NARG_(__VA_ARGS__, \
+        COALESCE5, COALESCE4, \
+        COALESCE3, COALESCE2)(__VA_ARGS__)
+
+// Helper macros for different numbers of arguments
+#define _COALESCE_NARG_(...) _COALESCE_NARG_I_(__VA_ARGS__)
+#define _COALESCE_NARG_I_(a1, a2, a3, a4, a5, N, ...) N
+#define COALESCE5(a, b, c, d, e) COALESCE2(COALESCE4(a, b, c, d), e)
+#define COALESCE4(a, b, c, d) COALESCE2(COALESCE3(a, b, c), d)
+#define COALESCE3(a, b, c) COALESCE2(COALESCE2(a, b), c)
+#define COALESCE2(a, b) (((a) != NULL) ? (a) : (b))
+
+struct __file_slurp_args {
+    u64 path_size;
+    u64 *out_size;
+    enum err *out_err;
+};
+#define file_slurp(Path, ...) \
+    _file_slurp( \
+        (Path), \
+        (struct __file_slurp_args) { \
+            .path_size=0, \
+            .out_size=NULL, \
+            .out_err=NULL, \
+            __VA_ARGS__ \
+        } \
+    )
+u8 *_file_slurp(const char *path, struct __file_slurp_args args);
+
+struct __file_spit_args {
+    bool append;
+    bool create;
+    u16 mode;
+    u64 path_size;
+    enum err *out_err;
+};
+#define file_spit(Path, Data, Size, ...) \
+    _file_spit( \
+        (Path), \
+        (Data), \
+        (Size), \
+        (struct __file_spit_args) { \
+            .append=false, \
+            .create=true, \
+            .mode=00644, \
+            .path_size=0, \
+            .out_err=NULL, \
+            __VA_ARGS__ \
+        } \
+    )
+enum err _file_spit(const char *_r_ path, u8 *_r_ data, u64 size,
+                    struct __file_spit_args args);
 
-void
-lib_free(const void *ptr)
-{
-    if ( ptr == NULL ) return;
+struct __cstr_from_i64_args {
+    bool alloc;
+    u64 *out_size;
+    enum err *out_err;
+};
+#define cstr_from_i64(Num, ...) \
+    _cstr_from_i64( \
+        (Num), \
+        (struct __cstr_from_i64_args) { \
+            .alloc=false, \
+            .out_size=NULL, \
+            .out_err=NULL, \
+            __VA_ARGS__ \
+        } \
+    )
+char *_cstr_from_i64(i64 num, struct __cstr_from_i64_args args);
 
-    free((void *)ptr);
-}
+struct rgb_pixel { u8 r, g, b; };
+struct rgb_pixel_array { struct rgb_pixel *data; u64 size; };
+struct rgb_pixel_da { struct rgb_pixel *data; u64 size; u64 cap; };
+
+struct ppm {
+    struct rgb_pixel *data;
+    u64 width;
+    u64 height;
+};
+
+struct __ppm_from_bytes_args {
+    enum err *out_err;
+};
+#define ppm_from_bytes(Data, Size, ...) \
+    _ppm_from_bytes( \
+        (Data), \
+        (Size), \
+        (struct __ppm_from_bytes_args) { \
+            .out_err=NULL, \
+            __VA_ARGS__ \
+        } \
+    )
+struct ppm _ppm_from_bytes(const u8 *_r_ data, u64 size,
+                           struct __ppm_from_bytes_args args);
+
+struct __ppm_to_bytes_args {
+    u64 *out_size;
+    enum err *out_err;
+};
+#define ppm_to_bytes(PPM, ...) \
+    _ppm_to_bytes( \
+        (PPM), \
+        (struct __ppm_to_bytes_args) { \
+            .out_size=NULL, \
+            .out_err=NULL, \
+            __VA_ARGS__ \
+        } \
+    )
+u8 *_ppm_to_bytes(struct ppm *_r_ ppm, struct __ppm_to_bytes_args args);
+
+# if defined(IMP) || defined(IMP_LIB)
 
 const char *
 temp_sprintf(const char *fmt, ...)
@@ -265,6 +375,330 @@ temp_sprintf(const char *fmt, ...)
     return (const char *) buf;
 }
 
+u8 *
+_file_slurp(const char *path, struct __file_slurp_args args)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    i32 fd = -1;
+    u8 *data = NULL;
+
+    if ( args.out_err != NULL ) { perr = args.out_err; }
+    if ( *perr != ERR_OK ) { goto exit_err; }
+
+    if ( path == NULL ) { *perr = ERR_NULL_ARG; goto exit_err; }
+
+    char *_path = (char *) path;
+    if ( args.path_size != 0 ) {
+        _path = calloc(args.path_size+1, sizeof(*_path));
+        if ( _path == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+        memcpy(_path, path, args.path_size);
+    }
+
+    fd = open(_path, O_RDONLY);
+    if ( fd < 0 ) { *perr = ERR_FAILED_OPEN; goto exit_err; }
+
+    i64 file_size = lseek(fd, 0, SEEK_END);
+    if ( file_size < 0 ) { *perr = ERR_PATH_FILE_FAILED_SEEK; goto exit_err; }
+    if ( file_size == 0 ) { *perr = ERR_PATH_FILE_EMPTY; goto exit_err; }
+    {
+        i64 ss = lseek(fd, 0, SEEK_SET);
+        if ( ss < 0 ) { *perr = ERR_PATH_FILE_FAILED_SEEK; goto exit_err; }
+    }
+
+    data = calloc((u64)file_size + 1, sizeof(*data));
+    if ( data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+
+    {
+        i64 rd = read(fd, data, (u64) file_size);
+        if ( rd < 0 ) { *perr = ERR_FAILED_READ; goto exit_err; }
+        if ( rd == 0 ) { *perr = ERR_PATH_FILE_EMPTY; goto exit_err; }
+    }
+
+    if ( close(fd) != 0 ) {
+        /* It should be possible to handle EIO */
+        *perr = ERR_FAILED_CLOSE;
+        goto exit_err;
+    }
+
+    if ( args.out_size != NULL ) { *args.out_size = (u64) file_size; }
+
+    *perr = ERR_OK;
+    return data;
+exit_err:
+    if ( fd > 0 ) { close(fd); }
+    if ( data != NULL ) { free(data); }
+    if ( *perr == ERR_OK ) { *perr = ERR_GENERAL_ERROR; }
+    return NULL;
+}
+
+enum err _file_spit(const char *_r_ path, u8 *_r_ data, u64 size,
+                    struct __file_spit_args args)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    i32 fd = -1;
+
+    if ( args.out_err != NULL ) { perr = args.out_err; }
+    if ( *perr != ERR_OK ) { goto exit_err; }
+
+    if ( path == NULL ) { *perr = ERR_NULL_ARG; goto exit_err; }
+    if ( data == NULL ) { *perr = ERR_NULL_ARG; goto exit_err; }
+    if ( size == 0 ) { *perr = ERR_SIZE_ZERO; goto exit_err; }
+
+    char *_path = (char *) path;
+    if ( args.path_size != 0 ) {
+        _path = calloc(args.path_size+1, sizeof(*_path));
+        if ( _path == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+        memcpy(_path, path, args.path_size);
+    }
+
+    {
+        i32 flags = O_WRONLY;
+        flags |= (args.append) * O_APPEND;
+        flags |= (args.create) * O_CREAT;
+        fd = open(_path, flags, args.mode);
+        if ( fd < 0 ) { *perr = ERR_FAILED_OPEN; goto exit_err; }
+    }
+
+    {
+        i64 wr = write(fd, data, size);
+        if ( wr < 0 ) { *perr = ERR_FAILED_WRITE; goto exit_err; }
+        if ( ((u64)wr) != size ) { *perr = ERR_FAILED_WRITE; goto exit_err; }
+    }
+
+    if ( close(fd) != 0 ) {
+        /* It should be possible to handle EIO */
+        *perr = ERR_FAILED_CLOSE;
+        goto exit_err;
+    }
+
+    *perr = ERR_OK;
+    return *perr;
+exit_err:
+    if ( fd > 0 ) { close(fd); }
+    if ( *perr == ERR_OK ) { *perr = ERR_GENERAL_ERROR; }
+    return *perr;
+}
+
+char *
+_cstr_from_i64(i64 num, struct __cstr_from_i64_args args)
+{
+    static char data[32];
+    static u64 cap = 32;
+    u64 size = 0;
+
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    if ( args.out_err != NULL ) { perr = args.out_err; }
+    if ( *perr != ERR_OK ) { goto exit_err; }
+
+    if ( num < 0 ) {
+        data[size++] = '-';
+        num *= -1;
+    }
+
+    u64 _num = (u64)num;
+
+    while ( _num > 0 ) {
+        u8 mod = (u8) (_num % 10);
+        _num /= 10;
+        data[size++] = (char) (mod + '0');
+        if ( size >= cap ) { *perr = ERR_TOO_BIG; goto exit_err; }
+    }
+
+    u64 limit = (u64) size / 2;
+    for ( u64 i = 0; i < limit; ++i ) {
+        char t = data[i];
+        u64 other = (size-1)-i;
+        data[i] = data[other];
+        data[other] = t;
+    }
+
+    data[size] = 0;
+
+    if ( args.out_size != NULL ) { *args.out_size = size; }
+
+    if ( args.alloc ) {
+        char *ret = NULL;
+        ret = calloc(size+1, sizeof(*ret));
+        if ( ret == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+        memcpy(ret, data, size);
+        return ret;
+    }
+
+    return data;
+exit_err:
+    if ( *perr != ERR_OK ) { *perr = ERR_GENERAL_ERROR; }
+    return NULL;
+}
+
+struct ppm
+_ppm_from_bytes(const u8 *_r_ data, u64 size,
+                struct __ppm_from_bytes_args args)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    if ( args.out_err != NULL ) { perr = args.out_err; }
+    if ( *perr != ERR_OK ) { goto exit_err; }
+
+    if ( data == NULL ) { *perr = ERR_NULL_ARG; goto exit_err; }
+    if ( size == 0 ) { *perr = ERR_SIZE_ZERO; goto exit_err; }
+    if ( size < 12 ) { *perr = ERR_INVALID_SIZE; goto exit_err; }
+
+    if ( *data++ != 'P' || *data++ != '6' || *data++ != '\n' ) {
+        *perr = ERR_INVALID_PPM_FORMAT; goto exit_err;
+    }
+
+    u64 height = 0;
+    u64 width = 0;
+
+    {
+        u64 i = 0;
+        while ( *data != ' ' ) {
+            if ( *data < '0' || *data > '9' || i > 6 ) {
+                *perr = ERR_INVALID_PPM_FORMAT; goto exit_err;
+            }
+
+            width = ((u64) (*data++ - '0')) + (width * 10);
+            ++i;
+        }
+
+        data++;
+
+        i = 0;
+        while ( *data != '\n' ) {
+            if ( *data < '0' || *data > '9' || i > 6 ) {
+                *perr = ERR_INVALID_PPM_FORMAT; goto exit_err;
+            }
+
+            height = ((u64) (*data++ - '0')) + (height * 10);
+            ++i;
+        }
+
+        data++;
+    }
+
+    if ( *data++ != '2' || *data++ != '5' || *data++ != '5' || *data++ != '\n' ) {
+        *perr = ERR_INVALID_PPM_FORMAT; goto exit_err;
+    }
+
+    struct rgb_pixel_da da = {0};
+    da.size = 0;
+    da.cap = (height * width) + 1;
+    da.data = calloc(da.cap, sizeof(*da.data));
+    if ( da.data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+
+    struct rgb_pixel *reint = (struct rgb_pixel *) data;
+    for ( u64 i = 0; i < da.cap; ++i ) {
+        da.data[da.size++] = reint[i];
+    }
+
+    struct ppm ret = {
+        .data=da.data,
+        .width=width,
+        .height=height
+    };
+
+    return ret;
+exit_err:
+    if ( *perr == ERR_OK ) { *perr = ERR_GENERAL_ERROR; }
+    return (struct ppm){0};
+}
+
+u8 *
+_ppm_to_bytes(struct ppm *_r_ ppm, struct __ppm_to_bytes_args args)
+{
+    enum err err = ERR_OK;
+    enum err *perr = &err;
+
+    if ( args.out_err != NULL ) { perr = args.out_err; }
+    if ( *perr != ERR_OK ) { goto exit_err; }
+
+    if ( ppm == NULL ) { *perr = ERR_NULL_ARG; goto exit_err; }
+    if ( ppm->data == NULL ) { *perr = ERR_NULL_ARG; goto exit_err; }
+    if ( ppm->width == 0 || ppm->height == 0 ) {
+        *perr = ERR_SIZE_ZERO; goto exit_err;
+    }
+
+    u8 *data = NULL;
+    u64 size = 0;
+    u64 cap  = 32;
+
+    data = calloc(cap, sizeof(*data));
+    if ( data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+
+    data[size++] = 'P';
+    data[size++] = '6';
+    data[size++] = '\n';
+
+
+    {
+        u64 width_size = 0;
+        char *width = cstr_from_i64((i64)ppm->width, .out_size=&width_size);
+        for ( u64 i = 0; i < width_size; ++i ) {
+            data[size++] = (u8)width[i];
+            if ( size >= cap ) {
+                cap *= 2;
+                data = realloc(data, cap);
+                if ( data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+            }
+        }
+
+        data[size++] = ' ';
+        if ( size >= cap ) {
+            cap *= 2;
+            data = realloc(data, cap);
+            if ( data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+        }
+
+        u64 height_size = 0;
+        char *height = cstr_from_i64((i64)ppm->height, .out_size=&height_size);
+        for ( u64 i = 0; i < height_size; ++i ) {
+            data[size++] = (u8)height[i];
+            if ( size >= cap ) {
+                cap *= 2;
+                data = realloc(data, cap);
+                if ( data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+            }
+        }
+    }
+
+    if ( size+6 >= cap ) {
+        cap *= 2;
+        data = realloc(data, cap);
+        if ( data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+    }
+    data[size++] = '\n';
+    data[size++] = '2';
+    data[size++] = '5';
+    data[size++] = '5';
+    data[size++] = '\n';
+
+    u64 end = ppm->width * ppm->height;
+    for ( u64 i = 0; i < end; ++i ) {
+        if ( size+4 >= cap ) {
+            cap *= 2;
+            data = realloc(data, cap);
+            if ( data == NULL ) { *perr = ERR_FAILED_ALLOC; goto exit_err; }
+        }
+        data[size++] = ppm->data[i].r;
+        data[size++] = ppm->data[i].g;
+        data[size++] = ppm->data[i].b;
+    }
+
+    if ( args.out_size != NULL ) { *args.out_size = size; }
+
+    return data;
+exit_err:
+    if ( data == NULL ) { free(data); }
+    return NULL;
+}
+
 # endif /* defined(IMP) || defined(IMP_LIB) */
 
 /* ------------------------------ END LIB DEF ------------------------------ */
@@ -337,6 +771,17 @@ const char *cstr_dup(const char *cstr, enum err *out_err);
 bool cstr_eq(const char *_r_ cstr1, u64 size1,
              const char *_r_ cstr2, u64 size2, enum err *_r_ out_err);
 
+struct __cstr_has {
+    char *prefix;
+    u64 prefix_size;
+    char *suffix;
+    u64 suffix_size;
+    char c;
+    char *cstr;
+    u64 cstr_size;
+};
+bool _cstr_has(const char *_r_ cstr, u64 size, enum err *_r_ out_err);
+
 bool cstr_endswith(const char *_r_ cstr, u64 size,
                    const char *_r_ end, u64 end_size, enum err *_r_ out_err);
 
@@ -433,7 +878,11 @@ cstr_eq(const char *_r_ cstr1, u64 size1,
     LIB_ARG_MUST_NOT_BE_NULL(cstr1, out_err, false);
     LIB_ARG_MUST_NOT_BE_NULL(cstr2, out_err, false);
 
+    if ( size1 == 0 ) { size1 = cstr_len(cstr1, NULL); }
+    if ( size2 == 0 ) { size2 = cstr_len(cstr2, NULL); }
+
     if ( size1 != size2 ) return false;
+    if ( cstr1 == cstr2 ) return true;
 
     for ( u64 i = 0; i < size1; ++i ) {
         if ( cstr1[i] != cstr2[i] ) return false;
@@ -802,6 +1251,25 @@ dyn_arr_destroy(struct dyn_arr *da, enum err *out_err)
         LIB_SET_IF_NOT_NULL((OutErr), ERR_OK); \
     } while(0)
 
+# define DA_RESERVE(Da, Amount, OutErr) \
+    do { \
+        if ( (OutErr) != NULL ) { \
+            if ( *(OutErr) != ERR_OK ) { \
+                break; \
+            } \
+        } \
+        if ( ((Da).cap - (Da).size) >= (Amount) ) { break; } \
+        (Da).cap = (Da).cap + (Amount); \
+        (Da).data = realloc((Da).data, sizeof(*(Da).data) * (Da).cap); \
+        if ( (Da).data == NULL ) { \
+            LIB_SET_IF_NOT_NULL((OutErr), ERR_FAILED_REALLOC); \
+            break; \
+        } \
+        memset((Da).data + (Da).size, 0, \
+               sizeof(*(Da).data) * ((Da).cap - (Da).size)); \
+        LIB_SET_IF_NOT_NULL((OutErr), ERR_OK); \
+    } while(0)
+
 # define DA_APPEND_DATA(Da, Val, ItemNum, ErrVar) \
     do { \
         if ( (ErrVar) != NULL ) { \
@@ -834,6 +1302,23 @@ dyn_arr_destroy(struct dyn_arr *da, enum err *out_err)
         LIB_SET_IF_NOT_NULL((ErrVar), ERR_OK); \
     } while(0)
 
+# define DA_DELETE(Da, Index, ErrVar) \
+    do { \
+        if ( (ErrVar) != NULL ) { \
+            if ( *(ErrVar) != ERR_OK ) { \
+                break; \
+            } \
+        } \
+        if ( (Index) >= (Da).size ) { \
+            LIB_SET_IF_NOT_NULL((ErrVar), ERR_INDEX_TOO_LARGE); \
+            break; \
+        } \
+        --(Da).size; \
+        memmove((Da).data + (Index), ((Da).data+(Index))+1, \
+                sizeof(*(Da).data)*((Da).size - ((Index)+1))); \
+        memset((Da).data + (Da).size, 0, sizeof(*(Da).data)); \
+    } while(0)
+
 # define DAV_APPEND(Da, Val, ValSize, ErrVar) \
     do { \
         if ( ErrVar != NULL ) { \
@@ -1740,14 +2225,14 @@ struct tokenizer {
 
     struct token last;
 
-    struct path src;
+    struct str src;
     struct str code;
 
     void *edata; /* This is not used by the lib */
 };
 
 
-struct tokenizer tokenizer_create(struct str code, struct path src,
+struct tokenizer tokenizer_create(struct str code, struct str src,
                                   struct tokenizer_options *opts,
                                   enum err *out_err);
 
@@ -1790,7 +2275,7 @@ bool tokenizer_is_str_lit(struct tokenizer *tkn, struct str str);
 #  if defined(IMP) || defined(IMP_TOKENIZER)
 
 struct tokenizer
-tokenizer_create(struct str code, struct path src,
+tokenizer_create(struct str code, struct str src,
                  struct tokenizer_options *opts, enum err *out_err)
 {
     struct tokenizer empty = {0};
@@ -2340,6 +2825,11 @@ struct cmd {
         u64 size;
         u64 cap;
     } envs;
+    struct {
+        i32 *data;
+        u64 size;
+        u64 cap;
+    } fds;
 };
 
 struct __cmd_create_args {
@@ -2413,8 +2903,13 @@ enum err cmd_append_env(struct cmd *_r_ cmd, struct str env,
 
 struct __cmd_exec_args {
     struct procs *procs;
+    u64 procs_num; /* When it is 0 will use nproc */
     bool reset;
     bool search_path;
+    bool verbose;
+    i32 *fd_in, *fd_out, *fd_err;
+    struct str_builder *capture_out;
+    struct str_builder *capture_err;
 };
 #  define cmd_exec(Cmd, OutErr, ...) \
     _cmd_exec( \
@@ -2422,6 +2917,7 @@ struct __cmd_exec_args {
         (struct __cmd_exec_args) { \
             .reset=true, \
             .search_path=true, \
+            .verbose=false, \
             __VA_ARGS__ \
         }, \
         (OutErr) \
@@ -2429,6 +2925,9 @@ struct __cmd_exec_args {
 enum err _cmd_exec(struct cmd *_r_ cmd, struct __cmd_exec_args args,
                    enum err *_r_ out_err);
 
+#  define cmd_run_cap_out _cmd_run_cap_out
+char *_cmd_run_cap_out(struct cmd *_r_ cmd, enum err *_r_ out_err);
+
 enum err cmd_clear_args(struct cmd *cmd, enum err *out_err);
 enum err cmd_destroy(struct cmd *cmd, enum err *out_err);
 
@@ -2653,6 +3152,9 @@ _cmd_exec(struct cmd *_r_ cmd, struct __cmd_exec_args args,
 
     i32 pid = -1;
     i32 status = 0;
+    i32 stdin_pipe[2] = {-1};
+    i32 stdout_pipe[2] = {-1};
+    i32 stderr_pipe[2] = {-1};
 
     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);
@@ -2664,13 +3166,80 @@ _cmd_exec(struct cmd *_r_ cmd, struct __cmd_exec_args args,
 
     cmd->args.data[cmd->args.size] = NULL;
 
+    if ( args.capture_out != NULL ) {
+        DA_RESERVE(*args.capture_out, 128, perr);
+        if ( *perr != ERR_OK ) { return *perr; }
+        if ( pipe(stdout_pipe) < 0 ) { *perr = ERR_FAILED_PIPE; return *perr; }
+        DA_APPEND(cmd->fds, stdout_pipe[0], perr);
+        if ( *perr != ERR_OK ) { return *perr; }
+    }
+
+    if ( args.capture_err != NULL ) {
+        DA_RESERVE(*args.capture_err, 128, perr);
+        if ( *perr != ERR_OK ) { return *perr; }
+        if ( pipe(stderr_pipe) < 0 ) { *perr = ERR_FAILED_PIPE; return *perr; }
+        DA_APPEND(cmd->fds, stderr_pipe[0], perr);
+        if ( *perr != ERR_OK ) { return *perr; }
+    }
+
+
+    if ( args.fd_in != NULL ) {
+        if ( stdin_pipe[0] == -1 ) {
+            if ( pipe(stdin_pipe) < 0 ) { *perr = ERR_FAILED_PIPE; return *perr; }
+            DA_APPEND(cmd->fds, stdin_pipe[1], perr);
+            if ( *perr != ERR_OK ) { return *perr; }
+        }
+        *args.fd_in = stdin_pipe[1];
+    }
+
+    if ( args.fd_out != NULL ) {
+        if ( stdout_pipe[0] == -1 ) {
+            if ( pipe(stdout_pipe) < 0 ) { *perr = ERR_FAILED_PIPE; return *perr; }
+            DA_APPEND(cmd->fds, stdout_pipe[0], perr);
+            if ( *perr != ERR_OK ) { return *perr; }
+        }
+        *args.fd_out = stdout_pipe[0];
+    }
+
+    if ( args.fd_err != NULL ) {
+        if ( stderr_pipe[0] == -1 ) {
+            if ( pipe(stderr_pipe) < 0 ) { *perr = ERR_FAILED_PIPE; return *perr; }
+            DA_APPEND(cmd->fds, stderr_pipe[0], perr);
+            if ( *perr != ERR_OK ) { return *perr; }
+        }
+        *args.fd_err = stderr_pipe[0];
+    }
+
     pid = fork();
     if ( pid < 0 ) {
         LIB_SET_IF_NOT_NULL(out_err, ERR_FAILED_FORK);
         return ERR_FAILED_FORK;
     }
 
+    if ( args.verbose == true ) {
+        printf("Running: ");
+        u64 i = 0;
+        while ( cmd->args.data[i] != NULL ) {
+            printf("%s ", cmd->args.data[i]);
+            ++i;
+        }
+        printf("\n");
+        fflush(stdout);
+    }
+
     if ( pid == 0 ) {
+        if ( stdin_pipe[0] > 0 ) {
+            if ( close(stdin_pipe[1]) ) { exit(1); }
+            if ( dup2(stdin_pipe[0], STDIN_FILENO) < 0 ) { exit(1); }
+        }
+        if ( stdout_pipe[1] > 0 ) {
+            if ( close(stdout_pipe[0]) ) { exit(1); }
+            if ( dup2(stdout_pipe[1], STDOUT_FILENO) < 0 ) { exit(1); }
+        }
+        if ( stderr_pipe[1] > 0 ) {
+            if ( close(stderr_pipe[0]) ) { exit(1); }
+            if ( dup2(stderr_pipe[1], STDERR_FILENO) < 0 ) { exit(1); }
+        }
         (
             (args.search_path == true)
                 ? execvpe
@@ -2679,12 +3248,63 @@ _cmd_exec(struct cmd *_r_ cmd, struct __cmd_exec_args args,
         exit(1);
     }
 
+    if ( stdin_pipe[0] > 0 ) {
+        if ( close(stdin_pipe[0]) ) { *perr = ERR_FAILED_CLOSE; return *perr; }
+    }
+    if ( stdout_pipe[1] > 0 ) {
+        if ( close(stdout_pipe[1]) ) { *perr = ERR_FAILED_CLOSE; return *perr; }
+    }
+    if ( stderr_pipe[1] > 0 ) {
+        if ( close(stderr_pipe[1]) ) { *perr = ERR_FAILED_CLOSE; return *perr; }
+    }
+
     if ( args.reset == true ) {
         cmd_clear_args(cmd, NULL);
     }
 
     if ( args.procs != NULL ) {
+        u64 nproc = args.procs_num;
+
         DA_APPEND(*args.procs, pid, perr);
+        if ( nproc == 0 ) return *perr;
+
+        if ( args.procs->size >= nproc ) {
+            pid = args.procs->data[0];
+            do {
+                if ((perr) != ((void * ) 0)) {
+                    if ( * (perr) != ERR_OK) {
+                        break;
+                    }
+                }
+                if ((0) >= ( * args.procs).size) {
+                    if ((perr) != ((void * ) 0)) {
+                        *(perr) = ERR_INDEX_TOO_LARGE;
+                    };
+                    break;
+                }--( * args.procs).size;
+                printf("-------------------------------------------------\n");
+                DA_FOREACH_INDEX(*args.procs, i) {
+                    printf("\t - %ld -> pid: %d\n", i, args.procs->data[i]);
+                }
+                printf("-------------------------------------------------\n");
+                memmove(( * args.procs).data + (0), (( * args.procs).data + (0)) + 1, sizeof( * ( * args.procs).data) * (( * args.procs).size - ((0) + 1)));
+                memset(( * args.procs).data + ( * args.procs).size, 0, sizeof( * ( * args.procs).data));
+            } while (0);
+            /* DA_DELETE(*args.procs, 0, perr); */
+            if ( *perr != ERR_OK ) return *perr;
+            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;
+                }
+            }
+        }
+
         return *perr;
     }
 
@@ -2726,6 +3346,11 @@ 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);
 
+    if ( cmd->fds.data != NULL ) {
+        DA_FOREACH(cmd->fds, it) { close(*it); }
+    }
+    LIB_FREE(cmd->fds.data);
+
     if ( cmd->args.data != NULL ) {
         DA_FOREACH(cmd->args, it) {
             LIB_FREE(*it);
@@ -2803,6 +3428,8 @@ _cmd_go_rebuild_yourself(const char *src, char **argv,
 
             if ( err == ERR_OK ) execv(argv[0], argv);
             if ( err == ERR_OK ) exit(0);
+
+            cmd_destroy(&cmd, NULL);
         }
     } while(0);
 
@@ -4249,33 +4876,4 @@ exit:
 #  undef ALLOC_FUNC
 # endif /* LIB_H_ALLOC_FUNC */
 
-# ifdef LIB_H_STDBOOL
-#  undef LIB_H_STDBOOL
-
-#  undef bool
-#  undef true
-#  undef false
-# endif /* LIB_H_STDBOOL */
-
-# ifdef LIB_H_NULL
-#  undef LIB_H_NULL
-
-#  undef NULL
-# endif /* LIB_H_NULL */
-
-# ifdef LIB_H_UNISTD
-#  undef LIB_H_UNISTD
-
-#  undef R_OK
-#  undef W_OK
-#  undef X_OK
-#  undef F_OK
-# endif /* LIB_H_UNISTD */
-
-# ifdef LIB_H_STDINT
-#  undef LIB_H_STDINT
-
-#  undef intmax_t
-# endif /* LIB_H_STDINT */
-
 #endif /* LIB_H */