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