summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile_Eina.am9
-rw-r--r--src/lib/eina/Eina.h1
-rw-r--r--src/lib/eina/eina_main.c3
-rw-r--r--src/lib/eina/eina_vpath.c349
-rw-r--r--src/lib/eina/eina_vpath.h74
-rw-r--r--src/tests/eina/eina_suite.c1
-rw-r--r--src/tests/eina/eina_suite.h1
-rw-r--r--src/tests/eina/eina_test_vpath.c53
8 files changed, 488 insertions, 3 deletions
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 186bd1e8e1..33c7c660c2 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -107,7 +107,8 @@ lib/eina/eina_slice.h \
lib/eina/eina_inline_slice.x \
lib/eina/eina_inline_modinfo.x \
lib/eina/eina_freeq.h \
-lib/eina/eina_slstr.h
+lib/eina/eina_slstr.h \
+lib/eina/eina_vpath.h
lib_eina_libeina_la_SOURCES = \
@@ -183,7 +184,8 @@ lib/eina/eina_quaternion.c \
lib/eina/eina_bezier.c \
lib/eina/eina_safepointer.c \
lib/eina/eina_freeq.c \
-lib/eina/eina_slstr.c
+lib/eina/eina_slstr.c \
+lib/eina/eina_vpath.c
if HAVE_WIN32
@@ -356,7 +358,8 @@ tests/eina/eina_test_bezier.c \
tests/eina/eina_test_safepointer.c \
tests/eina/eina_test_slice.c \
tests/eina/eina_test_freeq.c \
-tests/eina/eina_test_slstr.c
+tests/eina/eina_test_slstr.c \
+tests/eina/eina_test_vpath.c
tests_eina_eina_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
-DTESTS_WD=\"`pwd`\" \
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index 256de025bc..b403d308f8 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -274,6 +274,7 @@ extern "C" {
#include <eina_slstr.h>
#include <eina_debug.h>
#include <eina_promise.h>
+#include <eina_vpath.h>
#undef EAPI
#define EAPI
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index 1a152d0da7..275e4a0baa 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -68,6 +68,7 @@
#include "eina_evlog.h"
#include "eina_freeq.h"
#include "eina_slstr.h"
+#include "eina_vpath.h"
/*============================================================================*
* Local *
@@ -155,6 +156,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
S(safepointer);
S(slstr);
S(promise);
+ S(vpath);
#undef S
struct eina_desc_setup
@@ -202,6 +204,7 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
S(safepointer),
S(slstr),
S(promise),
+ S(vpath),
#undef S
};
static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) /
diff --git a/src/lib/eina/eina_vpath.c b/src/lib/eina/eina_vpath.c
new file mode 100644
index 0000000000..af57afae97
--- /dev/null
+++ b/src/lib/eina/eina_vpath.c
@@ -0,0 +1,349 @@
+#include <Eina.h>
+
+#include "eina_private.h"
+
+static Eina_Hash *vpath_data = NULL;
+
+#ifdef CRI
+#undef CRI
+#endif
+#define CRI(...) EINA_LOG_DOM_CRIT(_eina_vpath_log_dom, __VA_ARGS__)
+
+#ifdef ERR
+#undef ERR
+#endif
+#define ERR(...) EINA_LOG_DOM_ERR(_eina_vpath_log_dom, __VA_ARGS__)
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(...) EINA_LOG_DOM_DBG(_eina_vpath_log_dom, __VA_ARGS__)
+
+static int _eina_vpath_log_dom = -1;
+
+static inline void
+_eina_vpath_data_add(const char *key, const char *value)
+{
+ eina_hash_add(vpath_data, key, eina_stringshare_add(value));
+}
+
+static inline Eina_Stringshare*
+_eina_vpath_data_get(const char *key)
+{
+ return eina_hash_find(vpath_data, key);
+}
+
+
+static char*
+_fallback_runtime_dir(const char *home)
+{
+ char buf[PATH_MAX];
+ struct stat st;
+
+#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
+ if (setuid(geteuid()) != 0)
+ {
+ fprintf(stderr,
+ "FATAL: Cannot setuid - errno=%i\n",
+ errno);
+ abort();
+ }
+#endif
+ // fallback - make ~/.run
+ snprintf(buf, sizeof(buf), "%s/.run", home);
+ if (!!mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR))
+ {
+ if (errno == EEXIST)
+ {
+ if (stat(buf, &st) == 0)
+ {
+ // some sanity checks - but not for security
+ if (!(st.st_mode & S_IFDIR))
+ {
+ // fatal - exists but is not a dir
+ fprintf(stderr,
+ "FATAL: run dir '%s' exists but not a dir\n",
+ buf);
+ abort();
+ }
+#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
+ if (st.st_uid != geteuid())
+ {
+ // fatal - run dir doesn't belong to user
+ fprintf(stderr,
+ "FATAL: run dir '%s' not owned by uid %i\n",
+ buf, (int)geteuid());
+ abort();
+ }
+#endif
+ }
+ else
+ {
+ // fatal - we cant create our run dir in ~/
+ fprintf(stderr,
+ "FATAL: Cannot verify run dir '%s' errno=%i\n",
+ buf, errno);
+ abort();
+ }
+ }
+ else
+ {
+ // fatal - we cant create our run dir in ~/
+ fprintf(stderr,
+ "FATAL: Cannot create run dir '%s' - errno=%i\n",
+ buf, errno);
+ abort();
+ }
+ }
+#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
+ if (setreuid(uid, geteuid()) != 0)
+ {
+ fprintf(stderr,
+ "FATAL: Cannot setreuid - errno=%i\n",
+ errno);
+ abort();
+ }
+#endif
+
+ return strdup(buf);
+}
+
+static char*
+_fallback_home_dir()
+{
+ char buf[PATH_MAX];
+ /* Windows does not have getuid(), but home can't be NULL */
+#ifdef HAVE_GETEUID
+ uid_t uid = geteuid();
+ struct stat st;
+
+ snprintf(buf, sizeof(buf), "/tmp/%i", (int)uid);
+ if (mkdir(buf, S_IRUSR | S_IWUSR | S_IXUSR) < 0)
+ {
+ if (errno != EEXIST)
+ {
+ if (stat("/tmp", &st) == 0)
+ snprintf(buf, sizeof(buf), "/tmp");
+ else
+ snprintf(buf, sizeof(buf), "/");
+ }
+ }
+ if (stat(buf, &st) != 0)
+ {
+ if (stat("/tmp", &st) == 0)
+ snprintf(buf, sizeof(buf), "/tmp");
+ else
+ snprintf(buf, sizeof(buf), "/");
+ }
+#else
+ snprintf(buf, sizeof(buf), "/");
+#endif
+ return strdup(buf);
+}
+
+static void
+_eina_vpath_interface_sys_init(void)
+{
+ const char *home, *tmp;
+
+ // $HOME / ~/ etc.
+ home = eina_environment_home_get();
+ if (!home)
+ home = _fallback_home_dir();
+
+ // tmp dir - system wide
+ tmp = eina_environment_tmp_get();
+
+ _eina_vpath_data_add("home", home);
+ _eina_vpath_data_add("tmp", tmp);
+}
+
+Eina_Bool
+eina_vpath_init(void)
+{
+ vpath_data = eina_hash_string_superfast_new((Eina_Free_Cb)eina_stringshare_del);
+
+ _eina_vpath_interface_sys_init();
+
+ _eina_vpath_log_dom = eina_log_domain_register("vpath", "cyan");
+ return EINA_TRUE;
+}
+
+Eina_Bool
+eina_vpath_shutdown(void)
+{
+ eina_log_domain_unregister(_eina_vpath_log_dom);
+ _eina_vpath_log_dom = -1;
+ return EINA_TRUE;
+}
+
+EAPI char*
+eina_vpath_resolve(const char* path)
+{
+ // XXX: implement parse of path then look up in hash if not just create
+ // object where path and result are the same and return that with
+ // path set and result set to resolved path - return obj handler calls
+ // "do" on object to get the result inside fetched or failed callback.
+ // if it's a url then we need a new classs that overrides the do and
+ // begins a fetch and on finish calls the event cb or when wait is called
+ /* FIXME: not working for WIndows */
+ // /* <- full path
+
+ if (!path) return NULL;
+
+ if (path[0] == '~')
+ {
+ // ~/ <- home directory
+ if (path[1] == '/')
+ {
+ char buf[PATH_MAX];
+ const char *home = eina_hash_find(vpath_data, "home");
+
+ if (home)
+ {
+ snprintf(buf, sizeof(buf), "%s%s", home, path + 1);
+ return strdup(buf);
+ }
+ }
+#ifdef HAVE_GETPWENT
+ // ~username/ <- homedir of user "username"
+ else
+ {
+ const char *p;
+ struct passwd pwent, *pwent2 = NULL;
+ char *name, buf[PATH_MAX], pwbuf[8129];
+
+ for (p = path + 1; *p; p++)
+ {
+ if (*p =='/') break;
+ }
+ name = alloca(p - path);
+ strncpy(name, path + 1, p - path - 1);
+ name[p - path - 1] = 0;
+ if (!getpwnam_r(name, &pwent, pwbuf, sizeof(pwbuf), &pwent2))
+ {
+ if ((pwent2) && (pwent.pw_dir))
+ {
+ return strdup(buf);
+ }
+ }
+ }
+#endif /* HAVE_GETPWENT */
+ return NULL;
+ }
+ // (:xxx:)/* ... <- meta hash table
+ else if ((path[0] == '(') && (path[1] == ':'))
+ {
+ const char *p, *end, *meta;
+ char *name, buf[PATH_MAX];
+ int max_len = strlen(path);
+ Eina_Bool found = EINA_FALSE;
+
+ for (p = path + 2; p <= path + max_len - 2; p++)
+ {
+ if ((p[0] ==':') && (p[1] == ')'))
+ {
+ end = p;
+ found = EINA_TRUE;
+ break;
+ }
+ }
+ p += 2;
+
+ if (!found)
+ {
+ ERR("(: Needs to have a matching ':)'\nThe string was: %s", path);
+ return NULL;
+ }
+
+ if (*p != '/')
+ {
+ ERR("A / is expected after :)\nThe string was: %s", path);
+ return NULL;
+ }
+
+ if (found)
+ {
+ name = alloca(end - path);
+ strncpy(name, path + 2, end - path - 2);
+ name[end - path - 2] = 0;
+ meta = _eina_vpath_data_get(name);
+ if (meta)
+ {
+ snprintf(buf, sizeof(buf), "%s%s", meta, end + 2);
+ return strdup(buf);
+ }
+ else
+ {
+ ERR("Meta key '%s' was not registered!\nThe string was: %s", name, path);
+ return NULL;
+ }
+ }
+ }
+ //just return the path, since we assume that this is a normal path
+ else
+ {
+ return strdup(path);
+ }
+
+ ERR("The path has to start with either '~/' or '(:NAME:)/' or be a normal path \nThe string was: %s", path);
+
+ return NULL;
+}
+
+EAPI void
+eina_vpath_interface_app_set(const char *app_domain, Eina_Prefix *app_pfx)
+{
+ char buf[PATH_MAX];
+
+ EINA_SAFETY_ON_NULL_RETURN(app_domain);
+ EINA_SAFETY_ON_NULL_RETURN(app_pfx);
+
+ _eina_vpath_data_add("app.dir", eina_prefix_get(app_pfx));
+ _eina_vpath_data_add("app.bin", eina_prefix_bin_get(app_pfx));
+ _eina_vpath_data_add("app.lib", eina_prefix_lib_get(app_pfx));
+ _eina_vpath_data_add("app.data", eina_prefix_data_get(app_pfx));
+ _eina_vpath_data_add("app.locale", eina_prefix_locale_get(app_pfx));
+ snprintf(buf, sizeof(buf), "%s/%s",
+ _eina_vpath_data_get("config"), app_domain);
+ _eina_vpath_data_add("app.config", buf);
+ snprintf(buf, sizeof(buf), "%s/%s",
+ _eina_vpath_data_get("cache"), app_domain);
+ _eina_vpath_data_add("app.cache", buf);
+ snprintf(buf, sizeof(buf), "%s/%s",
+ _eina_vpath_data_get("data"), app_domain);
+ _eina_vpath_data_add("app.local", buf);
+}
+
+EAPI void
+eina_vpath_interface_user_set(Eina_Vpath_Interface_User *user)
+{
+ char buf[PATH_MAX];
+ Eina_Bool free_run = EINA_FALSE;
+
+ EINA_SAFETY_ON_NULL_RETURN(user);
+
+ if (!user->run)
+ {
+ user->run = _fallback_runtime_dir(_eina_vpath_data_get("home"));
+ free_run = EINA_TRUE;
+ }
+
+#define ADD(a) _eina_vpath_data_add("usr." #a , user->a)
+ ADD(desktop);
+ ADD(documents);
+ ADD(downloads);
+ ADD(music);
+ ADD(pictures);
+ ADD(pub);
+ ADD(templates);
+ ADD(videos);
+ ADD(data);
+ ADD(config);
+ ADD(cache);
+ ADD(run);
+#undef ADD
+
+ if (free_run)
+ free((char*)user->run);
+}
diff --git a/src/lib/eina/eina_vpath.h b/src/lib/eina/eina_vpath.h
new file mode 100644
index 0000000000..40adffeca1
--- /dev/null
+++ b/src/lib/eina/eina_vpath.h
@@ -0,0 +1,74 @@
+#ifndef EINA_VPATH_H
+#define EINA_VPATH_H
+
+#include "eina_prefix.h"
+
+/*
+ * Eina_vpath
+ * eina vpath is a path that can be prefixed with a virtual path.
+ *
+ * A virutla path can either start with (:XXXXXXXX:) that indicates a virtual path, OR normal with / or ./ or ../ or ~
+ * The char sequence in between (: and :) are used as key to lookup the real value.
+ * The key has to be set by a interface before, otherwise you will get a error.
+ *
+ * The symbol ~ is interpretated as the home directory of the running user, and will be replaced.
+ * Additional infos: https://phab.enlightenment.org/w/eina_vpath/
+ */
+
+/**
+ * This datatype is a vpath, this means you can use the syntax described above.
+ */
+typedef const char* Eina_Vpath;
+
+
+typedef struct
+{
+ const char *desktop;
+ const char *documents;
+ const char *downloads;
+ const char *music;
+ const char *pictures;
+ const char *pub;
+ const char *templates;
+ const char *videos;
+ const char *data;
+ const char *config;
+ const char *cache;
+ const char *run;
+} Eina_Vpath_Interface_User;
+
+/**
+ * Make the app specific paths accessable as virtual path
+ *
+ * This will create :
+ * - app.dir
+ * - app.bin
+ * - app.lib
+ * - app.data
+ * - app.locale
+ * - app.config
+ * - app.cache
+ * - app.local
+ *
+ * If you do NOT call this api the virtual paths for app.* will be unset
+ */
+EAPI void eina_vpath_interface_app_set(const char *app_name, Eina_Prefix *p);
+
+/**
+ * Create the desktop specific vpaths
+ *
+ * The virtual paths will be named usr.<field-name-of-struct>
+ *
+ * If you do NOT call this api the virtual paths for usr.* will be unset.
+ */
+EAPI void eina_vpath_interface_user_set(Eina_Vpath_Interface_User *user);
+
+/*
+ * Translate a virtual path into a normal path.
+ *
+ * @return a string that is not virtual anymore
+ *
+ */
+EAPI char* eina_vpath_resolve(Eina_Vpath path);
+
+#endif
diff --git a/src/tests/eina/eina_suite.c b/src/tests/eina/eina_suite.c
index 8e833356f3..bd3bf42a4e 100644
--- a/src/tests/eina/eina_suite.c
+++ b/src/tests/eina/eina_suite.c
@@ -87,6 +87,7 @@ static const Efl_Test_Case etc[] = {
{ "Free Queue", eina_test_freeq },
{ "Util", eina_test_util },
{ "Short Lived Strings", eina_test_slstr },
+ { "Vpath", eina_test_vpath },
{ NULL, NULL }
};
diff --git a/src/tests/eina/eina_suite.h b/src/tests/eina/eina_suite.h
index 7bf643e478..9b8351bd25 100644
--- a/src/tests/eina/eina_suite.h
+++ b/src/tests/eina/eina_suite.h
@@ -74,5 +74,6 @@ void eina_test_safepointer(TCase *tc);
void eina_test_slice(TCase *tc);
void eina_test_freeq(TCase *tc);
void eina_test_slstr(TCase *tc);
+void eina_test_vpath(TCase *tc);
#endif /* EINA_SUITE_H_ */
diff --git a/src/tests/eina/eina_test_vpath.c b/src/tests/eina/eina_test_vpath.c
new file mode 100644
index 0000000000..b09d2d5c9a
--- /dev/null
+++ b/src/tests/eina/eina_test_vpath.c
@@ -0,0 +1,53 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Eina.h>
+#include <check.h>
+
+
+START_TEST(eina_test_vpath_valid)
+{
+ int ret;
+ char test[PATH_MAX];
+
+ ret = eina_init();
+ ck_assert_int_ne(ret, 0);
+
+ ck_assert_str_eq(eina_vpath_resolve("/"), "/");
+ ck_assert_str_eq(eina_vpath_resolve("./"), "./");
+ ck_assert_str_eq(eina_vpath_resolve("..bla"), "..bla");
+ ck_assert_str_eq(eina_vpath_resolve(".bla"), ".bla");
+
+ snprintf(test, sizeof(test), "%s/", eina_environment_home_get());
+ ck_assert_str_eq(eina_vpath_resolve("~/"), test);
+
+ snprintf(test, sizeof(test), "%s/bla", eina_environment_home_get());
+ ck_assert_str_eq(eina_vpath_resolve("(:home:)/bla"), test);
+
+ ret = eina_shutdown();
+}
+END_TEST
+
+START_TEST(eina_test_vpath_invalid)
+{
+ int ret;
+
+ ret = eina_init();
+ ck_assert_int_ne(ret, 0);
+
+ ck_assert_ptr_null(eina_vpath_resolve("(:asdfasdfafasdf"));
+ ck_assert_ptr_null(eina_vpath_resolve("(:missing_slash:)"));
+ ck_assert_ptr_null(eina_vpath_resolve("(:"));
+ ck_assert_ptr_null(eina_vpath_resolve("(:home:)"));
+ ck_assert_ptr_null(eina_vpath_resolve("(:wrong_meta_key:)/"));
+
+ ret = eina_shutdown();
+}
+END_TEST
+
+void eina_test_vpath(TCase *tc)
+{
+ tcase_add_test(tc, eina_test_vpath_invalid);
+ tcase_add_test(tc, eina_test_vpath_valid);
+}