all repos — mgba @ 52c66de6945adefa72d87cf5c461af4b30fd1d68

mGBA Game Boy Advance Emulator

src/util/vfs/vfs-dirent.c (view raw)

  1/* Copyright (c) 2013-2015 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 <dirent.h>
 11
 12static bool _vdClose(struct VDir* vd);
 13static void _vdRewind(struct VDir* vd);
 14static struct VDirEntry* _vdListNext(struct VDir* vd);
 15static struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode);
 16
 17static const char* _vdeName(struct VDirEntry* vde);
 18
 19struct VDirEntryDE {
 20	struct VDirEntry d;
 21	struct dirent* ent;
 22};
 23
 24struct VDirDE {
 25	struct VDir d;
 26	DIR* de;
 27	struct VDirEntryDE vde;
 28	char* path;
 29};
 30
 31struct VDir* VDirOpen(const char* path) {
 32	DIR* de = opendir(path);
 33	if (!de) {
 34		return 0;
 35	}
 36
 37	struct VDirDE* vd = malloc(sizeof(struct VDirDE));
 38	if (!vd) {
 39		closedir(de);
 40		return 0;
 41	}
 42
 43	vd->d.close = _vdClose;
 44	vd->d.rewind = _vdRewind;
 45	vd->d.listNext = _vdListNext;
 46	vd->d.openFile = _vdOpenFile;
 47	vd->path = strdup(path);
 48	vd->de = de;
 49
 50	vd->vde.d.name = _vdeName;
 51
 52	return &vd->d;
 53}
 54
 55struct VFile* VDirOptionalOpenIncrementFile(struct VDir* dir, const char* realPath, const char* prefix, const char* infix, const char* suffix, int mode) {
 56	char path[PATH_MAX];
 57	path[PATH_MAX - 1] = '\0';
 58	char realPrefix[PATH_MAX];
 59	realPrefix[PATH_MAX - 1] = '\0';
 60	if (!dir) {
 61		if (!realPath) {
 62			return 0;
 63		}
 64		const char* separatorPoint = strrchr(realPath, '/');
 65		const char* dotPoint;
 66		size_t len;
 67		if (!separatorPoint) {
 68			strcpy(path, "./");
 69			separatorPoint = realPath;
 70			dotPoint = strrchr(realPath, '.');
 71		} else {
 72			path[0] = '\0';
 73			dotPoint = strrchr(separatorPoint, '.');
 74
 75			if (separatorPoint - realPath + 1 >= PATH_MAX - 1) {
 76				return 0;
 77			}
 78
 79			len = separatorPoint - realPath;
 80			strncat(path, realPath, len);
 81			path[len] = '\0';
 82			++separatorPoint;
 83		}
 84
 85		if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
 86			return 0;
 87		}
 88
 89		if (dotPoint >= separatorPoint) {
 90			len = dotPoint - separatorPoint;
 91		} else {
 92			len = PATH_MAX - 1;
 93		}
 94
 95		strncpy(realPrefix, separatorPoint, len);
 96		realPrefix[len] = '\0';
 97
 98		prefix = realPrefix;
 99		dir = VDirOpen(path);
100	}
101	if (!dir) {
102		// This shouldn't be possible
103		return 0;
104	}
105	dir->rewind(dir);
106	struct VDirEntry* dirent;
107	size_t prefixLen = strlen(prefix);
108	size_t infixLen = strlen(infix);
109	unsigned next = 0;
110	while ((dirent = dir->listNext(dir))) {
111		const char* filename = dirent->name(dirent);
112		char* dotPoint = strrchr(filename, '.');
113		size_t len = strlen(filename);
114		if (dotPoint) {
115			len = (dotPoint - filename);
116		}
117		const char* separator = strnrstr(filename, infix, len);
118		if (!separator) {
119			continue;
120		}
121		len = separator - filename;
122		if (len != prefixLen) {
123			continue;
124		}
125		if (strncmp(filename, prefix, prefixLen) == 0) {
126			int nlen;
127			separator += infixLen;
128			snprintf(path, PATH_MAX - 1, "%%u%s%%n", suffix);
129			unsigned increment;
130			if (sscanf(separator, path, &increment, &nlen) < 1) {
131				continue;
132			}
133			len = strlen(separator);
134			if (nlen < (ssize_t) len) {
135				continue;
136			}
137			if (next <= increment) {
138				next = increment + 1;
139			}
140		}
141	}
142	snprintf(path, PATH_MAX - 1, "%s%s%u%s", prefix, infix, next, suffix);
143	path[PATH_MAX - 1] = '\0';
144	return dir->openFile(dir, path, mode);
145}
146
147bool _vdClose(struct VDir* vd) {
148	struct VDirDE* vdde = (struct VDirDE*) vd;
149	if (closedir(vdde->de) < 0) {
150		return false;
151	}
152	free(vdde->path);
153	free(vdde);
154	return true;
155}
156
157void _vdRewind(struct VDir* vd) {
158	struct VDirDE* vdde = (struct VDirDE*) vd;
159	rewinddir(vdde->de);
160}
161
162struct VDirEntry* _vdListNext(struct VDir* vd) {
163	struct VDirDE* vdde = (struct VDirDE*) vd;
164	vdde->vde.ent = readdir(vdde->de);
165	if (vdde->vde.ent) {
166		return &vdde->vde.d;
167	}
168
169	return 0;
170}
171
172struct VFile* _vdOpenFile(struct VDir* vd, const char* path, int mode) {
173	struct VDirDE* vdde = (struct VDirDE*) vd;
174	if (!path) {
175		return 0;
176	}
177	const char* dir = vdde->path;
178	char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2));
179	sprintf(combined, "%s%s%s", dir, PATH_SEP, path);
180
181	struct VFile* file = VFileOpen(combined, mode);
182	free(combined);
183	return file;
184}
185
186const char* _vdeName(struct VDirEntry* vde) {
187	struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde;
188	if (vdede->ent) {
189		return vdede->ent->d_name;
190	}
191	return 0;
192}