• 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 "path_tools.h"
17 
18 #include <cstdlib>
19 
20 #include <base/containers/string.h>
21 #include <base/containers/string_view.h>
22 #include <core/log.h>
23 #include <core/namespace.h>
24 
25 #if defined(__OHOS_PLATFORM__) || defined(__linux__) || defined(__APPLE__)
26 #include <unistd.h>
27 #elif defined(_WIN32)
28 #include <direct.h>
29 
30 #include "util/string_util.h"
31 #endif
32 
33 CORE_BEGIN_NAMESPACE()
34 using BASE_NS::string;
35 using BASE_NS::string_view;
36 
IsRelative(const string_view path)37 bool IsRelative(const string_view path)
38 {
39     if (path.empty()) {
40         return true;
41     }
42     return path[0] != '/';
43 }
44 
ParseUri(string_view uri,string_view & protocol,string_view & path)45 bool ParseUri(string_view uri, string_view& protocol, string_view& path)
46 {
47     const size_t index = uri.find(':');
48     if (index != string_view::npos) {
49         protocol = uri.substr(0, index);
50         // remove scheme and separator
51         uri.remove_prefix(index + 1U);
52         // remove the authority separator if it's there
53         if (uri.starts_with("//")) {
54             uri.remove_prefix(2U);
55         }
56         path = uri;
57         return true;
58     }
59 
60     return false;
61 }
62 
NormalizePath(string_view path)63 string NormalizePath(string_view path)
64 {
65     if (path.empty()) {
66         return "/";
67     }
68     string res;
69     res.reserve(path.size());
70     res.push_back('/');
71     while (!path.empty()) {
72         if (path[0] == '/') {
73             path = path.substr(1);
74             continue;
75         }
76         const auto pos = path.find_first_of('/', 0);
77         const string_view sub = path.substr(0, pos);
78         if (sub == "..") {
79             if ((!res.empty()) && (res.back() == '/')) {
80                 res.resize(res.size() - 1);
81             }
82             if (res.empty()) {
83                 // trying to back out of root. (i.e. invalid path)
84                 return "";
85             }
86 
87             if (const auto p = res.find_last_of('/'); p != string::npos) {
88                 res.resize(p);
89             } else {
90                 res.clear();
91             }
92             if (pos == string_view::npos) {
93                 res.push_back('/');
94                 break;
95             }
96         } else if (sub == ".") {
97             path = path.substr(pos);
98             continue;
99         } else {
100             res.append(sub);
101         }
102 
103         if (pos == string_view::npos) {
104             break;
105         }
106         res.push_back('/');
107         path = path.substr(pos);
108     }
109     return res;
110 }
111 
GetCurrentDirectory()112 string GetCurrentDirectory()
113 {
114     string basePath;
115 #if defined(__OHOS_PLATFORM__) || defined(__linux__) || defined(__APPLE__)
116     // OSX and linux both implement the "null buf" extension which allocates the required amount of space.
117     auto tmp = getcwd(nullptr, 0);
118     if (tmp) {
119         basePath = tmp;
120         if (basePath.back() != '/') {
121             basePath += '/';
122         }
123         free(tmp);
124     } else {
125         // fallback to root (either out-of-memory or the CWD is inaccessible for current user)
126         basePath = "/";
127         CORE_LOG_F("Could not get current working directory, initializing base path as '/'");
128     }
129 #elif defined(_WIN32)
130     // Windows also implements the "null buf" extension, but uses a different name and "format".
131     auto tmp = _getcwd(nullptr, 0);
132     if (tmp) {
133         basePath = tmp;
134         StringUtil::FindAndReplaceAll(basePath, "\\", "/");
135         free(tmp);
136     } else {
137         // fallback to root (yes, technically it's the root of current drive, which again can change when ever.
138         // but then again _getcwd should always work, except in out-of-memory cases where this is the least of our
139         // problems.
140         basePath = "/";
141         CORE_LOG_F("Could not get current working directory, initializing base path as '/'");
142     }
143 #else
144     // Unsupported platform.fallback to root.
145     basePath = "/";
146 #endif
147 
148     // Make sure that we end with a slash.
149     if (basePath.back() != '/') {
150         basePath.push_back('/');
151     }
152     // And make sure we start with a slash also.
153     if (basePath.front() != '/') {
154         basePath.insert(0, "/");
155     }
156     return basePath;
157 }
158 
159 #if _WIN32
SplitPath(string_view pathIn,string_view & drive,string_view & path,string_view & filename,string_view & ext)160 void SplitPath(string_view pathIn, string_view& drive, string_view& path, string_view& filename, string_view& ext)
161 {
162     drive = path = filename = ext = {};
163     if (pathIn[0] == '/') {
164         // see if there is a drive after
165         if (pathIn[2] == ':') { // 2: index of ':'
166             // yes.
167             // remove the first '/' to help later parsing
168             pathIn = pathIn.substr(1);
169         }
170     }
171     // extract the drive
172     if (pathIn[1] == ':') {
173         drive = pathIn.substr(0, 1);
174         pathIn = pathIn.substr(2); // 2: remove the drive part
175     }
176     auto lastSlash = pathIn.find_last_of('/');
177     if (lastSlash != string_view::npos) {
178         filename = pathIn.substr(lastSlash + 1);
179         path = pathIn.substr(0, lastSlash + 1);
180     } else {
181         filename = pathIn;
182     }
183     auto lastDot = filename.find_last_of('.');
184     if (lastDot != string_view::npos) {
185         ext = filename.substr(lastDot + 1);
186         filename = filename.substr(0, lastDot);
187     }
188 }
189 #endif
190 CORE_END_NAMESPACE()
191