From 5c5692ae98d4130cf4f7ee8ac54681239e88cd7e Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 16 Jul 2025 15:56:36 +0200 Subject: [PATCH] Dev (#2307) * Refactor whereami --- src/utils/file_utils.c | 17 +- src/utils/whereami.c | 423 ++++++++--------------------------------- src/utils/whereami.h | 5 +- 3 files changed, 84 insertions(+), 361 deletions(-) diff --git a/src/utils/file_utils.c b/src/utils/file_utils.c index fd3be897c..ec301e349 100644 --- a/src/utils/file_utils.c +++ b/src/utils/file_utils.c @@ -375,14 +375,8 @@ static inline const char *lib_find(const char *exe_path, const char *rel_path) const char *find_rel_exe_dir(const char *dir) { - char *path = find_executable_path(); + const char *path = find_executable_path(); INFO_LOG("Detected executable path at %s", path); - size_t strlen_path = strlen(path); - // Remove any last path slash - if (strlen_path > 1 && (path[strlen_path - 1] == '/' || path[strlen_path - 1] == '\\')) - { - path[strlen_path - 1] = '\0'; - } struct stat info; const char *attempts[5] = { "/../", "/lib/", "/../lib/", "/", "/../../lib/" }; @@ -412,16 +406,10 @@ const char *find_lib_dir(void) } return strdup(lib_dir_env); } - char *path = find_executable_path(); + const char *path = find_executable_path(); INFO_LOG("Detected executable path at %s", path); - size_t strlen_path = strlen(path); - // Remove any last path slash - if (strlen_path > 1 && (path[strlen_path - 1] == '/' || path[strlen_path - 1] == '\\')) - { - path[strlen_path - 1] = '\0'; - } const char *lib_path = NULL; if ((lib_path = lib_find(path, "/../lib/c3/"))) goto DONE; if ((lib_path = lib_find(path, "/../lib/"))) goto DONE; @@ -436,7 +424,6 @@ const char *find_lib_dir(void) INFO_LOG("Could not find the standard library /lib/std/"); DONE:; - free(path); return lib_path; } diff --git a/src/utils/whereami.c b/src/utils/whereami.c index ada531243..ea76d34b5 100644 --- a/src/utils/whereami.c +++ b/src/utils/whereami.c @@ -5,203 +5,47 @@ // Code based off Gregory Pakosz's whereami. #include "whereami.h" +#include "lib.h" #include "common.h" +#define MAX_EXE_PATH 4096 + #if PLATFORM_WINDOWS +#include #include -#endif -#include - -#ifndef NOINLINE -#if defined(_MSC_VER) -#define NOINLINE __declspec(noinline) -#elif defined(__GNUC__) -#define NOINLINE __attribute__((noinline)) -#else -#error unsupported compiler -#endif -#endif - -#if defined(_MSC_VER) -#define RETURN_ADDRESS() _ReturnAddress() -#elif defined(__GNUC__) -#define RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) -#else -#error unsupported compiler -#endif - -#if PLATFORM_WINDOWS - -#if defined(_MSC_VER) -#pragma warning(push, 3) -#endif - -#include - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -static int get_module_path_(HMODULE module, char *out, int capacity, int *dirname_length) +static int get_executable_path_raw(char *buffer) { - 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 *) 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) - { - free(path); - } + wchar_t buffer1[MAX_EXE_PATH]; + wchar_t buffer2[MAX_EXE_PATH]; + DWORD size = GetModuleFileNameW(NULL, buffer1, MAX_EXE_PATH); + if (!size) error_exit("Failed to get module path."); + if (size == MAX_EXE_PATH) error_exit("Module path too long"); + if (!_wfullpath(buffer2, buffer1, MAX_EXE_PATH)) error_exit("Failed to get the full module path."); + int length_ = (int)wcslen(buffer2); + int length = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, buffer, MAX_EXE_PATH, NULL, NULL); + if (!length || len == MAX_EXE_PATH) error_exit("Failed to convert module path."); + buffer[length] = 0; return length; } -NOINLINE -int get_executable_path_raw(char *out, int capacity, int *dirname_length) -{ - return get_module_path_(NULL, out, capacity, dirname_length); -} - - -#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(USE_PROC_SELF_EXE) - -#include -#include -#include - -#if defined(__linux__) - -#include - -#else -#include -#endif -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -#include +#elif defined(__linux__) #if !defined(PROC_SELF_EXE) -#if defined(__sun) -#define PROC_SELF_EXE "/proc/self/path/a.out" -#else #define PROC_SELF_EXE "/proc/self/exe" #endif -#endif -static int get_executable_path_raw(char *out, int capacity, int *dirname_length) + +static int get_executable_path_raw(char *out) { - char buffer[PATH_MAX]; - char *resolved = NULL; - int length = -1; - - for (;;) - { - resolved = realpath(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; - } - + char buffer[MAX_EXE_PATH]; + char *resolved = realpath(PROC_SELF_EXE, buffer); + if (!resolved) error_exit("Failed to retrieve executable path"); + int length = (int)strlen(resolved); + if (length >= MAX_EXE_PATH) error_exit("Executable path too long"); + memcpy(out, resolved, length); + out[length] = 0; return length; } @@ -210,64 +54,23 @@ static int get_executable_path_raw(char *out, int capacity, int *dirname_length) #define _DARWIN_BETTER_REALPATH #include -#include #include -#include "lib.h" - -static int get_executable_path_raw(char *out, int capacity, int *dirname_length) +static int get_executable_path_raw(char *out) { - char buffer1[PATH_MAX]; - char buffer2[PATH_MAX]; - char *path = buffer1; - char *resolved = NULL; - int length = -1; + assert(out); + char buffer1[MAX_EXE_PATH]; + char buffer2[MAX_EXE_PATH]; - for (;;) - { - uint32_t size = (uint32_t) sizeof(buffer1); - if (_NSGetExecutablePath(path, &size) == -1) - { - path = (char *) cmalloc(size); - if (!_NSGetExecutablePath(path, &size)) - { - break; - } - } + uint32_t size = MAX_EXE_PATH; + if (_NSGetExecutablePath(buffer1, &size) == -1) error_exit("Executable path too long"); + char *resolved = realpath(buffer1, buffer2); + if (!resolved) error_exit("Failed to retrieve executable path"); - 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) - { - free(path); - } + int length = (int)strlen(resolved); + if (length >= MAX_EXE_PATH) error_exit("Executable path too long"); + memcpy(out, resolved, length); + out[length] = 0; return length; } @@ -283,58 +86,21 @@ static int get_executable_path_raw(char *out, int capacity, int *dirname_length) #define PROC_SELF_EXE "/proc/self/exefile" #endif -static int get_executable_path_raw(char *out, int capacity, int *dirname_length) +static int get_executable_path_raw(char *out) { - char buffer1[PATH_MAX]; - char buffer2[PATH_MAX]; - char *resolved = NULL; - FILE *self_exe = NULL; - int length = -1; - - for (;;) - { - self_exe = fopen(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; - } - + assert(out); + char buffer1[MAX_EXE_PATH]; + char buffer2[MAX_EXE_PATH]; + FILE *self_exe = fopen(PROC_SELF_EXE, "r"); + if (!self_exe) error_exit("Failed to find the executable path"); + if (!fgets(buffer1, PATH_MAX, self_exe)) error_exit("Failed to find the executable path"); + char *resolved = realpath(buffer1, buffer2); + if (!resolved) error_exit("Failed to resolve the executable path"); + int length = (int) strlen(resolved); + if (length >= MAX_EXE_PATH) error_exit("Executable path too long"); + memcpy(out, resolved, length); + out[length] = 0; fclose(self_exe); - return length; } @@ -348,62 +114,29 @@ static int get_executable_path_raw(char *out, int capacity, int *dirname_length) #include #include -static int get_executable_path_raw(char *out, int capacity, int *dirname_length) +static int get_executable_path_raw(char *out) { - char buffer1[PATH_MAX]; - char buffer2[PATH_MAX]; - char *path = buffer1; - char *resolved = NULL; - int length = -1; - - for (;;) - { + char buffer1[MAX_EXE_PATH]; + char buffer2[MAX_EXE_PATH]; #if defined(__NetBSD__) - int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; #else - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; #endif - size_t size = sizeof(buffer1); + size_t size = MAX_EXE_PATH; - 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) + if (sysctl(mib, (u_int) (sizeof(mib) / sizeof(mib[0])), buffer1, &size, NULL, 0) != 0) { - free(path); + error_exit("Failed to get the executable path"); } + char *resolved = realpath(buffer1, buffer2); + if (!resolved) error_exit("Failed to resolve the executable path"); + int length = (int)strlen(resolved); + + if (length >= MAX_EXE_PATH) error_exit("Executable path too long"); + memcpy(out, resolved, length); + out[length] = 0; return length; } @@ -413,29 +146,31 @@ static int get_executable_path_raw(char *out, int capacity, int *dirname_length) #endif -char *find_executable_path(void) +static char buffer[MAX_EXE_PATH]; +static bool path_found = false; +const char *find_executable_path(void) { - int len = get_executable_path_raw(NULL, 0, NULL); - if (len < 0) return ""; - char *path = malloc((unsigned)len + 1); - get_executable_path_raw(path, len, NULL); - path[len] = '\0'; + if (path_found) return buffer; + int len = get_executable_path_raw(buffer); + path_found = true; + // Convert backslashes to forward slashes, + // but consider the case of d:\foo? for (int i = 0; i < len; ++i) { - if ('\\' == path[i]) path[i] = '/'; + if ('\\' == buffer[i]) buffer[i] = '/'; } for (int i = len - 1; i >= 0; i--) { - switch (path[i]) + switch (buffer[i]) { case '/': case '\\': - path[i + 1] = '\0'; - return path; + buffer[i] = '\0'; + return buffer; default: break; } } - path[len] = '\0'; - return path; + buffer[0] = 0; + return buffer; } \ No newline at end of file diff --git a/src/utils/whereami.h b/src/utils/whereami.h index 6c19de0f3..b6bae985c 100644 --- a/src/utils/whereami.h +++ b/src/utils/whereami.h @@ -1,7 +1,8 @@ #pragma once -// Copyright (c) 2020 Christoffer Lerno. All rights reserved. +// Copyright (c) 2020-2025 Christoffer Lerno. All rights reserved. // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. -char *find_executable_path(void); +// This guarantees that the path does not end with '/' +const char *find_executable_path(void);