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}