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