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