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