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}