소스 검색

Adding lib.h

Vinicius Teshima 5 달 전
부모
커밋
c45c675ff7
1개의 변경된 파일1490개의 추가작업 그리고 0개의 파일을 삭제
  1. 1490 0
      src/lib.h

+ 1490 - 0
src/lib.h

@@ -0,0 +1,1490 @@
+#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 _STDDEF_H
+#  define LIB_H_STDDEF
+
+#  define size_t unsigned long
+#  define ssize_t long
+#  ifndef NULL
+#   define NULL ((void *) 0)
+#  endif
+# endif /* _STDDEF_H */
+
+# ifndef _SYS_STAT_H
+#  define LIB_H_SYS_STAT
+
+#  define mode_t unsigned int
+# endif /* _SYS_STAT_H */
+
+# 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)(size_t)
+# define FREE_FUNC void (*free)(void *)
+
+# define MACRO_STR_CONCAT_X(lhs, rhs) lhs ## rhs
+# define MACRO_STR_CONCAT(lhs, rhs) MACRO_STR_CONCAT_X(lhs, rhs)
+
+# define MACRO_STR_VALUE(arg) #arg
+# define MACRO_STR_VALUE_X(arg) MACRO_STR_VALUE(arg)
+
+# define BOOL2CSTR(bool) ((bool) ? "True" : "False")
+
+enum err {
+	ERR_OK = 0,
+	ERR_NOT_INT,
+	ERR_TOO_BIG,
+	ERR_NULL_ARG,
+	ERR_NOT_FOUND,
+	ERR_FAILED_OPEN,
+	ERR_FAILED_CLOSE,
+	ERR_FAILED_READ,
+	ERR_FAILED_WRITE,
+	ERR_WROTE_WRONG_AMOUNT,
+	ERR_FAILED_ALLOC,
+	ERR_MKDIR_FAILED,
+	ERR_PATH_EMPTY,
+	ERR_PATH_INVALID,
+	ERR_PATH_FILE_EMPTY,
+	ERR_PATH_FILE_FAILED_SEEK,
+	ERR_SGFX_POS_OUTSIDE_CANVAS
+};
+
+const char *err_to_name[] = {
+	"ERR_OK",
+	"ERR_NOT_INT",
+	"ERR_TOO_BIG",
+	"ERR_NULL_ARG",
+	"ERR_NOT_FOUND",
+	"ERR_FAILED_OPEN",
+	"ERR_FAILED_CLOSE",
+	"ERR_FAILED_READ",
+	"ERR_FAILED_WRITE",
+	"ERR_WROTE_WRONG_AMOUNT",
+	"ERR_FAILED_ALLOC",
+	"ERR_MKDIR_FAILED",
+	"ERR_PATH_EMPTY",
+	"ERR_PATH_INVALID",
+	"ERR_PATH_FILE_EMPTY",
+	"ERR_PATH_FILE_FAILED_SEEK",
+	"ERR_SGFX_POS_OUTSIDE_CANVAS",
+};
+
+/* ---------------------------- END GLOBAL DEF ----------------------------- */
+
+/* ----------------------------- START INT DEF ----------------------------- */
+
+typedef          long int i64;
+typedef unsigned long int u64;
+
+typedef          int i32;
+typedef unsigned int u32;
+
+typedef          short int i16;
+typedef unsigned short int u16;
+
+typedef          char i8;
+typedef unsigned char u8;
+
+/* ------------------------------ END INT DEF ------------------------------ */
+
+/* ----------------------------- START LIB DEF ----------------------------- */
+
+/* ------------------------------ END LIB DEF ------------------------------ */
+
+/* ----------------------------- START VEC DEF ----------------------------- */
+
+# define _VEC2_STRUCT_DEF(type) \
+	struct MACRO_STR_CONCAT(type, vec2) { \
+		type x, y; \
+	}; \
+	struct MACRO_STR_CONCAT(type, vec2s) { \
+		type w, h; \
+	}
+
+
+_VEC2_STRUCT_DEF(i64);
+_VEC2_STRUCT_DEF(u64);
+
+_VEC2_STRUCT_DEF(i32);
+_VEC2_STRUCT_DEF(u32);
+
+_VEC2_STRUCT_DEF(i16);
+_VEC2_STRUCT_DEF(u16);
+
+_VEC2_STRUCT_DEF(i8);
+_VEC2_STRUCT_DEF(u8);
+
+/* ------------------------------ END VEC DEF ------------------------------ */
+
+/* ---------------------------- START CSTR DEF ----------------------------- */
+
+size_t cstr_len_max(const char *cstr, size_t max,
+			bool *out_hit_max, enum err *out_err);
+
+/* ----------------------------- END CSTR DEF ------------------------------ */
+
+/* ---------------------------- START STR DEF ------------------------------ */
+
+# ifndef STR_SIZE_LIMIT
+#  define STR_SIZE_LIMIT 1028
+# endif /* STR_SIZE_LIMIT */
+
+struct str {
+	const char *data;
+	u64 size;
+	bool should_be_freed;
+};
+
+const struct str STR_EMPTY = {"", 0, false};
+
+struct str_tokenizer {
+	struct str str;
+	size_t cur;
+	char c;
+};
+
+struct str str_from_cstr(const char *cstr, size_t cstr_size,
+				enum err *out_err);
+struct str str_from_cstr_ns(const char *cstr, enum err *out_err);
+
+struct str str_from_i64(i64 num, ALLOC_FUNC, enum err *out_err);
+
+struct str str_dup(struct str str, ALLOC_FUNC);
+
+intmax_t str_to_int(struct str str, enum err *err);
+
+struct str str_rstrip(struct str str);
+struct str str_lstrip(struct str str);
+struct str str_strip(struct str str);
+
+u64 str_lindex(struct str str, char c);
+u64 str_rindex(struct str str, char c);
+
+struct str_tokenizer str_tokenize(struct str str, char c);
+struct str str_tokenizer_next(struct str_tokenizer *st);
+
+struct str str_slice(struct str str, size_t from, size_t to);
+
+bool str_eq_cstr(struct str str, const char *cstr, size_t cstr_size);
+bool str_startswith_cstr(struct str str, const char *cstr, size_t cstr_size);
+
+bool str_is_int(struct str str);
+
+struct str_builder {
+	char *data;
+	u64 size;
+	u64 cap;
+};
+
+struct str str_builder_to_str(const struct str_builder *str_bldr);
+
+
+/* ----------------------------- END STR DEF ------------------------------- */
+
+/* ---------------------------- START ENV DEF ------------------------------ */
+
+# define GETENV_FUNC char *(*getenv)(const char *)
+
+struct str getenv_as_str(GETENV_FUNC, const char *env, enum err *out_err);
+struct path getenv_as_path(GETENV_FUNC, const char *env, enum err *out_err);
+
+/* ----------------------------- END ENV DEF ------------------------------- */
+
+/* ---------------------------- START PATH DEF ----------------------------- */
+
+# ifndef PATH_SIZE_LIMIT
+#  define PATH_SIZE_LIMIT 1024
+# endif /* PATH_SIZE_LIMIT */
+
+struct path {
+	char data[PATH_SIZE_LIMIT];
+	size_t size;
+};
+
+struct path path_from_str(struct str str, enum err *out_err);
+struct path path_from_cstr(const char *cstr, size_t cstr_size,
+				enum err *out_err);
+struct path path_from_cstr_ns(const char *cstr, enum err *out_err);
+
+struct path path_get_xdg_state_home(GETENV_FUNC, enum err *out_err);
+struct path path_get_xdg_data_home(GETENV_FUNC, enum err *out_err);
+
+struct path path_dirname(struct path path, enum err *out_err);
+
+struct path path_join(struct path lhs, struct path rhs, enum err *out_err);
+struct path path_join_with_str(struct path lhs, struct str rhs,
+				enum err *out_err);
+struct path path_join_with_cstr(struct path lhs,
+				const char *rhs, size_t rhs_size,
+				enum err *out_err);
+struct path path_join_with_cstr_ns(struct path lhs, const char *rhs,
+					enum err *out_err);
+
+bool path_exists(struct path path, int (*access)(const char *, int));
+bool path_can_read(struct path path, int (*access)(const char *, int));
+bool path_can_write(struct path path, int (*access)(const char *, int));
+bool path_can_execute(struct path path, int (*access)(const char *, int));
+
+bool path_mkdir(struct path path, mode_t mode, bool do_create_parents,
+		int (*mkdir)(const char *, mode_t), enum err *out_err);
+
+bool path_touch(struct path path, int (*open)(const char *, int),
+		enum err *out_err);
+
+
+struct file {
+	u8 *data;
+	u64 size;
+};
+
+struct file path_file_read_all(const struct path *path, enum err *out_err);
+enum err path_file_save(const struct path *path, const struct file *file,
+				enum err *out_err);
+
+/* ----------------------------- END PATH DEF ------------------------------ */
+
+/* ---------------------------- START SGFX DEF ----------------------------- */
+
+# ifndef SGFX_WIDTH
+#  define SGFX_WIDTH 800
+# endif /* SGFX_WIDTH */
+
+# ifndef SGFX_HEIGHT
+#  define SGFX_HEIGHT 600
+# endif /* SGFX_HEIGHT */
+
+# define SGFX_WIDTH_CSTR MACRO_STR_VALUE_X(SGFX_WIDTH)
+# define SGFX_HEIGHT_CSTR MACRO_STR_VALUE_X(SGFX_HEIGHT)
+
+struct sgfx_rgb {
+	u8 r, g, b;
+};
+
+struct sgfx_rgba {
+	u8 r, g, b, a;
+};
+
+struct sgfx_canvas {
+	u32 pixels[SGFX_WIDTH * SGFX_HEIGHT];
+	u64 width;
+	u64 height;
+	u64 cap;
+};
+
+enum err sgfx_canvas_populate(struct sgfx_canvas *canvas, enum err *out_err);
+
+enum err sgfx_canvas_fill(struct sgfx_canvas *canvas, u32 color,
+				enum err *out_err);
+
+enum err sgfx_canvas_fill_rect(struct sgfx_canvas *canvas,
+				struct u64vec2 pos, struct u64vec2s size,
+				u32 color,
+				enum err *out_err);
+
+enum err sgfx_canvas_save_to_ppm(const struct sgfx_canvas *canvas,
+					struct path path, enum err *out_err);
+
+/* ----------------------------- END SGFX DEF ------------------------------ */
+
+/* --------------------------- START RAYLIB DEF ---------------------------- */
+
+enum err raylib_draw_str(const struct str_builder *str, Font font,
+				Vector2 position, float fontSize,
+				float spacing, Color tint, enum err *out_err);
+
+/* ---------------------------- END RAYLIB DEF ----------------------------- */
+
+/* ----------------------------- START GUI DEF ----------------------------- */
+
+/* ------------------------------ END GUI DEF ------------------------------ */
+
+# if defined(IMP) || defined(IMP_STR)
+
+#  define _SET_IF_NOT_NULL(var, err) \
+	if ( var != NULL ) { \
+		*var = err; \
+	}
+
+#  define _ARG_IF_NOT_NULL_MUST_BE(arg, val, ret_val) \
+	if ( arg != NULL ) { \
+		if ( *arg != val ) { \
+			return ret_val; \
+		} \
+	}
+
+#  define _ARG_IF_NOT_NULL_MUST_BE_RET_IT(arg, val) \
+	if ( arg != NULL ) { \
+		if ( *arg != val ) { \
+			return *arg; \
+		} \
+	}
+
+#  define _ARG_MUST_NOT_BE_NULL_RET_ERR(arg) \
+	if ( (arg) == NULL ) { \
+		return ERR_NULL_ARG; \
+	}
+
+#  define _ARG_MUST_NOT_BE_NULL(arg, err_var, ret_val) \
+	if ( (arg) == NULL ) { \
+		_SET_IF_NOT_NULL(err_var, ERR_NULL_ARG); \
+		return ret_val; \
+	}
+
+#  define _ARG_MUST_NOT_BE_NULL_SET_RET_ERR(arg, err_var) \
+	if ( (arg) == NULL ) { \
+		_SET_IF_NOT_NULL(err_var, ERR_NULL_ARG); \
+		return ERR_NULL_ARG; \
+	}
+
+#  ifndef _CTYPE_H
+#   define LIB_H_CTYPE
+
+#   define isspace(c) ((c) == ' ' || (c) == '\f' \
+			|| (c) == '\n' || (c) == '\r' \
+			|| (c) == '\t' || (c) == '\v')
+
+#   define isdigit(c) ((c) >= '0' && (c) <= '9')
+
+#  endif /* _CTYPE_H */
+
+
+#  ifndef _STRING_H
+#   define LIB_H_STRING
+
+void *_str_memcpy(void *dest, const void *src, size_t n);
+void *
+_str_memcpy(void *dest, const void *src, size_t n)
+{
+	size_t i = 0;
+	char *_dest = (char *) dest;
+	char *_src = (char *) src;
+
+	for ( i = 0; i < n; ++i ) {
+		_dest[i] = _src[i];
+	}
+
+	return dest;
+}
+
+#   define memcpy _str_memcpy
+#  endif /* _STRING_H */
+
+/* ----------------------------- START LIB IMP ----------------------------- */
+
+/* ------------------------------ END LIB IMP ------------------------------ */
+
+/* ---------------------------- START CSTR IMP ----------------------------- */
+
+size_t
+cstr_len_max(const char *cstr, size_t max,
+		bool *out_hit_max, enum err *out_err)
+{
+	size_t ret = 0;
+
+	_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, 0);
+
+	if ( cstr == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NULL_ARG);
+		return 0;
+	}
+
+_loop:
+	if ( ret >= max ) {
+		_SET_IF_NOT_NULL(out_hit_max, true);
+		goto _loop_end;
+	}
+	if ( cstr[ret] == '\0' ) {
+		goto _loop_end;
+	}
+
+	++ret;
+	goto _loop;
+_loop_end:
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return ret;
+}
+
+/* ----------------------------- END CSTR IMP ------------------------------ */
+
+/* ---------------------------- START STR DEF ------------------------------ */
+
+struct str
+str_from_cstr(const char *cstr, size_t cstr_size, enum err *out_err)
+{
+	struct str str = {0};
+
+	if ( cstr == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NULL_ARG);
+		return str;
+	}
+
+	if ( cstr_size >= STR_SIZE_LIMIT ) {
+		_SET_IF_NOT_NULL(out_err, ERR_TOO_BIG);
+		return str;
+	}
+
+	str.data = cstr;
+	str.size = cstr_size;
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return str;
+}
+
+struct str
+str_from_cstr_ns(const char *cstr, enum err *out_err)
+{
+	struct str empty = {0};
+	struct str str = {0};
+	bool too_big = false;
+	enum err err = ERR_OK;
+
+	if ( cstr == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NULL_ARG);
+		return empty;
+	}
+
+	str.size = cstr_len_max(cstr, STR_SIZE_LIMIT, &too_big, &err);
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return empty;
+	}
+
+	if ( too_big ) {
+		_SET_IF_NOT_NULL(out_err, ERR_TOO_BIG);
+		return empty;
+	}
+
+	str.data = cstr;
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return str;
+}
+
+struct str
+str_from_i64(i64 num, ALLOC_FUNC, enum err *out_err)
+{
+    #define BUF_CAP ((u64) 256)
+	char *data = NULL;
+	char buf[256] = {0};
+	u64 buf_size = 0;
+	u64 _num = 0;
+	struct str ret = {0};
+
+	_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, STR_EMPTY);
+
+	data = alloc(STR_SIZE_LIMIT);
+	ret.should_be_freed = true;
+
+	if ( num < 0 ) {
+		/* TODO: Check for cap */
+		buf[buf_size++] = '-';
+		num *= -1;
+	}
+
+	_num = (u64)num;
+
+	while ( 1 ) {
+		u8 mod = (u8) (_num % 10);
+
+		/* TODO: Check for cap */
+		buf[buf_size++] = (char) (mod + 48);
+
+		_num /= 10;
+
+		if ( buf_size >= BUF_CAP ) {
+			_loop:
+			/* TODO: Check for cap */
+			data[ret.size++] = buf[--buf_size];
+			if ( buf_size > 0 ) {
+				goto _loop;
+			}
+		}
+
+		if ( _num == 0 ) {
+			break;
+		}
+	}
+	if ( buf_size == 0 ) {
+		goto exit;
+	}
+
+	_out_loop:
+	data[ret.size++] = buf[--buf_size];
+	if ( buf_size > 0 ) {
+		goto _out_loop;
+	}
+
+	exit:
+	data[ret.size] = 0;
+	ret.data = data;
+	_SET_IF_NOT_NULL(out_err, ERR_OK)
+	return ret;
+    #undef BUF_CAP
+}
+
+
+struct str
+str_dup(struct str str, ALLOC_FUNC)
+{
+	struct str ret = str;
+
+	ret.data = alloc(ret.size * sizeof(*ret.data));
+
+	memcpy((char *)ret.data, str.data, str.size);
+
+	ret.should_be_freed = true;
+	return ret;
+}
+
+intmax_t
+str_to_int(struct str str, enum err *out_err)
+{
+	intmax_t res = 0;
+	size_t i = 0;
+	bool negative = false;
+	int digits[] = {
+		1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000
+	};
+
+	if ( ! str_is_int(str) ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NOT_INT);
+		return -1;
+	}
+
+	if ( str.size > STR_SIZE_LIMIT ) {
+		_SET_IF_NOT_NULL(out_err, ERR_TOO_BIG);
+		return -1;
+	}
+
+	negative = (str.data[0] == '-');
+	if ( str.data[0] == '-' || str.data[0] == '+' ) {
+		++str.data;
+		--str.size;
+	}
+
+	for ( i = 0; i < str.size; ++i ) {
+		intmax_t digit = digits[(str.size - (i + 1))];
+		res += (str.data[i] - 48) * digit;
+	}
+
+	if ( negative == true ) {
+		res *= -1;
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return res;
+}
+
+struct str
+str_rstrip(struct str str)
+{
+	while ( isspace(str.data[str.size-1]) ) {
+		--str.size;
+	}
+	return str;
+}
+
+struct str
+str_lstrip(struct str str)
+{
+	while ( isspace(*str.data) ) {
+		++str.data;
+		--str.size;
+	}
+	return str;
+}
+
+struct str
+str_strip(struct str str)
+{
+	return str_lstrip(str_rstrip(str));
+}
+
+u64
+str_lindex(struct str str, char c)
+{
+	size_t i = 0;
+	for ( ; i < str.size; ++i ) {
+		if ( str.data[i] == c ) {
+			return i;
+		}
+	}
+	return (size_t) -1;
+}
+
+u64
+str_rindex(struct str str, char c)
+{
+	size_t i = str.size - 1;
+	for ( ; i > 0; --i ) {
+		if ( str.data[i] == c ) {
+			return i;
+		}
+	}
+	if ( str.data[i] == c ) {
+		return i;
+	}
+	return (size_t) -1;
+}
+
+struct str_tokenizer
+str_tokenize(struct str str, char c)
+{
+	struct str_tokenizer st = {0};
+	st.str = str;
+	st.c = c;
+	return st;
+}
+
+struct str
+str_tokenizer_next(struct str_tokenizer *st)
+{
+	struct str str;
+
+	if ( st == NULL ) {
+		goto ret_err;
+	}
+
+	if ( st->cur >= st->str.size ) {
+		goto ret_done;
+	}
+
+	if ( st->str.data[st->cur] == '\0' ) {
+		goto ret_done;
+	}
+
+	str = st->str;
+	str.data += st->cur;
+	str.size = 0;
+
+	while ( str.data[str.size] != st->c
+		&& st->cur < st->str.size ) {
+		++str.size;
+		++st->cur;
+	}
+	++st->cur;
+
+	if ( str.size == 0 ) {
+		goto ret_empty;
+	}
+
+	return str;
+
+ret_empty:
+	str.data = "";
+	str.size = 0;
+	return str;
+
+ret_done:
+	str.data = "";
+	str.size = (size_t) -1;
+	return str;
+
+ret_err:
+	str.data = "";
+	str.size = (size_t) -2;
+	return str;
+}
+
+struct str
+str_slice(struct str str, size_t from, size_t to)
+{
+	if ( from > str.size ) {
+		goto ret_err;
+	}
+	if ( from > to ) {
+		goto ret_err;
+	}
+
+	to = ( to > str.size ) * str.size \
+	     + ( to <= str.size ) * to;
+	
+	str.data += from;
+	str.size = to - from;
+
+	return str;
+
+ret_err:
+	str.data = "";
+	str.size = (size_t) -2;
+	return str;
+}
+
+bool
+str_eq_cstr(struct str str, const char *cstr, size_t cstr_size)
+{
+	size_t i = 0;
+	if ( str.size != cstr_size ) {
+		return false;
+	}
+
+	for ( i = 0; i < str.size; ++i ) {
+		if ( str.data[i] != cstr[i] ) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+bool
+str_startswith_cstr(struct str str, const char *cstr, size_t cstr_size)
+{
+	size_t i = 0;
+
+	if ( cstr_size > str.size ) {
+		return false;
+	}
+
+	for ( i = 0; i < cstr_size; ++i ) {
+		if ( str.data[i] != cstr[i] ) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+bool
+str_is_int(struct str str)
+{
+	size_t i = 0;
+
+	if ( str.size == 0 ) {
+		return false;
+	}
+
+	if ( str.size == 1 ) {
+		return isdigit(str.data[0]);
+	}
+
+	i += (str.data[0] == '-' || str.data[0] == '+');
+
+	for ( ; i < str.size; ++i ) {
+		if ( ! isdigit(str.data[i]) ) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+struct str
+str_builder_to_str(const struct str_builder *str_bldr)
+{
+	struct str str = {0};
+
+	str.data = str_bldr->data;
+	str.size = str_bldr->size;
+	str.should_be_freed = true;
+
+	return str;
+}
+
+/* ----------------------------- END STR IMP ------------------------------- */
+
+/* ---------------------------- START ENV IMP ------------------------------ */
+
+struct str
+getenv_as_str(GETENV_FUNC, const char *name, enum err *out_err)
+{
+	struct str empty = {0};
+	struct str str = {0};
+	char *res = NULL;
+
+	if ( getenv == NULL || name == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NULL_ARG);
+		return empty;
+	}
+
+	res = getenv(name);
+	if ( res == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NOT_FOUND);
+		return str;
+	}
+
+	return str_from_cstr_ns(res, out_err);
+}
+
+struct path
+getenv_as_path(GETENV_FUNC, const char *name, enum err *out_err)
+{
+	struct path empty = {0};
+	struct str str = {0};
+	enum err err;
+
+	if ( getenv == NULL || name == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NULL_ARG);
+		return empty;
+	}
+
+	str = getenv_as_str(getenv, name, &err);
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return empty;
+	}
+
+	return path_from_str(str, out_err);
+}
+
+/* ----------------------------- END ENV IMP ------------------------------- */
+
+/* ---------------------------- START PATH IMP ----------------------------- */
+
+struct path
+path_from_str(struct str str, enum err *out_err)
+{
+	struct path path = {0};
+
+	if ( str.size > PATH_SIZE_LIMIT ) {
+		_SET_IF_NOT_NULL(out_err, ERR_TOO_BIG);
+		return path;
+	}
+
+	memcpy(path.data, str.data, str.size);
+
+	path.size = str.size;
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return path;
+}
+
+struct path
+path_from_cstr(const char *cstr, size_t cstr_size, enum err *out_err)
+{
+	enum err err;
+	struct str str;
+	struct path path = {0};
+
+	str = str_from_cstr(cstr, cstr_size, &err);
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return path;
+	}
+
+	return path_from_str(str, out_err);
+}
+
+struct path
+path_from_cstr_ns(const char *cstr, enum err *out_err)
+{
+	enum err err;
+	struct str str;
+	struct path path = {0};
+
+	_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, path);
+
+	str = str_from_cstr_ns(cstr, &err);
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return path;
+	}
+
+	return path_from_str(str, out_err);
+}
+
+struct path
+path_get_xdg_state_home(GETENV_FUNC, enum err *out_err)
+{
+	struct path empty = {0};
+	struct path path = {0};
+	enum err err;
+
+	if ( getenv == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NULL_ARG);
+		return empty;
+	}
+
+	path = getenv_as_path(getenv, "XDG_STATE_HOME", &err);
+	if ( err == ERR_NOT_FOUND ) {
+		path = getenv_as_path(getenv, "HOME", &err);
+		if ( err ) {
+			_SET_IF_NOT_NULL(out_err, err);
+			return empty;
+		}
+		path = path_join_with_cstr(path, ".local/state", 12, NULL);
+	} else if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return empty;
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return path;
+}
+
+struct path
+path_get_xdg_data_home(GETENV_FUNC, enum err *out_err)
+{
+	struct path empty = {0};
+	struct path path = {0};
+	enum err err;
+
+	if ( getenv == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_NULL_ARG);
+		return empty;
+	}
+
+	path = getenv_as_path(getenv, "XDG_DATA_HOME", &err);
+	if ( err == ERR_NOT_FOUND ) {
+		path = getenv_as_path(getenv, "HOME", &err);
+		if ( err ) {
+			_SET_IF_NOT_NULL(out_err, err);
+			return empty;
+		}
+		path = path_join_with_cstr(path, ".local/share", 12, NULL);
+	} else if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return empty;
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return path;
+}
+
+struct path
+path_dirname(struct path path, enum err *out_err)
+{
+	struct path empty = {0};
+	struct path ret = {0};
+	size_t i = 0;
+
+	if ( path.size == 0 || path.data[0] == 0 ) {
+		_SET_IF_NOT_NULL(out_err, ERR_PATH_EMPTY);
+		return empty;
+	}
+
+	i = path.size;
+loop:
+	--i;
+
+	if ( path.data[i] == '/' ) {
+		goto exit_loop;
+	}
+
+	if ( i == 0 ) {
+		ret.data[0] = '.';
+		ret.data[1] = 0;
+		ret.size = 1;
+
+		goto ret_ok;
+	}
+	goto loop;
+exit_loop:
+
+	memcpy(ret.data, path.data, i);
+	ret.size = i;
+
+ret_ok:
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return ret;
+}
+
+struct path
+path_join(struct path lhs, struct path rhs, enum err *out_err)
+{
+	struct path path = {0};
+	if ( (lhs.size + rhs.size + 1) > PATH_SIZE_LIMIT ) {
+		_SET_IF_NOT_NULL(out_err, ERR_TOO_BIG);
+		return path;
+	}
+
+	/* TODO: Check if lhs or rhs have / at the end and start respectively */
+
+	path.size = (lhs.size + rhs.size + 1);
+
+	memcpy(path.data, lhs.data, lhs.size);
+	path.data[lhs.size] = '/';
+	memcpy(path.data + lhs.size + 1, rhs.data, rhs.size);
+
+	return path;
+}
+
+struct path
+path_join_with_str(struct path lhs, struct str rhs, enum err *out_err)
+{
+	struct path path = {0};
+	enum err err;
+
+	path = path_from_str(rhs, &err);
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return path;
+	}
+
+	return path_join(lhs, path, out_err);
+}
+
+struct path
+path_join_with_cstr(struct path lhs, const char *rhs, size_t rhs_size,
+			enum err *out_err)
+{
+	struct path path = {0};
+	enum err err;
+
+	path = path_from_cstr(rhs, rhs_size, &err);
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return path;
+	}
+
+	return path_join(lhs, path, out_err);
+}
+
+struct path
+path_join_with_cstr_ns(struct path lhs, const char *rhs, enum err *out_err)
+{
+	struct path empty = {0};
+	struct path path = {0};
+	enum err err;
+
+	_ARG_MUST_NOT_BE_NULL(rhs, out_err, empty);
+
+	path = path_from_cstr_ns(rhs, &err);
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return empty;
+	}
+
+	return path_join(lhs, path, out_err);
+}
+
+bool
+path_exists(struct path path, int (*access)(const char *, int))
+{
+	return access(path.data, F_OK) == 0;
+}
+
+bool
+path_can_read(struct path path, int (*access)(const char *, int))
+{
+	return access(path.data, R_OK) == 0;
+}
+
+bool
+path_can_write(struct path path, int (*access)(const char *, int))
+{
+	return access(path.data, W_OK) == 0;
+}
+
+bool
+path_can_execute(struct path path, int (*access)(const char *, int))
+{
+	return access(path.data, X_OK) == 0;
+}
+
+bool
+path_mkdir(struct path path, mode_t mode, bool do_create_parents,
+		int (*mkdir)(const char *, mode_t), enum err *out_err)
+{
+	size_t i = 0;
+
+	_ARG_MUST_NOT_BE_NULL(mkdir, out_err, false);
+
+	if ( path.size == 1 ) {
+		_SET_IF_NOT_NULL(out_err, ERR_PATH_INVALID);
+		return 1;
+	}
+
+	if ( do_create_parents ) {
+		for ( i = 1; i < path.size; ++i ) {
+			if ( path.data[i] == '/' ) {
+				path.data[i] = 0;
+				mkdir(path.data, mode);
+				path.data[i] = '/';
+			}
+		}
+	}
+
+	if ( ! mkdir(path.data, mode) ) {
+		_SET_IF_NOT_NULL(out_err, ERR_MKDIR_FAILED);
+		return false;
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return true;
+}
+
+/*
+bool
+path_touch(struct path path, int (*open)(const char *, int), enum err *out_err)
+{
+	int fd = -1;
+
+	_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, false);
+
+	_ARG_MUST_NOT_BE_NULL(open, out_err, false);
+
+	fd = open(path.data, 0100 | 00644);
+	if ( fd == -1 ) {
+		_SET_IF_NOT_NULL(out_err, ERR_PATH_FAILED_OPEN);
+		return false;
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return true;
+}
+*/
+
+/* TODO: Replace this */
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+struct file
+path_file_read_all(const struct path *path, enum err *out_err)
+{
+	struct file empty = {0};
+	struct file file = {0};
+	i64 file_size = -1;
+	i32 fd = -1;
+
+	_ARG_IF_NOT_NULL_MUST_BE(out_err, ERR_OK, empty);
+	_ARG_MUST_NOT_BE_NULL(path, out_err, empty);
+
+	fd = open(path->data, O_RDONLY);
+	if ( fd < 0 ) {
+		_SET_IF_NOT_NULL(out_err, ERR_FAILED_OPEN);
+		goto exit_err;
+	}
+
+	file_size = lseek(fd, 0, SEEK_END);
+	if ( file_size < 0 ) {
+		_SET_IF_NOT_NULL(out_err, ERR_PATH_FILE_FAILED_SEEK);
+		goto exit_err;
+	}
+	lseek(fd, 0, SEEK_SET);
+	file.size = (u64) file_size;
+
+	if ( file.size == 0 ) {
+		_SET_IF_NOT_NULL(out_err, ERR_PATH_FILE_EMPTY);
+		goto exit_err;
+	}
+
+	file.data = calloc(file.size + 1, sizeof(u8));
+	if ( file.data == NULL ) {
+		_SET_IF_NOT_NULL(out_err, ERR_FAILED_ALLOC);
+		goto exit_err;
+	}
+
+	{
+		i64 rd = read(fd, file.data, file.size);
+		if ( rd < 0 ) {
+			_SET_IF_NOT_NULL(out_err, ERR_FAILED_READ);
+			goto exit_err;
+		}
+		if ( rd == 0 ) {
+			_SET_IF_NOT_NULL(out_err, ERR_PATH_FILE_EMPTY);
+			goto exit_err;
+		}
+	}
+
+	if ( close(fd) != 0 ) {
+		/* It should be possible to handle EIO */
+		_SET_IF_NOT_NULL(out_err, ERR_FAILED_CLOSE);
+		return empty;
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return file;
+exit_err:
+	if ( file.data != NULL ) {
+		free(file.data);
+	}
+	if ( fd > 0 ) {
+		if ( close(fd) != 0 ) {
+			/* It should be possible to handle EIO */
+			_SET_IF_NOT_NULL(out_err, ERR_FAILED_CLOSE);
+			return empty;
+		}
+	}
+	return empty;
+}
+
+enum err
+path_file_save(const struct path *path, const struct file *file,
+		enum err *out_err)
+{
+	enum err err = ERR_OK;
+	i64 wrote = -1;
+	i32 fd = -1;
+
+	_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+	_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(path, out_err);
+	_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(file, out_err);
+
+	fd = open(path->data, O_WRONLY | O_CREAT | O_TRUNC,
+			S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+	if ( fd < 0 ) {
+		err = ERR_FAILED_OPEN;
+		goto exit;
+	}
+
+	wrote = write(fd, file->data, file->size);
+	if ( wrote == -1 ) {
+		err = ERR_FAILED_WRITE;
+		goto exit;
+	}
+
+	if ( ((u64) wrote) != file->size ) {
+		err = ERR_WROTE_WRONG_AMOUNT;
+		goto exit;
+	}
+
+	err = ERR_OK;
+
+exit:
+	if ( close(fd) != 0 ) {
+		/* It should be possible to handle EIO */
+		err = ERR_FAILED_CLOSE;
+		goto exit;
+	}
+
+	_SET_IF_NOT_NULL(out_err, err);
+	return err;
+}
+
+/* ----------------------------- END PATH IMP ------------------------------ */
+
+/* ---------------------------- START SGFX IMP ----------------------------- */
+
+enum err
+sgfx_canvas_populate(struct sgfx_canvas *canvas, enum err *out_err)
+{
+	_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+	_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(canvas, out_err);
+
+	canvas->width  = SGFX_WIDTH;
+	canvas->height = SGFX_HEIGHT;
+	canvas->cap = SGFX_WIDTH * SGFX_HEIGHT;
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK)
+	return ERR_OK;
+}
+
+enum err
+sgfx_canvas_fill(struct sgfx_canvas *canvas, u32 color, enum err *out_err)
+{
+	u64 i = 0;
+
+	_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+	_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(canvas, out_err);
+
+	for ( i = 0; i < canvas->cap; ++i ) {
+		canvas->pixels[i] = color;
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK)
+	return ERR_OK;
+}
+
+enum err
+sgfx_canvas_fill_rect(struct sgfx_canvas *canvas,
+			struct u64vec2 pos, struct u64vec2s size, u32 color,
+			enum err *out_err)
+{
+	u64 i = 0;
+	u64 j = 0;
+	u64 startp = 0;
+	u64 endp = 0;
+
+	_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+	_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(canvas, out_err);
+
+	/* TODO: Create a diferent error for when size lands outside canvas */
+	if ( pos.x > canvas->width || pos.y > canvas->height
+	     || (pos.x + size.w) > canvas->width
+	     || (pos.y + size.h) > canvas->height ) {
+		_SET_IF_NOT_NULL(out_err, ERR_SGFX_POS_OUTSIDE_CANVAS);
+		return ERR_SGFX_POS_OUTSIDE_CANVAS;
+	}
+
+	/*
+	printf("pos.x -> %ld || pos.y -> %ld\n", pos.x, pos.y);
+	printf("size.w -> %ld || size.h -> %ld\n", size.w, size.h);
+	printf("(pos.x + size.w) -> %ld || (pos.y + size.h) -> %ld\n", (pos.x + size.w), (pos.y + size.h));
+	*/
+
+	startp = (canvas->width * pos.y) + pos.x;
+	endp = (canvas->width * (pos.y + size.h)) + (pos.x + size.w);
+	if ( endp >= canvas->cap ) {
+		endp = canvas->cap;
+	}
+	for ( i = startp; i < endp; i += canvas->width ) {
+		for ( j = i; j < i+size.w; ++j ) {
+			canvas->pixels[j] = color;
+		}
+	}
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return ERR_OK;
+}
+
+enum err
+sgfx_canvas_save_to_ppm(const struct sgfx_canvas *canvas, struct path path,
+			enum err *out_err)
+{
+#define __T(s) s, cstr_len_max(s, 255, NULL, out_err)
+	/* enum err err = ERR_OK; */
+	/* u64 i = 0; */
+	/* i32 fd = -1; */
+	(void) path;
+
+	_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+
+	_ARG_MUST_NOT_BE_NULL_SET_RET_ERR(canvas, out_err);
+
+	/* TODO: Check the errors */
+	/*
+	fd = open(path.data, O_RDWR | O_CREAT, 00644);
+
+	if ( err ) {
+		_SET_IF_NOT_NULL(out_err, err);
+		return err;
+	}
+
+	write(fd, __T("P6\n"));
+
+	write(fd, __T(SGFX_WIDTH_CSTR"\n"));
+	write(fd, __T(SGFX_HEIGHT_CSTR"\n"));
+
+	write(fd, __T("255\n"));
+
+	for ( i = 0; i < canvas->cap; ++i ) {
+		struct sgfx_rgb rgb = {0};
+		rgb.r = (u8)(canvas->pixels[i] >> 24);
+		rgb.g = (u8)(canvas->pixels[i] >> 16);
+		rgb.b = (u8)(canvas->pixels[i] >> 8);
+		write(fd, &rgb, sizeof(struct sgfx_rgb));
+	}
+
+	close(fd);
+	*/
+
+	_SET_IF_NOT_NULL(out_err, ERR_OK);
+	return ERR_OK;
+#undef __T
+}
+
+/* ----------------------------- END SGFX IMP ------------------------------ */
+
+/* --------------------------- START RAYLIB IMP ---------------------------- */
+
+enum err
+raylib_draw_str(const struct str_builder *str, Font font, Vector2 position,
+		float fontSize, float spacing, Color tint, enum err *out_err)
+{
+	enum err err = ERR_OK;
+	char *cstr = NULL;
+
+	_ARG_IF_NOT_NULL_MUST_BE_RET_IT(out_err, ERR_OK);
+
+	cstr = calloc(str->size + 1, sizeof(char));
+	if ( cstr == NULL ) {
+		err = ERR_FAILED_ALLOC;
+		goto exit;
+	}
+
+	memcpy(cstr, str->data, str->size);
+
+	DrawTextEx(font, cstr, position, fontSize, spacing, tint);
+
+	err = ERR_OK;
+
+exit:
+	if ( cstr != NULL ) {
+		free(cstr);
+	}
+	_SET_IF_NOT_NULL(out_err, err);
+	return err;
+}
+
+/* ---------------------------- END RAYLIB IMP ----------------------------- */
+
+#  ifdef LIB_H_CTYPE
+#   undef LIB_H_CTYPE
+
+#   undef isspace
+#   undef isdigit
+#  endif /* LIB_H_CTYPE */
+
+#  ifdef LIB_H_STRING
+#   undef LIB_H_STRING
+
+#   undef memcpy
+#  endif /* LIB_H_STRING */
+
+#  undef _SET_IF_NOT_NULL
+#  undef _ARG_IF_NOT_NULL_MUST_BE
+#  undef _ARG_MUST_NOT_BE_NULL
+
+# endif /* defined(IMP) || defined(IMP_STR) */
+
+# ifdef LIB_H_ALLOC_FUNC
+#  undef LIB_H_ALLOC_FUNC
+
+#  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_SYS_STAT
+#  undef LIB_H_SYS_STAT
+
+#  undef mode_t
+# endif /* LIB_H_SYS_STAT */
+
+# ifdef LIB_H_STDDEF
+#  undef LIB_H_STDDEF
+
+#  undef size_t
+#  undef ssize_t
+#  undef NULL
+# endif /* LIB_H_STDDEF */
+
+# 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 */