1 /* EINA - EFL data type library
2 * Copyright (C) 2011 Carsten Haitzler
3 *
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.
8 *
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.
13 *
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/>.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #ifdef STDC_HEADERS
24 # include <stdlib.h>
25 # include <stddef.h>
26 #else
27 # ifdef HAVE_STDLIB_H
28 # include <stdlib.h>
29 # endif
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <limits.h>
39
40 #if defined HAVE_DLADDR && ! defined _WIN32
41 # include <dlfcn.h>
42 #endif
43
44 #ifdef _WIN32
45 # include <direct.h> /* getcwd */
46 # include <evil_private.h> /* path_is_absolute realpath dladdr */
47 #endif
48
49 #include "eina_config.h"
50 #include "eina_private.h"
51 #include "eina_alloca.h"
52 #include "eina_log.h"
53 #include "eina_str.h"
54 #include "eina_file.h"
55
56 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
57 #include "eina_safety_checks.h"
58 #include "eina_prefix.h"
59
60 #ifdef _WIN32
61 # define PSEP_C ';'
62 #else
63 # define PSEP_C ':'
64 #endif /* _WIN32 */
65
66 /*============================================================================*
67 * Local *
68 *============================================================================*/
69
70 /**
71 * @cond LOCAL
72 */
73
74 struct _Eina_Prefix
75 {
76 char *exe_path;
77
78 char *prefix_path;
79 char *prefix_path_bin;
80 char *prefix_path_data;
81 char *prefix_path_lib;
82 char *prefix_path_locale;
83
84 unsigned char fallback : 1;
85 unsigned char no_common_prefix : 1;
86 unsigned char env_used : 1;
87 };
88
89 #define STRDUP_REP(x, y) do { if (x) free(x); x = strdup(y); } while (0)
90 #define IF_FREE_NULL(p) do { if (p) { free(p); p = NULL; } } while (0)
91
92 #ifndef EINA_LOG_COLOR_DEFAULT
93 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
94 #endif
95
96 #ifdef ERR
97 #undef ERR
98 #endif
99 #define ERR(...) EINA_LOG_DOM_ERR(_eina_prefix_log_dom, __VA_ARGS__)
100
101 #ifdef WRN
102 #undef WRN
103 #endif
104 #define WRN(...) EINA_LOG_DOM_WARN(_eina_prefix_log_dom, __VA_ARGS__)
105
106 #ifdef INF
107 #undef INF
108 #endif
109 #define INF(...) EINA_LOG_DOM_INFO(_eina_prefix_log_dom, __VA_ARGS__)
110
111 #ifdef DBG
112 #undef DBG
113 #endif
114 #define DBG(...) EINA_LOG_DOM_DBG(_eina_prefix_log_dom, __VA_ARGS__)
115
116 static int _eina_prefix_log_dom = -1;
117
118 static int
_path_join_multiple(char * buf,int bufsize,...)119 _path_join_multiple(char *buf, int bufsize, ...)
120 {
121 va_list ap;
122 int used = 0;
123
124 EINA_SAFETY_ON_NULL_RETURN_VAL(buf, -1);
125 EINA_SAFETY_ON_TRUE_RETURN_VAL(bufsize < 1, -1);
126
127 va_start(ap, bufsize);
128 while (used < bufsize - 1)
129 {
130 const char *comp = va_arg(ap, const char *);
131 int complen, seplen;
132 if (!comp) break;
133
134
135 seplen = (used == 0) ? 0 : 1;
136 complen = strlen(comp);
137 if (seplen + complen >= bufsize -1)
138 {
139 va_end(ap);
140 buf[0] = '\0';
141 return -1;
142 }
143
144 if (used > 0)
145 buf[used] = EINA_PATH_SEP_C;
146
147 memcpy(buf + used + seplen, comp, complen);
148 used += complen + seplen;
149 }
150 va_end(ap);
151 buf[used] = '\0';
152 return used;
153 }
154
155 static void
_path_sep_fix(char * buf)156 _path_sep_fix(char *buf)
157 {
158 #ifdef _WIN32
159 for (; *buf != '\0'; buf++)
160 {
161 if (*buf == '/')
162 *buf = EINA_PATH_SEP_C;
163 }
164 #else
165 (void)buf;
166 #endif
167 }
168
169 static Eina_Bool
_path_absolute_check(const char * path)170 _path_absolute_check(const char *path)
171 {
172 #ifdef _WIN32
173 return evil_path_is_absolute(path);
174 #else
175 return (path[0] == EINA_PATH_SEP_C);
176 #endif
177 }
178
179 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)180 _fallback(Eina_Prefix *pfx, const char *pkg_bin, const char *pkg_lib,
181 const char *pkg_data, const char *pkg_locale, const char *envprefix)
182 {
183 char *p;
184
185 STRDUP_REP(pfx->prefix_path, pkg_bin);
186 if (!pfx->prefix_path) return 0;
187 p = strrchr(pfx->prefix_path, EINA_PATH_SEP_C);
188 if (p) *p = 0;
189 STRDUP_REP(pfx->prefix_path_bin, pkg_bin);
190 STRDUP_REP(pfx->prefix_path_lib, pkg_lib);
191 STRDUP_REP(pfx->prefix_path_data, pkg_data);
192 STRDUP_REP(pfx->prefix_path_locale, pkg_locale);
193 WRN("Could not determine its installed prefix for '%s'\n"
194 " so am falling back on the compiled in default:\n"
195 " %s\n"
196 " implied by the following:\n"
197 " bindir = %s\n"
198 " libdir = %s\n"
199 " datadir = %s\n"
200 " localedir = %s\n"
201 " Try setting the following environment variables:\n"
202 " %s_PREFIX - points to the base prefix of install\n"
203 " or the next 4 variables\n"
204 " %s_BIN_DIR - provide a specific binary directory\n"
205 " %s_LIB_DIR - provide a specific library directory\n"
206 " %s_DATA_DIR - provide a specific data directory\n"
207 " %s_LOCALE_DIR - provide a specific locale directory",
208 envprefix,
209 pfx->prefix_path, pkg_bin, pkg_lib, pkg_data, pkg_locale,
210 envprefix, envprefix, envprefix, envprefix, envprefix);
211 pfx->fallback = 1;
212 return 1;
213 }
214
215 static int
_try_proc(Eina_Prefix * pfx,void * symbol)216 _try_proc(Eina_Prefix *pfx, void *symbol)
217 {
218 #ifndef _WIN32
219 FILE *f;
220 char buf[4096];
221
222 f = fopen("/proc/self/maps", "rb");
223 if (!f)
224 {
225 WRN("Couldn't read /proc/self/maps to lookup symbol=%p", symbol);
226 return 0;
227 }
228 DBG("Check /proc/self/maps for symbol=%p", symbol);
229 while (fgets(buf, sizeof(buf), f))
230 {
231 int len;
232 char *p, mode[5] = "";
233 unsigned long ptr1 = 0, ptr2 = 0;
234
235 len = strlen(buf);
236 if (buf[len - 1] == '\n')
237 {
238 buf[len - 1] = 0;
239 len--;
240 }
241 if (sscanf(buf, "%lx-%lx %4s", &ptr1, &ptr2, mode) == 3)
242 {
243 if (!strcmp(mode, "r-xp"))
244 {
245 if (((void *)ptr1 <= symbol) && (symbol < (void *)ptr2))
246 {
247 p = strchr(buf, '/');
248 if (p)
249 {
250 if (len > 10)
251 {
252 if (!strcmp(buf + len - 10, " (deleted)"))
253 buf[len - 10] = 0;
254 }
255 STRDUP_REP(pfx->exe_path, p);
256 INF("Found %p in /proc/self/maps: %s (%s)", symbol, pfx->exe_path, buf);
257 fclose(f);
258 return 1;
259 }
260 else
261 {
262 DBG("Found %p in /proc/self/maps but not a file (%s)", symbol, buf);
263 break;
264 }
265 }
266 }
267 }
268 }
269 fclose(f);
270 WRN("Couldn't find symbol %p in a file in /proc/self/maps", symbol);
271 return 0;
272 #else
273 return 0;
274 (void)pfx;
275 (void)symbol;
276 #endif
277 }
278
279 static int
_try_argv(Eina_Prefix * pfx,const char * argv0)280 _try_argv(Eina_Prefix *pfx, const char *argv0)
281 {
282 char *path, *p, *cp;
283 int len, lenexe;
284 char buf[PATH_MAX], buf2[PATH_MAX];
285
286 /* 1. is argv0 abs path? */
287 if (_path_absolute_check(argv0))
288 {
289 if (access(argv0, X_OK) == 0)
290 {
291 INF("Executable argv0 is full path = %s", argv0);
292 STRDUP_REP(pfx->exe_path, argv0);
293 return 1;
294 }
295 WRN("Non executable argv0: %s", argv0);
296 return 0;
297 }
298
299 /* 2. relative path */
300 if (strchr(argv0, EINA_PATH_SEP_C))
301 {
302 if (getcwd(buf2, sizeof(buf2)))
303 {
304 size_t len = strlen(buf2) + 1 + strlen(argv0) + 1;
305 char *joined = alloca(len);
306 eina_file_path_join(joined, len, buf2, argv0);
307 if (realpath(joined, buf))
308 {
309 if (access(buf, X_OK) == 0)
310 {
311 INF("Executable relative argv0=%s, cwd=%s, realpath=%s",
312 argv0, buf2, buf);
313 STRDUP_REP(pfx->exe_path, buf);
314 return 1;
315 }
316 WRN("Non executable relative argv0=%s, cwd=%s, realpath=%s",
317 argv0, buf2, buf);
318 }
319 else
320 WRN("No realpath for argv0=%s, cwd=%s", argv0, buf2);
321 }
322 else
323 WRN("Couldn't get current directory to lookup argv0=%s", argv0);
324 }
325
326 /* 3. argv0 no path - look in PATH */
327 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
328 if (getuid() != geteuid()) return 0;
329 #endif
330 path = getenv("PATH");
331 if (!path)
332 {
333 DBG("No env PATH to lookup argv0=%s", argv0);
334 return 0;
335 }
336 p = path;
337 cp = p;
338 lenexe = strlen(argv0);
339 while ((p = strchr(cp, PSEP_C)))
340 {
341 len = p - cp;
342 if ((len == 0) || (len + lenexe + 2 >= (int)sizeof(buf2)))
343 {
344 cp = p + 1;
345 continue;
346 }
347
348 strncpy(buf2, cp, len);
349 buf2[len] = EINA_PATH_SEP_C;
350 strcpy(buf2 + len + 1, argv0);
351 if (realpath(buf2, buf))
352 {
353 if (access(buf, X_OK) == 0)
354 {
355 STRDUP_REP(pfx->exe_path, buf);
356 INF("Path %s is executable", pfx->exe_path);
357 return 1;
358 }
359 else
360 DBG("Path not executable %s", buf);
361 }
362 else
363 DBG("No realpath for argv0=%s in %.*s", argv0, len, cp);
364 cp = p + 1;
365 }
366 /* 4. big problems. arg[0] != executable - weird execution */
367 WRN("Couldn't find argv0=%s in current directory or env PATH=%s",
368 argv0, path);
369 return 0;
370 }
371
372 static int
_get_env_var(char ** var,const char * envprefix,const char * envsuffix,const char * prefix,const char * dir)373 _get_env_var(char **var, const char *envprefix, const char *envsuffix, const char *prefix, const char *dir)
374 {
375 char env[64];
376 const char *s;
377
378 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
379 if (getuid() != geteuid()) return 0;
380 #endif
381 snprintf(env, sizeof(env), "%s_%s_DIR", envprefix, envsuffix);
382 s = getenv(env);
383 if (s)
384 {
385 INF("Have prefix env %s = %s", env, s);
386 STRDUP_REP(*var, s);
387 return 1;
388 }
389 else if (prefix)
390 {
391 size_t len = strlen(prefix) + 1 + strlen(dir) + 1;
392 char *buf;
393
394 buf = alloca(len);
395 eina_file_path_join(buf, len, prefix, dir);
396 INF("Have %s_PREFIX = %s, use %s = %s", envprefix, prefix, env, buf);
397 STRDUP_REP(*var, buf);
398 return 1;
399 }
400 else
401 {
402 DBG("No env %s_PREFIX or %s for dir '%s'", envprefix, env, dir);
403 STRDUP_REP(*var, "");
404 return 0;
405 }
406 }
407
408 static int
_get_env_vars(Eina_Prefix * pfx,const char * envprefix,const char * bindir,const char * libdir,const char * datadir,const char * localedir)409 _get_env_vars(Eina_Prefix *pfx,
410 const char *envprefix,
411 const char *bindir,
412 const char *libdir,
413 const char *datadir,
414 const char *localedir)
415 {
416 char env[32];
417 const char *prefix;
418 int ret = 0;
419
420 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
421 if (getuid() == geteuid())
422 #endif
423 {
424 snprintf(env, sizeof(env), "%s_PREFIX", envprefix);
425 if ((prefix = getenv(env))) STRDUP_REP(pfx->prefix_path, prefix);
426
427 ret += _get_env_var(&pfx->prefix_path_bin, envprefix, "BIN", prefix, bindir);
428 ret += _get_env_var(&pfx->prefix_path_lib, envprefix, "LIB", prefix, libdir);
429 ret += _get_env_var(&pfx->prefix_path_data, envprefix, "DATA", prefix, datadir);
430 ret += _get_env_var(&pfx->prefix_path_locale, envprefix, "LOCALE", prefix, localedir);
431 }
432 return ret;
433 }
434
435 static int
_common_prefix_find(const char * bin,const char * lib,const char * data,const char * locale)436 _common_prefix_find(const char *bin, const char *lib, const char *data, const char *locale)
437 {
438 const char *b = bin;
439 const char *i = lib;
440 const char *d = data;
441 const char *o = locale;
442
443 for (; (*b) && (*i) && (*d) && (*o); b++, i++, d++, o++)
444 {
445 if (*b != *i) break;
446 if (*b != *d) break;
447 if (*b != *o) break;
448 }
449 return b - bin;
450 }
451
452 /**
453 * @endcond
454 */
455
456
457 /*============================================================================*
458 * Global *
459 *============================================================================*/
460
461
462 /*============================================================================*
463 * API *
464 *============================================================================*/
465
466
467 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)468 eina_prefix_new(const char *argv0, void *symbol, const char *envprefix,
469 const char *sharedir, const char *magicsharefile,
470 const char *pkg_bin, const char *pkg_lib,
471 const char *pkg_data, const char *pkg_locale)
472 {
473 Eina_Prefix *pfx;
474 char *p, buf[4096], *tmp, *magic = NULL;
475 struct stat st;
476 int prefixlen;
477 const char *bindir = "bin";
478 const char *libdir = "lib";
479 const char *datadir = "share";
480 const char *localedir = "share";
481 Eina_Bool from_lib = EINA_FALSE, from_bin = EINA_FALSE;
482
483 DBG("EINA PREFIX: argv0=%s, symbol=%p, magicsharefile=%s, envprefix=%s",
484 argv0, symbol, magicsharefile, envprefix);
485 DBG("EINA PREFIX: share=%s, bin=%s, lib=%s, data=%s, locale=%s",
486 sharedir, pkg_bin, pkg_lib, pkg_data, pkg_locale);
487
488 EINA_SAFETY_ON_NULL_RETURN_VAL(pkg_bin, NULL);
489 EINA_SAFETY_ON_NULL_RETURN_VAL(pkg_lib, NULL);
490 EINA_SAFETY_ON_NULL_RETURN_VAL(pkg_data, NULL);
491 EINA_SAFETY_ON_NULL_RETURN_VAL(pkg_locale, NULL);
492
493 pfx = calloc(1, sizeof(Eina_Prefix));
494 if (!pfx) return NULL;
495
496 /* if provided with a share dir use datadir/sharedir as the share dir */
497 if (sharedir)
498 {
499 int len;
500
501 len = eina_file_path_join(buf, sizeof(buf), datadir, sharedir);
502 if (len > 0)
503 {
504 _path_sep_fix(buf + strlen(datadir) + strlen(EINA_PATH_SEP_S));
505 tmp = alloca(len + 1);
506 strcpy(tmp, buf);
507 datadir = tmp;
508 }
509 }
510 if (magicsharefile && (!getenv("EFL_RUN_IN_TREE")))
511 {
512 magic = alloca(strlen(magicsharefile) + 1);
513 strcpy(magic, magicsharefile);
514 _path_sep_fix(magic);
515 }
516
517 /* look at compile-time package bin/lib/datadir etc. and figure out the
518 * bin, lib and data dirs from these, if possible. i.e.
519 * bin = /usr/local/bin
520 * lib = /usr/local/lib
521 * data = /usr/local/share/enlightenment
522 * thus they all have a common prefix string of /usr/local/ and
523 * bindir = bin
524 * libdir = lib
525 * datadir = share/enlightenment
526 * this addresses things like libdir is lib64 or lib32 or other such
527 * junk distributions like to do so then:
528 * bin = /usr/local/bin
529 * lib = /usr/local/lib64
530 * data = /usr/local/share/enlightenment
531 * then
532 * bindir = bin
533 * libdir = lib64
534 * datadir = share/enlightennment
535 * in theory this should also work with debians new multiarch style like
536 * bindir = bin
537 * libdir = lib/i386-linux-gnu
538 * or
539 * libdir = lib/x86_64-linux-gnu
540 * all with a common prefix that can be relocated
541 */
542 prefixlen = _common_prefix_find(pkg_bin, pkg_lib, pkg_data, pkg_locale);
543 if (prefixlen > 0)
544 {
545 bindir = pkg_bin + prefixlen;
546 libdir = pkg_lib + prefixlen;
547 datadir = pkg_data + prefixlen;
548 localedir = pkg_locale + prefixlen;
549 DBG("Prefix common=%.*s, bin=%s, lib=%s, data=%s, locale=%s",
550 prefixlen, pkg_bin, bindir, libdir, datadir, localedir);
551 }
552 /* 3. some galoot thought it awesome not to give us a common prefix at compile time
553 * so fall back to the compile time directories. we are no longer relocatable */
554 else
555 {
556 STRDUP_REP(pfx->prefix_path_bin, pkg_bin);
557 STRDUP_REP(pfx->prefix_path_lib, pkg_lib);
558 STRDUP_REP(pfx->prefix_path_data, pkg_data);
559 STRDUP_REP(pfx->prefix_path_locale, pkg_locale);
560 pfx->no_common_prefix = 1;
561 DBG("Can't work out a common prefix - compiled in fallback");
562 }
563
564 /* if user provides env vars - then use that or also more specific sub
565 * dirs for bin, lib, data and locale */
566 if ((envprefix) &&
567 (_get_env_vars(pfx, envprefix, bindir, libdir, datadir, localedir) > 0))
568 {
569 pfx->env_used = 1;
570 return pfx;
571 }
572
573 if (symbol)
574 {
575 #ifdef HAVE_DLADDR
576 Dl_info info_dl;
577
578 if (dladdr(symbol, &info_dl))
579 {
580 if (info_dl.dli_fname)
581 {
582 if (_path_absolute_check(info_dl.dli_fname))
583 {
584 INF("dladdr for symbol=%p: %s", symbol, info_dl.dli_fname);
585 char *rlink = realpath(info_dl.dli_fname, NULL);
586 if (rlink)
587 {
588 IF_FREE_NULL(pfx->exe_path);
589 pfx->exe_path = rlink;
590 }
591 else
592 {
593 STRDUP_REP(pfx->exe_path, info_dl.dli_fname);
594 }
595 from_lib = EINA_TRUE;
596 }
597 else
598 WRN("dladdr for symbol=%p: %s is relative", symbol, info_dl.dli_fname);
599 }
600 else
601 WRN("no dladdr filename for symbol=%p", symbol);
602 }
603 else
604 WRN("no dladdr for symbol=%p", symbol);
605 #endif
606
607 if (!pfx->exe_path)
608 _try_proc(pfx, symbol);
609 /* no from_lib/from_bin as we're not sure it came from lib or bin! */
610 }
611
612 /* no env var or symbol - examine process and possible argv0 */
613 if ((argv0) && (!pfx->exe_path))
614 {
615 if (!_try_argv(pfx, argv0))
616 {
617 WRN("Fallback - couldn't resolve based on argv0=%s", argv0);
618 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale,
619 envprefix);
620 return pfx;
621 }
622
623 from_bin = EINA_TRUE;
624 }
625
626 if (!pfx->exe_path)
627 {
628 WRN("Fallback - no variables, symbol or argv0 could be used.");
629 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, envprefix);
630 return pfx;
631 }
632 /* _exe_path is now a full absolute path TO this exe - figure out rest */
633 /* if
634 * exe = /blah/whatever/bin/exe
635 * or
636 * exe = /blah/whatever/lib/libexe.so
637 * then
638 * prefix = /blah/whatever
639 * bin_dir = /blah/whatever/bin
640 * data_dir = /blah/whatever/share/enlightenment
641 * lib_dir = /blah/whatever/lib
642 *
643 * new case - debian multiarch goop.
644 * exe = /blah/whatever/lib/arch/libexe.so
645 */
646 DBG("From exe %s figure out the rest", pfx->exe_path);
647 p = strrchr(pfx->exe_path, EINA_PATH_SEP_C);
648 if (p)
649 {
650 p--;
651 while (p >= pfx->exe_path)
652 {
653 if (*p == EINA_PATH_SEP_C)
654 {
655 if (pfx->prefix_path) free(pfx->prefix_path);
656 pfx->prefix_path = malloc(p - pfx->exe_path + 1);
657 if (pfx->prefix_path)
658 {
659 Eina_Bool magic_found = EINA_FALSE;
660 int checks_passed = 0;
661
662 strncpy(pfx->prefix_path, pfx->exe_path,
663 p - pfx->exe_path);
664 pfx->prefix_path[p - pfx->exe_path] = 0;
665 DBG("Have prefix = %s", pfx->prefix_path);
666
667 /* bin */
668 eina_file_path_join(buf, sizeof(buf), pfx->prefix_path, bindir);
669 STRDUP_REP(pfx->prefix_path_bin, buf);
670 DBG("Have bin = %s", pfx->prefix_path_bin);
671 if ((!from_bin) && (stat(buf, &st) == 0))
672 checks_passed++;
673
674 /* lib */
675 eina_file_path_join(buf, sizeof(buf), pfx->prefix_path, libdir);
676 STRDUP_REP(pfx->prefix_path_lib, buf);
677 DBG("Have lib = %s", pfx->prefix_path_lib);
678 if ((!from_lib) && (stat(buf, &st) == 0))
679 checks_passed++;
680
681 /* locale */
682 eina_file_path_join(buf, sizeof(buf), pfx->prefix_path, localedir);
683 STRDUP_REP(pfx->prefix_path_locale, buf);
684 DBG("Have locale = %s", pfx->prefix_path_locale);
685 if (stat(buf, &st) == 0)
686 checks_passed++;
687
688 /* check if magic file is there - then our guess is right */
689 if (!magic)
690 DBG("No magic file");
691 else
692 {
693 DBG("Magic = %s", magic);
694 _path_join_multiple(buf, sizeof(buf),
695 pfx->prefix_path,
696 datadir,
697 magic, NULL);
698 DBG("Check in %s", buf);
699
700 if (stat(buf, &st) == 0)
701 {
702 checks_passed++;
703 magic_found = EINA_TRUE;
704 DBG("Magic path %s stat passed", buf);
705 }
706 else
707 WRN("Missing magic path %s", buf);
708 }
709
710 if (((!magic) && (checks_passed > 0)) ||
711 ((magic) && (magic_found)))
712 {
713 eina_file_path_join(buf, sizeof(buf), pfx->prefix_path, datadir);
714 STRDUP_REP(pfx->prefix_path_data, buf);
715 }
716 else
717 {
718 for (;p > pfx->exe_path; p--)
719 {
720 if (*p == EINA_PATH_SEP_C)
721 {
722 p--;
723 break;
724 }
725 }
726 if (p > pfx->exe_path)
727 {
728 int newlen = p - pfx->exe_path;
729 DBG("Go back one directory (%.*s)", newlen, pfx->exe_path);
730 continue;
731 }
732 WRN("No Prefix path (exhausted search depth)");
733 _fallback(pfx, pkg_bin, pkg_lib, pkg_data,
734 pkg_locale, envprefix);
735 }
736 }
737 else
738 {
739 WRN("No Prefix path (alloc fail)");
740 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale,
741 envprefix);
742 }
743 return pfx;
744 }
745 p--;
746 }
747 }
748 WRN("Final fallback");
749 _fallback(pfx, pkg_bin, pkg_lib, pkg_data, pkg_locale, envprefix);
750 return pfx;
751 }
752
753 EAPI void
eina_prefix_free(Eina_Prefix * pfx)754 eina_prefix_free(Eina_Prefix *pfx)
755 {
756 EINA_SAFETY_ON_NULL_RETURN(pfx);
757
758 IF_FREE_NULL(pfx->exe_path);
759 IF_FREE_NULL(pfx->prefix_path);
760 IF_FREE_NULL(pfx->prefix_path_bin);
761 IF_FREE_NULL(pfx->prefix_path_data);
762 IF_FREE_NULL(pfx->prefix_path_lib);
763 IF_FREE_NULL(pfx->prefix_path_locale);
764 free(pfx);
765 }
766
767 EAPI const char *
eina_prefix_get(Eina_Prefix * pfx)768 eina_prefix_get(Eina_Prefix *pfx)
769 {
770 EINA_SAFETY_ON_NULL_RETURN_VAL(pfx, "");
771 return pfx->prefix_path;
772 }
773
774 EAPI const char *
eina_prefix_bin_get(Eina_Prefix * pfx)775 eina_prefix_bin_get(Eina_Prefix *pfx)
776 {
777 EINA_SAFETY_ON_NULL_RETURN_VAL(pfx, "");
778 return pfx->prefix_path_bin;
779 }
780
781 EAPI const char *
eina_prefix_lib_get(Eina_Prefix * pfx)782 eina_prefix_lib_get(Eina_Prefix *pfx)
783 {
784 EINA_SAFETY_ON_NULL_RETURN_VAL(pfx, "");
785 return pfx->prefix_path_lib;
786 }
787
788 EAPI const char *
eina_prefix_data_get(Eina_Prefix * pfx)789 eina_prefix_data_get(Eina_Prefix *pfx)
790 {
791 EINA_SAFETY_ON_NULL_RETURN_VAL(pfx, "");
792 return pfx->prefix_path_data;
793 }
794
795 EAPI const char *
eina_prefix_locale_get(Eina_Prefix * pfx)796 eina_prefix_locale_get(Eina_Prefix *pfx)
797 {
798 EINA_SAFETY_ON_NULL_RETURN_VAL(pfx, "");
799 return pfx->prefix_path_locale;
800 }
801
802 Eina_Bool
eina_prefix_init(void)803 eina_prefix_init(void)
804 {
805 _eina_prefix_log_dom = eina_log_domain_register("eina_prefix",
806 EINA_LOG_COLOR_DEFAULT);
807 if (_eina_prefix_log_dom < 0)
808 {
809 EINA_LOG_ERR("Could not register log domain: eina_prefix");
810 return EINA_FALSE;
811 }
812
813 return EINA_TRUE;
814 }
815
816 Eina_Bool
eina_prefix_shutdown(void)817 eina_prefix_shutdown(void)
818 {
819 eina_log_domain_unregister(_eina_prefix_log_dom);
820 _eina_prefix_log_dom = -1;
821 return EINA_TRUE;
822 }
823