Line data Source code
1 : /* Copyright (C) 2019 Wildfire Games.
2 : *
3 : * Permission is hereby granted, free of charge, to any person obtaining
4 : * a copy of this software and associated documentation files (the
5 : * "Software"), to deal in the Software without restriction, including
6 : * without limitation the rights to use, copy, modify, merge, publish,
7 : * distribute, sublicense, and/or sell copies of the Software, and to
8 : * permit persons to whom the Software is furnished to do so, subject to
9 : * the following conditions:
10 : *
11 : * The above copyright notice and this permission notice shall be included
12 : * in all copies or substantial portions of the Software.
13 : *
14 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 : */
22 :
23 : /*
24 : * helper functions for directory access
25 : */
26 :
27 : #include "precompiled.h"
28 :
29 : #include "lib/file/vfs/vfs_util.h"
30 :
31 : #include <cstdio>
32 : #include <cstring>
33 : #include <queue>
34 :
35 : #include "lib/regex.h"
36 : #include "lib/sysdep/filesystem.h"
37 :
38 :
39 : namespace vfs {
40 :
41 226 : Status GetPathnames(const PIVFS& fs, const VfsPath& path, const wchar_t* filter, VfsPaths& pathnames)
42 : {
43 452 : std::vector<CFileInfo> files;
44 226 : RETURN_STATUS_IF_ERR(fs->GetDirectoryEntries(path, &files, 0));
45 :
46 212 : pathnames.reserve(files.size());
47 :
48 2021 : for(size_t i = 0; i < files.size(); i++)
49 : {
50 1809 : if(match_wildcard(files[i].Name().string().c_str(), filter))
51 1790 : pathnames.push_back(path / files[i].Name());
52 : }
53 :
54 212 : return INFO::OK;
55 : }
56 :
57 :
58 6 : Status ForEachFile(const PIVFS& fs, const VfsPath& startPath, FileCallback cb, uintptr_t cbData, const wchar_t* pattern, size_t flags, DirCallback dircb, uintptr_t dircbData)
59 : {
60 : // (declare here to avoid reallocations)
61 12 : CFileInfos files;
62 12 : DirectoryNames subdirectoryNames;
63 :
64 : // (a FIFO queue is more efficient than recursion because it uses less
65 : // stack space and avoids seeks due to breadth-first traversal.)
66 12 : std::queue<VfsPath> pendingDirectories;
67 6 : pendingDirectories.push(startPath/"");
68 6 : while(!pendingDirectories.empty())
69 : {
70 6 : const VfsPath& path = pendingDirectories.front();
71 :
72 6 : RETURN_STATUS_IF_ERR(fs->GetDirectoryEntries(path, &files, &subdirectoryNames));
73 :
74 6 : if(dircb)
75 0 : RETURN_STATUS_IF_ERR(dircb(path, dircbData));
76 :
77 90 : for(size_t i = 0; i < files.size(); i++)
78 : {
79 168 : const CFileInfo fileInfo = files[i];
80 84 : if(!match_wildcard(fileInfo.Name().string().c_str(), pattern))
81 0 : continue;
82 :
83 168 : const VfsPath pathname(path / fileInfo.Name()); // (CFileInfo only stores the name)
84 84 : RETURN_STATUS_IF_ERR(cb(pathname, fileInfo, cbData));
85 : }
86 :
87 6 : if(!(flags & DIR_RECURSIVE))
88 6 : break;
89 :
90 0 : for(size_t i = 0; i < subdirectoryNames.size(); i++)
91 0 : pendingDirectories.push(path / subdirectoryNames[i]/"");
92 0 : pendingDirectories.pop();
93 : }
94 :
95 6 : return INFO::OK;
96 : }
97 :
98 :
99 0 : void NextNumberedFilename(const PIVFS& fs, const VfsPath& pathnameFormat, size_t& nextNumber, VfsPath& nextPathname)
100 : {
101 : // (first call only:) scan directory and set nextNumber according to
102 : // highest matching filename found. this avoids filling "holes" in
103 : // the number series due to deleted files, which could be confusing.
104 : // example: add 1st and 2nd; [exit] delete 1st; [restart]
105 : // add 3rd -> without this measure it would get number 1, not 3.
106 0 : if(nextNumber == 0)
107 : {
108 0 : const VfsPath nameFormat = pathnameFormat.Filename();
109 0 : const VfsPath path = pathnameFormat.Parent()/"";
110 :
111 0 : size_t maxNumber = 0;
112 0 : CFileInfos files;
113 0 : fs->GetDirectoryEntries(path, &files, 0);
114 0 : for(size_t i = 0; i < files.size(); i++)
115 : {
116 : int number;
117 0 : if(swscanf_s(files[i].Name().string().c_str(), nameFormat.string().c_str(), &number) == 1)
118 0 : maxNumber = std::max(size_t(number), maxNumber);
119 : }
120 :
121 0 : nextNumber = maxNumber+1;
122 : }
123 :
124 : // now increment number until that file doesn't yet exist.
125 : // this is fairly slow, but typically only happens once due
126 : // to scan loop above. (we still need to provide for looping since
127 : // someone may have added files in the meantime)
128 : // we don't bother with binary search - this isn't a bottleneck.
129 0 : do
130 : {
131 : wchar_t pathnameBuf[PATH_MAX];
132 0 : swprintf_s(pathnameBuf, ARRAY_SIZE(pathnameBuf), pathnameFormat.string().c_str(), nextNumber++);
133 0 : nextPathname = pathnameBuf;
134 : }
135 0 : while(fs->GetFileInfo(nextPathname, 0) == INFO::OK);
136 0 : }
137 :
138 3 : } // namespace vfs
|