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