• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 
16 #include "util/path_util.h"
17 
18 #include <base/math/mathf.h>
19 
20 using namespace BASE_NS;
21 
22 UTIL_BEGIN_NAMESPACE()
23 
24 namespace PathUtil {
25 
NormalizePath(string_view path)26 string NormalizePath(string_view path)
27 {
28     if (path.empty()) {
29         return "";
30     }
31 
32     string res;
33     if (path[0] != '/') {
34         res.reserve(path.size() + 1);
35         res.push_back('/');
36     } else {
37         res.reserve(path.size());
38     }
39     while (!path.empty()) {
40         if (path[0] == '/') {
41             if (res.empty()) {
42                 res.push_back('/');
43             }
44             path = path.substr(1);
45             continue;
46         }
47         auto pos = path.find_first_of("/", 0);
48         string_view sub = path.substr(0, pos);
49         if (sub == ".") {
50             path = path.substr(pos);
51             continue;
52         } else if (sub == "..") {
53             if ((!res.empty()) && (res.back() == '/')) {
54                 res.resize(res.size() - 1);
55             }
56             if (auto p = res.find_last_of('/'); string::npos != p) {
57                 res.resize(p);
58             } else {
59                 if (res.empty()) {
60                     // trying to back out of root. (ie. invalid path)
61                     return "";
62                 }
63                 res.clear();
64             }
65             if (pos == string::npos) {
66                 res.push_back('/');
67                 break;
68             }
69         } else {
70             res.append(sub);
71         }
72         if (pos == string::npos) {
73             break;
74         } else {
75             res.push_back('/');
76         }
77         path = path.substr(pos);
78     }
79     if (res[0] != '/') {
80         res.insert(0, "/");
81     }
82     return res;
83 }
84 
GetParentPath(string_view path)85 string GetParentPath(string_view path)
86 {
87     if (path.size() > 1 && path[path.size() - 1] == '/' && path[path.size() - 2] != '/') {
88         // Allow (ignore) trailing '/' for folders.
89         path = path.substr(0, path.size() - 1);
90     }
91 
92     const size_t separatorPos = path.rfind('/');
93     if (separatorPos == string::npos) {
94         return "";
95     } else {
96         return string(path.substr(0, separatorPos + 1));
97     }
98 }
99 
ResolvePath(string_view parent,string_view uri,bool allowQueryString)100 string ResolvePath(string_view parent, string_view uri, bool allowQueryString)
101 {
102     size_t queryPos = allowQueryString ? string::npos : uri.find('?');
103     string_view path = (string::npos != queryPos) ? uri.substr(0, queryPos) : uri;
104 
105     if (parent.empty()) {
106         return string(path);
107     }
108 
109     if (path.empty()) {
110         return string(parent);
111     }
112 
113     if (path[0] == '/') {
114         path = path.substr(1);
115     } else if (path.find("://") != path.npos) {
116         return string(path);
117     }
118 
119     // NOTE: Resolve always assumes the parent path is a directory even if there is no '/' in the end
120     if (parent.back() == '/') {
121         return parent + path;
122     } else {
123         return parent + "/" + path;
124     }
125 }
126 
GetRelativePath(string_view path,string_view relativeTo)127 string GetRelativePath(string_view path, string_view relativeTo)
128 {
129     // remove the common prefix
130     {
131         int lastSeparator = -1;
132 
133         for (size_t i = 0, iMax = Math::min(path.size(), relativeTo.size()); i < iMax; ++i) {
134             if (path[i] != relativeTo[i]) {
135                 break;
136             }
137             if (path[i] == '/') {
138                 lastSeparator = int(i);
139             }
140         }
141 
142         const auto lastPos = static_cast<size_t>(static_cast<size_t>(lastSeparator) + 1);
143         path.remove_prefix(lastPos);
144         relativeTo.remove_prefix(lastPos);
145     }
146 
147     if (path[1] == ':' && relativeTo[1] == ':' && path[0] != relativeTo[0]) {
148         // files are on different drives
149         return string(path);
150     }
151 
152     // count and remove the directories left in relative_to
153     auto directoriesCount = 0;
154     {
155         auto nextSeparator = relativeTo.find('/');
156         while (nextSeparator != string::npos) {
157             relativeTo.remove_prefix(nextSeparator + 1);
158             nextSeparator = relativeTo.find('/');
159             ++directoriesCount;
160         }
161     }
162 
163     string relativePath = "";
164     for (auto i = 0, iMax = directoriesCount; i < iMax; ++i) {
165         relativePath.append("../");
166     }
167 
168     relativePath.append(path);
169 
170     return relativePath;
171 }
172 
GetFilename(string_view path)173 string GetFilename(string_view path)
174 {
175     if (!path.empty() && path[path.size() - 1] == '/') {
176         // Return a name also for folders.
177         path = path.substr(0, path.size() - 1);
178     }
179 
180     size_t cutPos = path.find_last_of("\\/");
181     if (string::npos != cutPos) {
182         return string(path.substr(cutPos + 1));
183     } else {
184         return string(path);
185     }
186 }
187 
GetExtension(string_view path)188 string GetExtension(string_view path)
189 {
190     size_t fileExtCut = path.rfind('.');
191     if (fileExtCut != string::npos) {
192         size_t queryCut = path.find('?', fileExtCut);
193         if (queryCut != string::npos) {
194             return string(path.substr(fileExtCut + 1, queryCut));
195         } else {
196             return string(path.substr(fileExtCut + 1, queryCut));
197         }
198     }
199     return "";
200 }
201 
GetBaseName(string_view path)202 string GetBaseName(string_view path)
203 {
204     auto filename = GetFilename(path);
205     size_t fileExtCut = filename.rfind(".");
206     if (string::npos != fileExtCut) {
207         filename.erase(fileExtCut);
208     }
209     return filename;
210 }
211 
GetUriParameters(string_view uri)212 unordered_map<string, string> GetUriParameters(string_view uri)
213 {
214     const size_t queryPos = uri.find('?');
215     if (queryPos != string::npos) {
216         unordered_map<string, string> params;
217         size_t paramStartPos = queryPos;
218         while (paramStartPos < uri.size()) {
219             size_t paramValuePos = uri.find('=', paramStartPos + 1);
220             size_t paramEndPos = uri.find('&', paramStartPos + 1);
221             if (paramEndPos == string::npos) {
222                 paramEndPos = uri.size();
223             }
224             if (paramValuePos != string::npos && paramValuePos < paramEndPos) {
225                 auto key = uri.substr(paramStartPos + 1, paramValuePos - paramStartPos - 1);
226                 auto value = uri.substr(paramValuePos + 1, paramEndPos - paramValuePos - 1);
227                 params[key] = value;
228             } else {
229                 auto key = uri.substr(paramStartPos + 1, paramEndPos - paramStartPos - 1);
230                 params[key] = key;
231             }
232             paramStartPos = paramEndPos;
233         }
234         return params;
235     }
236     return {};
237 }
238 
ResolveUri(string_view contextUri,string_view uri,bool allowQueryString)239 string ResolveUri(string_view contextUri, string_view uri, bool allowQueryString)
240 {
241     return ResolvePath(GetParentPath(contextUri), uri, allowQueryString);
242 }
243 
244 } // namespace PathUtil
245 
246 UTIL_END_NAMESPACE()
247