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