Adl bug fix: Non-portable fucntions
[geeqie.git] / src / whereami.c
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
12 extern "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
63 static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length)
64 {
65   wchar_t buffer1[MAX_PATH];
66   wchar_t buffer2[MAX_PATH];
67   wchar_t* path = NULL;
68   int length = -1;
69
70   for (;;)
71   {
72     DWORD size;
73     int length_, length__;
74
75     size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0]));
76
77     if (size == 0)
78       break;
79     else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0])))
80     {
81       DWORD size_ = size;
82       do
83       {
84         wchar_t* path_;
85
86         path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2);
87         if (!path_)
88           break;
89         size_ *= 2;
90         path = path_;
91         size = GetModuleFileNameW(module, path, size_);
92       }
93       while (size == size_);
94
95       if (size == size_)
96         break;
97     }
98     else
99       path = buffer1;
100
101     if (!_wfullpath(buffer2, path, MAX_PATH))
102       break;
103     length_ = (int)wcslen(buffer2);
104     length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL);
105
106     if (length__ == 0)
107       length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL);
108     if (length__ == 0)
109       break;
110
111     if (length__ <= capacity && dirname_length)
112     {
113       int i;
114
115       for (i = length__ - 1; i >= 0; --i)
116       {
117         if (out[i] == '\\')
118         {
119           *dirname_length = i;
120           break;
121         }
122       }
123     }
124
125     length = length__;
126
127     break;
128   }
129
130   if (path != buffer1)
131     WAI_FREE(path);
132
133   return length;
134 }
135
136 WAI_NOINLINE WAI_FUNCSPEC
137 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
138 {
139   return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length);
140 }
141
142 WAI_NOINLINE 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
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
178 #if !defined(WAI_PROC_SELF_EXE)
179 #if defined(__sun)
180 #define WAI_PROC_SELF_EXE "/proc/self/path/a.out"
181 #else
182 #define WAI_PROC_SELF_EXE "/proc/self/exe"
183 #endif
184 #endif
185
186 WAI_FUNCSPEC
187 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
188 {
189   char buffer[PATH_MAX];
190   char* resolved = NULL;
191   int length = -1;
192
193   for (;;)
194   {
195     resolved = realpath(WAI_PROC_SELF_EXE, buffer);
196     if (!resolved)
197       break;
198
199     length = (int)strlen(resolved);
200     if (length <= capacity)
201     {
202       memcpy(out, resolved, length);
203
204       if (dirname_length)
205       {
206         int i;
207
208         for (i = length - 1; i >= 0; --i)
209         {
210           if (out[i] == '/')
211           {
212             *dirname_length = i;
213             break;
214           }
215         }
216       }
217     }
218
219     break;
220   }
221
222   return length;
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
243 WAI_NOINLINE WAI_FUNCSPEC
244 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
245 {
246   int length = -1;
247   FILE* maps = NULL;
248
249   for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r)
250   {
251     maps = fopen(WAI_PROC_SELF_MAPS, "r");
252     if (!maps)
253       break;
254
255     for (;;)
256     {
257       char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];
258       uint64_t low, high;
259       char perms[5];
260       uint64_t offset;
261       uint32_t major, minor;
262       char path[PATH_MAX];
263       uint32_t inode;
264
265       if (!fgets(buffer, sizeof(buffer), maps))
266         break;
267
268       if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8)
269       {
270         uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS();
271         if (low <= addr && addr <= high)
272         {
273           char* resolved;
274
275           resolved = realpath(path, buffer);
276           if (!resolved)
277             break;
278
279           length = (int)strlen(resolved);
280 #if defined(__ANDROID__) || defined(ANDROID)
281           if (length > 4
282               &&buffer[length - 1] == 'k'
283               &&buffer[length - 2] == 'p'
284               &&buffer[length - 3] == 'a'
285               &&buffer[length - 4] == '.')
286           {
287             int fd = open(path, O_RDONLY);
288             if (fd == -1)
289             {
290               length = -1; // retry
291               break;
292             }
293
294             char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0);
295             if (begin == MAP_FAILED)
296             {
297               close(fd);
298               length = -1; // retry
299               break;
300             }
301
302             char* p = begin + offset - 30; // minimum size of local file header
303             while (p >= begin) // scan backwards
304             {
305               if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found
306               {
307                 uint16_t length_ = *((uint16_t*)(p + 26));
308
309                 if (length + 2 + length_ < (int)sizeof(buffer))
310                 {
311                   memcpy(&buffer[length], "!/", 2);
312                   memcpy(&buffer[length + 2], p + 30, length_);
313                   length += 2 + length_;
314                 }
315
316                 break;
317               }
318
319               --p;
320             }
321
322             munmap(begin, offset);
323             close(fd);
324           }
325 #endif
326           if (length <= capacity)
327           {
328             memcpy(out, resolved, length);
329
330             if (dirname_length)
331             {
332               int i;
333
334               for (i = length - 1; i >= 0; --i)
335               {
336                 if (out[i] == '/')
337                 {
338                   *dirname_length = i;
339                   break;
340                 }
341               }
342             }
343           }
344
345           break;
346         }
347       }
348     }
349
350     fclose(maps);
351     maps = NULL;
352
353     if (length != -1)
354       break;
355   }
356
357   if (maps)
358     fclose(maps);
359
360   return length;
361 }
362
363 #elif defined(__APPLE__)
364
365 #define _DARWIN_BETTER_REALPATH
366 #include <mach-o/dyld.h>
367 #include <limits.h>
368 #include <stdlib.h>
369 #include <string.h>
370 #include <dlfcn.h>
371
372 WAI_FUNCSPEC
373 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
374 {
375   char buffer1[PATH_MAX];
376   char buffer2[PATH_MAX];
377   char* path = buffer1;
378   char* resolved = NULL;
379   int length = -1;
380
381   for (;;)
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     break;
416   }
417
418   if (path != buffer1)
419     WAI_FREE(path);
420
421   return length;
422 }
423
424 WAI_NOINLINE WAI_FUNCSPEC
425 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
426 {
427   char buffer[PATH_MAX];
428   char* resolved = NULL;
429   int length = -1;
430
431   for(;;)
432   {
433     Dl_info info;
434
435     if (dladdr(WAI_RETURN_ADDRESS(), &info))
436     {
437       resolved = realpath(info.dli_fname, buffer);
438       if (!resolved)
439         break;
440
441       length = (int)strlen(resolved);
442       if (length <= capacity)
443       {
444         memcpy(out, resolved, length);
445
446         if (dirname_length)
447         {
448           int i;
449
450           for (i = length - 1; i >= 0; --i)
451           {
452             if (out[i] == '/')
453             {
454               *dirname_length = i;
455               break;
456             }
457           }
458         }
459       }
460     }
461
462     break;
463   }
464
465   return length;
466 }
467
468 #elif defined(__QNXNTO__)
469
470 #include <limits.h>
471 #include <stdio.h>
472 #include <stdlib.h>
473 #include <string.h>
474 #include <dlfcn.h>
475
476 #if !defined(WAI_PROC_SELF_EXE)
477 #define WAI_PROC_SELF_EXE "/proc/self/exefile"
478 #endif
479
480 WAI_FUNCSPEC
481 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
482 {
483   char buffer1[PATH_MAX];
484   char buffer2[PATH_MAX];
485   char* resolved = NULL;
486   FILE* self_exe = NULL;
487   int length = -1;
488
489   for (;;)
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     break;
523   }
524
525   fclose(self_exe);
526
527   return length;
528 }
529
530 WAI_FUNCSPEC
531 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
532 {
533   char buffer[PATH_MAX];
534   char* resolved = NULL;
535   int length = -1;
536
537   for(;;)
538   {
539     Dl_info info;
540
541     if (dladdr(WAI_RETURN_ADDRESS(), &info))
542     {
543       resolved = realpath(info.dli_fname, buffer);
544       if (!resolved)
545         break;
546
547       length = (int)strlen(resolved);
548       if (length <= capacity)
549       {
550         memcpy(out, resolved, length);
551
552         if (dirname_length)
553         {
554           int i;
555
556           for (i = length - 1; i >= 0; --i)
557           {
558             if (out[i] == '/')
559             {
560               *dirname_length = i;
561               break;
562             }
563           }
564         }
565       }
566     }
567
568     break;
569   }
570
571   return length;
572 }
573
574 #elif defined(__DragonFly__) || defined(__FreeBSD__) || \
575       defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
576
577 #include <limits.h>
578 #include <stdlib.h>
579 #include <string.h>
580 #include <sys/types.h>
581 #include <sys/sysctl.h>
582 #include <dlfcn.h>
583
584 #if defined(__OpenBSD__)
585
586 #include <unistd.h>
587
588 WAI_FUNCSPEC
589 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
590 {
591   char buffer1[4096];
592   char buffer2[PATH_MAX];
593   char buffer3[PATH_MAX];
594   char** argv = (char**)buffer1;
595   char* resolved = NULL;
596   int length = -1;
597
598   for (;;)
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     break;
684   }
685
686   if (argv != (char**)buffer1)
687     WAI_FREE(argv);
688
689   return length;
690 }
691
692 #else
693
694 WAI_FUNCSPEC
695 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
696 {
697   char buffer1[PATH_MAX];
698   char buffer2[PATH_MAX];
699   char* path = buffer1;
700   char* resolved = NULL;
701   int length = -1;
702
703   for (;;)
704   {
705 #if defined(__NetBSD__)
706     int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
707 #else
708     int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
709 #endif
710     size_t size = sizeof(buffer1);
711
712     if (sysctl(mib, 4, path, &size, NULL, 0) != 0)
713         break;
714
715     resolved = realpath(path, buffer2);
716     if (!resolved)
717       break;
718
719     length = (int)strlen(resolved);
720     if (length <= capacity)
721     {
722       memcpy(out, resolved, length);
723
724       if (dirname_length)
725       {
726         int i;
727
728         for (i = length - 1; i >= 0; --i)
729         {
730           if (out[i] == '/')
731           {
732             *dirname_length = i;
733             break;
734           }
735         }
736       }
737     }
738
739     break;
740   }
741
742   return length;
743 }
744
745 #endif
746
747 WAI_NOINLINE WAI_FUNCSPEC
748 int 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