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