18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci#include "path_tools.h"
178bf80f4bSopenharmony_ci
188bf80f4bSopenharmony_ci#include <cstdlib>
198bf80f4bSopenharmony_ci
208bf80f4bSopenharmony_ci#include <base/containers/string.h>
218bf80f4bSopenharmony_ci#include <base/containers/string_view.h>
228bf80f4bSopenharmony_ci#include <core/log.h>
238bf80f4bSopenharmony_ci#include <core/namespace.h>
248bf80f4bSopenharmony_ci
258bf80f4bSopenharmony_ci#include "util/string_util.h"
268bf80f4bSopenharmony_ci
278bf80f4bSopenharmony_ci#if defined(__linux__) || defined(__APPLE__)
288bf80f4bSopenharmony_ci#include <unistd.h>
298bf80f4bSopenharmony_ci#elif defined(_WIN32)
308bf80f4bSopenharmony_ci#include <direct.h>
318bf80f4bSopenharmony_ci#endif
328bf80f4bSopenharmony_ci
338bf80f4bSopenharmony_ciCORE_BEGIN_NAMESPACE()
348bf80f4bSopenharmony_ciusing BASE_NS::string;
358bf80f4bSopenharmony_ciusing BASE_NS::string_view;
368bf80f4bSopenharmony_ci
378bf80f4bSopenharmony_cibool IsRelative(const string_view path)
388bf80f4bSopenharmony_ci{
398bf80f4bSopenharmony_ci    if (path.empty()) {
408bf80f4bSopenharmony_ci        return true;
418bf80f4bSopenharmony_ci    }
428bf80f4bSopenharmony_ci    return path[0] != '/';
438bf80f4bSopenharmony_ci}
448bf80f4bSopenharmony_ci
458bf80f4bSopenharmony_cibool ParseUri(string_view uri, string_view& protocol, string_view& path)
468bf80f4bSopenharmony_ci{
478bf80f4bSopenharmony_ci    const size_t index = uri.find(':');
488bf80f4bSopenharmony_ci    if (index != string_view::npos) {
498bf80f4bSopenharmony_ci        protocol = uri.substr(0, index);
508bf80f4bSopenharmony_ci        // remove scheme and separator
518bf80f4bSopenharmony_ci        uri.remove_prefix(index + 1U);
528bf80f4bSopenharmony_ci        // remove the authority separator if it's there
538bf80f4bSopenharmony_ci        if (uri.starts_with("//")) {
548bf80f4bSopenharmony_ci            uri.remove_prefix(2U);
558bf80f4bSopenharmony_ci        }
568bf80f4bSopenharmony_ci        path = uri;
578bf80f4bSopenharmony_ci        return true;
588bf80f4bSopenharmony_ci    }
598bf80f4bSopenharmony_ci
608bf80f4bSopenharmony_ci    return false;
618bf80f4bSopenharmony_ci}
628bf80f4bSopenharmony_ci
638bf80f4bSopenharmony_cistring NormalizePath(string_view path)
648bf80f4bSopenharmony_ci{
658bf80f4bSopenharmony_ci    if (path.empty()) {
668bf80f4bSopenharmony_ci        return "/";
678bf80f4bSopenharmony_ci    }
688bf80f4bSopenharmony_ci    string res;
698bf80f4bSopenharmony_ci    res.reserve(path.size());
708bf80f4bSopenharmony_ci    res.push_back('/');
718bf80f4bSopenharmony_ci    while (!path.empty()) {
728bf80f4bSopenharmony_ci        if (path[0] == '/') {
738bf80f4bSopenharmony_ci            path = path.substr(1);
748bf80f4bSopenharmony_ci            continue;
758bf80f4bSopenharmony_ci        }
768bf80f4bSopenharmony_ci        auto pos = path.find_first_of('/', 0);
778bf80f4bSopenharmony_ci        if (const string_view sub = path.substr(0, pos); sub == "..") {
788bf80f4bSopenharmony_ci            if ((!res.empty()) && (res.back() == '/')) {
798bf80f4bSopenharmony_ci                res.resize(res.size() - 1);
808bf80f4bSopenharmony_ci            }
818bf80f4bSopenharmony_ci
828bf80f4bSopenharmony_ci            if (auto p = res.find_last_of('/'); string::npos != p) {
838bf80f4bSopenharmony_ci                res.resize(p);
848bf80f4bSopenharmony_ci            } else {
858bf80f4bSopenharmony_ci                if (res.empty()) {
868bf80f4bSopenharmony_ci                    // trying to back out of root. (ie. invalid path)
878bf80f4bSopenharmony_ci                    return "";
888bf80f4bSopenharmony_ci                }
898bf80f4bSopenharmony_ci                res.clear();
908bf80f4bSopenharmony_ci            }
918bf80f4bSopenharmony_ci            if (pos == string::npos) {
928bf80f4bSopenharmony_ci                res.push_back('/');
938bf80f4bSopenharmony_ci                break;
948bf80f4bSopenharmony_ci            }
958bf80f4bSopenharmony_ci        } else if (sub == ".") {
968bf80f4bSopenharmony_ci            path = path.substr(pos);
978bf80f4bSopenharmony_ci            continue;
988bf80f4bSopenharmony_ci        } else {
998bf80f4bSopenharmony_ci            res.append(sub);
1008bf80f4bSopenharmony_ci        }
1018bf80f4bSopenharmony_ci        if (pos == string::npos) {
1028bf80f4bSopenharmony_ci            break;
1038bf80f4bSopenharmony_ci        }
1048bf80f4bSopenharmony_ci        res.push_back('/');
1058bf80f4bSopenharmony_ci        path = path.substr(pos);
1068bf80f4bSopenharmony_ci    }
1078bf80f4bSopenharmony_ci    return res;
1088bf80f4bSopenharmony_ci}
1098bf80f4bSopenharmony_ci
1108bf80f4bSopenharmony_cistring GetCurrentDirectory()
1118bf80f4bSopenharmony_ci{
1128bf80f4bSopenharmony_ci    string basePath;
1138bf80f4bSopenharmony_ci#if defined(__linux__) || defined(__APPLE__)
1148bf80f4bSopenharmony_ci    // OSX and linux both implement the "null buf" extension which allocates the required amount of space.
1158bf80f4bSopenharmony_ci    auto tmp = getcwd(nullptr, 0);
1168bf80f4bSopenharmony_ci    if (tmp) {
1178bf80f4bSopenharmony_ci        basePath = tmp;
1188bf80f4bSopenharmony_ci        if (basePath.back() != '/') {
1198bf80f4bSopenharmony_ci            basePath += '/';
1208bf80f4bSopenharmony_ci        }
1218bf80f4bSopenharmony_ci        free(tmp);
1228bf80f4bSopenharmony_ci    } else {
1238bf80f4bSopenharmony_ci        // fallback to root (either out-of-memory or the CWD is inaccessible for current user)
1248bf80f4bSopenharmony_ci        basePath = "/";
1258bf80f4bSopenharmony_ci        CORE_LOG_F("Could not get current working directory, initializing base path as '/'");
1268bf80f4bSopenharmony_ci    }
1278bf80f4bSopenharmony_ci#elif defined(_WIN32)
1288bf80f4bSopenharmony_ci    // Windows also implements the "null buf" extension, but uses a different name and "format".
1298bf80f4bSopenharmony_ci    auto tmp = _getcwd(nullptr, 0);
1308bf80f4bSopenharmony_ci    if (tmp) {
1318bf80f4bSopenharmony_ci        basePath = tmp;
1328bf80f4bSopenharmony_ci        StringUtil::FindAndReplaceAll(basePath, "\\", "/");
1338bf80f4bSopenharmony_ci        free(tmp);
1348bf80f4bSopenharmony_ci    } else {
1358bf80f4bSopenharmony_ci        // fallback to root (yes, technically it's the root of current drive, which again can change when ever.
1368bf80f4bSopenharmony_ci        // but then again _getcwd should always work, except in out-of-memory cases where this is the least of our
1378bf80f4bSopenharmony_ci        // problems.
1388bf80f4bSopenharmony_ci        basePath = "/";
1398bf80f4bSopenharmony_ci        CORE_LOG_F("Could not get current working directory, initializing base path as '/'");
1408bf80f4bSopenharmony_ci    }
1418bf80f4bSopenharmony_ci#else
1428bf80f4bSopenharmony_ci    // Unsupported platform.fallback to root.
1438bf80f4bSopenharmony_ci    basePath = "/";
1448bf80f4bSopenharmony_ci#endif
1458bf80f4bSopenharmony_ci
1468bf80f4bSopenharmony_ci    // Make sure that we end with a slash.
1478bf80f4bSopenharmony_ci    if (basePath.back() != '/') {
1488bf80f4bSopenharmony_ci        basePath.push_back('/');
1498bf80f4bSopenharmony_ci    }
1508bf80f4bSopenharmony_ci    // And make sure we start with a slash also.
1518bf80f4bSopenharmony_ci    if (basePath.front() != '/') {
1528bf80f4bSopenharmony_ci        basePath.insert(0, "/");
1538bf80f4bSopenharmony_ci    }
1548bf80f4bSopenharmony_ci    return basePath;
1558bf80f4bSopenharmony_ci}
1568bf80f4bSopenharmony_ci
1578bf80f4bSopenharmony_ci#if _WIN32
1588bf80f4bSopenharmony_civoid SplitPath(string_view pathIn, string_view& drive, string_view& path, string_view& filename, string_view& ext)
1598bf80f4bSopenharmony_ci{
1608bf80f4bSopenharmony_ci    drive = path = filename = ext = {};
1618bf80f4bSopenharmony_ci    if (pathIn[0] == '/') {
1628bf80f4bSopenharmony_ci        // see if there is a drive after
1638bf80f4bSopenharmony_ci        if (pathIn[2] == ':') { // 2: index of ':'
1648bf80f4bSopenharmony_ci            // yes.
1658bf80f4bSopenharmony_ci            // remove the first '/' to help later parsing
1668bf80f4bSopenharmony_ci            pathIn = pathIn.substr(1);
1678bf80f4bSopenharmony_ci        }
1688bf80f4bSopenharmony_ci    }
1698bf80f4bSopenharmony_ci    // extract the drive
1708bf80f4bSopenharmony_ci    if (pathIn[1] == ':') {
1718bf80f4bSopenharmony_ci        drive = pathIn.substr(0, 1);
1728bf80f4bSopenharmony_ci        pathIn = pathIn.substr(2); // 2: remove the drive part
1738bf80f4bSopenharmony_ci    }
1748bf80f4bSopenharmony_ci    auto lastSlash = pathIn.find_last_of('/');
1758bf80f4bSopenharmony_ci    if (lastSlash != string_view::npos) {
1768bf80f4bSopenharmony_ci        filename = pathIn.substr(lastSlash + 1);
1778bf80f4bSopenharmony_ci        path = pathIn.substr(0, lastSlash + 1);
1788bf80f4bSopenharmony_ci    } else {
1798bf80f4bSopenharmony_ci        filename = pathIn;
1808bf80f4bSopenharmony_ci    }
1818bf80f4bSopenharmony_ci    auto lastDot = filename.find_last_of('.');
1828bf80f4bSopenharmony_ci    if (lastDot != string_view::npos) {
1838bf80f4bSopenharmony_ci        ext = filename.substr(lastDot + 1);
1848bf80f4bSopenharmony_ci        filename = filename.substr(0, lastDot);
1858bf80f4bSopenharmony_ci    }
1868bf80f4bSopenharmony_ci}
1878bf80f4bSopenharmony_ci#endif
1888bf80f4bSopenharmony_ciCORE_END_NAMESPACE()
189