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