diff --git a/legacy/eina/src/include/Eina.h b/legacy/eina/src/include/Eina.h index efeefc4c23..f5a3756f49 100644 --- a/legacy/eina/src/include/Eina.h +++ b/legacy/eina/src/include/Eina.h @@ -160,6 +160,7 @@ extern "C" { #include "eina_simple_xml_parser.h" #include "eina_object.h" #include "eina_lock.h" +#include "eina_prefix.h" #ifdef __cplusplus } diff --git a/legacy/eina/src/include/Makefile.am b/legacy/eina/src/include/Makefile.am index 5da8fe843a..ec822ec04b 100644 --- a/legacy/eina/src/include/Makefile.am +++ b/legacy/eina/src/include/Makefile.am @@ -55,7 +55,8 @@ eina_unicode.h \ eina_quadtree.h \ eina_simple_xml_parser.h \ eina_object.h \ -eina_lock.h +eina_lock.h \ +eina_prefix.h if EINA_HAVE_THREADS if EINA_HAVE_WINCE diff --git a/legacy/eina/src/include/eina_prefix.h b/legacy/eina/src/include/eina_prefix.h new file mode 100644 index 0000000000..c755485abf --- /dev/null +++ b/legacy/eina/src/include/eina_prefix.h @@ -0,0 +1,211 @@ +#ifndef EINA_PREFIX_H_ +#define EINA_PREFIX_H_ + +/** + * @addtogroup Eina_Prefix_Group Group + * + * @brief These functions provide the ability to determine the runtime + * location of a software package + * + * @{ + */ + +/** + * @typedef Eina_Prefix + * This is a prefix object that is returned by e_prefix_new() when trying + * to determine the runtime location of the software in question so other + * data files such as images, sound files, other executable utilities, + * libraries, modules and locale files can be found. + */ +typedef struct _Eina_Prefix Eina_Prefix; + +/** + * @brief Create a new prefix handle given some input information + * + * @param argv0 If this is an executable this is argv[0] of the binary, or NULL if it is used from a shared library + * @param symbol This is a symbol (function for example) inside the binary or library to find the source location of. Provide NULL if not used + * @param envprefix This is the prefix to any environment variables that may override prefix detection and give the exact location of the software + * @param sharedir This is the directory inside the standard share or data dir where the software will store data files + * @param magicsharefile This is a magic file to check existence of to determine the prefix find was correct, and it must be located in the data dir under the share dir provided above, or NULL if the check is not to be done + * @param pkg_bin This is the compile-time binary install dir + * @param pkg_lib This is the compile-time library install dir + * @param pkg_data This is the compile-time share/data install dir + * @param pkg_locale This is the compile-time locale install dir + * @return The prefix handle, or NULL on failure + * + * Applications and libraries are most often not just single executables nor + * single shared library binares, but also come with extra modules they + * have to load, extra binary utilities they need to run, or have data files + * they need to load. A very primitve application ASSUMES a fixed install + * location at compile-time, but this disallows the ability to re-locate + * the application (or library) somewhere else after compilation (if you run + * out of space on a given disk, partition etc. for example), or necessitate + * the need for having to maintain enviornment variables for every piece of + * software to let it know its location, or or have to use large sets of + * symlinks pointing from the compiled location to the new one. + * + * Being re-locatable at runtime allows much easier distribution and + * installation into places like the users own home directory, instead of + * on a system partition, if the developer wishes for easier distribution + * of pre-compiled binaries. + * + * The prefix system is designed to locate where the given software is + * installed (under a common prefix) at runtime and then report specific + * locations of this prefix and common directories inside this prefix like + * the binary, library, data and locale directories. + * + * To do this some information needs to be provided to eina_prefix_new(). If + * you have developed a binary executable, then provide argv[0] as the @p argv0 + * argument. This plus the PATH environment variable help the prefix system + * to determine its location. Call eina_prefix_new() early on before you + * change working directory or anything about argv[0] so it gets accurate + * information. It will use the first argument, being the executable itself, + * to look in absolutel directories, relative paths and PATH to see if it + * finds the right executable to determine just where the actual binary is + * installed and being run from. If you develop a share library, just pass + * NULL as argv0 + * + * It would prefer to use the @p symbol function to determine location as + * that function will be unique inside the application and try and trace + * back which file this function comes from (be it a binary or shared library) + * as this avoids more expensive searches via @p argv0. It will use this + * symbol if given in preference to argv0. + * + * The @p envprefix parameter, provides a string prefix to prepend before + * environment variables to allow a fallback to specific environment variables + * to locate the software. For example if "MYAPP" is provided a the prefix, + * then it uses "MYAPP_PREFIX" as a master environment variable to specify + * the exact install prefix for the software, or more specific environment + * variables like "MYAPP_BIN_DIR", "MYAPP_LIB_DIR", "MYAPP_DATA_DIR" and + * "MYAPP_LOCALE_DIR" which can be set by the user or scripts before + * launching. If not provided (NULL) enviornment variables will not be + * used to override compiled-in defaults or auto detections. + * + * The @p sharedir string provides a subdirectory inside the system shared + * data dir for data files. For example, if the system dir is + * /usr/local/share then this dir name is appended, creating + * /usr/local/share/appname if this dir was the "appname" string. It is + * expexcted the application or library installs data files in this directory. + * + * The @p magicsharefile is a filename or path of something inside the share + * or data dir to be used to test that the prefix detection worked. For + * example, your app will install a wallpaper image as + * /usr/local/share/appname/images/wallpaper.jpg and so to check that this + * worked, provide "images/wallpaper.jpg" as the @p magicsharefile string + * so detection can know if it worked or not. + * + * The @p pkg_bin, @p pkg_lib, @p pkg_data and @p pkg_locale are compile-time + * strings (the kind standard autoconf/automake define) to be passed in + * so there can be a fallback to compiled-in defaults as well as use them + * to determine actual names of directories like libdirs maybe changing to + * be lib32 or lib64 instead of lib etc. + * + * Compile the following defining at compile time your prefixes like (example): + * + * gcc appname.c -o appname \ + * -DPACKAGE_BIN_DIR=/usr/local/bin \ + * -DPACKAGE_LIB_DIR=/usr/local/lib \ + * -DPACKAGE_DATA_DIR=/usr/local/share/appname \ + * -DLOCALE_DIR=/usr/local/share/locale + * + * (of course add appropriate compile flags to linking etc. etc. and note that + * locale dir is optional. if you don't need it provide data dir as the + * locale dir. also note that the magicsharefile is optional for testing and + * ensuring that the prefix check is correct. this file must be installed + * in the application data dir (eg /usr/local/share/appname) and be referred + * to using a unix-style relative path from that dir, eg directory/filename.png) + * + * @code + * static Eina_Prefix *pfx = NULL; + * + * int main(argc, char **argv) + * { + * pfx = e_prefix_new(argv[0], main, "APPNAME", "appname", NULL, + * PACKAGE_BIN_DIR, PACKAGE_LIB_DIR, + * PACKAGE_DATA_DIR, LOCALE_DIR); + * if (!pfx) printf("ERROR: Critical error in finding prefix\n"); + * printf("install prefix is: %s\n", eina_prefix_get(pfx)); + * printf("binaries are in: %s\n", eina_prefix_bin_get(pfx)); + * printf("libraries are in: %s\n", eina_prefix_lib_get(pfx)); + * printf("data files are in: %s\n", eina_prefix_data_get(pfx)); + * e_prefix_free(pfx); + * } + * @endcode + */ +EAPI Eina_Prefix * +eina_prefix_new(const char *argv0, void *symbol, const char *envprefix, + const char *sharedir, const char *magicsharefile, + const char *pkg_bin, const char *pkg_lib, + const char *pkg_data, const char *pkg_locale); + +/** + * @brief Free the prefix object and all its contents + * + * @param pfx The prefix object + * + * Free the prefix object and all its allocated content. It will be invalid + * to access the object after being freed. + * + */ +EAPI void +eina_prefix_free(Eina_Prefix *pfx); + +/** + * @brief Get the prefix base directory + * + * @param pfx The prefix object + * + * This returns the base prefix (eg "/usr/local", "/usr", "/opt/appname" or + * "/home/user/myapps/appname" etc.) that the software resides in at runtime. + */ +EAPI const char * +eina_prefix_get(Eina_Prefix *pfx); + +/** + * @brief Get the binary installation directory + * + * @param pfx The prefix object + * + * This returns the location of installed binaries (eg "/usr/local/bin", + * "/usr/bin", "/opt/appname/bin", "/home/user/myapps/appname/bin" etc.). + */ +EAPI const char * +eina_prefix_bin_get(Eina_Prefix *pfx); + +/** + * @brief Get the library installation directory + * + * @param pfx The prefix object + * + * This returns the location of installed binaries (eg "/usr/local/lib", + * "/usr/lib32", "/opt/appname/lib64", "/home/user/myapps/appname/lib" etc.). + */ +EAPI const char * +eina_prefix_lib_get(Eina_Prefix *pfx); + +/** + * @brief Get the data installation directory + * + * @param pfx The prefix object + * + * This returns the location of installed binaries (eg "/usr/local/share/appname", + * "/usr/share/appname", "/opt/appname/share/appname", "/home/user/myapps/appname/share/appname" etc.). + */ +EAPI const char * +eina_prefix_data_get(Eina_Prefix *pfx); + +/** + * @brief Get the locale installation directory + * + * @param pfx The prefix object + * + * This returns the location of installed binaries (eg "/usr/local/share/locale", + * "/usr/share/locale", "/opt/appname/share/locale", "/home/user/myapps/appname/share/locale" etc.). + */ +EAPI const char * +eina_prefix_locale_get(Eina_Prefix *pfx); + +/** + * @} + */ +#endif diff --git a/legacy/eina/src/lib/Makefile.am b/legacy/eina/src/lib/Makefile.am index 2fbaa817da..b7fbfe019e 100644 --- a/legacy/eina/src/lib/Makefile.am +++ b/legacy/eina/src/lib/Makefile.am @@ -47,7 +47,8 @@ eina_ustrbuf.c \ eina_ustringshare.c \ eina_value.c \ eina_simple_xml_parser.c \ -eina_object.c +eina_object.c \ +eina_prefix.c if EINA_HAVE_WIN32 base_sources += eina_file_win32.c diff --git a/legacy/eina/src/lib/eina_prefix.c b/legacy/eina/src/lib/eina_prefix.c new file mode 100644 index 0000000000..c4e1420801 --- /dev/null +++ b/legacy/eina/src/lib/eina_prefix.c @@ -0,0 +1,562 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_DLADDR +# include +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include "eina_config.h" +#include "eina_private.h" +#include "eina_log.h" +#include "eina_prefix.h" + +#ifdef _WIN32 +# define DSEP_C '\\' +# define DSEP_S "\\" +#else +# define DSEP_C '/' +# define DSEP_S "/" +#endif /* _WIN32 */ + +struct _Eina_Prefix +{ + char *exe_path; + + char *prefix_path; + char *prefix_path_bin; + char *prefix_path_data; + char *prefix_path_lib; + char *prefix_path_locale; + + unsigned char fallback : 1; + unsigned char no_common_prefix : 1; + unsigned char env_used : 1; +}; + +/* local subsystem globals */ + +#define STRDUP_REP(x, y) do { if (x) free(x); x = strdup(y); } while (0) +#define IF_FREE_NULL(p) do { if (p) { free(p); p = NULL; } } while (0) + +/* local subsystem functions */ + +static int +_fallback(Eina_Prefix *pfx, const char *pkg_bin, const char *pkg_lib, + const char *pkg_data, const char *pkg_locale, const char *envprefix) +{ + char *p; + + STRDUP_REP(pfx->prefix_path, pkg_bin); + if (!pfx->prefix_path) return 0; + p = strrchr(pfx->prefix_path, DSEP_C); + if (p) *p = 0; + STRDUP_REP(pfx->prefix_path_bin, pkg_bin); + STRDUP_REP(pfx->prefix_path_lib, pkg_lib); + STRDUP_REP(pfx->prefix_path_data, pkg_data); + STRDUP_REP(pfx->prefix_path_locale, pkg_locale); + fprintf(stderr, + "WARNING: Could not determine its installed prefix for '%s'\n" + " so am falling back on the compiled in default:\n" + " %s\n" + " implied by the following:\n" + " bindir = %s\n" + " libdir = %s\n" + " datadir = %s\n" + " localedir = %s\n" + " Try setting the following environment variables:\n" + " %s_PREFIX - points to the base prefix of install\n" + " or the next 4 variables\n" + " %s_BIN_DIR - provide a specific binary directory\n" + " %s_LIB_DIR - provide a specific library directory\n" + " %s_DATA_DIR - provide a specific data directory\n" + " %s_LOCALE_DIR - provide a specific locale directory\n" + , envprefix, + pfx->prefix_path, pkg_bin, pkg_lib, pkg_data, pkg_locale, + envprefix, envprefix, envprefix, envprefix, envprefix); + pfx->fallback = 1; + return 1; +} + +#ifndef _WIN32 +static int +_try_proc(Eina_Prefix *pfx, void *symbol) +{ + FILE *f; + char buf[4096]; + + f = fopen("/proc/self/maps", "rb"); + if (!f) return 0; + while (fgets(buf, sizeof(buf), f)) + { + int len; + char *p, mode[5] = ""; + unsigned long ptr1 = 0, ptr2 = 0; + + len = strlen(buf); + if (buf[len - 1] == '\n') + { + buf[len - 1] = 0; + len--; + } + if (sscanf(buf, "%lx-%lx %4s", &ptr1, &ptr2, mode) == 3) + { + if (!strcmp(mode, "r-xp")) + { + if (((void *)ptr1 <= symbol) && (symbol < (void *)ptr2)) + { + p = strchr(buf, '/'); + if (p) + { + if (len > 10) + { + if (!strcmp(buf + len - 10, " (deleted)")) + buf[len - 10] = 0; + } + STRDUP_REP(pfx->exe_path, p); + fclose(f); + return 1; + } + else break; + } + } + } + } + fclose(f); + return 0; +} +#endif + +static int +_try_argv(Eina_Prefix *pfx, const char *argv0) +{ + char *path, *p, *cp, *s; + int len, lenexe; + char buf[PATH_MAX], buf2[PATH_MAX], buf3[PATH_MAX]; + + /* 1. is argv0 abs path? */ +#ifdef _WIN32 + if (argv0[0] && (argv0[1] == ':')) +#else + if (argv0[0] == '/') +#endif + { + STRDUP_REP(pfx->exe_path, argv0); + if (access(pfx->exe_path, X_OK) == 0) return 1; + IF_FREE_NULL(pfx->exe_path); + return 0; + } + /* 2. relative path */ + if (strchr(argv0, DSEP_C)) + { + if (getcwd(buf3, sizeof(buf3))) + { + snprintf(buf2, sizeof(buf2), "%s" DSEP_S "%s", buf3, argv0); + if (realpath(buf2, buf)) + { + STRDUP_REP(pfx->exe_path, buf); + if (access(pfx->exe_path, X_OK) == 0) return 1; + IF_FREE_NULL(pfx->exe_path); + } + } + } + /* 3. argv0 no path - look in PATH */ + path = getenv("PATH"); + if (!path) return 0; + p = path; + cp = p; + lenexe = strlen(argv0); + while ((p = strchr(cp, ':'))) + { + len = p - cp; + s = malloc(len + 1 + lenexe + 1); + if (s) + { + strncpy(s, cp, len); + s[len] = DSEP_C; + strcpy(s + len + 1, argv0); + if (realpath(s, buf)) + { + if (access(buf, X_OK) == 0) + { + STRDUP_REP(pfx->exe_path, buf); + free(s); + return 1; + } + } + free(s); + } + cp = p + 1; + } + cp = getcwd(buf3, sizeof(buf3)); + if (cp) + { + len = strlen(cp); + s = malloc(len + 1 + lenexe + 1); + if (s) + { + strncpy(s, cp, len); + s[len] = DSEP_C; + strcpy(s + len + 1, argv0); + if (realpath(s, buf)) + { + if (access(buf, X_OK) == 0) + { + STRDUP_REP(pfx->exe_path, buf); + free(s); + return 1; + } + } + free(s); + } + } + /* 4. big problems. arg[0] != executable - weird execution */ + return 0; +} + +static int +_get_env_var(char **var, const char *env, const char *prefix, const char *dir) +{ + char buf[PATH_MAX]; + const char *s = getenv(env); + + if (s) + { + STRDUP_REP(*var, s); + return 1; + } + else if (prefix) + { + snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", prefix, dir); + STRDUP_REP(*var, buf); + return 1; + } + return 0; +} + +static int +_get_env_vars(Eina_Prefix *pfx, + const char *envprefix, + const char *bindir, + const char *libdir, + const char *datadir, + const char *localedir) +{ + char env[1024]; + const char *s; + int ret = 0; + + snprintf(env, sizeof(env), "%s_PREFIX", envprefix); + if ((s = getenv(env))) STRDUP_REP(pfx->prefix_path, s); + snprintf(env, sizeof(env), "%s_BIN_DIR", envprefix); + ret += _get_env_var(&pfx->prefix_path_bin, env, s, bindir); + snprintf(env, sizeof(env), "%s_LIB_DIR", envprefix); + ret += _get_env_var(&pfx->prefix_path_lib, env, s, libdir); + snprintf(env, sizeof(env), "%s_DATA_DIR", envprefix); + ret += _get_env_var(&pfx->prefix_path_data, env, s, datadir); + snprintf(env, sizeof(env), "%s_LOCALE_DIR", envprefix); + ret += _get_env_var(&pfx->prefix_path_locale, env, s, localedir); + return ret; +} + +/* externally accessible functions */ +EAPI Eina_Prefix * +eina_prefix_new(const char *argv0, void *symbol, const char *envprefix, + const char *sharedir, const char *magicsharefile, + const char *pkg_bin, const char *pkg_lib, + const char *pkg_data, const char *pkg_locale) +{ + Eina_Prefix *pfx; + char *p, buf[4096], *tmp, *magic = NULL; + struct stat st; + const char *p1, *p2; + const char *pkg_bin_p = NULL; + const char *pkg_lib_p = NULL; + const char *pkg_data_p = NULL; + const char *pkg_locale_p = NULL; + const char *bindir = "bin"; + const char *libdir = "lib"; + const char *datadir = "share"; + const char *localedir = "share"; + + pfx = calloc(1, sizeof(Eina_Prefix)); + if (!pfx) return NULL; + + /* if provided with a share dir use datadir/sharedir as the share dir */ + if (sharedir) + { + int len; + + len = snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", datadir, sharedir); + if (len > 0) + { +#ifdef _WIN32 + /* on win32 convert / to \ for path here */ + for (p = buf + strlen(datadir) + strlen(DSEP_S); *p; p++) + { + if (*p == '/') *p = DSEP_C; + } +#endif + tmp = alloca(len + 1); + strcpy(tmp, buf); + datadir = tmp; + } + } + if (magicsharefile) + { + magic = alloca(strlen(magicsharefile)); + strcpy(magic, magicsharefile); +#ifdef _WIN32 + /* on win32 convert / to \ for path here */ + for (p = magic; *p; p++) + { + if (*p == '/') *p = DSEP_C; + } +#endif + } + + /* look at compile-time package bin/lib/datadir etc. and figure out the + * bin, lib and data dirs from these, if possible. i.e. + * bin = /usr/local/bin + * lib = /usr/local/lib + * data = /usr/local/share/enlightenment + * thus they all have a common prefix string of /usr/local/ and + * bindir = bin + * libdir = lib + * datadir = share/enlightenment + * this addresses things like libdir is lib64 or lib32 or other such + * junk distributions like to do so then: + * bin = /usr/local/bin + * lib = /usr/local/lib64 + * data = /usr/local/share/enlightenment + * then + * bindir = bin + * libdir = lib64 + * datadir = share/enlightennment + * all with a common prefix that can be relocated + */ + /* 1. check last common char in bin and lib strings */ + for (p1 = pkg_bin, p2 = pkg_lib; *p1 && *p2; p1++, p2++) + { + if (*p1 != *p2) + { + pkg_bin_p = p1; + pkg_lib_p = p2; + break; + } + } + /* 1. check last common char in bin and data strings */ + for (p1 = pkg_bin, p2 = pkg_data; *p1 && *p2; p1++, p2++) + { + if (*p1 != *p2) + { + pkg_data_p = p2; + break; + } + } + /* 1. check last common char in bin and locale strings */ + for (p1 = pkg_bin, p2 = pkg_locale; *p1 && *p2; p1++, p2++) + { + if (*p1 != *p2) + { + pkg_locale_p = p2; + break; + } + } + /* 2. if all the common string offsets match we compiled with a common prefix */ + if (((pkg_bin_p - pkg_bin) == (pkg_lib_p - pkg_lib)) + && ((pkg_bin_p - pkg_bin) == (pkg_data_p - pkg_data)) + && ((pkg_bin_p - pkg_bin) == (pkg_locale_p - pkg_locale)) + ) + { + bindir = pkg_bin_p; + libdir = pkg_lib_p; + datadir = pkg_data_p; + localedir = pkg_locale_p; + } + /* 3. some galoot thought it awesome not to give us a common prefix at compile time + * so fall back to the compile time directories. we are no longer relocatable */ + else + { + STRDUP_REP(pfx->prefix_path_bin, pkg_bin); + STRDUP_REP(pfx->prefix_path_lib, pkg_lib); + STRDUP_REP(pfx->prefix_path_data, pkg_data); + STRDUP_REP(pfx->prefix_path_locale, pkg_locale); + pfx->no_common_prefix = 1; + } + + /* if user provides env vars - then use that or also more specific sub + * dirs for bin, lib, data and locale */ + if ((envprefix) && + (_get_env_vars(pfx, envprefix, bindir, libdir, datadir, localedir) > 0)) + { + pfx->env_used = 1; + return pfx; + } + +#ifdef HAVE_DLADDR + if (symbol) + { + Dl_info info_dl; + + if (dladdr(symbol, &info_dl)) + { + STRDUP_REP(pfx->exe_path, info_dl.dli_fname); + } + } +#endif + /* no env var - examine process and possible argv0 */ + if ((argv0) && (!pfx->exe_path) && (symbol)) + { +#ifndef _WIN32 + if (!_try_proc(pfx, symbol)) + { +#endif + if (!_try_argv(pfx, argv0)) + { + _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, + envprefix); + return pfx; + } +#ifndef _WIN32 + } +#endif + } + if (!pfx->exe_path) + { + _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, envprefix); + return pfx; + } + /* _exe_path is now a full absolute path TO this exe - figure out rest */ + /* if + * exe = /blah/whatever/bin/exe + * or + * exe = /blah/whatever/lib/libexe.so + * then + * prefix = /blah/whatever + * bin_dir = /blah/whatever/bin + * data_dir = /blah/whatever/share/enlightenment + * lib_dir = /blah/whatever/lib + */ + p = strrchr(pfx->exe_path, DSEP_C); + if (p) + { + p--; + while (p >= pfx->exe_path) + { + if (*p == DSEP_C) + { + pfx->prefix_path = malloc(p - pfx->exe_path + 1); + if (pfx->prefix_path) + { + strncpy(pfx->prefix_path, pfx->exe_path, + p - pfx->exe_path); + pfx->prefix_path[p - pfx->exe_path] = 0; + + /* bin */ + snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", + pfx->prefix_path, bindir); + STRDUP_REP(pfx->prefix_path_bin, buf); + /* lib */ + snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", + pfx->prefix_path, libdir); + STRDUP_REP(pfx->prefix_path_lib, buf); + /* locale */ + snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", + pfx->prefix_path, localedir); + STRDUP_REP(pfx->prefix_path_locale, buf); + /* check if magic file is there - then our guess is right */ + if (magic) + snprintf(buf, sizeof(buf), + "%s" DSEP_S "%s" DSEP_S "%s", + pfx->prefix_path, datadir, magic); + if ((!magic) || (stat(buf, &st) == 0)) + { + snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", + pfx->prefix_path, datadir); + STRDUP_REP(pfx->prefix_path_data, buf); + } + /* magic file not there. time to start hunting! */ + else + _fallback(pfx, pkg_bin, pkg_lib, pkg_data, + pkg_locale, envprefix); + } + else + _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, + envprefix); + return pfx; + } + p--; + } + } + _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, envprefix); + return pfx; +} + +EAPI void +eina_prefix_free(Eina_Prefix *pfx) +{ + if (!pfx) return; + + IF_FREE_NULL(pfx->exe_path); + IF_FREE_NULL(pfx->prefix_path); + IF_FREE_NULL(pfx->prefix_path_bin); + IF_FREE_NULL(pfx->prefix_path_data); + IF_FREE_NULL(pfx->prefix_path_lib); + IF_FREE_NULL(pfx->prefix_path_locale); + free(pfx); +} + +EAPI const char * +eina_prefix_get(Eina_Prefix *pfx) +{ + if (!pfx) return ""; + return pfx->prefix_path; +} + +EAPI const char * +eina_prefix_bin_get(Eina_Prefix *pfx) +{ + if (!pfx) return ""; + return pfx->prefix_path_bin; +} + +EAPI const char * +eina_prefix_lib_get(Eina_Prefix *pfx) +{ + if (!pfx) return ""; + return pfx->prefix_path_lib; +} + +EAPI const char * +eina_prefix_data_get(Eina_Prefix *pfx) +{ + if (!pfx) return ""; + return pfx->prefix_path_data; +} + +EAPI const char * +eina_prefix_locale_get(Eina_Prefix *pfx) +{ + if (!pfx) return ""; + return pfx->prefix_path_locale; +}