bu5hm4n pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=7165003bc60177818a491db0d6a557dd0767554b

commit 7165003bc60177818a491db0d6a557dd0767554b
Author: Marcel Hollerbach <mar...@osg.samsung.com>
Date:   Sun Feb 18 22:12:58 2018 +0100

    introduce eina_vpath!
    
    Its the successor of efl.vpath, the api is synchronous, and supports
    addtional vpath paths, new apis cal always be added to use them.
---
 src/Makefile_Eina.am             |   9 +-
 src/lib/eina/Eina.h              |   1 +
 src/lib/eina/eina_main.c         |   3 +
 src/lib/eina/eina_vpath.c        | 349 +++++++++++++++++++++++++++++++++++++++
 src/lib/eina/eina_vpath.h        |  74 +++++++++
 src/tests/eina/eina_suite.c      |   1 +
 src/tests/eina/eina_suite.h      |   1 +
 src/tests/eina/eina_test_vpath.c |  53 ++++++
 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);
+}

-- 


Reply via email to