1 /* EINA - EFL data type library
2 * Copyright (C) 2011 Carsten Haitzler
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
35 # define alloca __builtin_alloca
37 # define alloca __alloca
38 # elif defined _MSC_VER
40 # define alloca _alloca
41 # elif !defined HAVE_ALLOCA
45 void *alloca (size_t);
52 #include <sys/types.h>
72 #include "eina_config.h"
73 #include "eina_private.h"
75 #include "eina_prefix.h"
87 /*============================================================================*
89 *============================================================================*/
100 char *prefix_path_bin;
101 char *prefix_path_data;
102 char *prefix_path_lib;
103 char *prefix_path_locale;
105 unsigned char fallback : 1;
106 unsigned char no_common_prefix : 1;
107 unsigned char env_used : 1;
110 #define STRDUP_REP(x, y) do { if (x) free(x); x = strdup(y); } while (0)
111 #define IF_FREE_NULL(p) do { if (p) { free(p); p = NULL; } } while (0)
113 #ifndef EINA_LOG_COLOR_DEFAULT
114 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
120 #define ERR(...) EINA_LOG_DOM_ERR(_eina_prefix_log_dom, __VA_ARGS__)
125 #define WRN(...) EINA_LOG_DOM_WARN(_eina_prefix_log_dom, __VA_ARGS__)
130 #define INF(...) EINA_LOG_DOM_INFO(_eina_prefix_log_dom, __VA_ARGS__)
135 #define DBG(...) EINA_LOG_DOM_DBG(_eina_prefix_log_dom, __VA_ARGS__)
137 static int _eina_prefix_log_dom = -1;
140 _fallback(Eina_Prefix *pfx, const char *pkg_bin, const char *pkg_lib,
141 const char *pkg_data, const char *pkg_locale, const char *envprefix)
145 STRDUP_REP(pfx->prefix_path, pkg_bin);
146 if (!pfx->prefix_path) return 0;
147 p = strrchr(pfx->prefix_path, DSEP_C);
149 STRDUP_REP(pfx->prefix_path_bin, pkg_bin);
150 STRDUP_REP(pfx->prefix_path_lib, pkg_lib);
151 STRDUP_REP(pfx->prefix_path_data, pkg_data);
152 STRDUP_REP(pfx->prefix_path_locale, pkg_locale);
153 WRN("Could not determine its installed prefix for '%s'\n"
154 " so am falling back on the compiled in default:\n"
156 " implied by the following:\n"
161 " Try setting the following environment variables:\n"
162 " %s_PREFIX - points to the base prefix of install\n"
163 " or the next 4 variables\n"
164 " %s_BIN_DIR - provide a specific binary directory\n"
165 " %s_LIB_DIR - provide a specific library directory\n"
166 " %s_DATA_DIR - provide a specific data directory\n"
167 " %s_LOCALE_DIR - provide a specific locale directory",
169 pfx->prefix_path, pkg_bin, pkg_lib, pkg_data, pkg_locale,
170 envprefix, envprefix, envprefix, envprefix, envprefix);
177 _try_proc(Eina_Prefix *pfx, void *symbol)
182 DBG("Try /proc/self/maps");
183 f = fopen("/proc/self/maps", "rb");
185 DBG("Exists /proc/self/maps");
186 while (fgets(buf, sizeof(buf), f))
189 char *p, mode[5] = "";
190 unsigned long ptr1 = 0, ptr2 = 0;
193 if (buf[len - 1] == '\n')
198 if (sscanf(buf, "%lx-%lx %4s", &ptr1, &ptr2, mode) == 3)
200 if (!strcmp(mode, "r-xp"))
202 if (((void *)ptr1 <= symbol) && (symbol < (void *)ptr2))
204 DBG("Found in /proc/self/maps: %s", buf);
205 p = strchr(buf, '/');
208 DBG("Found in /proc/self/maps: found last /");
211 if (!strcmp(buf + len - 10, " (deleted)"))
214 STRDUP_REP(pfx->exe_path, p);
215 INF("Found in /proc/self/maps: guess exe path is %s", pfx->exe_path);
225 WRN("Failed in /proc/self/maps");
231 _try_argv(Eina_Prefix *pfx, const char *argv0)
233 char *path, *p, *cp, *s;
235 char buf[PATH_MAX], buf2[PATH_MAX], buf3[PATH_MAX];
237 DBG("Try argv0 = %s", argv0);
238 /* 1. is argv0 abs path? */
240 if (evil_path_is_absolute(argv0))
242 if (argv0[0] == DSEP_C)
245 DBG("Match arvg0 is full path: %s", argv0);
246 STRDUP_REP(pfx->exe_path, argv0);
247 if (access(pfx->exe_path, X_OK) == 0)
249 INF("Executable argv0 = %s", argv0);
252 IF_FREE_NULL(pfx->exe_path);
253 DBG("Non existent argv0: %s", argv0);
256 /* 2. relative path */
257 if (strchr(argv0, DSEP_C))
259 DBG("Relative path argv0: %s", argv0);
260 if (getcwd(buf3, sizeof(buf3)))
262 snprintf(buf2, sizeof(buf2), "%s" DSEP_S "%s", buf3, argv0);
263 DBG("Relative to CWD: %s", buf2);
264 if (realpath(buf2, buf))
266 DBG("Realpath is: %s", buf);
267 STRDUP_REP(pfx->exe_path, buf);
268 if (access(pfx->exe_path, X_OK) == 0)
270 INF("Path %s is executable", pfx->exe_path);
273 DBG("Fail check for executable: %s", pfx->exe_path);
274 IF_FREE_NULL(pfx->exe_path);
278 /* 3. argv0 no path - look in PATH */
279 DBG("Look for argv0=%s in $PATH", argv0);
280 path = getenv("PATH");
284 lenexe = strlen(argv0);
285 while ((p = strchr(cp, PSEP_C)))
288 s = malloc(len + 1 + lenexe + 1);
293 strcpy(s + len + 1, argv0);
294 DBG("Try path: %s", s);
295 if (realpath(s, buf))
297 DBG("Realpath is: %s", buf);
298 if (access(buf, X_OK) == 0)
300 STRDUP_REP(pfx->exe_path, buf);
301 INF("Path %s is executable", pfx->exe_path);
310 /* 4. big problems. arg[0] != executable - weird execution */
315 _get_env_var(char **var, const char *env, const char *prefix, const char *dir)
318 const char *s = getenv(env);
320 DBG("Try env var %s", env);
323 INF("Have env %s = %s", env, s);
329 snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", prefix, dir);
330 INF("Have prefix %s = %s", prefix, buf);
331 STRDUP_REP(*var, buf);
338 _get_env_vars(Eina_Prefix *pfx,
339 const char *envprefix,
343 const char *localedir)
349 snprintf(env, sizeof(env), "%s_PREFIX", envprefix);
350 if ((s = getenv(env))) STRDUP_REP(pfx->prefix_path, s);
351 snprintf(env, sizeof(env), "%s_BIN_DIR", envprefix);
352 ret += _get_env_var(&pfx->prefix_path_bin, env, s, bindir);
353 snprintf(env, sizeof(env), "%s_LIB_DIR", envprefix);
354 ret += _get_env_var(&pfx->prefix_path_lib, env, s, libdir);
355 snprintf(env, sizeof(env), "%s_DATA_DIR", envprefix);
356 ret += _get_env_var(&pfx->prefix_path_data, env, s, datadir);
357 snprintf(env, sizeof(env), "%s_LOCALE_DIR", envprefix);
358 ret += _get_env_var(&pfx->prefix_path_locale, env, s, localedir);
367 /*============================================================================*
369 *============================================================================*/
372 /*============================================================================*
374 *============================================================================*/
378 eina_prefix_new(const char *argv0, void *symbol, const char *envprefix,
379 const char *sharedir, const char *magicsharefile,
380 const char *pkg_bin, const char *pkg_lib,
381 const char *pkg_data, const char *pkg_locale)
384 char *p, buf[4096], *tmp, *magic = NULL;
387 const char *pkg_bin_p = NULL;
388 const char *pkg_lib_p = NULL;
389 const char *pkg_data_p = NULL;
390 const char *pkg_locale_p = NULL;
391 const char *bindir = "bin";
392 const char *libdir = "lib";
393 const char *datadir = "share";
394 const char *localedir = "share";
396 DBG("EINA PREFIX: argv0=%s, symbol=%p, magicsharefile=%s, envprefix=%s",
397 argv0, symbol, magicsharefile, envprefix);
398 pfx = calloc(1, sizeof(Eina_Prefix));
399 if (!pfx) return NULL;
401 /* if provided with a share dir use datadir/sharedir as the share dir */
406 len = snprintf(buf, sizeof(buf), "%s" DSEP_S "%s", datadir, sharedir);
410 /* on win32 convert / to \ for path here */
411 for (p = buf + strlen(datadir) + strlen(DSEP_S); *p; p++)
413 if (*p == '/') *p = DSEP_C;
416 tmp = alloca(len + 1);
423 magic = alloca(strlen(magicsharefile));
424 strcpy(magic, magicsharefile);
426 /* on win32 convert / to \ for path here */
427 for (p = magic; *p; p++)
429 if (*p == '/') *p = DSEP_C;
434 /* look at compile-time package bin/lib/datadir etc. and figure out the
435 * bin, lib and data dirs from these, if possible. i.e.
436 * bin = /usr/local/bin
437 * lib = /usr/local/lib
438 * data = /usr/local/share/enlightenment
439 * thus they all have a common prefix string of /usr/local/ and
442 * datadir = share/enlightenment
443 * this addresses things like libdir is lib64 or lib32 or other such
444 * junk distributions like to do so then:
445 * bin = /usr/local/bin
446 * lib = /usr/local/lib64
447 * data = /usr/local/share/enlightenment
451 * datadir = share/enlightennment
452 * in theory this should also work with debians new multiarch style like
454 * libdir = lib/i386-linux-gnu
456 * libdir = lib/x86_64-linux-gnu
457 * all with a common prefix that can be relocated
459 /* 1. check last common char in bin and lib strings */
460 for (p1 = pkg_bin, p2 = pkg_lib; *p1 && *p2; p1++, p2++)
469 /* 1. check last common char in bin and data strings */
470 for (p1 = pkg_bin, p2 = pkg_data; *p1 && *p2; p1++, p2++)
478 /* 1. check last common char in bin and locale strings */
479 for (p1 = pkg_bin, p2 = pkg_locale; *p1 && *p2; p1++, p2++)
487 /* 2. if all the common string offsets match we compiled with a common prefix */
488 if (((pkg_bin_p - pkg_bin) == (pkg_lib_p - pkg_lib))
489 && ((pkg_bin_p - pkg_bin) == (pkg_data_p - pkg_data))
490 && ((pkg_bin_p - pkg_bin) == (pkg_locale_p - pkg_locale))
495 datadir = pkg_data_p;
496 localedir = pkg_locale_p;
497 DBG("Prefix relative bindir = %s", bindir);
498 DBG("Prefix relative libdir = %s", libdir);
499 DBG("Prefix relative datadir = %s", datadir);
500 DBG("Prefix relative localedir = %s", localedir);
502 /* 3. some galoot thought it awesome not to give us a common prefix at compile time
503 * so fall back to the compile time directories. we are no longer relocatable */
506 STRDUP_REP(pfx->prefix_path_bin, pkg_bin);
507 STRDUP_REP(pfx->prefix_path_lib, pkg_lib);
508 STRDUP_REP(pfx->prefix_path_data, pkg_data);
509 STRDUP_REP(pfx->prefix_path_locale, pkg_locale);
510 pfx->no_common_prefix = 1;
511 DBG("Can't work out a common prefix - compiled in fallback");
514 /* if user provides env vars - then use that or also more specific sub
515 * dirs for bin, lib, data and locale */
517 (_get_env_vars(pfx, envprefix, bindir, libdir, datadir, localedir) > 0))
524 DBG("Try dladdr on %p", symbol);
529 if (dladdr(symbol, &info_dl))
531 DBG("Dlinfo worked");
532 if (info_dl.dli_fname)
534 DBG("Dlinfo dli_fname = %s", info_dl.dli_fname);
536 if (evil_path_is_absolute(info_dl.dli_fname))
538 if (info_dl.dli_fname[0] == DSEP_C)
541 INF("Dlsym gave full path = %s", info_dl.dli_fname);
542 STRDUP_REP(pfx->exe_path, info_dl.dli_fname);
548 /* no env var - examine process and possible argv0 */
549 if ((argv0) && (!pfx->exe_path) && (symbol))
552 if (!_try_proc(pfx, symbol))
555 if (!_try_argv(pfx, argv0))
557 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale,
567 WRN("Fallback - nothing found");
568 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, envprefix);
571 /* _exe_path is now a full absolute path TO this exe - figure out rest */
573 * exe = /blah/whatever/bin/exe
575 * exe = /blah/whatever/lib/libexe.so
577 * prefix = /blah/whatever
578 * bin_dir = /blah/whatever/bin
579 * data_dir = /blah/whatever/share/enlightenment
580 * lib_dir = /blah/whatever/lib
582 * new case - debian multiarch goop.
583 * exe = /blah/whatever/lib/arch/libexe.so
585 DBG("From exe %s figure out the rest", pfx->exe_path);
586 p = strrchr(pfx->exe_path, DSEP_C);
590 while (p >= pfx->exe_path)
594 if (pfx->prefix_path) free(pfx->prefix_path);
595 pfx->prefix_path = malloc(p - pfx->exe_path + 1);
596 if (pfx->prefix_path)
598 strncpy(pfx->prefix_path, pfx->exe_path,
600 pfx->prefix_path[p - pfx->exe_path] = 0;
601 DBG("Have prefix = %s", pfx->prefix_path);
604 snprintf(buf, sizeof(buf), "%s" DSEP_S "%s",
605 pfx->prefix_path, bindir);
606 STRDUP_REP(pfx->prefix_path_bin, buf);
607 DBG("Have bin = %s", pfx->prefix_path_bin);
609 snprintf(buf, sizeof(buf), "%s" DSEP_S "%s",
610 pfx->prefix_path, libdir);
611 STRDUP_REP(pfx->prefix_path_lib, buf);
612 DBG("Have lib = %s", pfx->prefix_path_lib);
614 snprintf(buf, sizeof(buf), "%s" DSEP_S "%s",
615 pfx->prefix_path, localedir);
616 STRDUP_REP(pfx->prefix_path_locale, buf);
617 DBG("Have locale = %s", pfx->prefix_path_locale);
618 /* check if magic file is there - then our guess is right */
621 DBG("Magic = %s", magic);
622 snprintf(buf, sizeof(buf),
623 "%s" DSEP_S "%s" DSEP_S "%s",
624 pfx->prefix_path, datadir, magic);
625 DBG("Check in %s", buf);
627 if ((!magic) || (stat(buf, &st) == 0))
630 DBG("Magic path %s stat passed", buf);
632 DBG("No magic file");
633 snprintf(buf, sizeof(buf), "%s" DSEP_S "%s",
634 pfx->prefix_path, datadir);
635 STRDUP_REP(pfx->prefix_path_data, buf);
637 /* magic file not there. time to start hunting! */
642 for (;p > pfx->exe_path; p--)
650 if (p > pfx->exe_path)
653 DBG("Go back one directory");
657 _fallback(pfx, pkg_bin, pkg_lib, pkg_data,
658 pkg_locale, envprefix);
663 WRN("No Prefix path (alloc fail)");
664 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale,
672 WRN("Final fallback");
673 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, envprefix);
678 eina_prefix_free(Eina_Prefix *pfx)
682 IF_FREE_NULL(pfx->exe_path);
683 IF_FREE_NULL(pfx->prefix_path);
684 IF_FREE_NULL(pfx->prefix_path_bin);
685 IF_FREE_NULL(pfx->prefix_path_data);
686 IF_FREE_NULL(pfx->prefix_path_lib);
687 IF_FREE_NULL(pfx->prefix_path_locale);
692 eina_prefix_get(Eina_Prefix *pfx)
695 return pfx->prefix_path;
699 eina_prefix_bin_get(Eina_Prefix *pfx)
702 return pfx->prefix_path_bin;
706 eina_prefix_lib_get(Eina_Prefix *pfx)
709 return pfx->prefix_path_lib;
713 eina_prefix_data_get(Eina_Prefix *pfx)
716 return pfx->prefix_path_data;
720 eina_prefix_locale_get(Eina_Prefix *pfx)
723 return pfx->prefix_path_locale;
727 eina_prefix_init(void)
729 _eina_prefix_log_dom = eina_log_domain_register("eina_prefix",
730 EINA_LOG_COLOR_DEFAULT);
731 if (_eina_prefix_log_dom < 0)
733 EINA_LOG_ERR("Could not register log domain: eina_prefix");
741 eina_prefix_shutdown(void)
743 eina_log_domain_unregister(_eina_prefix_log_dom);
744 _eina_prefix_log_dom = -1;