medfall

A super great game engine
Log | Files | Refs

whereami.cc (12958B)


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