move around - flatter.
[framework/uifw/embryo.git] / src / bin / embryo_cc_prefix.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <dirent.h>
15 #include <sys/stat.h>
16 #include <sys/time.h>
17 #include <sys/param.h>
18 #include <math.h>
19 #include <fnmatch.h>
20 #include <limits.h>
21 #include <ctype.h>
22 #include <time.h>
23 #include <dirent.h>
24 #include <dlfcn.h>      /* dlopen,dlclose,etc */
25 #ifdef HAVE_EVIL
26 # include <Evil.h>      /* for realpath */
27 #else
28 # include <pwd.h>
29 # include <grp.h>
30 # include <glob.h>
31 #endif /* ! HAVE_EVIL */
32
33 #include "embryo_cc_prefix.h"
34
35 /* local subsystem functions */
36 static int _e_prefix_share_hunt(void);
37 static int _e_prefix_fallbacks(void);
38 static int _e_prefix_try_proc(void);
39 static int _e_prefix_try_argv(char *argv0);
40
41 /* local subsystem globals */
42 static char *_exe_path = NULL;
43 static char *_prefix_path = NULL;
44 static char *_prefix_path_bin = NULL;
45 static char *_prefix_path_data = NULL;
46 static char *_prefix_path_lib = NULL;
47
48 #define E_FREE(p) { if (p) {free(p); p = NULL;} }
49
50 /*#define PREFIX_CACHE_FILE 1*/
51 #define SHARE_D "share/embryo"
52 #define MAGIC_FILE "include/default.inc"
53 #define MAGIC_DAT SHARE_D"/"MAGIC_FILE
54
55 /* externally accessible functions */
56 int
57 e_prefix_determine(char *argv0)
58 {
59    char *p, buf[4096];
60    struct stat st;
61
62    e_prefix_shutdown();
63
64    /* if user provides E_PREFIX - then use that or also more specific sub
65     * dirs for bin, lib, data and locale */
66    if (getenv("E_PREFIX"))
67      {
68         _prefix_path = strdup(getenv("E_PREFIX"));
69         if (getenv("E_BIN_DIR"))
70           snprintf(buf, sizeof(buf), "%s/bin", getenv("E_BIN_DIR"));
71         else
72           snprintf(buf, sizeof(buf), "%s/bin", _prefix_path);
73         _prefix_path_bin = strdup(buf);
74
75         if (getenv("E_LIB_DIR"))
76           snprintf(buf, sizeof(buf), "%s/lib", getenv("E_LIB_DIR"));
77         else
78           snprintf(buf, sizeof(buf), "%s/lib", _prefix_path);
79         _prefix_path_lib = strdup(buf);
80
81         if (getenv("E_DATA_DIR"))
82           snprintf(buf, sizeof(buf), "%s/"SHARE_D, getenv("E_DATA_DIR"));
83         else
84           snprintf(buf, sizeof(buf), "%s/"SHARE_D, _prefix_path);
85         _prefix_path_data = strdup(buf);
86         return 1;
87      }
88    /* no env var - examine process and possible argv0 */
89    if (!_e_prefix_try_proc())
90      {
91         if (!_e_prefix_try_argv(argv0))
92           {
93              _e_prefix_fallbacks();
94              return 0;
95           }
96      }
97    /* _exe_path is now a full absolute path TO this exe - figure out rest */
98    /*   if
99     * exe        = /blah/whatever/bin/exe
100     *   then
101     * prefix     = /blah/whatever
102     * bin_dir    = /blah/whatever/bin
103     * data_dir   = /blah/whatever/share/enlightenment
104     * lib_dir    = /blah/whatever/lib
105     */
106    p = strrchr(_exe_path, '/');
107    if (p)
108      {
109         p--;
110         while (p >= _exe_path)
111           {
112              if (*p == '/')
113                {
114                   _prefix_path = malloc(p - _exe_path + 1);
115                   if (_prefix_path)
116                     {
117                        strncpy(_prefix_path, _exe_path, p - _exe_path);
118                        _prefix_path[p - _exe_path] = 0;
119
120                        /* bin and lib always together */
121                        snprintf(buf, sizeof(buf), "%s/bin", _prefix_path);
122                        _prefix_path_bin = strdup(buf);
123                        snprintf(buf, sizeof(buf), "%s/lib", _prefix_path);
124                        _prefix_path_lib = strdup(buf);
125
126                        /* check if AUTHORS file is there - then our guess is right */
127                        snprintf(buf, sizeof(buf), "%s/"MAGIC_DAT, _prefix_path);
128                        if (stat(buf, &st) == 0)
129                          {
130                             snprintf(buf, sizeof(buf), "%s/"SHARE_D, _prefix_path);
131                             _prefix_path_data = strdup(buf);
132                          }
133                        /* AUTHORS file not there. time to start hunting! */
134                        else
135                          {
136                             if (_e_prefix_share_hunt())
137                               {
138                                  return 1;
139                               }
140                             else
141                               {
142                                  e_prefix_fallback();
143                                  return 0;
144                               }
145                          }
146                        return 1;
147                     }
148                   else
149                     {
150                        e_prefix_fallback();
151                        return 0;
152                     }
153                }
154              p--;
155           }
156      }
157    e_prefix_fallback();
158    return 0;
159 }
160
161 void
162 e_prefix_shutdown(void)
163 {
164    E_FREE(_exe_path);
165    E_FREE(_prefix_path);
166    E_FREE(_prefix_path_bin);
167    E_FREE(_prefix_path_data);
168    E_FREE(_prefix_path_lib);
169 }
170
171 void
172 e_prefix_fallback(void)
173 {
174    e_prefix_shutdown();
175    _e_prefix_fallbacks();
176 }
177
178 const char *
179 e_prefix_get(void)
180 {
181    return _prefix_path;
182 }
183
184 const char *
185 e_prefix_bin_get(void)
186 {
187    return _prefix_path_bin;
188 }
189
190 const char *
191 e_prefix_data_get(void)
192 {
193    return _prefix_path_data;
194 }
195
196 const char *
197 e_prefix_lib_get(void)
198 {
199    return _prefix_path_lib;
200 }
201
202 /* local subsystem functions */
203 static int
204 _e_prefix_share_hunt(void)
205 {
206    char buf[4096], buf2[4096], *p;
207    struct stat st;
208
209    /* sometimes this isnt the case - so we need to do a more exhaustive search
210     * through more parent and subdirs. hre is an example i have seen:
211     *
212     * /blah/whatever/exec/bin/exe
213     * ->
214     * /blah/whatever/exec/bin
215     * /blah/whatever/common/share/enlightenment
216     * /blah/whatever/exec/lib
217     */
218
219    /* this is pure black magic to try and find data shares */
220    /* 2. cache file doesn't exist or is invalid - we need to search - this is
221     * where the real black magic begins */
222
223    /* BLACK MAGIC 1:
224     * /blah/whatever/dir1/bin
225     * /blah/whatever/dir2/share/enlightenment
226     */
227    if (!_prefix_path_data)
228      {
229         DIR                *dirp;
230         struct dirent      *dp;
231
232         snprintf(buf, sizeof(buf), "%s", _prefix_path);
233         p = strrchr(buf, '/');
234         if (p) *p = 0;
235         dirp = opendir(buf);
236         if (dirp)
237           {
238              char *file;
239
240              while ((dp = readdir(dirp)))
241                {
242                   if ((strcmp(dp->d_name, ".")) && (strcmp(dp->d_name, "..")))
243                     {
244                        file = dp->d_name;
245                        snprintf(buf2, sizeof(buf2), "%s/%s/"MAGIC_DAT, buf, file);
246                        if (stat(buf2, &st) == 0)
247                          {
248                             snprintf(buf2, sizeof(buf2), "%s/%s/"SHARE_D, buf, file);
249                             _prefix_path_data = strdup(buf2);
250                             break;
251                          }
252                     }
253                }
254              closedir(dirp);
255           }
256      }
257
258    /* BLACK MAGIC 2:
259     * /blah/whatever/dir1/bin
260     * /blah/whatever/share/enlightenment
261     */
262    if (!_prefix_path_data)
263      {
264         snprintf(buf, sizeof(buf), "%s", _prefix_path);
265         p = strrchr(buf, '/');
266         if (p) *p = 0;
267         snprintf(buf2, sizeof(buf2), "%s/"MAGIC_DAT, buf);
268         if (stat(buf, &st) == 0)
269           {
270              snprintf(buf2, sizeof(buf2), "%s/"SHARE_D, buf);
271              _prefix_path_data = strdup(buf2);
272           }
273      }
274
275    /* add more black magic as required as we discover weridnesss - remember
276     * this is to save users having to set environment variables to tell
277     * e where it lives, so e auto-adapts. so these code snippets are just
278     * logic to figure that out for the user
279     */
280
281    /* done. we found it - write cache file */
282    if (_prefix_path_data)
283      {
284         return 1;
285      }
286    /* fail. everything failed */
287    return 0;
288 }
289
290 static int
291 _e_prefix_fallbacks(void)
292 {
293    char *p;
294
295    _prefix_path = strdup(PACKAGE_BIN_DIR);
296    p = strrchr(_prefix_path, '/');
297    if (p) *p = 0;
298    _prefix_path_bin    = strdup(PACKAGE_BIN_DIR);
299    _prefix_path_data   = strdup(PACKAGE_DATA_DIR);
300    _prefix_path_lib    = strdup(PACKAGE_LIB_DIR);
301    printf("WARNING: Embryo could not determine its installed prefix\n"
302           "         and is falling back on the compiled in default:\n"
303           "           %s\n"
304           "         You might like to try setting the following environment variables:\n"
305           "           E_PREFIX     - points to the base prefix of install\n"
306           "           E_BIN_DIR    - optional in addition to E_PREFIX to provide\n"
307           "                          a more specific binary directory\n"
308           "           E_LIB_DIR    - optional in addition to E_PREFIX to provide\n"
309           "                          a more specific library dir\n"
310           "           E_DATA_DIR   - optional in addition to E_PREFIX to provide\n"
311           "                          a more specific location for shared data\n"
312           ,
313           _prefix_path);
314    return 1;
315 }
316
317 static int
318 _e_prefix_try_proc(void)
319 {
320    FILE *f;
321    char buf[4096];
322    void *func = NULL;
323
324    func = (void *)_e_prefix_try_proc;
325    f = fopen("/proc/self/maps", "r");
326    if (!f) return 0;
327    while (fgets(buf, sizeof(buf), f))
328      {
329         int len;
330         char *p, mode[5] = "";
331         unsigned long ptr1 = 0, ptr2 = 0;
332
333         len = strlen(buf);
334         if (buf[len - 1] == '\n')
335           {
336              buf[len - 1] = 0;
337              len--;
338           }
339         if (sscanf(buf, "%lx-%lx %4s", &ptr1, &ptr2, mode) == 3)
340           {
341              if (!strcmp(mode, "r-xp"))
342                {
343                   if (((void *)ptr1 <= func) && (func < (void *)ptr2))
344                     {
345                        p = strchr(buf, '/');
346                        if (p)
347                          {
348                             if (len > 10)
349                               {
350                                  if (!strcmp(buf + len - 10, " (deleted)"))
351                                    buf[len - 10] = 0;
352                               }
353                             _exe_path = strdup(p);
354                             fclose(f);
355                             return 1;
356                          }
357                        else
358                          break;
359                     }
360                }
361           }
362      }
363    fclose(f);
364    return 0;
365 }
366
367 static int
368 _e_prefix_try_argv(char *argv0)
369 {
370    char *path, *p, *cp, *s;
371    int len, lenexe;
372    char buf[4096], buf2[4096], buf3[4096];
373
374    /* 1. is argv0 abs path? */
375    if (argv0[0] == '/')
376      {
377         _exe_path = strdup(argv0);
378         if (access(_exe_path, X_OK) == 0) return 1;
379         free(_exe_path);
380         _exe_path = NULL;
381         return 0;
382      }
383    /* 2. relative path */
384    if (strchr(argv0, '/'))
385      {
386         if (getcwd(buf3, sizeof(buf3)))
387           {
388              snprintf(buf2, sizeof(buf2), "%s/%s", buf3, argv0);
389              if (realpath(buf2, buf))
390                {
391                   _exe_path = strdup(buf);
392                   if (access(_exe_path, X_OK) == 0) return 1;
393                   free(_exe_path);
394                   _exe_path = NULL;
395                }
396           }
397      }
398    /* 3. argv0 no path - look in PATH */
399    path = getenv("PATH");
400    if (!path) return 0;
401    p = path;
402    cp = p;
403    lenexe = strlen(argv0);
404    while ((p = strchr(cp, ':')))
405      {
406         len = p - cp;
407         s = malloc(len + 1 + lenexe + 1);
408         if (s)
409           {
410              strncpy(s, cp, len);
411              s[len] = '/';
412              strcpy(s + len + 1, argv0);
413              if (realpath(s, buf))
414                {
415                   if (access(buf, X_OK) == 0)
416                     {
417                        _exe_path = strdup(buf);
418                        free(s);
419                        return 1;
420                     }
421                }
422              free(s);
423           }
424         cp = p + 1;
425      }
426    len = strlen(cp);
427    s = malloc(len + 1 + lenexe + 1);
428    if (s)
429      {
430         strncpy(s, cp, len);
431         s[len] = '/';
432         strcpy(s + len + 1, argv0);
433         if (realpath(s, buf))
434           {
435              if (access(buf, X_OK) == 0)
436                {
437                   _exe_path = strdup(buf);
438                   free(s);
439                   return 1;
440                }
441           }
442         free(s);
443      }
444    /* 4. big problems. arg[0] != executable - weird execution */
445    return 0;
446 }