• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "path_tools.h"
16 
17 #include "util/string_util.h"
18 
19 #if defined(__linux__) || defined(__APPLE__)
20 #include <unistd.h>
21 #elif defined(_WIN32)
22 #include <direct.h>
23 #endif
24 
25 CORE_BEGIN_NAMESPACE()
26 using BASE_NS::string;
27 using BASE_NS::string_view;
28 
IsRelative(const string_view path)29 bool IsRelative(const string_view path)
30 {
31     if (path.empty()) {
32         return true;
33     }
34     return path[0] != '/';
35 }
36 
ParseUri(const string_view uri,string_view & protocol,string_view & path)37 bool ParseUri(const string_view uri, string_view& protocol, string_view& path)
38 {
39     const size_t index = uri.find("://");
40     if (index != string_view::npos) {
41         protocol = uri.substr(0, index);
42         path = uri.substr(index + 3); // 3: path size without protocol
43         return true;
44     }
45 
46     return false;
47 }
48 
NormalizePath(string_view path)49 string NormalizePath(string_view path)
50 {
51     if (path.empty()) {
52         return "/";
53     }
54     string res;
55     res.reserve(path.size());
56     res.push_back('/');
57     while (!path.empty()) {
58         if (path[0] == '/') {
59             path = path.substr(1);
60             continue;
61         }
62         auto pos = path.find_first_of('/', 0);
63         if (const string_view sub = path.substr(0, pos); sub == ".") {
64             path = path.substr(pos);
65             continue;
66         } else if (sub == "..") {
67             if ((!res.empty()) && (res.back() == '/')) {
68                 res.resize(res.size() - 1);
69             }
70 
71             if (auto p = res.find_last_of('/'); string::npos != p) {
72                 res.resize(p);
73             } else {
74                 if (res.empty()) {
75                     // trying to back out of root. (ie. invalid path)
76                     return "";
77                 }
78                 res.clear();
79             }
80             if (pos == string::npos) {
81                 res.push_back('/');
82                 break;
83             }
84         } else {
85             res.append(sub);
86         }
87         if (pos == string::npos) {
88             break;
89         } else {
90             res.push_back('/');
91         }
92         path = path.substr(pos);
93     }
94     return res;
95 }
96 
GetCurrentDirectory()97 string GetCurrentDirectory()
98 {
99     string basePath;
100 #if defined(__linux__) || defined(__APPLE__)
101     // OSX and linux both implement the "null buf" extension which allocates the required amount of space.
102     auto tmp = getcwd(nullptr, 0);
103     if ((tmp) && (string_view(tmp).back() == '/')) {
104         basePath = tmp;
105         free(tmp);
106     } else {
107         // fallback to root (either out-of-memory or the CWD is inaccessible for current user)
108         basePath = "/";
109         CORE_LOG_F("Could not get current working directory, initializing base path as '/'");
110     }
111 #elif defined(_WIN32)
112     // Windows also implements the "null buf" extension, but uses a different name and "format".
113     auto tmp = _getcwd(nullptr, 0);
114     if (tmp) {
115         basePath = tmp;
116         StringUtil::FindAndReplaceAll(basePath, "\\", "/");
117         free(tmp);
118     } else {
119         // fallback to root (yes, technically it's the root of current drive, which again can change when ever.
120         // but then again _getcwd should always work, except in out-of-memory cases where this is the least of our
121         // problems.
122         basePath = "/";
123         CORE_LOG_F("Could not get current working directory, initializing base path as '/'");
124     }
125 #else
126     // Unsupported platform.fallback to root.
127     basePath = "/";
128 #endif
129 
130     // Make sure that we end with a slash.
131     if (basePath.back() != '/') {
132         basePath.push_back('/');
133     }
134     // And make sure we start with a slash also.
135     if (basePath.front() != '/') {
136         basePath.insert(0, "/");
137     }
138     return basePath;
139 }
140 
141 #if _WIN32
SplitPath(string_view pathIn,string_view & drive,string_view & path,string_view & filename,string_view & ext)142 void SplitPath(string_view pathIn, string_view& drive, string_view& path, string_view& filename, string_view& ext)
143 {
144     drive = path = filename = ext = {};
145     if (pathIn[0] == '/') {
146         // see if there is a drive after
147         if (pathIn[2] == ':') { // 2: index of ':'
148             // yes.
149             // remove the first '/' to help later parsing
150             pathIn = pathIn.substr(1);
151         }
152     }
153     // extract the drive
154     if (pathIn[1] == ':') {
155         drive = pathIn.substr(0, 1);
156         pathIn = pathIn.substr(2); // 2: remove the drive part
157     }
158     auto lastSlash = pathIn.find_last_of('/');
159     if (lastSlash != string_view::npos) {
160         filename = pathIn.substr(lastSlash + 1);
161         path = pathIn.substr(0, lastSlash + 1);
162     } else {
163         filename = pathIn;
164     }
165     auto lastDot = filename.find_last_of('.');
166     if (lastDot != string_view::npos) {
167         ext = filename.substr(lastDot + 1);
168         filename = filename.substr(0, lastDot);
169     }
170 }
171 #endif
172 CORE_END_NAMESPACE()
173