17#define HASH_FUNCTION(keyptr, keylen, hashv) \
19 if (!(keyptr) || (keylen) == 0) { \
22 (hashv) = fnv1a_hash_bytes((keyptr), (keylen)); \
31#define BIN_SUFFIX ".exe"
55#define PLATFORM_MAX_PATH_LENGTH 32767
56#elif defined(__linux__)
59#define PLATFORM_MAX_PATH_LENGTH 4096
61#define PLATFORM_MAX_PATH_LENGTH PATH_MAX
63#elif defined(__APPLE__)
66#define PLATFORM_MAX_PATH_LENGTH 1024
68#define PLATFORM_MAX_PATH_LENGTH PATH_MAX
72#define PLATFORM_MAX_PATH_LENGTH 4096
137static atomic_bool g_cache_initialized =
false;
142static bool is_executable_file(
const char *path) {
146 DWORD attrs = GetFileAttributesA(path);
147 if (attrs == INVALID_FILE_ATTRIBUTES) {
151 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
155 HANDLE h = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
156 if (h == INVALID_HANDLE_VALUE) {
163 return (access(path, X_OK) == 0);
170static bool check_binary_in_path_uncached(
const char *bin_name) {
171 char bin_with_suffix[512];
176 if (strstr(bin_name,
".exe") == NULL) {
179 SAFE_STRNCPY(bin_with_suffix, bin_name,
sizeof(bin_with_suffix));
182 SAFE_STRNCPY(bin_with_suffix, bin_name,
sizeof(bin_with_suffix));
192 size_t path_len = strlen(path_env);
193 char *path_copy =
SAFE_MALLOC(path_len + 1,
char *);
201 char *saveptr = NULL;
204 while (dir != NULL) {
206 if (dir[0] ==
'\0') {
215 if (is_executable_file(full_path)) {
230static void init_cache_once(
void) {
231 bool expected =
false;
232 if (!atomic_compare_exchange_strong(&g_cache_initialized, &expected,
true)) {
237 g_bin_path_cache = NULL;
241 log_error(
"Failed to initialize binary PATH cache rwlock");
242 atomic_store(&g_cache_initialized,
false);
262 if (!atomic_load(&g_cache_initialized)) {
266 if (g_bin_path_cache) {
271 HASH_ITER(hh, g_bin_path_cache, entry, tmp) {
272 HASH_DELETE(hh, g_bin_path_cache, entry);
273 free_cache_entry(entry);
278 g_bin_path_cache = NULL;
281 atomic_store(&g_cache_initialized,
false);
289 if (!bin_name || bin_name[0] ==
'\0') {
295 if (!atomic_load(&g_cache_initialized)) {
298 return check_binary_in_path_uncached(bin_name);
304 HASH_FIND_STR(g_bin_path_cache, bin_name, entry);
317 bool found = check_binary_in_path_uncached(bin_name);
337 HASH_ADD_KEYPTR(hh, g_bin_path_cache, entry->
bin_name, strlen(entry->
bin_name), entry);
352 if (!exe_path || path_size == 0) {
358 DWORD len = GetModuleFileNameA(NULL, exe_path, (DWORD)path_size);
363 if (len >= path_size) {
365 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (
size_t)len,
371#elif defined(__linux__)
372 ssize_t len = readlink(
"/proc/self/exe", exe_path, path_size - 1);
377 if ((
size_t)len >= path_size - 1) {
379 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (
size_t)len,
383 exe_path[len] =
'\0';
386#elif defined(__APPLE__)
388 int result = _NSGetExecutablePath(exe_path, &bufsize);
#️⃣ FNV-1a Hash Function Implementation
#define SAFE_STRNCPY(dst, src, size)
#define SAFE_MALLOC(size, cast)
#define SAFE_GETENV(name)
#define SAFE_STRERROR(errnum)
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
const char ** log_get_color_array(void)
Get the appropriate color array based on terminal capabilities.
#define log_error(...)
Log an ERROR message.
#define log_debug(...)
Log a DEBUG message.
📝 Logging API with multiple log levels and terminal output control
Binary PATH cache entry structure for binary detection caching.
bool in_path
Whether binary was found in PATH (true = found, false = not found)
char * bin_name
Binary name string (allocated, owned by cache) - also used as uthash key.
UT_hash_handle hh
uthash handle
#define PLATFORM_MAX_PATH_LENGTH
#️⃣ Wrapper for uthash.h that ensures common.h is included first