hoshi-lang dev
Yet another programming language
Loading...
Searching...
No Matches
fs.cpp
Go to the documentation of this file.
1/*
2// Created by XIaokang00010 on 2025/12/09.
3*/
4
5// Ensure standard functions like strdup, realpath, etc., are exposed
6
7#ifdef _WIN32
8#include <cstring>
9#include <windows.h>
10
11struct DirectoryHandle {
12 HANDLE hFind = INVALID_HANDLE_VALUE;
13 WIN32_FIND_DATAA findFileData;
14 bool firstEntry = true;
15};
16
17#else
18#include <dirent.h> // POSIX header for directory operations
19#endif
20
21#define _POSIX_C_SOURCE 200809L
22#define _XOPEN_SOURCE 700
23#define _CRT_SECURE_NO_WARNINGS // For MSVC
24
25#include "fs.h"
26
27#include <errno.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/stat.h>
32
33#ifdef _WIN32
34#include <direct.h>
35#include <io.h>
36#include <windows.h>
37
38// Windows specific types and macros for 64-bit file support
39#define stat_struct struct __stat64
40#define stat_func _stat64
41#define strdup _strdup
42#define getcwd _getcwd
43
44// Windows doesn't always define standard POSIX file type macros
45#ifndef S_ISREG
46#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
47#endif
48#ifndef S_ISDIR
49#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
50#endif
51
52#define PATH_SEPARATOR '\\'
53#else
54#include <limits.h>
55#include <pwd.h>
56#include <sys/types.h>
57#include <unistd.h>
58
59#define stat_struct struct stat
60#define stat_func stat
61#define PATH_SEPARATOR '/'
62#endif
63
64static int get_stat(const char *path, stat_struct *buf) {
65 if (!path || !*path)
66 return -1;
67 return stat_func(path, buf);
68}
69
70bool runtime_fs_exists(const char *path) {
71#ifdef _WIN32
72 return _access(path, 0) == 0;
73#else
74 return access(path, F_OK) == 0;
75#endif
76}
77
78bool runtime_fs_isfile(const char *path) {
80 if (get_stat(path, &s) != 0)
81 return false;
82 return S_ISREG(s.st_mode);
83}
84
85bool runtime_fs_isdir(const char *path) {
87 if (get_stat(path, &s) != 0)
88 return false;
89 return S_ISDIR(s.st_mode);
90}
91
92bool runtime_fs_issymlink(const char *path) {
93#ifdef _WIN32
94 DWORD attr = GetFileAttributesA(path);
95 if (attr == INVALID_FILE_ATTRIBUTES)
96 return false;
97 return (attr & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
98#else
99 struct stat s;
100 if (lstat(path, &s) != 0)
101 return false;
102 return S_ISLNK(s.st_mode);
103#endif
104}
105
106int64_t runtime_fs_get_mtime(const char *path) {
107 stat_struct s;
108 if (get_stat(path, &s) != 0)
109 return -1;
110 return (int64_t)s.st_mtime;
111}
112
113uint64_t runtime_fs_get_size(const char *path) {
114 stat_struct s;
115 if (get_stat(path, &s) != 0)
116 return 0;
117 return (uint64_t)s.st_size;
118}
119
120int64_t runtime_fs_get_ctime(const char *path) {
121 stat_struct s;
122 if (get_stat(path, &s) != 0)
123 return -1;
124 return (int64_t)s.st_ctime;
125}
126
127int64_t runtime_fs_get_atime(const char *path) {
128 stat_struct s;
129 if (get_stat(path, &s) != 0)
130 return -1;
131 return (int64_t)s.st_atime;
132}
133
134int runtime_fs_get_uid(const char *path) {
135 stat_struct s;
136 if (get_stat(path, &s) != 0)
137 return -1;
138 return (int)s.st_uid; // Note: On Windows this is usually 0
139}
140
142#ifdef _WIN32
143 DWORD len = GetTempPathA(0, nullptr);
144 if (len == 0)
145 return nullptr;
146
147 char *buf = (char *)malloc(len + 1);
148 if (!buf)
149 return nullptr;
150
151 if (GetTempPathA(len + 1, buf) == 0) {
152 free(buf);
153 return nullptr;
154 }
155
156 // Remove trailing backslash if present (consistency preference)
157 size_t actual_len = strlen(buf);
158 if (actual_len > 0 && buf[actual_len - 1] == '\\') {
159 buf[actual_len - 1] = '\0';
160 }
161 return buf;
162#else
163 const char *env_temp = getenv("TMPDIR");
164 if (!env_temp)
165 env_temp = getenv("TMP");
166 if (!env_temp)
167 env_temp = getenv("TEMP");
168 if (!env_temp)
169 env_temp = getenv("TEMPDIR");
170 if (!env_temp)
171 env_temp = "/tmp";
172
173 return strdup(env_temp);
174#endif
175}
176
178#ifdef _WIN32
179 const char *drive = getenv("HOMEDRIVE");
180 const char *path = getenv("HOMEPATH");
181 const char *userprofile = getenv("USERPROFILE");
182
183 if (userprofile) {
184 return strdup(userprofile);
185 } else if (drive && path) {
186 size_t len = strlen(drive) + strlen(path) + 1;
187 char *buf = (char *)malloc(len);
188 if (buf) {
189 sprintf(buf, "%s%s", drive, path);
190 }
191 return buf;
192 }
193 return nullptr;
194#else
195 const char *home = getenv("HOME");
196 if (home) {
197 return strdup(home);
198 }
199
200 // Fallback using password database
201 struct passwd *pwd = getpwuid(getuid());
202 if (pwd) {
203 return strdup(pwd->pw_dir);
204 }
205 return nullptr;
206#endif
207}
208
210 // Portable way to get CWD without guessing buffer size
211 // Start with a reasonable size, typically 1024 or 4096
212 size_t size = 1024;
213 char *buf = (char *)malloc(size);
214
215 if (!buf)
216 return nullptr;
217
218 while (getcwd(buf, (int)size) == nullptr) {
219 if (errno == ERANGE) {
220 size *= 2;
221 char *new_buf = (char *)realloc(buf, size);
222 if (!new_buf) {
223 free(buf);
224 return nullptr;
225 }
226 buf = new_buf;
227 } else {
228 free(buf);
229 return nullptr;
230 }
231 }
232
233 // Optional: Trim unused memory
234 char *final_buf = strdup(buf);
235 free(buf);
236 return final_buf;
237}
238
239char *runtime_fs_realpath(const char *path) {
240 if (!path)
241 return nullptr;
242#ifdef _WIN32
243 // _fullpath with nullptr automatically mallocs
244 return _fullpath(nullptr, path, 0);
245#else
246 // realpath with nullptr automatically mallocs (POSIX.1-2008)
247 return realpath(path, nullptr);
248#endif
249}
250
251void runtime_fs_finalize(void *res) {
252 if (res) {
253 free(res);
254 }
255}
256
257bool runtime_fs_mkdir(const char *path, int mode) {
258 if (!path)
259 return false;
260#ifdef _WIN32
261 return _mkdir(path) == 0;
262#else
263 return mkdir(path, mode) == 0;
264#endif
265}
266
267bool runtime_fs_rmdir(const char *path) {
268 if (!path)
269 return false;
270#ifdef _WIN32
271 return _rmdir(path) == 0;
272#else
273 return rmdir(path) == 0;
274#endif
275}
276
277bool runtime_fs_remove(const char *path) {
278#ifdef _WIN32
279 if (remove(path) == 0) {
280 return true;
281 }
282 return RemoveDirectoryA(path) != 0;
283#else
284 return remove(path) == 0;
285#endif
286}
287
288bool runtime_fs_rename(const char *old_path, const char *new_path) {
289 return rename(old_path, new_path) == 0;
290}
291
292void *runtime_fs_opendir(const char *name) {
293#ifdef _WIN32
294 char *concatenated = (char *)malloc(strlen(name) + 3);
295 if (!concatenated)
296 return nullptr;
297 strcpy(concatenated, name);
298 strcat(concatenated, "/*");
299
300 DirectoryHandle *dir = (DirectoryHandle *)malloc(sizeof(DirectoryHandle));
301 if (!dir) {
302 free(concatenated);
303 return nullptr;
304 }
305 dir->hFind = FindFirstFileA(concatenated, &(dir->findFileData));
306
307 if (dir->hFind == INVALID_HANDLE_VALUE) {
308 free(dir);
309 free(concatenated);
310 return nullptr;
311 }
312 dir->firstEntry = true;
313 return dir;
314#else
315 return opendir(name);
316#endif
317}
318
319char *runtime_fs_readdir(void *dir) {
320 if (!dir)
321 return nullptr;
322
323#ifdef _WIN32
324 DirectoryHandle *handle = (DirectoryHandle *)dir;
325
326 if (handle->firstEntry) {
327 handle->firstEntry = false;
328 return handle->findFileData.cFileName;
329 }
330
331 if (FindNextFileA(handle->hFind, &(handle->findFileData)) != 0) {
332 return handle->findFileData.cFileName;
333 }
334 return nullptr;
335#else
336 struct dirent *entry = readdir((DIR *)dir);
337 if (!entry) {
338 return nullptr;
339 }
340 return entry->d_name;
341#endif
342}
343
344void runtime_fs_closedir(void *dir) {
345 if (!dir)
346 return;
347
348#ifdef _WIN32
349 DirectoryHandle *handle = (DirectoryHandle *)dir;
350 if (handle->hFind != INVALID_HANDLE_VALUE) {
351 FindClose(handle->hFind);
352 }
353 free(dir);
354#else
355 closedir((DIR *)dir);
356#endif
357}
358
359bool runtime_fs_symlink(const char *src_path, const char *dest_path) {
360#ifdef _WIN32
361 DWORD flags = 0;
362 if (runtime_fs_isdir(src_path)) {
363 flags |= 0x1; // SYMBOLIC_LINK_FLAG_DIRECTORY
364 }
365 flags |= 0x2;
366 return CreateSymbolicLinkA(dest_path, src_path, flags) != 0;
367#else
368 int rc = symlink(src_path, dest_path);
369 return rc == 0;
370#endif
371}
char * runtime_fs_home_dir()
Definition fs.cpp:177
bool runtime_fs_rmdir(const char *path)
Definition fs.cpp:267
int64_t runtime_fs_get_atime(const char *path)
Definition fs.cpp:127
static int get_stat(const char *path, stat_struct *buf)
Definition fs.cpp:64
void runtime_fs_finalize(void *res)
Definition fs.cpp:251
bool runtime_fs_symlink(const char *src_path, const char *dest_path)
Definition fs.cpp:359
char * runtime_fs_cwd()
Definition fs.cpp:209
bool runtime_fs_isdir(const char *path)
Definition fs.cpp:85
int runtime_fs_get_uid(const char *path)
Definition fs.cpp:134
bool runtime_fs_rename(const char *old_path, const char *new_path)
Definition fs.cpp:288
#define stat_struct
Definition fs.cpp:59
int64_t runtime_fs_get_mtime(const char *path)
Definition fs.cpp:106
void runtime_fs_closedir(void *dir)
Definition fs.cpp:344
bool runtime_fs_issymlink(const char *path)
Definition fs.cpp:92
int64_t runtime_fs_get_ctime(const char *path)
Definition fs.cpp:120
char * runtime_fs_temp_dir()
Definition fs.cpp:141
bool runtime_fs_exists(const char *path)
Definition fs.cpp:70
char * runtime_fs_realpath(const char *path)
Definition fs.cpp:239
void * runtime_fs_opendir(const char *name)
Definition fs.cpp:292
bool runtime_fs_mkdir(const char *path, int mode)
Definition fs.cpp:257
#define stat_func
Definition fs.cpp:60
bool runtime_fs_isfile(const char *path)
Definition fs.cpp:78
char * runtime_fs_readdir(void *dir)
Definition fs.cpp:319
bool runtime_fs_remove(const char *path)
Definition fs.cpp:277
uint64_t runtime_fs_get_size(const char *path)
Definition fs.cpp:113