]>
Commit | Line | Data |
---|---|---|
1 | // (‑●‑●)> released under the WTFPL v2 license, by Gregory Pakosz (@gpakosz) | |
2 | // https://github.com/gpakosz/whereami | |
3 | ||
4 | // in case you want to #include "whereami.c" in a larger compilation unit | |
5 | #if !defined(WHEREAMI_H) | |
6 | #include <whereami.h> | |
7 | #endif | |
8 | ||
9 | #ifdef __cplusplus | |
10 | extern "C" { | |
11 | #endif | |
12 | ||
13 | #if defined(__linux__) | |
14 | // make realpath() available: | |
15 | #define _DEFAULT_SOURCE | |
16 | #endif | |
17 | ||
18 | #if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) | |
19 | #include <stdlib.h> | |
20 | #endif | |
21 | ||
22 | #if !defined(WAI_MALLOC) | |
23 | #define WAI_MALLOC(size) malloc(size) | |
24 | #endif | |
25 | ||
26 | #if !defined(WAI_FREE) | |
27 | #define WAI_FREE(p) free(p) | |
28 | #endif | |
29 | ||
30 | #if !defined(WAI_REALLOC) | |
31 | #define WAI_REALLOC(p, size) realloc(p, size) | |
32 | #endif | |
33 | ||
34 | #ifndef WAI_NOINLINE | |
35 | #if defined(_MSC_VER) | |
36 | #define WAI_NOINLINE __declspec(noinline) | |
37 | #elif defined(__GNUC__) | |
38 | #define WAI_NOINLINE __attribute__((noinline)) | |
39 | #else | |
40 | #error unsupported compiler | |
41 | #endif | |
42 | #endif | |
43 | ||
44 | #if defined(_MSC_VER) | |
45 | #define WAI_RETURN_ADDRESS() _ReturnAddress() | |
46 | #elif defined(__GNUC__) | |
47 | #define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) | |
48 | #else | |
49 | #error unsupported compiler | |
50 | #endif | |
51 | ||
52 | #if defined(_WIN32) | |
53 | ||
54 | #define WIN32_LEAN_AND_MEAN | |
55 | #if defined(_MSC_VER) | |
56 | #pragma warning(push, 3) | |
57 | #endif | |
58 | #include <windows.h> | |
59 | //#include <intrin.h> // not required and doesn't exist in old mingw environments | |
60 | #if defined(_MSC_VER) | |
61 | #pragma warning(pop) | |
62 | #endif | |
63 | ||
64 | static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) | |
65 | { | |
66 | wchar_t buffer1[MAX_PATH]; | |
67 | wchar_t buffer2[MAX_PATH]; | |
68 | wchar_t* path = NULL; | |
69 | int length = -1; | |
70 | ||
71 | for (;;) | |
72 | { | |
73 | DWORD size; | |
74 | int length_, length__; | |
75 | ||
76 | size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); | |
77 | ||
78 | if (size == 0) | |
79 | break; | |
80 | else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0]))) | |
81 | { | |
82 | DWORD size_ = size; | |
83 | do | |
84 | { | |
85 | wchar_t* path_; | |
86 | ||
87 | path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2); | |
88 | if (!path_) | |
89 | break; | |
90 | size_ *= 2; | |
91 | path = path_; | |
92 | size = GetModuleFileNameW(module, path, size_); | |
93 | } | |
94 | while (size == size_); | |
95 | ||
96 | if (size == size_) | |
97 | break; | |
98 | } | |
99 | else | |
100 | path = buffer1; | |
101 | ||
102 | if (!_wfullpath(buffer2, path, MAX_PATH)) | |
103 | break; | |
104 | length_ = (int)wcslen(buffer2); | |
105 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL); | |
106 | ||
107 | if (length__ == 0) | |
108 | length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); | |
109 | if (length__ == 0) | |
110 | break; | |
111 | ||
112 | if (length__ <= capacity && dirname_length) | |
113 | { | |
114 | int i; | |
115 | ||
116 | for (i = length__ - 1; i >= 0; --i) | |
117 | { | |
118 | if (out[i] == '\\') | |
119 | { | |
120 | *dirname_length = i; | |
121 | break; | |
122 | } | |
123 | } | |
124 | } | |
125 | ||
126 | length = length__; | |
127 | ||
128 | break; | |
129 | } | |
130 | ||
131 | if (path != buffer1) | |
132 | WAI_FREE(path); | |
133 | ||
134 | return length; | |
135 | } | |
136 | ||
137 | WAI_NOINLINE | |
138 | WAI_FUNCSPEC | |
139 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) | |
140 | { | |
141 | return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length); | |
142 | } | |
143 | ||
144 | // GetModuleHandleEx() is not available on old mingw environments. We don't need getModulePath() yet. | |
145 | // Sacrifice it for the time being to improve backwards compatibility | |
146 | /* WAI_NOINLINE | |
147 | WAI_FUNCSPEC | |
148 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) | |
149 | { | |
150 | HMODULE module; | |
151 | int length = -1; | |
152 | ||
153 | #if defined(_MSC_VER) | |
154 | #pragma warning(push) | |
155 | #pragma warning(disable: 4054) | |
156 | #endif | |
157 | if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module)) | |
158 | #if defined(_MSC_VER) | |
159 | #pragma warning(pop) | |
160 | #endif | |
161 | { | |
162 | length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length); | |
163 | } | |
164 | ||
165 | return length; | |
166 | } | |
167 | */ | |
168 | ||
169 | #elif defined(__linux__) | |
170 | ||
171 | #include <stdio.h> | |
172 | #include <stdlib.h> | |
173 | #include <string.h> | |
174 | // #include <limits.h> // not all linux distributions define PATH_MAX in limits.h because it depends on the filesystem. Therefore use... | |
175 | #include <linux/limits.h> | |
176 | #ifndef __STDC_FORMAT_MACROS | |
177 | #define __STDC_FORMAT_MACROS | |
178 | #endif | |
179 | #include <inttypes.h> | |
180 | ||
181 | #if !defined(WAI_PROC_SELF_EXE) | |
182 | #define WAI_PROC_SELF_EXE "/proc/self/exe" | |
183 | #endif | |
184 | ||
185 | WAI_FUNCSPEC | |
186 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) | |
187 | { | |
188 | char buffer[PATH_MAX]; | |
189 | char* resolved = NULL; | |
190 | int length = -1; | |
191 | ||
192 | for (;;) | |
193 | { | |
194 | resolved = realpath(WAI_PROC_SELF_EXE, buffer); | |
195 | if (!resolved) | |
196 | break; | |
197 | ||
198 | length = (int)strlen(resolved); | |
199 | if (length <= capacity) | |
200 | { | |
201 | memcpy(out, resolved, length); | |
202 | ||
203 | if (dirname_length) | |
204 | { | |
205 | int i; | |
206 | ||
207 | for (i = length - 1; i >= 0; --i) | |
208 | { | |
209 | if (out[i] == '/') | |
210 | { | |
211 | *dirname_length = i; | |
212 | break; | |
213 | } | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | break; | |
219 | } | |
220 | ||
221 | return length; | |
222 | } | |
223 | ||
224 | #if !defined(WAI_PROC_SELF_MAPS_RETRY) | |
225 | #define WAI_PROC_SELF_MAPS_RETRY 5 | |
226 | #endif | |
227 | ||
228 | #if !defined(WAI_PROC_SELF_MAPS) | |
229 | #define WAI_PROC_SELF_MAPS "/proc/self/maps" | |
230 | #endif | |
231 | ||
232 | #if defined(__ANDROID__) || defined(ANDROID) | |
233 | #include <fcntl.h> | |
234 | #include <sys/mman.h> | |
235 | #endif | |
236 | ||
237 | WAI_NOINLINE | |
238 | WAI_FUNCSPEC | |
239 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) | |
240 | { | |
241 | int length = -1; | |
242 | FILE* maps = NULL; | |
243 | int i; | |
244 | ||
245 | for (i = 0; i < WAI_PROC_SELF_MAPS_RETRY; ++i) | |
246 | { | |
247 | maps = fopen(WAI_PROC_SELF_MAPS, "r"); | |
248 | if (!maps) | |
249 | break; | |
250 | ||
251 | for (;;) | |
252 | { | |
253 | char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX]; | |
254 | uint64_t low, high; | |
255 | char perms[5]; | |
256 | uint64_t offset; | |
257 | uint32_t major, minor; | |
258 | char path[PATH_MAX]; | |
259 | uint32_t inode; | |
260 | ||
261 | if (!fgets(buffer, sizeof(buffer), maps)) | |
262 | break; | |
263 | ||
264 | if (sscanf(buffer, "%" SCNx64 "-%" SCNx64 " %s %" SCNx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8) | |
265 | { | |
266 | uint64_t addr = (uint64_t)(uintptr_t)WAI_RETURN_ADDRESS(); | |
267 | if (low <= addr && addr <= high) | |
268 | { | |
269 | char* resolved; | |
270 | ||
271 | resolved = realpath(path, buffer); | |
272 | if (!resolved) | |
273 | break; | |
274 | ||
275 | length = (int)strlen(resolved); | |
276 | #if defined(__ANDROID__) || defined(ANDROID) | |
277 | if (length > 4 | |
278 | &&buffer[length - 1] == 'k' | |
279 | &&buffer[length - 2] == 'p' | |
280 | &&buffer[length - 3] == 'a' | |
281 | &&buffer[length - 4] == '.') | |
282 | { | |
283 | int fd = open(path, O_RDONLY); | |
284 | char* begin; | |
285 | char* p; | |
286 | ||
287 | begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); | |
288 | p = begin + offset; | |
289 | ||
290 | while (p >= begin) // scan backwards | |
291 | { | |
292 | if (*((uint32_t*)p) == 0x04034b50UL) // local file header found | |
293 | { | |
294 | uint16_t length_ = *((uint16_t*)(p + 26)); | |
295 | ||
296 | if (length + 2 + length_ < (int)sizeof(buffer)) | |
297 | { | |
298 | memcpy(&buffer[length], "!/", 2); | |
299 | memcpy(&buffer[length + 2], p + 30, length_); | |
300 | length += 2 + length_; | |
301 | } | |
302 | ||
303 | break; | |
304 | } | |
305 | ||
306 | p -= 4; | |
307 | } | |
308 | ||
309 | munmap(begin, offset); | |
310 | close(fd); | |
311 | } | |
312 | #endif | |
313 | if (length <= capacity) | |
314 | { | |
315 | memcpy(out, resolved, length); | |
316 | ||
317 | if (dirname_length) | |
318 | { | |
319 | int i; | |
320 | ||
321 | for (i = length - 1; i >= 0; --i) | |
322 | { | |
323 | if (out[i] == '/') | |
324 | { | |
325 | *dirname_length = i; | |
326 | break; | |
327 | } | |
328 | } | |
329 | } | |
330 | } | |
331 | ||
332 | break; | |
333 | } | |
334 | } | |
335 | } | |
336 | ||
337 | fclose(maps); | |
338 | ||
339 | if (length != -1) | |
340 | break; | |
341 | } | |
342 | ||
343 | return length; | |
344 | } | |
345 | ||
346 | #elif defined(__APPLE__) | |
347 | ||
348 | #define _DARWIN_BETTER_REALPATH | |
349 | #include <mach-o/dyld.h> | |
350 | #include <limits.h> | |
351 | #include <stdlib.h> | |
352 | #include <string.h> | |
353 | #include <dlfcn.h> | |
354 | ||
355 | WAI_FUNCSPEC | |
356 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) | |
357 | { | |
358 | char buffer1[PATH_MAX]; | |
359 | char buffer2[PATH_MAX]; | |
360 | char* path = buffer1; | |
361 | char* resolved = NULL; | |
362 | int length = -1; | |
363 | ||
364 | for (;;) | |
365 | { | |
366 | uint32_t size = (uint32_t)sizeof(buffer1); | |
367 | if (_NSGetExecutablePath(path, &size) == -1) | |
368 | { | |
369 | path = (char*)WAI_MALLOC(size); | |
370 | if (!_NSGetExecutablePath(path, &size)) | |
371 | break; | |
372 | } | |
373 | ||
374 | resolved = realpath(path, buffer2); | |
375 | if (!resolved) | |
376 | break; | |
377 | ||
378 | length = (int)strlen(resolved); | |
379 | if (length <= capacity) | |
380 | { | |
381 | memcpy(out, resolved, length); | |
382 | ||
383 | if (dirname_length) | |
384 | { | |
385 | int i; | |
386 | ||
387 | for (i = length - 1; i >= 0; --i) | |
388 | { | |
389 | if (out[i] == '/') | |
390 | { | |
391 | *dirname_length = i; | |
392 | break; | |
393 | } | |
394 | } | |
395 | } | |
396 | } | |
397 | ||
398 | break; | |
399 | } | |
400 | ||
401 | if (path != buffer1) | |
402 | WAI_FREE(path); | |
403 | ||
404 | return length; | |
405 | } | |
406 | ||
407 | WAI_NOINLINE | |
408 | WAI_FUNCSPEC | |
409 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) | |
410 | { | |
411 | char buffer[PATH_MAX]; | |
412 | char* resolved = NULL; | |
413 | int length = -1; | |
414 | ||
415 | for(;;) | |
416 | { | |
417 | Dl_info info; | |
418 | ||
419 | if (dladdr(WAI_RETURN_ADDRESS(), &info)) | |
420 | { | |
421 | resolved = realpath(info.dli_fname, buffer); | |
422 | if (!resolved) | |
423 | break; | |
424 | ||
425 | length = (int)strlen(resolved); | |
426 | if (length <= capacity) | |
427 | { | |
428 | memcpy(out, resolved, length); | |
429 | ||
430 | if (dirname_length) | |
431 | { | |
432 | int i; | |
433 | ||
434 | for (i = length - 1; i >= 0; --i) | |
435 | { | |
436 | if (out[i] == '/') | |
437 | { | |
438 | *dirname_length = i; | |
439 | break; | |
440 | } | |
441 | } | |
442 | } | |
443 | } | |
444 | } | |
445 | ||
446 | break; | |
447 | } | |
448 | ||
449 | return length; | |
450 | } | |
451 | ||
452 | #elif defined(__QNXNTO__) | |
453 | ||
454 | #include <limits.h> | |
455 | #include <stdio.h> | |
456 | #include <stdlib.h> | |
457 | #include <string.h> | |
458 | #include <dlfcn.h> | |
459 | ||
460 | #if !defined(WAI_PROC_SELF_EXE) | |
461 | #define WAI_PROC_SELF_EXE "/proc/self/exefile" | |
462 | #endif | |
463 | ||
464 | WAI_FUNCSPEC | |
465 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) | |
466 | { | |
467 | char buffer1[PATH_MAX]; | |
468 | char buffer2[PATH_MAX]; | |
469 | char* resolved = NULL; | |
470 | FILE* self_exe = NULL; | |
471 | int length = -1; | |
472 | ||
473 | for (;;) | |
474 | { | |
475 | self_exe = fopen(WAI_PROC_SELF_EXE, "r"); | |
476 | if (!self_exe) | |
477 | break; | |
478 | ||
479 | if (!fgets(buffer1, sizeof(buffer1), self_exe)) | |
480 | break; | |
481 | ||
482 | resolved = realpath(buffer1, buffer2); | |
483 | if (!resolved) | |
484 | break; | |
485 | ||
486 | length = (int)strlen(resolved); | |
487 | if (length <= capacity) | |
488 | { | |
489 | memcpy(out, resolved, length); | |
490 | ||
491 | if (dirname_length) | |
492 | { | |
493 | int i; | |
494 | ||
495 | for (i = length - 1; i >= 0; --i) | |
496 | { | |
497 | if (out[i] == '/') | |
498 | { | |
499 | *dirname_length = i; | |
500 | break; | |
501 | } | |
502 | } | |
503 | } | |
504 | } | |
505 | ||
506 | break; | |
507 | } | |
508 | ||
509 | fclose(self_exe); | |
510 | ||
511 | return length; | |
512 | } | |
513 | ||
514 | WAI_FUNCSPEC | |
515 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) | |
516 | { | |
517 | char buffer[PATH_MAX]; | |
518 | char* resolved = NULL; | |
519 | int length = -1; | |
520 | ||
521 | for(;;) | |
522 | { | |
523 | Dl_info info; | |
524 | ||
525 | if (dladdr(WAI_RETURN_ADDRESS(), &info)) | |
526 | { | |
527 | resolved = realpath(info.dli_fname, buffer); | |
528 | if (!resolved) | |
529 | break; | |
530 | ||
531 | length = (int)strlen(resolved); | |
532 | if (length <= capacity) | |
533 | { | |
534 | memcpy(out, resolved, length); | |
535 | ||
536 | if (dirname_length) | |
537 | { | |
538 | int i; | |
539 | ||
540 | for (i = length - 1; i >= 0; --i) | |
541 | { | |
542 | if (out[i] == '/') | |
543 | { | |
544 | *dirname_length = i; | |
545 | break; | |
546 | } | |
547 | } | |
548 | } | |
549 | } | |
550 | } | |
551 | ||
552 | break; | |
553 | } | |
554 | ||
555 | return length; | |
556 | } | |
557 | ||
558 | #elif defined(__DragonFly__) || defined(__FreeBSD__) || \ | |
559 | defined(__FreeBSD_kernel__) || defined(__NetBSD__) | |
560 | ||
561 | #include <limits.h> | |
562 | #include <stdlib.h> | |
563 | #include <string.h> | |
564 | #include <sys/types.h> | |
565 | #include <sys/sysctl.h> | |
566 | #include <dlfcn.h> | |
567 | ||
568 | WAI_FUNCSPEC | |
569 | int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) | |
570 | { | |
571 | char buffer1[PATH_MAX]; | |
572 | char buffer2[PATH_MAX]; | |
573 | char* path = buffer1; | |
574 | char* resolved = NULL; | |
575 | int length = -1; | |
576 | ||
577 | for (;;) | |
578 | { | |
579 | int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; | |
580 | size_t size = sizeof(buffer1); | |
581 | ||
582 | if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0) | |
583 | break; | |
584 | ||
585 | resolved = realpath(path, buffer2); | |
586 | if (!resolved) | |
587 | break; | |
588 | ||
589 | length = (int)strlen(resolved); | |
590 | if (length <= capacity) | |
591 | { | |
592 | memcpy(out, resolved, length); | |
593 | ||
594 | if (dirname_length) | |
595 | { | |
596 | int i; | |
597 | ||
598 | for (i = length - 1; i >= 0; --i) | |
599 | { | |
600 | if (out[i] == '/') | |
601 | { | |
602 | *dirname_length = i; | |
603 | break; | |
604 | } | |
605 | } | |
606 | } | |
607 | } | |
608 | ||
609 | break; | |
610 | } | |
611 | ||
612 | if (path != buffer1) | |
613 | WAI_FREE(path); | |
614 | ||
615 | return length; | |
616 | } | |
617 | ||
618 | WAI_NOINLINE | |
619 | WAI_FUNCSPEC | |
620 | int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) | |
621 | { | |
622 | char buffer[PATH_MAX]; | |
623 | char* resolved = NULL; | |
624 | int length = -1; | |
625 | ||
626 | for(;;) | |
627 | { | |
628 | Dl_info info; | |
629 | ||
630 | if (dladdr(WAI_RETURN_ADDRESS(), &info)) | |
631 | { | |
632 | resolved = realpath(info.dli_fname, buffer); | |
633 | if (!resolved) | |
634 | break; | |
635 | ||
636 | length = (int)strlen(resolved); | |
637 | if (length <= capacity) | |
638 | { | |
639 | memcpy(out, resolved, length); | |
640 | ||
641 | if (dirname_length) | |
642 | { | |
643 | int i; | |
644 | ||
645 | for (i = length - 1; i >= 0; --i) | |
646 | { | |
647 | if (out[i] == '/') | |
648 | { | |
649 | *dirname_length = i; | |
650 | break; | |
651 | } | |
652 | } | |
653 | } | |
654 | } | |
655 | } | |
656 | ||
657 | break; | |
658 | } | |
659 | ||
660 | return length; | |
661 | } | |
662 | ||
663 | #else | |
664 | ||
665 | #error unsupported platform | |
666 | ||
667 | #endif | |
668 | ||
669 | #ifdef __cplusplus | |
670 | } | |
671 | #endif |