1 // (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses
2 // without any warranty.
3 // by Gregory Pakosz (@gpakosz)
4 // https://github.com/gpakosz/whereami
10 #if defined(__linux__)
11 // make realpath() available:
12 #define _DEFAULT_SOURCE
15 #else // WAI_PM3_TUNED
17 // in case you want to #include "whereami.c" in a larger compilation unit
18 #if !defined(WHEREAMI_H)
22 #endif // WAI_PM3_TUNED
28 #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC)
32 #if !defined(WAI_MALLOC)
33 #define WAI_MALLOC(size) malloc(size)
36 #if !defined(WAI_FREE)
37 #define WAI_FREE(p) free(p)
40 #if !defined(WAI_REALLOC)
41 #define WAI_REALLOC(p, size) realloc(p, size)
46 #define WAI_NOINLINE __declspec(noinline)
47 #elif defined(__GNUC__)
48 #define WAI_NOINLINE __attribute__((noinline))
50 #error unsupported compiler
55 #define WAI_RETURN_ADDRESS() _ReturnAddress()
56 #elif defined(__GNUC__)
57 #define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0))
59 #error unsupported compiler
64 #ifndef WIN32_LEAN_AND_MEAN
65 #define WIN32_LEAN_AND_MEAN
68 #pragma warning(push, 3)
76 static int WAI_PREFIX(getModulePath_
)(HMODULE module
, char *out
, int capacity
, int *dirname_length
) {
77 wchar_t buffer1
[MAX_PATH
];
78 wchar_t buffer2
[MAX_PATH
];
84 int length_
, length__
;
86 size
= GetModuleFileNameW(module
, buffer1
, sizeof(buffer1
) / sizeof(buffer1
[0]));
90 else if (size
== (DWORD
)(sizeof(buffer1
) / sizeof(buffer1
[0]))) {
95 path_
= (wchar_t *)WAI_REALLOC(path
, sizeof(wchar_t) * size_
* 2);
100 size
= GetModuleFileNameW(module
, path
, size_
);
101 } while (size
== size_
);
108 if (!_wfullpath(buffer2
, path
, MAX_PATH
))
110 length_
= (int)wcslen(buffer2
);
111 length__
= WideCharToMultiByte(CP_UTF8
, 0, buffer2
, length_
, out
, capacity
, NULL
, NULL
);
114 length__
= WideCharToMultiByte(CP_UTF8
, 0, buffer2
, length_
, NULL
, 0, NULL
, NULL
);
118 if (length__
<= capacity
&& dirname_length
) {
121 for (i
= length__
- 1; i
>= 0; --i
) {
122 if (out
[i
] == '\\') {
140 WAI_NOINLINE WAI_FUNCSPEC
141 int WAI_PREFIX(getExecutablePath
)(char *out
, int capacity
, int *dirname_length
) {
142 return WAI_PREFIX(getModulePath_
)(NULL
, out
, capacity
, dirname_length
);
145 // GetModuleHandleEx() is not available on old mingw environments. We don't need getModulePath() yet.
146 // Sacrifice it for the time being to improve backwards compatibility
147 #ifndef WAI_PM3_TUNED
149 WAI_NOINLINE WAI_FUNCSPEC
150 int WAI_PREFIX(getModulePath
)(char *out
, int capacity
, int *dirname_length
) {
154 #if defined(_MSC_VER)
155 #pragma warning(push)
156 #pragma warning(disable: 4054)
158 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
, (LPCTSTR
)WAI_RETURN_ADDRESS(), &module
))
159 #if defined(_MSC_VER)
163 length
= WAI_PREFIX(getModulePath_
)(module
, out
, capacity
, dirname_length
);
169 #endif // WAI_PM3_TUNED
171 #elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE)
176 #if defined(__linux__)
177 #include <linux/limits.h>
181 #ifndef __STDC_FORMAT_MACROS
182 #define __STDC_FORMAT_MACROS
184 #include <inttypes.h>
186 #if !defined(WAI_PROC_SELF_EXE)
188 #define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
190 #define WAI_PROC_SELF_EXE "/proc/self/exe"
195 int WAI_PREFIX(getExecutablePath
)(char *out
, int capacity
, int *dirname_length
) {
196 char buffer
[PATH_MAX
];
197 char *resolved
= NULL
;
201 resolved
= realpath(WAI_PROC_SELF_EXE
, buffer
);
205 length
= (int)strlen(resolved
);
206 if (length
<= capacity
) {
207 memcpy(out
, resolved
, length
);
209 if (dirname_length
) {
212 for (i
= length
- 1; i
>= 0; --i
) {
227 #if !defined(WAI_PROC_SELF_MAPS_RETRY)
228 #define WAI_PROC_SELF_MAPS_RETRY 5
231 #if !defined(WAI_PROC_SELF_MAPS)
233 #define WAI_PROC_SELF_MAPS "/proc/self/map"
235 #define WAI_PROC_SELF_MAPS "/proc/self/maps"
239 #if defined(__ANDROID__) || defined(ANDROID)
241 #include <sys/mman.h>
245 #ifndef WAI_PM3_TUNED
247 WAI_NOINLINE WAI_FUNCSPEC
248 int WAI_PREFIX(getModulePath
)(char *out
, int capacity
, int *dirname_length
) {
252 for (int r
= 0; r
< WAI_PROC_SELF_MAPS_RETRY
; ++r
) {
253 maps
= fopen(WAI_PROC_SELF_MAPS
, "r");
258 char buffer
[PATH_MAX
< 1024 ? 1024 : PATH_MAX
];
262 uint32_t major
, minor
;
266 if (!fgets(buffer
, sizeof(buffer
), maps
))
269 if (sscanf(buffer
, "%" PRIx64
"-%" PRIx64
" %s %" PRIx64
" %x:%x %u %s\n", &low
, &high
, perms
, &offset
, &major
, &minor
, &inode
, path
) == 8) {
270 uint64_t addr
= (uintptr_t)WAI_RETURN_ADDRESS();
271 if (low
<= addr
&& addr
<= high
) {
274 resolved
= realpath(path
, buffer
);
278 length
= (int)strlen(resolved
);
279 #if defined(__ANDROID__) || defined(ANDROID)
281 && buffer
[length
- 1] == 'k'
282 && buffer
[length
- 2] == 'p'
283 && buffer
[length
- 3] == 'a'
284 && buffer
[length
- 4] == '.') {
285 int fd
= open(path
, O_RDONLY
);
289 begin
= (char *)mmap(0, offset
+ sizeof(p
), PROT_READ
, MAP_SHARED
, fd
, 0);
292 while (p
>= begin
) { // scan backwards
293 if (*((uint32_t *)p
) == 0x04034b50UL
) { // local file header found
294 uint16_t length_
= *((uint16_t *)(p
+ 26));
296 if (length
+ 2 + length_
< (int)sizeof(buffer
)) {
297 memcpy(&buffer
[length
], "!/", 2);
298 memcpy(&buffer
[length
+ 2], p
+ 30, length_
);
299 length
+= 2 + length_
;
308 munmap(begin
, offset
);
312 if (length
<= capacity
) {
313 memcpy(out
, resolved
, length
);
315 if (dirname_length
) {
318 for (i
= length
- 1; i
>= 0; --i
) {
344 #endif // WAI_PM3_TUNED
346 #elif defined(__APPLE__)
348 #define _DARWIN_BETTER_REALPATH
349 #include <mach-o/dyld.h>
356 int WAI_PREFIX(getExecutablePath
)(char *out
, int capacity
, int *dirname_length
) {
357 char buffer1
[PATH_MAX
];
358 char buffer2
[PATH_MAX
];
359 char *path
= buffer1
;
360 char *resolved
= NULL
;
364 uint32_t size
= (uint32_t)sizeof(buffer1
);
365 if (_NSGetExecutablePath(path
, &size
) == -1) {
366 path
= (char *)WAI_MALLOC(size
);
367 if (!_NSGetExecutablePath(path
, &size
))
371 resolved
= realpath(path
, buffer2
);
375 length
= (int)strlen(resolved
);
376 if (length
<= capacity
) {
377 memcpy(out
, resolved
, length
);
379 if (dirname_length
) {
382 for (i
= length
- 1; i
>= 0; --i
) {
400 #ifndef WAI_PM3_TUNED
402 WAI_NOINLINE WAI_FUNCSPEC
403 int WAI_PREFIX(getModulePath
)(char *out
, int capacity
, int *dirname_length
) {
404 char buffer
[PATH_MAX
];
405 char *resolved
= NULL
;
411 if (dladdr(WAI_RETURN_ADDRESS(), &info
)) {
412 resolved
= realpath(info
.dli_fname
, buffer
);
416 length
= (int)strlen(resolved
);
417 if (length
<= capacity
) {
418 memcpy(out
, resolved
, length
);
420 if (dirname_length
) {
423 for (i
= length
- 1; i
>= 0; --i
) {
439 #endif // WAI_PM3_TUNED
441 #elif defined(__QNXNTO__)
449 #if !defined(WAI_PROC_SELF_EXE)
450 #define WAI_PROC_SELF_EXE "/proc/self/exefile"
454 int WAI_PREFIX(getExecutablePath
)(char *out
, int capacity
, int *dirname_length
) {
455 char buffer1
[PATH_MAX
];
456 char buffer2
[PATH_MAX
];
457 char *resolved
= NULL
;
458 FILE *self_exe
= NULL
;
462 self_exe
= fopen(WAI_PROC_SELF_EXE
, "r");
466 if (!fgets(buffer1
, sizeof(buffer1
), self_exe
))
469 resolved
= realpath(buffer1
, buffer2
);
473 length
= (int)strlen(resolved
);
474 if (length
<= capacity
) {
475 memcpy(out
, resolved
, length
);
477 if (dirname_length
) {
480 for (i
= length
- 1; i
>= 0; --i
) {
497 #ifndef WAI_PM3_TUNED
500 int WAI_PREFIX(getModulePath
)(char *out
, int capacity
, int *dirname_length
) {
501 char buffer
[PATH_MAX
];
502 char *resolved
= NULL
;
508 if (dladdr(WAI_RETURN_ADDRESS(), &info
)) {
509 resolved
= realpath(info
.dli_fname
, buffer
);
513 length
= (int)strlen(resolved
);
514 if (length
<= capacity
) {
515 memcpy(out
, resolved
, length
);
517 if (dirname_length
) {
520 for (i
= length
- 1; i
>= 0; --i
) {
536 #endif // WAI_PM3_TUNED
538 #elif defined(__DragonFly__) || defined(__FreeBSD__) || \
539 defined(__FreeBSD_kernel__) || defined(__NetBSD__)
544 #include <sys/types.h>
545 #include <sys/sysctl.h>
549 int WAI_PREFIX(getExecutablePath
)(char *out
, int capacity
, int *dirname_length
) {
550 char buffer1
[PATH_MAX
];
551 char buffer2
[PATH_MAX
];
552 char *path
= buffer1
;
553 char *resolved
= NULL
;
557 #if defined(__NetBSD__)
558 int mib
[4] = { CTL_KERN
, KERN_PROC_ARGS
, -1, KERN_PROC_PATHNAME
};
560 int mib
[4] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PATHNAME
, -1 };
562 size_t size
= sizeof(buffer1
);
564 if (sysctl(mib
, (u_int
)(sizeof(mib
) / sizeof(mib
[0])), path
, &size
, NULL
, 0) != 0)
567 resolved
= realpath(path
, buffer2
);
571 length
= (int)strlen(resolved
);
572 if (length
<= capacity
) {
573 memcpy(out
, resolved
, length
);
575 if (dirname_length
) {
578 for (i
= length
- 1; i
>= 0; --i
) {
596 #ifndef WAI_PM3_TUNED
598 WAI_NOINLINE WAI_FUNCSPEC
599 int WAI_PREFIX(getModulePath
)(char *out
, int capacity
, int *dirname_length
) {
600 char buffer
[PATH_MAX
];
601 char *resolved
= NULL
;
607 if (dladdr(WAI_RETURN_ADDRESS(), &info
)) {
608 resolved
= realpath(info
.dli_fname
, buffer
);
612 length
= (int)strlen(resolved
);
613 if (length
<= capacity
) {
614 memcpy(out
, resolved
, length
);
616 if (dirname_length
) {
619 for (i
= length
- 1; i
>= 0; --i
) {
635 #endif // WAI_PM3_TUNED
639 #error unsupported platform