all repos — mgba @ c14da05d8dca225010677643c32fea5c0ac8517a

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