LAL  7.5.0.1-b72065a
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
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 #include <stdbool.h>
63 
64 static 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 
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 
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 #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 
188 int 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 
245 int 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 
372 int 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 
423 int 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 
480 int 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 
529 int 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 
588 int 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 
693 int 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 
745 int 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:54
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