mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

whereami.cpp (13460B)


      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             char* begin;
    289             char* p;
    290 
    291             begin = (char*)mmap(0, offset + sizeof(p), PROT_READ, MAP_SHARED, fd, 0);
    292             p = begin + offset;
    293 
    294             while (p >= begin) // scan backwards
    295             {
    296               if (*((uint32_t*)p) == 0x04034b50UL) // local file header found
    297               {
    298                 uint16_t length_ = *((uint16_t*)(p + 26));
    299 
    300                 if (length + 2 + length_ < (int)sizeof(buffer))
    301                 {
    302                   memcpy(&buffer[length], "!/", 2);
    303                   memcpy(&buffer[length + 2], p + 30, length_);
    304                   length += 2 + length_;
    305                 }
    306 
    307                 break;
    308               }
    309 
    310               --p;
    311             }
    312 
    313             munmap(begin, offset);
    314             close(fd);
    315           }
    316 #endif
    317           if (length <= capacity)
    318           {
    319             memcpy(out, resolved, length);
    320 
    321             if (dirname_length)
    322             {
    323               int i;
    324 
    325               for (i = length - 1; i >= 0; --i)
    326               {
    327                 if (out[i] == '/')
    328                 {
    329                   *dirname_length = i;
    330                   break;
    331                 }
    332               }
    333             }
    334           }
    335 
    336           break;
    337         }
    338       }
    339     }
    340 
    341     fclose(maps);
    342     maps = NULL;
    343 
    344     if (length != -1)
    345       break;
    346   }
    347 
    348   if (maps)
    349     fclose(maps);
    350 
    351   return length;
    352 }
    353 
    354 #elif defined(__APPLE__)
    355 
    356 #define _DARWIN_BETTER_REALPATH
    357 #include <mach-o/dyld.h>
    358 #include <limits.h>
    359 #include <stdlib.h>
    360 #include <string.h>
    361 #include <dlfcn.h>
    362 
    363 WAI_FUNCSPEC
    364 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
    365 {
    366   char buffer1[PATH_MAX];
    367   char buffer2[PATH_MAX];
    368   char* path = buffer1;
    369   char* resolved = NULL;
    370   int length = -1;
    371 
    372   for (;;)
    373   {
    374     uint32_t size = (uint32_t)sizeof(buffer1);
    375     if (_NSGetExecutablePath(path, &size) == -1)
    376     {
    377       path = (char*)WAI_MALLOC(size);
    378       if (!_NSGetExecutablePath(path, &size))
    379         break;
    380     }
    381 
    382     resolved = realpath(path, buffer2);
    383     if (!resolved)
    384       break;
    385 
    386     length = (int)strlen(resolved);
    387     if (length <= capacity)
    388     {
    389       memcpy(out, resolved, length);
    390 
    391       if (dirname_length)
    392       {
    393         int i;
    394 
    395         for (i = length - 1; i >= 0; --i)
    396         {
    397           if (out[i] == '/')
    398           {
    399             *dirname_length = i;
    400             break;
    401           }
    402         }
    403       }
    404     }
    405 
    406     break;
    407   }
    408 
    409   if (path != buffer1)
    410     WAI_FREE(path);
    411 
    412   return length;
    413 }
    414 
    415 WAI_NOINLINE WAI_FUNCSPEC
    416 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
    417 {
    418   char buffer[PATH_MAX];
    419   char* resolved = NULL;
    420   int length = -1;
    421 
    422   for(;;)
    423   {
    424     Dl_info info;
    425 
    426     if (dladdr(WAI_RETURN_ADDRESS(), &info))
    427     {
    428       resolved = realpath(info.dli_fname, buffer);
    429       if (!resolved)
    430         break;
    431 
    432       length = (int)strlen(resolved);
    433       if (length <= capacity)
    434       {
    435         memcpy(out, resolved, length);
    436 
    437         if (dirname_length)
    438         {
    439           int i;
    440 
    441           for (i = length - 1; i >= 0; --i)
    442           {
    443             if (out[i] == '/')
    444             {
    445               *dirname_length = i;
    446               break;
    447             }
    448           }
    449         }
    450       }
    451     }
    452 
    453     break;
    454   }
    455 
    456   return length;
    457 }
    458 
    459 #elif defined(__QNXNTO__)
    460 
    461 #include <limits.h>
    462 #include <stdio.h>
    463 #include <stdlib.h>
    464 #include <string.h>
    465 #include <dlfcn.h>
    466 
    467 #if !defined(WAI_PROC_SELF_EXE)
    468 #define WAI_PROC_SELF_EXE "/proc/self/exefile"
    469 #endif
    470 
    471 WAI_FUNCSPEC
    472 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
    473 {
    474   char buffer1[PATH_MAX];
    475   char buffer2[PATH_MAX];
    476   char* resolved = NULL;
    477   FILE* self_exe = NULL;
    478   int length = -1;
    479 
    480   for (;;)
    481   {
    482     self_exe = fopen(WAI_PROC_SELF_EXE, "r");
    483     if (!self_exe)
    484       break;
    485 
    486     if (!fgets(buffer1, sizeof(buffer1), self_exe))
    487       break;
    488 
    489     resolved = realpath(buffer1, buffer2);
    490     if (!resolved)
    491       break;
    492 
    493     length = (int)strlen(resolved);
    494     if (length <= capacity)
    495     {
    496       memcpy(out, resolved, length);
    497 
    498       if (dirname_length)
    499       {
    500         int i;
    501 
    502         for (i = length - 1; i >= 0; --i)
    503         {
    504           if (out[i] == '/')
    505           {
    506             *dirname_length = i;
    507             break;
    508           }
    509         }
    510       }
    511     }
    512 
    513     break;
    514   }
    515 
    516   fclose(self_exe);
    517 
    518   return length;
    519 }
    520 
    521 WAI_FUNCSPEC
    522 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
    523 {
    524   char buffer[PATH_MAX];
    525   char* resolved = NULL;
    526   int length = -1;
    527 
    528   for(;;)
    529   {
    530     Dl_info info;
    531 
    532     if (dladdr(WAI_RETURN_ADDRESS(), &info))
    533     {
    534       resolved = realpath(info.dli_fname, buffer);
    535       if (!resolved)
    536         break;
    537 
    538       length = (int)strlen(resolved);
    539       if (length <= capacity)
    540       {
    541         memcpy(out, resolved, length);
    542 
    543         if (dirname_length)
    544         {
    545           int i;
    546 
    547           for (i = length - 1; i >= 0; --i)
    548           {
    549             if (out[i] == '/')
    550             {
    551               *dirname_length = i;
    552               break;
    553             }
    554           }
    555         }
    556       }
    557     }
    558 
    559     break;
    560   }
    561 
    562   return length;
    563 }
    564 
    565 #elif defined(__DragonFly__) || defined(__FreeBSD__) || \
    566       defined(__FreeBSD_kernel__) || defined(__NetBSD__)
    567 
    568 #include <limits.h>
    569 #include <stdlib.h>
    570 #include <string.h>
    571 #include <sys/types.h>
    572 #include <sys/sysctl.h>
    573 #include <dlfcn.h>
    574 
    575 WAI_FUNCSPEC
    576 int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length)
    577 {
    578   char buffer1[PATH_MAX];
    579   char buffer2[PATH_MAX];
    580   char* path = buffer1;
    581   char* resolved = NULL;
    582   int length = -1;
    583 
    584   for (;;)
    585   {
    586 #if defined(__NetBSD__)
    587     int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
    588 #else
    589     int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
    590 #endif
    591     size_t size = sizeof(buffer1);
    592 
    593     if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0)
    594         break;
    595 
    596     resolved = realpath(path, buffer2);
    597     if (!resolved)
    598       break;
    599 
    600     length = (int)strlen(resolved);
    601     if (length <= capacity)
    602     {
    603       memcpy(out, resolved, length);
    604 
    605       if (dirname_length)
    606       {
    607         int i;
    608 
    609         for (i = length - 1; i >= 0; --i)
    610         {
    611           if (out[i] == '/')
    612           {
    613             *dirname_length = i;
    614             break;
    615           }
    616         }
    617       }
    618     }
    619 
    620     break;
    621   }
    622 
    623   if (path != buffer1)
    624     WAI_FREE(path);
    625 
    626   return length;
    627 }
    628 
    629 WAI_NOINLINE WAI_FUNCSPEC
    630 int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length)
    631 {
    632   char buffer[PATH_MAX];
    633   char* resolved = NULL;
    634   int length = -1;
    635 
    636   for(;;)
    637   {
    638     Dl_info info;
    639 
    640     if (dladdr(WAI_RETURN_ADDRESS(), &info))
    641     {
    642       resolved = realpath(info.dli_fname, buffer);
    643       if (!resolved)
    644         break;
    645 
    646       length = (int)strlen(resolved);
    647       if (length <= capacity)
    648       {
    649         memcpy(out, resolved, length);
    650 
    651         if (dirname_length)
    652         {
    653           int i;
    654 
    655           for (i = length - 1; i >= 0; --i)
    656           {
    657             if (out[i] == '/')
    658             {
    659               *dirname_length = i;
    660               break;
    661             }
    662           }
    663         }
    664       }
    665     }
    666 
    667     break;
    668   }
    669 
    670   return length;
    671 }
    672 
    673 #else
    674 
    675 #error unsupported platform
    676 
    677 #endif
    678 
    679 #ifdef __cplusplus
    680 }
    681 #endif