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