forked from enlightenment/efl
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.
This commit is contained in:
parent
0a66978041
commit
7165003bc6
|
@ -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`\" \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) /
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue