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