|
|
@@ -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 */
|