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