]> git.sur5r.net Git - i3/i3/blob - libi3/get_exe_path.c
format **/*.c with clang-format-3.5
[i3/i3] / libi3 / get_exe_path.c
1 #include <unistd.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <limits.h>
5 #include <stdlib.h>
6 #include <errno.h>
7
8 #include "libi3.h"
9
10 /*
11  * This function returns the absolute path to the executable it is running in.
12  *
13  * The implementation follows http://stackoverflow.com/a/933996/712014
14  *
15  * Returned value must be freed by the caller.
16  */
17 char *get_exe_path(const char *argv0) {
18     size_t destpath_size = 1024;
19     size_t tmp_size = 1024;
20     char *destpath = smalloc(destpath_size);
21     char *tmp = smalloc(tmp_size);
22
23 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
24 /* Linux and Debian/kFreeBSD provide /proc/self/exe */
25 #if defined(__linux__) || defined(__FreeBSD_kernel__)
26     const char *exepath = "/proc/self/exe";
27 #elif defined(__FreeBSD__)
28     const char *exepath = "/proc/curproc/file";
29 #endif
30     ssize_t linksize;
31
32     while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
33         destpath_size = destpath_size * 2;
34         destpath = srealloc(destpath, destpath_size);
35     }
36     if (linksize != -1) {
37         /* readlink() does not NULL-terminate strings, so we have to. */
38         destpath[linksize] = '\0';
39         free(tmp);
40         return destpath;
41     }
42 #endif
43
44     /* argv[0] is most likely a full path if it starts with a slash. */
45     if (argv0[0] == '/') {
46         free(tmp);
47         free(destpath);
48         return sstrdup(argv0);
49     }
50
51     /* if argv[0] contains a /, prepend the working directory */
52     if (strchr(argv0, '/') != NULL) {
53         char *retgcwd;
54         while ((retgcwd = getcwd(tmp, tmp_size)) == NULL && errno == ERANGE) {
55             tmp_size = tmp_size * 2;
56             tmp = srealloc(tmp, tmp_size);
57         }
58         if (retgcwd != NULL) {
59             free(destpath);
60             sasprintf(&destpath, "%s/%s", tmp, argv0);
61             free(tmp);
62             return destpath;
63         }
64     }
65
66     /* Fall back to searching $PATH (or _CS_PATH in absence of $PATH). */
67     char *path = getenv("PATH");
68     if (path == NULL) {
69         /* _CS_PATH is typically something like "/bin:/usr/bin" */
70         while (confstr(_CS_PATH, tmp, tmp_size) > tmp_size) {
71             tmp_size = tmp_size * 2;
72             tmp = srealloc(tmp, tmp_size);
73         }
74         sasprintf(&path, ":%s", tmp);
75     } else {
76         path = strdup(path);
77     }
78     const char *component;
79     char *str = path;
80     while (1) {
81         if ((component = strtok(str, ":")) == NULL)
82             break;
83         str = NULL;
84         free(destpath);
85         sasprintf(&destpath, "%s/%s", component, argv0);
86         /* Of course this is not 100% equivalent to actually exec()ing the
87                  * binary, but meh. */
88         if (access(destpath, X_OK) == 0) {
89             free(path);
90             free(tmp);
91             return destpath;
92         }
93     }
94     free(destpath);
95     free(path);
96     free(tmp);
97
98     /* Last resort: maybe it’s in /usr/bin? */
99     return sstrdup("/usr/bin/i3-nagbar");
100 }