src/util/vfs.c (view raw)
1#include "util/vfs.h"
2
3#include "util/string.h"
4
5#include <fcntl.h>
6#include <dirent.h>
7
8#ifndef _WIN32
9#include <sys/mman.h>
10#define PATH_SEP '/'
11#else
12#include <io.h>
13#include <Windows.h>
14#define PATH_SEP '\\'
15#endif
16
17struct VFileFD {
18 struct VFile d;
19 int fd;
20#ifdef _WIN32
21 HANDLE hMap;
22#endif
23};
24
25static bool _vfdClose(struct VFile* vf);
26static off_t _vfdSeek(struct VFile* vf, off_t offset, int whence);
27static ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size);
28static ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size);
29static ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size);
30static void* _vfdMap(struct VFile* vf, size_t size, int flags);
31static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
32static void _vfdTruncate(struct VFile* vf, size_t size);
33
34static bool _vdClose(struct VDir* vd);
35static void _vdRewind(struct VDir* vd);
36static struct VDirEntry* _vdListNext(struct VDir* vd);
37static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode);
38
39static const char* _vdeName(struct VDirEntry* vde);
40
41struct VFile* VFileOpen(const char* path, int flags) {
42 int fd = open(path, flags, 0666);
43 return VFileFromFD(fd);
44}
45
46struct VFile* VFileFromFD(int fd) {
47 if (fd < 0) {
48 return 0;
49 }
50
51 struct VFileFD* vfd = malloc(sizeof(struct VFileFD));
52 if (!vfd) {
53 return 0;
54 }
55
56 vfd->fd = fd;
57 vfd->d.close = _vfdClose;
58 vfd->d.seek = _vfdSeek;
59 vfd->d.read = _vfdRead;
60 vfd->d.readline = _vfdReadline;
61 vfd->d.write = _vfdWrite;
62 vfd->d.map = _vfdMap;
63 vfd->d.unmap = _vfdUnmap;
64 vfd->d.truncate = _vfdTruncate;
65
66 return &vfd->d;
67}
68
69bool _vfdClose(struct VFile* vf) {
70 struct VFileFD* vfd = (struct VFileFD*) vf;
71 if (close(vfd->fd) < 0) {
72 return false;
73 }
74 free(vfd);
75 return true;
76}
77
78off_t _vfdSeek(struct VFile* vf, off_t offset, int whence) {
79 struct VFileFD* vfd = (struct VFileFD*) vf;
80 return lseek(vfd->fd, offset, whence);
81}
82
83ssize_t _vfdRead(struct VFile* vf, void* buffer, size_t size) {
84 struct VFileFD* vfd = (struct VFileFD*) vf;
85 return read(vfd->fd, buffer, size);
86}
87
88ssize_t _vfdReadline(struct VFile* vf, char* buffer, size_t size) {
89 struct VFileFD* vfd = (struct VFileFD*) vf;
90 size_t bytesRead = 0;
91 while (bytesRead < size - 1) {
92 size_t newRead = read(vfd->fd, &buffer[bytesRead], 1);
93 bytesRead += newRead;
94 if (!newRead || buffer[bytesRead] == '\n') {
95 break;
96 }
97 }
98 return buffer[bytesRead] = '\0';
99}
100
101ssize_t _vfdWrite(struct VFile* vf, const void* buffer, size_t size) {
102 struct VFileFD* vfd = (struct VFileFD*) vf;
103 return write(vfd->fd, buffer, size);
104}
105
106#ifndef _WIN32
107static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
108 struct VFileFD* vfd = (struct VFileFD*) vf;
109 int mmapFlags = MAP_PRIVATE;
110 if (flags & MAP_WRITE) {
111 mmapFlags = MAP_SHARED;
112 }
113 return mmap(0, size, PROT_READ | PROT_WRITE, mmapFlags, vfd->fd, 0);
114}
115
116static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
117 UNUSED(vf);
118 munmap(memory, size);
119}
120#else
121static void* _vfdMap(struct VFile* vf, size_t size, int flags) {
122 struct VFileFD* vfd = (struct VFileFD*) vf;
123 int createFlags = PAGE_WRITECOPY;
124 int mapFiles = FILE_MAP_COPY;
125 if (flags & MAP_WRITE) {
126 createFlags = PAGE_READWRITE;
127 mapFiles = FILE_MAP_WRITE;
128 }
129 size_t location = lseek(vfd->fd, 0, SEEK_CUR);
130 size_t fileSize = lseek(vfd->fd, 0, SEEK_END);
131 lseek(vfd->fd, location, SEEK_SET);
132 if (size > fileSize) {
133 size = fileSize;
134 }
135 vfd->hMap = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0);
136 return MapViewOfFile(hMap, mapFiles, 0, 0, size);
137}
138
139static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) {
140 UNUSED(size);
141 struct VFileFD* vfd = (struct VFileFD*) vf;
142 UnmapViewOfFile(memory);
143 CloseHandle(vfd->hMap);
144 vfd->hMap = 0;
145}
146#endif
147
148static void _vfdTruncate(struct VFile* vf, size_t size) {
149 struct VFileFD* vfd = (struct VFileFD*) vf;
150 ftruncate(vfd->fd, size);
151}
152
153struct VDirEntryDE {
154 struct VDirEntry d;
155 struct dirent* ent;
156};
157
158struct VDirDE {
159 struct VDir d;
160 DIR* de;
161 struct VDirEntryDE vde;
162 char* path;
163};
164
165struct VDir* VDirOpen(const char* path) {
166 DIR* de = opendir(path);
167 if (!de) {
168 return 0;
169 }
170
171 struct VDirDE* vd = malloc(sizeof(struct VDirDE));
172 if (!vd) {
173 return 0;
174 }
175
176 vd->d.close = _vdClose;
177 vd->d.rewind = _vdRewind;
178 vd->d.listNext = _vdListNext;
179 vd->d.openFile = _vdOpenFile;
180 vd->path = strdup(path);
181 vd->de = de;
182
183 vd->vde.d.name = _vdeName;
184
185 return &vd->d;
186}
187
188struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode) {
189 char path[PATH_MAX];
190 path[PATH_MAX - 1] = '\0';
191 struct VFile* vf;
192 if (!dir) {
193 if (!realPath) {
194 return 0;
195 }
196 char* dotPoint = strrchr(realPath, '.');
197 if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
198 return 0;
199 }
200 if (dotPoint > strrchr(realPath, '/')) {
201 int len = dotPoint - realPath;
202 strncpy(path, realPath, len);
203 path[len] = 0;
204 strncat(path + len, suffix, PATH_MAX - len - 1);
205 } else {
206 snprintf(path, PATH_MAX - 1, "%s%s", realPath, suffix);
207 }
208 vf = VFileOpen(path, mode);
209 } else {
210 snprintf(path, PATH_MAX - 1, "%s%s", prefix, suffix);
211 vf = dir->openFile(dir, path, mode);
212 }
213 return vf;
214}
215
216struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, const char* infix, const char* suffix, int mode) {
217 char path[PATH_MAX];
218 path[PATH_MAX - 1] = '\0';
219 char realPrefix[PATH_MAX];
220 realPrefix[PATH_MAX - 1] = '\0';
221 if (!dir) {
222 if (!realPath) {
223 return 0;
224 }
225 const char* separatorPoint = strrchr(realPath, '/');
226 const char* dotPoint;
227 size_t len;
228 if (!separatorPoint) {
229 strcpy(path, "./");
230 separatorPoint = realPath;
231 dotPoint = strrchr(realPath, '.');
232 } else {
233 path[0] = '\0';
234 dotPoint = strrchr(separatorPoint, '.');
235
236 if (separatorPoint - realPath + 1 >= PATH_MAX - 1) {
237 return 0;
238 }
239
240 len = separatorPoint - realPath;
241 strncat(path, realPath, len);
242 path[len] = '\0';
243 ++separatorPoint;
244 }
245
246 if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
247 return 0;
248 }
249
250 if (dotPoint >= separatorPoint) {
251 len = dotPoint - separatorPoint;
252 } else {
253 len = PATH_MAX - 1;
254 }
255
256 strncpy(realPrefix, separatorPoint, len);
257 realPrefix[len] = '\0';
258
259 prefix = realPrefix;
260 dir = VDirOpen(path);
261 }
262 if (!dir) {
263 // This shouldn't be possible
264 return 0;
265 }
266 dir->rewind(dir);
267 struct VDirEntry* dirent;
268 size_t prefixLen = strlen(prefix);
269 size_t infixLen = strlen(infix);
270 unsigned next = 0;
271 while ((dirent = dir->listNext(dir))) {
272 const char* filename = dirent->name(dirent);
273 char* dotPoint = strrchr(filename, '.');
274 size_t len = strlen(filename);
275 if (dotPoint) {
276 len = (dotPoint - filename);
277 }
278 const char* separator = strnrstr(filename, infix, len);
279 if (!separator) {
280 continue;
281 }
282 len = separator - filename;
283 if (len != prefixLen) {
284 continue;
285 }
286 if (strncmp(filename, prefix, prefixLen) == 0) {
287 int nlen;
288 separator += infixLen;
289 snprintf(path, PATH_MAX - 1, "%%u%s%%n", suffix);
290 unsigned increment;
291 if (sscanf(separator, path, &increment, &nlen) < 1) {
292 continue;
293 }
294 len = strlen(separator);
295 if (nlen < (ssize_t) len) {
296 continue;
297 }
298 if (next <= increment) {
299 next = increment + 1;
300 }
301 }
302 }
303 snprintf(path, PATH_MAX - 1, "%s%s%u%s", prefix, infix, next, suffix);
304 path[PATH_MAX - 1] = '\0';
305 return dir->openFile(dir, path, mode);
306}
307
308bool _vdClose(struct VDir* vd) {
309 struct VDirDE* vdde = (struct VDirDE*) vd;
310 if (closedir(vdde->de) < 0) {
311 return false;
312 }
313 free(vdde->path);
314 free(vdde);
315 return true;
316}
317
318void _vdRewind(struct VDir* vd) {
319 struct VDirDE* vdde = (struct VDirDE*) vd;
320 rewinddir(vdde->de);
321}
322
323struct VDirEntry* _vdListNext(struct VDir* vd) {
324 struct VDirDE* vdde = (struct VDirDE*) vd;
325 vdde->vde.ent = readdir(vdde->de);
326 if (vdde->vde.ent) {
327 return &vdde->vde.d;
328 }
329
330 return 0;
331}
332
333struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode) {
334 struct VDirDE* vdde = (struct VDirDE*) vd;
335 if (!path) {
336 return 0;
337 }
338 const char* dir = vdde->path;
339 char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
340 sprintf(combined, "%s%c%s", dir, PATH_SEP, path);
341
342 struct VFile* file = VFileOpen(combined, mode);
343 free(combined);
344 return file;
345}
346
347const char* _vdeName(struct VDirEntry* vde) {
348 struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde;
349 if (vdede->ent) {
350 return vdede->ent->d_name;
351 }
352 return 0;
353}