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