Line data Source code
1 : /* 2 : Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain 3 : Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany 4 : 5 : This software was partially supported by the 6 : EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). 7 : 8 : This software was partially supported by the 9 : ADA-FS project under the SPPEXA project funded by the DFG. 10 : 11 : This file is part of GekkoFS. 12 : 13 : GekkoFS is free software: you can redistribute it and/or modify 14 : it under the terms of the GNU General Public License as published by 15 : the Free Software Foundation, either version 3 of the License, or 16 : (at your option) any later version. 17 : 18 : GekkoFS is distributed in the hope that it will be useful, 19 : but WITHOUT ANY WARRANTY; without even the implied warranty of 20 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 : GNU General Public License for more details. 22 : 23 : You should have received a copy of the GNU General Public License 24 : along with GekkoFS. If not, see <https://www.gnu.org/licenses/>. 25 : 26 : SPDX-License-Identifier: GPL-3.0-or-later 27 : */ 28 : 29 : #include <common/path_util.hpp> 30 : 31 : #include <system_error> 32 : #include <cstring> 33 : #include <cassert> 34 : 35 : using namespace std; 36 : 37 : namespace gkfs::path { 38 : 39 : bool 40 284 : is_relative(const string& path) { 41 284 : return (!path.empty()) && (path.front() != separator); 42 : } 43 : 44 : bool 45 620 : is_absolute(const string& path) { 46 620 : return (!path.empty()) && (path.front() == separator); 47 : } 48 : 49 : bool 50 1378 : has_trailing_slash(const string& path) { 51 1378 : return (!path.empty()) && (path.back() == separator); 52 : } 53 : 54 : /** Add path prefix to a given C string. 55 : * 56 : * Returns a string composed by the `prefix_path` 57 : * followed by `raw_path`. 58 : * 59 : * This would return the same of: 60 : * ``` 61 : * string(raw_path).append(prefix_path); 62 : * ``` 63 : * But it is faster because it avoids to copy the `raw_path` twice. 64 : * 65 : * 66 : * Time cost approx: O(len(prefix_path)) + 2 O(len(raw_path)) 67 : * 68 : * Example: 69 : * ``` 70 : * prepend_path("/tmp/prefix", "./my/path") == "/tmp/prefix/./my/path" 71 : * ``` 72 : */ 73 : string 74 7 : prepend_path(const string& prefix_path, const char* raw_path) { 75 7 : assert(!has_trailing_slash(prefix_path)); 76 7 : ::size_t raw_len = ::strlen(raw_path); 77 7 : string res; 78 7 : res.reserve(prefix_path.size() + 1 + raw_len); 79 7 : res.append(prefix_path); 80 7 : res.push_back(separator); 81 7 : res.append(raw_path, raw_len); 82 7 : return res; 83 : } 84 : 85 : /** Split a path into its components 86 : * 87 : * Returns a vector of the components of the given path. 88 : * 89 : * Example: 90 : * split_path("/first/second/third") == ["first", "second", "third"] 91 : */ 92 : ::vector<string> 93 274 : split_path(const string& path) { 94 274 : ::vector<string> tokens; 95 274 : size_t start = string::npos; 96 274 : size_t end = (path.front() != separator) ? 0 : 1; 97 3522 : while(end != string::npos && end < path.size()) { 98 3248 : start = end; 99 3248 : end = path.find(separator, start); 100 6496 : tokens.push_back(path.substr(start, end - start)); 101 3248 : if(end != string::npos) { 102 2974 : ++end; 103 : } 104 : } 105 274 : return tokens; 106 : } 107 : 108 : 109 : /** Make an absolute path relative to a root path 110 : * 111 : * Convert @absolute_path into a relative one with respect to the given 112 : * @root_path. If @absolute_path do not start at the given @root_path an empty 113 : * string will be returned. NOTE: Trailing slash will be stripped from the new 114 : * constructed relative path. 115 : */ 116 : string 117 0 : absolute_to_relative(const string& root_path, const string& absolute_path) { 118 0 : assert(is_absolute(root_path)); 119 0 : assert(is_absolute(absolute_path)); 120 0 : assert(!has_trailing_slash(root_path)); 121 : 122 0 : auto diff_its = ::mismatch(absolute_path.cbegin(), absolute_path.cend(), 123 0 : root_path.cbegin()); 124 0 : if(diff_its.second != root_path.cend()) { 125 : // complete path doesn't start with root_path 126 0 : return {}; 127 : } 128 : 129 : // iterator to the starting char of the relative portion of the 130 : // @absolute_path 131 0 : auto rel_it_begin = diff_its.first; 132 : // iterator to the end of the relative portion of the @absolute_path 133 0 : auto rel_it_end = absolute_path.cend(); 134 : 135 : // relative path start exactly after the root_path prefix 136 0 : assert((size_t) (rel_it_begin - absolute_path.cbegin()) == 137 : root_path.size()); 138 : 139 0 : if(rel_it_begin == rel_it_end) { 140 : // relative path is empty, @absolute_path was equal to @root_path 141 0 : return {'/'}; 142 : } 143 : 144 : // remove the trailing slash from relative path 145 0 : if(has_trailing_slash(absolute_path) && 146 0 : rel_it_begin != 147 0 : rel_it_end - 148 0 : 1) { // the relative path is longer then 1 char ('/') 149 0 : --rel_it_end; 150 : } 151 : 152 0 : return {rel_it_begin, rel_it_end}; 153 : } 154 : 155 : /** 156 : * returns the directory name for given path 157 : * @param path 158 : * @return 159 : */ 160 : string 161 1064 : dirname(const string& path) { 162 1064 : assert(path.size() > 1 || path.front() == separator); 163 1064 : assert(path.size() == 1 || !has_trailing_slash(path)); 164 : 165 1064 : auto parent_path_size = path.find_last_of(separator); 166 1064 : assert(parent_path_size != string::npos); 167 1064 : if(parent_path_size == 0) { 168 : // parent is '/' 169 48 : parent_path_size = 1; 170 : } 171 1064 : return path.substr(0, parent_path_size); 172 : } 173 : 174 : } // namespace gkfs::path