xref: /aosp_15_r20/external/deqp/framework/delibs/decpp/deFilePath.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements C++ Base Library
3  * -----------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Filesystem path class.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "deFilePath.hpp"
25 
26 #include <vector>
27 #include <stdexcept>
28 
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 
32 #if (DE_OS == DE_OS_WIN32)
33 #define VC_EXTRALEAN
34 #define WIN32_LEAN_AND_MEAN
35 #define NOMINMAX
36 #include <windows.h>
37 #endif
38 
39 using std::string;
40 
41 namespace de
42 {
43 
44 #if (DE_OS == DE_OS_WIN32)
45 const std::string FilePath::separator = "\\";
46 #else
47 const std::string FilePath::separator = "/";
48 #endif
49 
FilePath(const std::vector<std::string> & components)50 FilePath::FilePath(const std::vector<std::string> &components)
51 {
52     for (size_t ndx = 0; ndx < components.size(); ndx++)
53     {
54         if (!m_path.empty() && !isSeparator(m_path[m_path.size() - 1]))
55             m_path += separator;
56         m_path += components[ndx];
57     }
58 }
59 
split(std::vector<std::string> & components) const60 void FilePath::split(std::vector<std::string> &components) const
61 {
62     components.clear();
63 
64     int curCompStart = 0;
65     int pos;
66 
67     if (isWinNetPath())
68         components.push_back(separator + separator);
69     else if (isRootPath() && !beginsWithDrive())
70         components.push_back(separator);
71 
72     for (pos = 0; pos < (int)m_path.length(); pos++)
73     {
74         const char c = m_path[pos];
75 
76         if (isSeparator(c))
77         {
78             if (pos - curCompStart > 0)
79                 components.push_back(m_path.substr(curCompStart, pos - curCompStart));
80 
81             curCompStart = pos + 1;
82         }
83     }
84 
85     if (pos - curCompStart > 0)
86         components.push_back(m_path.substr(curCompStart, pos - curCompStart));
87 }
88 
join(const std::vector<std::string> & components)89 FilePath FilePath::join(const std::vector<std::string> &components)
90 {
91     return FilePath(components);
92 }
93 
normalize(void)94 FilePath &FilePath::normalize(void)
95 {
96     std::vector<std::string> components;
97     std::vector<std::string> reverseNormalizedComponents;
98 
99     split(components);
100 
101     m_path = "";
102 
103     int numUp = 0;
104 
105     // Do in reverse order and eliminate any . or .. components
106     for (int ndx = (int)components.size() - 1; ndx >= 0; ndx--)
107     {
108         const std::string &comp = components[ndx];
109         if (comp == "..")
110             numUp += 1;
111         else if (comp == ".")
112             continue;
113         else if (numUp > 0)
114             numUp -= 1; // Skip part
115         else
116             reverseNormalizedComponents.push_back(comp);
117     }
118 
119     if (isAbsolutePath() && numUp > 0)
120         throw std::runtime_error("Cannot normalize path: invalid path");
121 
122     // Prepend necessary ".." components
123     while (numUp--)
124         reverseNormalizedComponents.push_back("..");
125 
126     if (reverseNormalizedComponents.empty() && components.back() == ".")
127         reverseNormalizedComponents.push_back("."); // Composed of "." components only
128 
129     *this = join(std::vector<std::string>(reverseNormalizedComponents.rbegin(), reverseNormalizedComponents.rend()));
130 
131     return *this;
132 }
133 
normalize(const FilePath & path)134 FilePath FilePath::normalize(const FilePath &path)
135 {
136     return FilePath(path).normalize();
137 }
138 
getBaseName(void) const139 std::string FilePath::getBaseName(void) const
140 {
141     std::vector<std::string> components;
142     split(components);
143     return !components.empty() ? components[components.size() - 1] : std::string("");
144 }
145 
getDirName(void) const146 std::string FilePath::getDirName(void) const
147 {
148     std::vector<std::string> components;
149     split(components);
150     if (components.size() > 1)
151     {
152         components.pop_back();
153         return FilePath(components).getPath();
154     }
155     else if (isAbsolutePath())
156         return separator;
157     else
158         return std::string(".");
159 }
160 
getFileExtension(void) const161 std::string FilePath::getFileExtension(void) const
162 {
163     std::string baseName = getBaseName();
164     size_t dotPos        = baseName.find_last_of('.');
165     if (dotPos == std::string::npos)
166         return std::string("");
167     else
168         return baseName.substr(dotPos + 1);
169 }
170 
exists(void) const171 bool FilePath::exists(void) const
172 {
173     FilePath normPath = FilePath::normalize(*this);
174     struct stat st;
175     int result = stat(normPath.getPath(), &st);
176     return result == 0;
177 }
178 
getType(void) const179 FilePath::Type FilePath::getType(void) const
180 {
181     FilePath normPath = FilePath::normalize(*this);
182     struct stat st;
183     int result = stat(normPath.getPath(), &st);
184 
185     if (result != 0)
186         return TYPE_UNKNOWN;
187 
188     int type = st.st_mode & S_IFMT;
189     if (type == S_IFREG)
190         return TYPE_FILE;
191     else if (type == S_IFDIR)
192         return TYPE_DIRECTORY;
193     else
194         return TYPE_UNKNOWN;
195 }
196 
beginsWithDrive(void) const197 bool FilePath::beginsWithDrive(void) const
198 {
199     for (int ndx = 0; ndx < (int)m_path.length(); ndx++)
200     {
201         if (m_path[ndx] == ':' && ndx + 1 < (int)m_path.length() && isSeparator(m_path[ndx + 1]))
202             return true; // First part is drive letter.
203         if (isSeparator(m_path[ndx]))
204             return false;
205     }
206     return false;
207 }
208 
isAbsolutePath(void) const209 bool FilePath::isAbsolutePath(void) const
210 {
211     return isRootPath() || isWinNetPath() || beginsWithDrive();
212 }
213 
FilePath_selfTest(void)214 void FilePath_selfTest(void)
215 {
216     DE_TEST_ASSERT(!FilePath(".").isAbsolutePath());
217     DE_TEST_ASSERT(!FilePath("..\\foo").isAbsolutePath());
218     DE_TEST_ASSERT(!FilePath("foo").isAbsolutePath());
219     DE_TEST_ASSERT(FilePath("\\foo/bar").isAbsolutePath());
220     DE_TEST_ASSERT(FilePath("/foo").isAbsolutePath());
221     DE_TEST_ASSERT(FilePath("\\").isAbsolutePath());
222     DE_TEST_ASSERT(FilePath("\\\\net\\loc").isAbsolutePath());
223     DE_TEST_ASSERT(FilePath("C:\\file.txt").isAbsolutePath());
224     DE_TEST_ASSERT(FilePath("c:/file.txt").isAbsolutePath());
225 
226     DE_TEST_ASSERT(string(".") == FilePath(".//.").normalize().getPath());
227     DE_TEST_ASSERT(string(".") == FilePath(".").normalize().getPath());
228     DE_TEST_ASSERT((string("..") + FilePath::separator + "test") ==
229                    FilePath("foo/../bar/../../test").normalize().getPath());
230     DE_TEST_ASSERT((FilePath::separator + "foo" + FilePath::separator + "foo.txt") ==
231                    FilePath("/foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath());
232     DE_TEST_ASSERT((string("c:") + FilePath::separator + "foo" + FilePath::separator + "foo.txt") ==
233                    FilePath("c:/foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath());
234     DE_TEST_ASSERT((FilePath::separator + FilePath::separator + "foo" + FilePath::separator + "foo.txt") ==
235                    FilePath("\\\\foo\\bar/..\\dir\\..\\foo.txt").normalize().getPath());
236 
237     DE_TEST_ASSERT(FilePath("foo/bar").getBaseName() == "bar");
238     DE_TEST_ASSERT(FilePath("foo/bar/").getBaseName() == "bar");
239     DE_TEST_ASSERT(FilePath("foo\\bar").getBaseName() == "bar");
240     DE_TEST_ASSERT(FilePath("foo\\bar\\").getBaseName() == "bar");
241     DE_TEST_ASSERT(FilePath("foo/bar").getDirName() == "foo");
242     DE_TEST_ASSERT(FilePath("foo/bar/").getDirName() == "foo");
243     DE_TEST_ASSERT(FilePath("foo\\bar").getDirName() == "foo");
244     DE_TEST_ASSERT(FilePath("foo\\bar\\").getDirName() == "foo");
245     DE_TEST_ASSERT(FilePath("/foo/bar/baz").getDirName() == FilePath::separator + "foo" + FilePath::separator + "bar");
246 }
247 
createDirectoryImpl(const char * path)248 static void createDirectoryImpl(const char *path)
249 {
250 #if (DE_OS == DE_OS_WIN32)
251     if (!CreateDirectory(path, DE_NULL))
252         throw std::runtime_error("Failed to create directory");
253 #elif (DE_OS == DE_OS_UNIX) || (DE_OS == DE_OS_OSX) || (DE_OS == DE_OS_IOS) || (DE_OS == DE_OS_ANDROID) || \
254     (DE_OS == DE_OS_SYMBIAN) || (DE_OS == DE_OS_QNX) || (DE_OS == DE_OS_FUCHSIA)
255     if (mkdir(path, 0777) != 0)
256         throw std::runtime_error("Failed to create directory");
257 #else
258 #error Implement createDirectoryImpl() for your platform.
259 #endif
260 }
261 
createDirectory(const char * path)262 void createDirectory(const char *path)
263 {
264     FilePath dirPath = FilePath::normalize(path);
265     FilePath parentPath(dirPath.getDirName());
266 
267     if (dirPath.exists())
268         throw std::runtime_error("Destination exists already");
269     else if (!parentPath.exists())
270         throw std::runtime_error("Parent directory doesn't exist");
271     else if (parentPath.getType() != FilePath::TYPE_DIRECTORY)
272         throw std::runtime_error("Parent is not directory");
273 
274     createDirectoryImpl(path);
275 }
276 
createDirectoryAndParents(const char * path)277 void createDirectoryAndParents(const char *path)
278 {
279     std::vector<std::string> createPaths;
280     FilePath curPath(path);
281 
282     if (curPath.exists())
283         throw std::runtime_error("Destination exists already");
284 
285     while (!curPath.exists())
286     {
287         createPaths.push_back(curPath.getPath());
288 
289         std::string parent = curPath.getDirName();
290         DE_CHECK_RUNTIME_ERR(parent != curPath.getPath());
291         curPath = FilePath(parent);
292     }
293 
294     // Create in reverse order.
295     for (std::vector<std::string>::const_reverse_iterator parentIter = createPaths.rbegin();
296          parentIter != createPaths.rend(); parentIter++)
297         createDirectory(parentIter->c_str());
298 }
299 
300 } // namespace de
301