Loading [MathJax]/extensions/TeX/AMSsymbols.js
LAL 7.7.0.1-3a66518
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
whereami.c
Go to the documentation of this file.
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
5
6// in case you want to #include "whereami.c" in a larger compilation unit
7#if !defined(WHEREAMI_H)
8#include <whereami.h>
9#endif
10
11#ifdef __cplusplus
12extern "C" {
13#endif
14
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#ifndef WIN32_LEAN_AND_MEAN
52#define WIN32_LEAN_AND_MEAN
53#endif
54#if defined(_MSC_VER)
55#pragma warning(push, 3)
56#endif
57#include <windows.h>
58#include <intrin.h>
59#if defined(_MSC_VER)
60#pragma warning(pop)
61#endif
62#include <stdbool.h>
63
64static 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 bool ok;
71
72 for (ok = false; !ok; ok = true)
73 {
74 DWORD size;
75 int length_, length__;
76
77 size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
78
79 if (size == 0)
80 break;
81 else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
82 {
83 DWORD size_ = size;
84 do
85 {
86 wchar_t* path_;
87
88 path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
89 if (!path_)
90 break;
91 size_ *= 2;
92 path = path_;
93 size = GetModuleFileNameW(module, path, size_);
94 }
95 while (size == size_);
96
97 if (size == size_)
98 break;
99 }
100 else
101 path = buffer1;
102
103 if (!_wfullpath(buffer2, path, MAX_PATH))
104 break;
105 length_ = (int)wcslen(buffer2);
106 length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
107
108 if (length__ == 0)
109 length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
110 if (length__ == 0)
111 break;
112
113 if (length__ <= capacity && dirname_length)
114 {
115 int i;
116
117 for (i = length__ - 1; i >= 0; --i)
118 {
119 if (out[i] == '\\')
120 {
121 *dirname_length = i;
122 break;
123 }
124 }
125 }
126
127 length = length__;
128 }
129
130 if (path != buffer1)
131 WAI_FREE(path);
132
133 return ok ? length : -1;
134}
135
137int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
138{
139 return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
140}
141
143int 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
152 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module))
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}
162
163#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE)
164
165#include <stdio.h>
166#include <stdlib.h>
167#include <string.h>
168#if defined(__linux__)
169#include <linux/limits.h>
170#else
171#include <limits.h>
172#endif
173#ifndef __STDC_FORMAT_MACROS
174#define __STDC_FORMAT_MACROS
175#endif
176#include <inttypes.h>
177#include <stdbool.h>
178
179#if !defined(WAI_PROC_SELF_EXE)
180#if defined(__sun)
181#define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
182#else
183#define WAI_PROC_SELF_EXE "/proc/self/exe"
184#endif
185#endif
186
188int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
189{
190 char buffer[PATH_MAX];
191 char* resolved = NULL;
192 int length = -1;
193 bool ok;
194
195 for (ok = false; !ok; ok = true)
196 {
197 resolved = realpath(WAI_PROC_SELF_EXE, buffer);
198 if (!resolved)
199 break;
200
201 length = (int)strlen(resolved);
202 if (length <= capacity)
203 {
204 memcpy(out, resolved, length);
205
206 if (dirname_length)
207 {
208 int i;
209
210 for (i = length - 1; i >= 0; --i)
211 {
212 if (out[i] == '/')
213 {
214 *dirname_length = i;
215 break;
216 }
217 }
218 }
219 }
220 }
221
222 return ok ? length : -1;
223}
224
225#if !defined(WAI_PROC_SELF_MAPS_RETRY)
226#define WAI_PROC_SELF_MAPS_RETRY 5
227#endif
228
229#if !defined(WAI_PROC_SELF_MAPS)
230#if defined(__sun)
231#define WAI_PROC_SELF_MAPS "/proc/self/map"
232#else
233#define WAI_PROC_SELF_MAPS "/proc/self/maps"
234#endif
235#endif
236
237#if defined(__ANDROID__) || defined(ANDROID)
238#include <fcntl.h>
239#include <sys/mman.h>
240#include <unistd.h>
241#endif
242#include <stdbool.h>
243
245int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
246{
247 int length = -1;
248 FILE* maps = NULL;
249
250 for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r)
251 {
252 maps = fopen(WAI_PROC_SELF_MAPS, "r");
253 if (!maps)
254 break;
255
256 for (;;)
257 {
258 char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
259 uint64_t low, high;
260 char perms[5];
261 uint64_t offset;
262 uint32_t major, minor;
263 char path[PATH_MAX];
264 uint32_t inode;
265
266 if (!fgets(buffer, sizeof(buffer), maps))
267 break;
268
269 if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
270 {
271 uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS();
272 if (low <= addr && addr <= high)
273 {
274 char* resolved;
275
276 resolved = realpath(path, buffer);
277 if (!resolved)
278 break;
279
280 length = (int)strlen(resolved);
281#if defined(__ANDROID__) || defined(ANDROID)
282 if (length > 4
283 &&buffer[length - 1] == 'k'
284 &&buffer[length - 2] == 'p'
285 &&buffer[length - 3] == 'a'
286 &&buffer[length - 4] == '.')
287 {
288 int fd = open(path, O_RDONLY);
289 if (fd == -1)
290 {
291 length = -1; // retry
292 break;
293 }
294
295 char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
296 if (begin == MAP_FAILED)
297 {
298 close(fd);
299 length = -1; // retry
300 break;
301 }
302
303 char* p = begin + offset - 30; // minimum size of local file header
304 while (p >= begin) // scan backwards
305 {
306 if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found
307 {
308 uint16_t length_ = *((uint16_t*)(p + 26));
309
310 if (length + 2 + length_ < (int)sizeof(buffer))
311 {
312 memcpy(&buffer[length], "!/", 2);
313 memcpy(&buffer[length + 2], p + 30, length_);
314 length += 2 + length_;
315 }
316
317 break;
318 }
319
320 --p;
321 }
322
323 munmap(begin, offset);
324 close(fd);
325 }
326#endif
327 if (length <= capacity)
328 {
329 memcpy(out, resolved, length);
330
331 if (dirname_length)
332 {
333 int i;
334
335 for (i = length - 1; i >= 0; --i)
336 {
337 if (out[i] == '/')
338 {
339 *dirname_length = i;
340 break;
341 }
342 }
343 }
344 }
345
346 break;
347 }
348 }
349 }
350
351 fclose(maps);
352 maps = NULL;
353
354 if (length != -1)
355 break;
356 }
357
358 return length;
359}
360
361#elif defined(__APPLE__)
362
363#define _DARWIN_BETTER_REALPATH
364#include <mach-o/dyld.h>
365#include <limits.h>
366#include <stdlib.h>
367#include <string.h>
368#include <dlfcn.h>
369#include <stdbool.h>
370
372int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
373{
374 char buffer1[PATH_MAX];
375 char buffer2[PATH_MAX];
376 char* path = buffer1;
377 char* resolved = NULL;
378 int length = -1;
379 bool ok;
380
381 for (ok = false; !ok; ok = true)
382 {
383 uint32_t size = (uint32_t)sizeof(buffer1);
384 if (_NSGetExecutablePath(path, &size) == -1)
385 {
386 path = (char*)WAI_MALLOC(size);
387 if (!_NSGetExecutablePath(path, &size))
388 break;
389 }
390
391 resolved = realpath(path, buffer2);
392 if (!resolved)
393 break;
394
395 length = (int)strlen(resolved);
396 if (length <= capacity)
397 {
398 memcpy(out, resolved, length);
399
400 if (dirname_length)
401 {
402 int i;
403
404 for (i = length - 1; i >= 0; --i)
405 {
406 if (out[i] == '/')
407 {
408 *dirname_length = i;
409 break;
410 }
411 }
412 }
413 }
414 }
415
416 if (path != buffer1)
417 WAI_FREE(path);
418
419 return ok ? length : -1;
420}
421
423int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
424{
425 char buffer[PATH_MAX];
426 char* resolved = NULL;
427 int length = -1;
428
429 for(;;)
430 {
431 Dl_info info;
432
433 if (dladdr(WAI_RETURN_ADDRESS(), &info))
434 {
435 resolved = realpath(info.dli_fname, buffer);
436 if (!resolved)
437 break;
438
439 length = (int)strlen(resolved);
440 if (length <= capacity)
441 {
442 memcpy(out, resolved, length);
443
444 if (dirname_length)
445 {
446 int i;
447
448 for (i = length - 1; i >= 0; --i)
449 {
450 if (out[i] == '/')
451 {
452 *dirname_length = i;
453 break;
454 }
455 }
456 }
457 }
458 }
459
460 break;
461 }
462
463 return length;
464}
465
466#elif defined(__QNXNTO__)
467
468#include <limits.h>
469#include <stdio.h>
470#include <stdlib.h>
471#include <string.h>
472#include <dlfcn.h>
473#include <stdbool.h>
474
475#if !defined(WAI_PROC_SELF_EXE)
476#define WAI_PROC_SELF_EXE "/proc/self/exefile"
477#endif
478
480int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
481{
482 char buffer1[PATH_MAX];
483 char buffer2[PATH_MAX];
484 char* resolved = NULL;
485 FILE* self_exe = NULL;
486 int length = -1;
487 bool ok;
488
489 for (ok = false; !ok; ok = true)
490 {
491 self_exe = fopen(WAI_PROC_SELF_EXE, "r");
492 if (!self_exe)
493 break;
494
495 if (!fgets(buffer1, sizeof(buffer1), self_exe))
496 break;
497
498 resolved = realpath(buffer1, buffer2);
499 if (!resolved)
500 break;
501
502 length = (int)strlen(resolved);
503 if (length <= capacity)
504 {
505 memcpy(out, resolved, length);
506
507 if (dirname_length)
508 {
509 int i;
510
511 for (i = length - 1; i >= 0; --i)
512 {
513 if (out[i] == '/')
514 {
515 *dirname_length = i;
516 break;
517 }
518 }
519 }
520 }
521 }
522
523 fclose(self_exe);
524
525 return ok ? length : -1;
526}
527
529int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
530{
531 char buffer[PATH_MAX];
532 char* resolved = NULL;
533 int length = -1;
534
535 for(;;)
536 {
537 Dl_info info;
538
539 if (dladdr(WAI_RETURN_ADDRESS(), &info))
540 {
541 resolved = realpath(info.dli_fname, buffer);
542 if (!resolved)
543 break;
544
545 length = (int)strlen(resolved);
546 if (length <= capacity)
547 {
548 memcpy(out, resolved, length);
549
550 if (dirname_length)
551 {
552 int i;
553
554 for (i = length - 1; i >= 0; --i)
555 {
556 if (out[i] == '/')
557 {
558 *dirname_length = i;
559 break;
560 }
561 }
562 }
563 }
564 }
565
566 break;
567 }
568
569 return length;
570}
571
572#elif defined(__DragonFly__) || defined(__FreeBSD__) || \
573 defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
574
575#include <limits.h>
576#include <stdlib.h>
577#include <string.h>
578#include <sys/types.h>
579#include <sys/sysctl.h>
580#include <dlfcn.h>
581#include <stdbool.h>
582
583#if defined(__OpenBSD__)
584
585#include <unistd.h>
586
588int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
589{
590 char buffer1[4096];
591 char buffer2[PATH_MAX];
592 char buffer3[PATH_MAX];
593 char** argv = (char**)buffer1;
594 char* resolved = NULL;
595 int length = -1;
596 bool ok;
597
598 for (ok = false; !ok; ok = true)
599 {
600 int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
601 size_t size;
602
603 if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0)
604 break;
605
606 if (size > sizeof(buffer1))
607 {
608 argv = (char**)WAI_MALLOC(size);
609 if (!argv)
610 break;
611 }
612
613 if (sysctl(mib, 4, argv, &size, NULL, 0) != 0)
614 break;
615
616 if (strchr(argv[0], '/'))
617 {
618 resolved = realpath(argv[0], buffer2);
619 if (!resolved)
620 break;
621 }
622 else
623 {
624 const char* PATH = getenv("PATH");
625 if (!PATH)
626 break;
627
628 size_t argv0_length = strlen(argv[0]);
629
630 const char* begin = PATH;
631 while (1)
632 {
633 const char* separator = strchr(begin, ':');
634 const char* end = separator ? separator : begin + strlen(begin);
635
636 if (end - begin > 0)
637 {
638 if (*(end -1) == '/')
639 --end;
640
641 if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2))
642 {
643 memcpy(buffer2, begin, end - begin);
644 buffer2[end - begin] = '/';
645 memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1);
646
647 resolved = realpath(buffer2, buffer3);
648 if (resolved)
649 break;
650 }
651 }
652
653 if (!separator)
654 break;
655
656 begin = ++separator;
657 }
658
659 if (!resolved)
660 break;
661 }
662
663 length = (int)strlen(resolved);
664 if (length <= capacity)
665 {
666 memcpy(out, resolved, length);
667
668 if (dirname_length)
669 {
670 int i;
671
672 for (i = length - 1; i >= 0; --i)
673 {
674 if (out[i] == '/')
675 {
676 *dirname_length = i;
677 break;
678 }
679 }
680 }
681 }
682 }
683
684 if (argv != (char**)buffer1)
685 WAI_FREE(argv);
686
687 return ok ? length : -1;
688}
689
690#else
691
693int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
694{
695 char buffer1[PATH_MAX];
696 char buffer2[PATH_MAX];
697 char* path = buffer1;
698 char* resolved = NULL;
699 int length = -1;
700 bool ok;
701
702 for (ok = false; !ok; ok = true)
703 {
704#if defined(__NetBSD__)
705 int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
706#else
707 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
708#endif
709 size_t size = sizeof(buffer1);
710
711 if (sysctl(mib, 4, path, &size, NULL, 0) != 0)
712 break;
713
714 resolved = realpath(path, buffer2);
715 if (!resolved)
716 break;
717
718 length = (int)strlen(resolved);
719 if (length <= capacity)
720 {
721 memcpy(out, resolved, length);
722
723 if (dirname_length)
724 {
725 int i;
726
727 for (i = length - 1; i >= 0; --i)
728 {
729 if (out[i] == '/')
730 {
731 *dirname_length = i;
732 break;
733 }
734 }
735 }
736 }
737 }
738
739 return ok ? length : -1;
740}
741
742#endif
743
745int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
746{
747 char buffer[PATH_MAX];
748 char* resolved = NULL;
749 int length = -1;
750
751 for(;;)
752 {
753 Dl_info info;
754
755 if (dladdr(WAI_RETURN_ADDRESS(), &info))
756 {
757 resolved = realpath(info.dli_fname, buffer);
758 if (!resolved)
759 break;
760
761 length = (int)strlen(resolved);
762 if (length <= capacity)
763 {
764 memcpy(out, resolved, length);
765
766 if (dirname_length)
767 {
768 int i;
769
770 for (i = length - 1; i >= 0; --i)
771 {
772 if (out[i] == '/')
773 {
774 *dirname_length = i;
775 break;
776 }
777 }
778 }
779 }
780 }
781
782 break;
783 }
784
785 return length;
786}
787
788#else
789
790#error unsupported platform
791
792#endif
793
794#ifdef __cplusplus
795}
796#endif
#define WAI_FUNCSPEC
Definition: FileIO.c:57
static const INT4 r
Definition: Random.c:82
#define WAI_MALLOC(size)
Definition: whereami.c:20
#define WAI_NOINLINE
Definition: whereami.c:35
#define WAI_FREE(p)
Definition: whereami.c:24
#define WAI_RETURN_ADDRESS()
Definition: whereami.c:44
#define WAI_REALLOC(p, size)
Definition: whereami.c:28
WAI_FUNCSPEC int WAI_PREFIX() getExecutablePath(char *out, int capacity, int *dirname_length)
Returns the path to the current executable.
WAI_FUNCSPEC int WAI_PREFIX() getModulePath(char *out, int capacity, int *dirname_length)
Returns the path to the current module.
#define WAI_PREFIX(function)
Definition: whereami.h:17