mudgangster

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

commit 75933825927c74101db4897eb308dc45ab9b0fc8
parent 4ad8e4a7073fdef762c986711f4a3b7caa1ac510
Author: Michael Savage <mikejsavage@gmail.com>
Date:   Sun,  3 May 2020 01:48:13 +0300

Check appdata/my documents/exe folder for scripts

Diffstat:
Alibs/whereami.lua | 1+
Alibs/whereami/whereami.cpp | 679+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibs/whereami/whereami.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmake.lua | 7++++++-
Msrc/lua/main.lua | 5+++--
Msrc/lua/script.lua | 88+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/script.cc | 26+++++++++++++++++++++++++-
7 files changed, 833 insertions(+), 40 deletions(-)

diff --git a/libs/whereami.lua b/libs/whereami.lua @@ -0,0 +1 @@ +lib( "whereami", { "libs/whereami/whereami.cpp" } ) diff --git a/libs/whereami/whereami.cpp b/libs/whereami/whereami.cpp @@ -0,0 +1,679 @@ +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +// in case you want to #include "whereami.c" in a larger compilation unit +#if !defined(WHEREAMI_H) +#include "whereami.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) +#include <stdlib.h> +#endif + +#if !defined(WAI_MALLOC) +#define WAI_MALLOC(size) malloc(size) +#endif + +#if !defined(WAI_FREE) +#define WAI_FREE(p) free(p) +#endif + +#if !defined(WAI_REALLOC) +#define WAI_REALLOC(p, size) realloc(p, size) +#endif + +#ifndef WAI_NOINLINE +#if defined(_MSC_VER) +#define WAI_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define WAI_NOINLINE __attribute__((noinline)) +#else +#error unsupported compiler +#endif +#endif + +#if defined(_MSC_VER) +#define WAI_RETURN_ADDRESS() _ReturnAddress() +#elif defined(__GNUC__) +#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#error unsupported compiler +#endif + +#if defined(_WIN32) + +#define WIN32_LEAN_AND_MEAN +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif +#include <windows.h> +#include <intrin.h> +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) +{ + wchar_t buffer1[MAX_PATH]; + wchar_t buffer2[MAX_PATH]; + wchar_t* path = NULL; + int length = -1; + + for (;;) + { + DWORD size; + int length_, length__; + + size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); + + if (size == 0) + break; + else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0]))) + { + DWORD size_ = size; + do + { + wchar_t* path_; + + path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2); + if (!path_) + break; + size_ *= 2; + path = path_; + size = GetModuleFileNameW(module, path, size_); + } + while (size == size_); + + if (size == size_) + break; + } + else + path = buffer1; + + if (!_wfullpath(buffer2, path, MAX_PATH)) + break; + length_ = (int)wcslen(buffer2); + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL); + + if (length__ == 0) + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); + if (length__ == 0) + break; + + if (length__ <= capacity && dirname_length) + { + int i; + + for (i = length__ - 1; i >= 0; --i) + { + if (out[i] == '\\') + { + *dirname_length = i; + break; + } + } + } + + length = length__; + + break; + } + + if (path != buffer1) + WAI_FREE(path); + + return length; +} + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length); +} + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + HMODULE module; + int length = -1; + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4054) +#endif + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module)) +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + { + length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length); + } + + return length; +} + +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE) + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if defined(__linux__) +#include <linux/limits.h> +#else +#include <limits.h> +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include <inttypes.h> + +#if !defined(WAI_PROC_SELF_EXE) +#if defined(__sun) +#define WAI_PROC_SELF_EXE "/proc/self/path/a.out" +#else +#define WAI_PROC_SELF_EXE "/proc/self/exe" +#endif +#endif + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for (;;) + { + resolved = realpath(WAI_PROC_SELF_EXE, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + return length; +} + +#if !defined(WAI_PROC_SELF_MAPS_RETRY) +#define WAI_PROC_SELF_MAPS_RETRY 5 +#endif + +#if !defined(WAI_PROC_SELF_MAPS) +#if defined(__sun) +#define WAI_PROC_SELF_MAPS "/proc/self/map" +#else +#define WAI_PROC_SELF_MAPS "/proc/self/maps" +#endif +#endif + +#if defined(__ANDROID__) || defined(ANDROID) +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> +#endif + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + int length = -1; + FILE* maps = NULL; + + for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r) + { + maps = fopen(WAI_PROC_SELF_MAPS, "r"); + if (!maps) + break; + + for (;;) + { + char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX]; + uint64_t low, high; + char perms[5]; + uint64_t offset; + uint32_t major, minor; + char path[PATH_MAX]; + uint32_t inode; + + if (!fgets(buffer, sizeof(buffer), maps)) + break; + + if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8) + { + uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS(); + if (low <= addr && addr <= high) + { + char* resolved; + + resolved = realpath(path, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); +#if defined(__ANDROID__) || defined(ANDROID) + if (length > 4 + &&buffer[length - 1] == 'k' + &&buffer[length - 2] == 'p' + &&buffer[length - 3] == 'a' + &&buffer[length - 4] == '.') + { + int fd = open(path, O_RDONLY); + char* begin; + char* p; + + begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); + p = begin + offset; + + while (p >= begin) // scan backwards + { + if (*((uint32_t*)p) == 0x04034b50UL) // local file header found + { + uint16_t length_ = *((uint16_t*)(p + 26)); + + if (length + 2 + length_ < (int)sizeof(buffer)) + { + memcpy(&buffer[length], "!/", 2); + memcpy(&buffer[length + 2], p + 30, length_); + length += 2 + length_; + } + + break; + } + + p -= 4; + } + + munmap(begin, offset); + close(fd); + } +#endif + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + } + } + + fclose(maps); + maps = NULL; + + if (length != -1) + break; + } + + if (maps) + fclose(maps); + + return length; +} + +#elif defined(__APPLE__) + +#define _DARWIN_BETTER_REALPATH +#include <mach-o/dyld.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* path = buffer1; + char* resolved = NULL; + int length = -1; + + for (;;) + { + uint32_t size = (uint32_t)sizeof(buffer1); + if (_NSGetExecutablePath(path, &size) == -1) + { + path = (char*)WAI_MALLOC(size); + if (!_NSGetExecutablePath(path, &size)) + break; + } + + resolved = realpath(path, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + if (path != buffer1) + WAI_FREE(path); + + return length; +} + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#elif defined(__QNXNTO__) + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> + +#if !defined(WAI_PROC_SELF_EXE) +#define WAI_PROC_SELF_EXE "/proc/self/exefile" +#endif + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* resolved = NULL; + FILE* self_exe = NULL; + int length = -1; + + for (;;) + { + self_exe = fopen(WAI_PROC_SELF_EXE, "r"); + if (!self_exe) + break; + + if (!fgets(buffer1, sizeof(buffer1), self_exe)) + break; + + resolved = realpath(buffer1, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + fclose(self_exe); + + return length; +} + +WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#elif defined(__DragonFly__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__NetBSD__) + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <dlfcn.h> + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* path = buffer1; + char* resolved = NULL; + int length = -1; + + for (;;) + { +#if defined(__NetBSD__) + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; +#else + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; +#endif + size_t size = sizeof(buffer1); + + if (sysctl(mib, (u_int)(sizeof(mib) / sizeof(mib[0])), path, &size, NULL, 0) != 0) + break; + + resolved = realpath(path, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + + if (path != buffer1) + WAI_FREE(path); + + return length; +} + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#else + +#error unsupported platform + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/libs/whereami/whereami.h b/libs/whereami/whereami.h @@ -0,0 +1,67 @@ +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +#ifndef WHEREAMI_H +#define WHEREAMI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WAI_FUNCSPEC + #define WAI_FUNCSPEC +#endif +#ifndef WAI_PREFIX +#define WAI_PREFIX(function) wai_##function +#endif + +/** + * Returns the path to the current executable. + * + * Usage: + * - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to + * retrieve the length of the path + * - allocate the destination buffer with `path = (char*)malloc(length + 1);` + * - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the + * path + * - add a terminal NUL character with `path[length] = '\0';` + * + * @param out destination buffer, optional + * @param capacity destination buffer capacity + * @param dirname_length optional recipient for the length of the dirname part + * of the path. + * + * @return the length of the executable path on success (without a terminal NUL + * character), otherwise `-1` + */ +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); + +/** + * Returns the path to the current module + * + * Usage: + * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve + * the length of the path + * - allocate the destination buffer with `path = (char*)malloc(length + 1);` + * - call `wai_getModulePath(path, length, NULL)` again to retrieve the path + * - add a terminal NUL character with `path[length] = '\0';` + * + * @param out destination buffer, optional + * @param capacity destination buffer capacity + * @param dirname_length optional recipient for the length of the dirname part + * of the path. + * + * @return the length of the module path on success (without a terminal NUL + * character), otherwise `-1` + */ +WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef WHEREAMI_H diff --git a/make.lua b/make.lua @@ -1,6 +1,7 @@ require( "ggbuild.gen_ninja" ) require( "libs.tracy" ) +require( "libs.whereami" ) obj_cxxflags( ".*", "-I source -I libs" ) @@ -37,7 +38,11 @@ bin( "mudgangster", { "src/ui.cc", "src/script.cc", "src/textbox.cc", "src/input.cc", "src/platform_network.cc", }, - libs = { platform_libs, "tracy" }, + libs = { + platform_libs, + "tracy", + "whereami" + }, rc = "src/rc", diff --git a/src/lua/main.lua b/src/lua/main.lua @@ -15,7 +15,8 @@ local handlers = require( "handlers" ) local printMain, newlineMain, printChat, newlineChat, setHandlers, urgent, setStatus, sock_connect, sock_send, sock_close, - get_time, set_font = ... + get_time, set_font, + exe_path = ... local socket_api = { connect = sock_connect, @@ -51,4 +52,4 @@ require( "status" ).init( setStatus ) setHandlers( handlers.input, handlers.macro, handlers.close, socket_data_handler, handlers.interval ) -script.load() +script.load( exe_path ) diff --git a/src/lua/script.lua b/src/lua/script.lua @@ -1,14 +1,25 @@ local lfs = require( "lfs" ) local serialize = require( "serialize" ) -local ScriptsDir +local ScriptsDirs if mud.os == "windows" then - ScriptsDir = os.getenv( "APPDATA" ) .. "\\Mud Gangster\\scripts" + ScriptsDirs = { + os.getenv( "APPDATA" ) .. "\\Mud Gangster\\scripts", + os.getenv( "USERPROFILE" ) .. "\\Documents\\Mud Gangster\\scripts", + } else - ScriptsDir = os.getenv( "HOME" ) .. "/.mudgangster/scripts" + ScriptsDirs = { os.getenv( "HOME" ) .. "/.mudgangster/scripts" } end -package.path = package.path .. ";" .. ScriptsDir .. "/?.lua" +-- TODO: missing exe dir +do + local paths = { } + for _, dir in ipairs( ScriptsDirs ) do + table.insert( paths, dir .. "/?.lua" ) + end + + package.path = package.path .. ";" .. table.concat( paths, ";" ) +end local function loadScript( name, path, padding ) local function throw( err ) @@ -92,55 +103,60 @@ local function loadScript( name, path, padding ) end end -local function loadScripts() - mud.print( "#s> Loading scripts..." ) +local function loadScriptsFrom( dir ) + mud.print( "\n#s> Loading scripts from %s... ", dir ) + + local readable, err = io.readable( dir ) - local readable, err = io.readable( ScriptsDir ) + if not readable then + mud.print( "#lrfailed!\n#s> %s", err ) + return + end - if readable then - local attr = lfs.attributes( ScriptsDir ) + local attr = lfs.attributes( dir ) - if attr.mode == "directory" then - local scripts = { } - local maxLen = 0 + if attr.mode == "directory" then + local scripts = { } + local maxLen = 0 - for script in lfs.dir( ScriptsDir ) do - if not script:match( "^%." ) then - local path = ScriptsDir .. "/" .. script - local scriptAttr = lfs.attributes( path ) + for script in lfs.dir( dir ) do + if not script:match( "^%." ) then + local path = dir .. "/" .. script + local scriptAttr = lfs.attributes( path ) - if scriptAttr.mode == "directory" then - maxLen = math.max( script:len(), maxLen ) + if scriptAttr.mode == "directory" then + maxLen = math.max( script:len(), maxLen ) - table.insert( scripts, { - name = script, - path = path, - } ) - end + table.insert( scripts, { + name = script, + path = path, + } ) end end + end - table.sort( scripts, function( a, b ) - return a.name < b.name - end ) + table.sort( scripts, function( a, b ) + return a.name < b.name + end ) - for _, script in ipairs( scripts ) do - loadScript( script.name, script.path, maxLen ) - end - else - mud.print( "#sfailed!\n> `%s' isn't a directory", ScriptsDir ) + for _, script in ipairs( scripts ) do + loadScript( script.name, script.path, maxLen ) end else - mud.print( "#sfailed!\n> %s", err ) + mud.print( "#lrfailed!\n#s> `%s' isn't a directory", dir ) end - - mud.event( "scriptsLoaded" ) end -local function closeScripts() +local function loadScripts( exe_path ) + for _, dir in ipairs( ScriptsDirs ) do + loadScriptsFrom( dir ) + end + + loadScriptsFrom( exe_path .. "/scripts" ) + + mud.event( "scriptsLoaded" ) end return { load = loadScripts, - close = closeScripts, } diff --git a/src/script.cc b/src/script.cc @@ -10,6 +10,8 @@ #include <lua.hpp> #endif +#include "whereami/whereami.h" + #if LUA_VERSION_NUM < 502 #define luaL_len lua_objlen #endif @@ -246,6 +248,26 @@ extern "C" int mud_set_font( lua_State * L ) { } // anon namespace +static void push_exe_dir( lua_State * L ) { + int len = wai_getExecutablePath( NULL, 0, NULL ); + if( len == -1 ) { + lua_pushliteral( L, "." ); + } + else { + char * buf = alloc_many< char >( len ); + + int dirlen; + if( wai_getExecutablePath( buf, len, &dirlen ) == -1 ) { + lua_pushliteral( L, "." ); + } + else { + lua_pushlstring( L, buf, dirlen ); + } + + free( buf ); + } +} + #if PLATFORM_WINDOWS extern "C" int luaopen_lpeg( lua_State * L ); extern "C" int luaopen_lfs( lua_State * L ); @@ -293,7 +315,9 @@ void script_init() { lua_pushcfunction( lua, mud_set_font ); - pcall( 12, "Error running main.lua" ); + push_exe_dir( lua ); + + pcall( 13, "Error running main.lua" ); } void script_term() {