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}
193
194struct VFile* VDirOptionalOpenFile(struct VDir* dir, const char* realPath, const char* prefix, const char* suffix, int mode) {
195 char path[PATH_MAX];
196 path[PATH_MAX - 1] = '\0';
197 struct VFile* vf;
198 if (!dir) {
199 if (!realPath) {
200 return 0;
201 }
202 char* dotPoint = strrchr(realPath, '.');
203 if (dotPoint - realPath + 1 >= PATH_MAX - 1) {
204 return 0;
205 }
206 if (dotPoint > strrchr(realPath, '/')) {
207 int len = dotPoint - realPath;
208 strncpy(path, realPath, len);
209 path[len] = 0;
210 strncat(path + len, suffix, PATH_MAX - len - 1);
211 } else {
212 snprintf(path, PATH_MAX - 1, "%s%s", realPath, suffix);
213 }
214 vf = VFileOpen(path, mode);
215 } else {
216 snprintf(path, PATH_MAX - 1, "%s%s", prefix, suffix);
217 vf = dir->openFile(dir, path, mode);
218 }
219 return vf;
220}