all repos — mgba @ 41b591e501790dcf88d7c533fe1109300eb3c984

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