ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
filesystem.c
Go to the documentation of this file.
1
8#include <ascii-chat/common.h>
12#include <ascii-chat/log/log.h>
13#include <errno.h>
14#include <string.h>
15#include <stdio.h>
16
17#ifdef __APPLE__
18#include <mach-o/dyld.h>
19#endif
20
21// Thread-local storage for error messages (avoids allocation)
22static _Thread_local char error_msg_buffer[512];
23
24const char *file_read_error_message(const char *path) {
25 switch (errno) {
26 case ENOENT:
27 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "File does not exist: %s", path);
28 break;
29 case EACCES:
30 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Permission denied (cannot read): %s", path);
31 break;
32 case EISDIR:
33 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Is a directory, not a file: %s", path);
34 break;
35 default:
36 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Failed to open for reading: %s (%s)", path,
38 break;
39 }
40 return error_msg_buffer;
41}
42
43const char *file_write_error_message(const char *path) {
44 switch (errno) {
45 case ENOENT:
46 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Directory does not exist: %s", path);
47 break;
48 case EACCES:
49 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Permission denied (cannot write): %s", path);
50 break;
51 case EROFS:
52 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Read-only filesystem: %s", path);
53 break;
54 case ENOSPC:
55 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "No space left on device: %s", path);
56 break;
57 case EISDIR:
58 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Is a directory, not a file: %s", path);
59 break;
60 default:
61 snprintf(error_msg_buffer, sizeof(error_msg_buffer), "Failed to open for writing: %s (%s)", path,
63 break;
64 }
65 return error_msg_buffer;
66}
67
68/* ============================================================================
69 * Binary PATH Cache (moved from system.c)
70 * ============================================================================ */
71
75typedef struct {
76 char *bin_name;
77 bool in_path;
78 UT_hash_handle hh;
80
81static bin_cache_entry_t *g_bin_path_cache = NULL;
82static rwlock_t g_cache_rwlock;
83static lifecycle_t g_cache_lc = LIFECYCLE_INIT;
84
88static void init_cache_once(void) {
89 if (!lifecycle_init(&g_cache_lc, "cache")) {
90 return;
91 }
92
93 g_bin_path_cache = NULL;
94
95 if (rwlock_init(&g_cache_rwlock, "binary_cache") != 0) {
96 log_error("Failed to initialize binary PATH cache rwlock");
97 lifecycle_init_abort(&g_cache_lc);
98 }
99}
100
104static void free_cache_entry(bin_cache_entry_t *entry) {
105 if (entry) {
106 if (entry->bin_name) {
107 SAFE_FREE(entry->bin_name);
108 }
109 SAFE_FREE(entry);
110 }
111}
112
114 if (!lifecycle_shutdown(&g_cache_lc)) {
115 return;
116 }
117
118 if (g_bin_path_cache) {
119 rwlock_wrlock(&g_cache_rwlock);
120
121 bin_cache_entry_t *entry, *tmp;
122 HASH_ITER(hh, g_bin_path_cache, entry, tmp) {
123 HASH_DELETE(hh, g_bin_path_cache, entry);
124 free_cache_entry(entry);
125 }
126
127 rwlock_wrunlock(&g_cache_rwlock);
128 rwlock_destroy(&g_cache_rwlock);
129 g_bin_path_cache = NULL;
130 }
131}
132
133bool platform_is_binary_in_path(const char *bin_name) {
134 if (!bin_name || bin_name[0] == '\0') {
135 return false;
136 }
137
138 init_cache_once();
139 if (!lifecycle_is_initialized(&g_cache_lc)) {
140 SET_ERRNO(ERROR_INVALID_STATE, "Binary PATH cache not initialized, checking directly (this should never happen)");
141 return check_binary_in_path_uncached(bin_name);
142 }
143
144 rwlock_rdlock(&g_cache_rwlock);
145 bin_cache_entry_t *entry = NULL;
146 HASH_FIND_STR(g_bin_path_cache, bin_name, entry);
147 rwlock_rdunlock(&g_cache_rwlock);
148
149 if (entry) {
150 log_dev("Binary '%s' %s in PATH (%s)", bin_name, colored_string(LOG_COLOR_INFO, "found"),
151 colored_string(LOG_COLOR_WARN, "cached"));
152 return entry->in_path;
153 }
154
155 bool found = check_binary_in_path_uncached(bin_name);
156
158 if (!entry) {
159 SET_ERRNO(ERROR_MEMORY, "Failed to allocate cache entry");
160 return found;
161 }
162
163 entry->bin_name = platform_strdup(bin_name);
164 if (!entry->bin_name) {
165 SET_ERRNO(ERROR_MEMORY, "Failed to duplicate binary name");
166 SAFE_FREE(entry);
167 return found;
168 }
169
170 entry->in_path = found;
171
172 rwlock_wrlock(&g_cache_rwlock);
173 HASH_ADD_KEYPTR(hh, g_bin_path_cache, entry->bin_name, strlen(entry->bin_name), entry);
174 rwlock_wrunlock(&g_cache_rwlock);
175
176 char *found_notfound_str =
177 found ? colored_string(LOG_COLOR_INFO, "found") : colored_string(LOG_COLOR_ERROR, "NOT found");
178 log_dev("Binary '%s' %s in PATH", bin_name, found_notfound_str);
179
180 return found;
181}
182
189// On Windows, platform_get_executable_path is in windows/filesystem.c
190#ifndef _WIN32
191bool platform_get_executable_path(char *exe_path, size_t path_size) {
192 if (!exe_path || path_size == 0) {
193 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters: exe_path=%p, path_size=%zu", (void *)exe_path, path_size);
194 return false;
195 }
196
197#ifdef _WIN32
198 DWORD len = GetModuleFileNameA(NULL, exe_path, (DWORD)path_size);
199 if (len == 0) {
200 SET_ERRNO_SYS(ERROR_INVALID_STATE, "GetModuleFileNameA failed: error code %lu", GetLastError());
201 return false;
202 }
203 if (len >= path_size) {
205 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (size_t)len,
206 path_size);
207 return false;
208 }
209 return true;
210
211#elif defined(__linux__)
212 ssize_t len = readlink("/proc/self/exe", exe_path, path_size - 1);
213 if (len < 0) {
214 SET_ERRNO_SYS(ERROR_INVALID_STATE, "readlink(\"/proc/self/exe\") failed: %s", SAFE_STRERROR(errno));
215 return false;
216 }
217 if ((size_t)len >= path_size - 1) {
219 "Executable path exceeds buffer size (path length >= %zu bytes, buffer size = %zu bytes)", (size_t)len,
220 path_size);
221 return false;
222 }
223 // NOLINTNEXTLINE(clang-analyzer-security.ArrayBound) - len bounded by path_size check above
224 exe_path[len] = '\0';
225 return true;
226
227#elif defined(__APPLE__)
228 uint32_t bufsize = (uint32_t)path_size;
229 int result = _NSGetExecutablePath(exe_path, &bufsize);
230 if (result != 0) {
231 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "_NSGetExecutablePath failed: path requires %u bytes, buffer size = %zu bytes",
232 bufsize, path_size);
233 return false;
234 }
235 return true;
236
237#else
238 SET_ERRNO(ERROR_GENERAL, "Unsupported platform - cannot get executable path");
239 return false;
240#endif
241}
242#endif // !_WIN32
⚠️‼️ Comprehensive thread-local error context system for ascii-chat
⚙️ Common definitions, error codes, macros, and types shared throughout the application
Cross-platform error handling utilities.
Cross-platform filesystem operations.
unsigned int uint32_t
Definition common.h:58
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define SAFE_STRERROR(errnum)
Definition common.h:465
#define SET_ERRNO_SYS(code, context_msg,...)
Set error code with custom message and system error context, returning the error code.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:56
@ ERROR_GENERAL
Definition error_codes.h:52
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER_OVERFLOW
#define log_dev(...)
Log a DEV message (most verbose, development only)
Definition log/log.h:534
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
@ LOG_COLOR_ERROR
Definition log/log.h:135
@ LOG_COLOR_INFO
Definition log/log.h:133
@ LOG_COLOR_WARN
Definition log/log.h:134
const char * file_write_error_message(const char *path)
Get human-readable error message for file write failure.
Definition filesystem.c:43
bool platform_get_executable_path(char *exe_path, size_t path_size)
Get the path to the current executable.
Definition filesystem.c:191
#define rwlock_wrunlock(lock)
Release a write lock (with debug tracking in debug builds)
Definition rwlock.h:349
int rwlock_init(rwlock_t *rwlock, const char *name)
Initialize a read-write lock with a name.
Definition threading.c:65
int rwlock_destroy(rwlock_t *lock)
Destroy a read-write lock.
#define rwlock_rdlock(lock)
Acquire a read lock (with debug tracking in debug builds)
Definition rwlock.h:294
bool platform_is_binary_in_path(const char *bin_name)
Check if a binary is available in the system PATH.
Definition filesystem.c:133
char * platform_strdup(const char *s)
Duplicate string (strdup replacement)
const char * platform_strerror(int errnum)
Get human-readable error message for a system error code.
Definition wasm/system.c:46
#define rwlock_wrlock(lock)
Acquire a write lock (with debug tracking in debug builds)
Definition rwlock.h:313
int errno
void platform_cleanup_binary_path_cache(void)
Cleanup the binary PATH cache.
Definition filesystem.c:113
const char * file_read_error_message(const char *path)
Get human-readable error message for file read failure.
Definition filesystem.c:24
#define rwlock_rdunlock(lock)
Release a read lock (with debug tracking in debug builds)
Definition rwlock.h:331
const char * colored_string(log_color_t color, const char *text)
Build a colored string for terminal output.
void lifecycle_init_abort(lifecycle_t *lc)
Definition lifecycle.c:99
bool lifecycle_is_initialized(const lifecycle_t *lc)
Definition lifecycle.c:155
bool lifecycle_init(lifecycle_t *lc, const char *name)
Definition lifecycle.c:26
bool lifecycle_shutdown(lifecycle_t *lc)
Definition lifecycle.c:108
Lock-free module lifecycle state machine using pure stdatomic.
#define LIFECYCLE_INIT
Static initializer for module-global lifecycle variables (no sync primitive)
Definition lifecycle.h:68
📝 Logging API with multiple log levels and terminal output control
Cross-platform mutex interface for ascii-chat.
Cache entry for binary PATH lookup.
Definition filesystem.c:75
char * bin_name
Binary name string (allocated, owned by cache) - also used as uthash key.
Definition filesystem.c:76
bool in_path
Whether binary was found in PATH (true = found, false = not found)
Definition filesystem.c:77
UT_hash_handle hh
uthash handle
Definition filesystem.c:78
Read-write lock type (POSIX: pthread_rwlock_t with debug tracking)
Definition rwlock.h:61
bool check_binary_in_path_uncached(const char *bin_name)
🔤 String Manipulation and Shell Escaping Utilities