Graphviz 13.1.2~dev.20250723.2326
Loading...
Searching...
No Matches
gv_find_me.c
Go to the documentation of this file.
1#include <assert.h>
2#include <stdbool.h>
3#include <stdint.h>
4#include <stdlib.h>
5#include <util/gv_find_me.h>
6
7#ifdef __APPLE__
8#include <mach-o/dyld.h>
9#endif
10
11#ifdef __FreeBSD__
12#include <sys/sysctl.h>
13#include <sys/types.h>
14#endif
15
16#if defined(_WIN32) && !defined(__CYGWIN__)
17#include <windows.h>
18#endif
19
20#if !defined(_WIN32)
21#include <unistd.h>
22#endif
23
24#ifndef _WIN32
26static char *readln(const char *path) {
27
28 char *resolved = NULL;
29 size_t size = 0;
30
31 while (true) {
32
33 // expand target buffer
34 {
35 char *const r = realloc(resolved, size == 0 ? 1024 : (size * 2));
36 if (r == NULL) {
37 // failed, out-of-memory
38 free(resolved);
39 return NULL;
40 }
41 resolved = r;
42 size = size == 0 ? 1024 : (size * 2);
43 }
44
45 // attempt to resolve
46 {
47 const ssize_t written = readlink(path, resolved, size);
48 if (written < 0) {
49 break;
50 }
51 if ((size_t)written < size) {
52 // success
53 resolved[written] = '\0';
54 return resolved;
55 }
56 }
57 }
58
59 // failed
60 free(resolved);
61 return NULL;
62}
63#endif
64
65char *gv_find_me(void) {
66
67 // macOS
68#ifdef __APPLE__
69 {
70 // determine how many bytes we will need to allocate
71 uint32_t buf_size = 0;
72 const int rc = _NSGetExecutablePath(NULL, &buf_size);
73 assert(rc != 0);
74 assert(buf_size > 0);
75
76 char *path = calloc(1, buf_size);
77 if (path == NULL) {
78 // failed, out-of-memory
79 return NULL;
80 }
81
82 // retrieve the actual path
83 if (_NSGetExecutablePath(path, &buf_size) < 0) {
84 free(path);
85 return NULL;
86 }
87
88 // try to resolve any levels of symlinks if possible
89 while (true) {
90 char *const buf = readln(path);
91 if (buf == NULL)
92 return path;
93
94 free(path);
95 path = buf;
96 }
97 }
98#elif defined(_WIN32)
99 {
100 char *path = NULL;
101 DWORD path_size = 0;
102 int rc = 0;
103
104 do {
105 {
106 const DWORD size = path_size == 0 ? 1024 : (path_size * 2);
107 char *const p = realloc(path, size);
108 if (p == NULL) {
109 // failed, out-of-memory
110 free(path);
111 return NULL;
112 }
113 path = p;
114 path_size = size;
115 }
116
117 rc = GetModuleFileNameA(NULL, path, path_size);
118 if (rc == 0) {
119 free(path);
120 return NULL;
121 }
122
123 } while ((DWORD)rc == path_size);
124
125 // the Windows APIs return '\'-separated directory components, but MinGW
126 // uses '/' as the canonical directory separator, so convert these
127#ifdef __MINGW32__
128 for (size_t i = 0; path[i] != '\0'; ++i) {
129 if (path[i] == '\\') {
130 path[i] = '/';
131 }
132 }
133#endif
134
135 return path;
136 }
137#else
138
139 // Linux, Cygwin
140 char *path = readln("/proc/self/exe");
141 if (path != NULL)
142 return path;
143
144 // DragonFly BSD, FreeBSD
145 path = readln("/proc/curproc/file");
146 if (path != NULL)
147 return path;
148
149 // NetBSD
150 path = readln("/proc/curproc/exe");
151 if (path != NULL)
152 return path;
153
154// /proc-less FreeBSD
155#ifdef __FreeBSD__
156 {
157 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
158 static const size_t MIB_LENGTH = sizeof(mib) / sizeof(mib[0]);
159
160 do {
161 // determine how long the path is
162 size_t buf_size = 0;
163 if (sysctl(mib, MIB_LENGTH, NULL, &buf_size, NULL, 0) < 0) {
164 break;
165 }
166 assert(buf_size > 0);
167
168 // make enough space for the target path
169 char *buf = calloc(1, buf_size);
170 if (buf == NULL) {
171 // failed, out-of-memory
172 return NULL;
173 }
174
175 // resolve it
176 if (sysctl(mib, MIB_LENGTH, buf, &buf_size, NULL, 0) == 0) {
177 return buf;
178 }
179 free(buf);
180 } while (0);
181 }
182#endif
183#endif
184
185 return NULL;
186}
void free(void *)
node NULL
Definition grammar.y:180
char * gv_find_me(void)
Definition gv_find_me.c:65
static char * readln(const char *path)
readlink-alike but dynamically allocates
Definition gv_find_me.c:26
platform abstraction for finding the path to yourself
Definition types.h:81