path.cpp (5981B)
1 /* ReaPack: Package manager for REAPER 2 * Copyright (C) 2015-2025 Christian Fillion 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "path.hpp" 19 20 #include <algorithm> 21 #include <boost/range/adaptor/reversed.hpp> 22 #include <vector> 23 24 static constexpr char UNIX_SEPARATOR = '/'; 25 26 #ifndef _WIN32 27 static constexpr char NATIVE_SEPARATOR = UNIX_SEPARATOR; 28 #else 29 #include <windows.h> // MAX_PATH 30 static constexpr char NATIVE_SEPARATOR = '\\'; 31 #endif 32 33 static constexpr const char *DOT = "."; 34 static constexpr const char *DOTDOT = ".."; 35 36 const Path Path::DATA("ReaPack"); 37 const Path Path::CACHE = Path::DATA + "cache"; 38 const Path Path::CONFIG("reapack.ini"); 39 const Path Path::REGISTRY = Path::DATA + "registry.db"; 40 41 Path Path::s_root; 42 43 static std::vector<std::string> Split(const std::string &input, int *attributes) 44 { 45 std::vector<std::string> list; 46 *attributes = 0; 47 48 size_t last = 0; 49 50 while(last < input.size()) { 51 const size_t pos = input.find_first_of("\\/", last); 52 53 std::string part; 54 55 if(pos == std::string::npos) 56 part = input.substr(last); 57 else if(last + pos == 0) { 58 #ifndef _WIN32 59 *attributes |= Path::Absolute; 60 #endif 61 last++; 62 continue; 63 } 64 else if(last == pos) { 65 #ifdef _WIN32 66 if(pos == 1) 67 *attributes |= Path::UNC | Path::Absolute; 68 #endif 69 last++; 70 continue; 71 } 72 else 73 part = input.substr(last, pos - last); 74 75 #ifdef _WIN32 76 if(list.empty() && part.size() == 2 && part[1] == ':' && isalpha(part[0])) 77 *attributes |= Path::Absolute; 78 #endif 79 80 if(part != DOT) 81 list.push_back(part); 82 83 last += part.size() + 1; 84 } 85 86 return list; 87 } 88 89 Path::Path(const std::string &path) : m_attributes{} 90 { 91 append(path); 92 } 93 94 void Path::append(const std::string &input, const bool traversal) 95 { 96 if(input.empty()) 97 return; 98 99 int attributes; 100 const auto &parts = Split(input, &attributes); 101 102 if(m_parts.empty() && attributes) 103 m_attributes = attributes; 104 105 for(const std::string &part : parts) { 106 if(part == DOTDOT) { 107 if(traversal) 108 removeLast(); 109 } 110 else 111 m_parts.push_back(part); 112 } 113 } 114 115 void Path::append(const Path &o) 116 { 117 if(m_parts.empty()) 118 m_attributes = o.attributes(); 119 120 m_parts.insert(m_parts.end(), o.m_parts.begin(), o.m_parts.end()); 121 } 122 123 void Path::clear() 124 { 125 m_parts.clear(); 126 } 127 128 void Path::remove(const size_t pos, size_t count) 129 { 130 if(pos > size()) 131 return; 132 else if(pos + count > size()) 133 count = size() - pos; 134 135 auto begin = m_parts.begin(); 136 std::advance(begin, pos); 137 138 auto end = begin; 139 std::advance(end, count); 140 141 m_parts.erase(begin, end); 142 143 if(!pos) 144 m_attributes = 0; 145 } 146 147 void Path::removeLast() 148 { 149 if(!empty()) 150 m_parts.pop_back(); 151 } 152 153 std::string Path::front() const 154 { 155 if(empty()) 156 return {}; 157 158 return m_parts.front(); 159 } 160 161 std::string Path::basename() const 162 { 163 if(empty()) 164 return {}; 165 166 return m_parts.back(); 167 } 168 169 Path Path::dirname() const 170 { 171 if(empty()) 172 return {}; 173 174 Path dir(*this); 175 dir.removeLast(); 176 return dir; 177 } 178 179 std::string Path::join(const bool nativeSeparator) const 180 { 181 const char sep = nativeSeparator ? NATIVE_SEPARATOR : UNIX_SEPARATOR; 182 183 std::string path; 184 185 #ifndef _WIN32 186 if(test(Absolute)) 187 path += sep; 188 #endif 189 190 for(const std::string &part : m_parts) { 191 #ifdef _WIN32 192 if(!path.empty()) 193 #else 194 if(path.size() > test(Absolute)) 195 #endif 196 path += sep; 197 198 path += part; 199 } 200 201 #ifdef _WIN32 202 if(test(Absolute) && path.size() >= MAX_PATH - (8+1+3)) { 203 path.insert(0, "\\\\?\\"); 204 205 if(test(UNC)) 206 path.insert(4, "UNC\\"); 207 } 208 else if(test(UNC)) 209 path.insert(0, "\\\\"); 210 #endif 211 212 return path; 213 } 214 215 bool Path::startsWith(const Path &o) const 216 { 217 if(m_parts.size() < o.size() || m_attributes != o.attributes()) 218 return false; 219 220 for(size_t i = 0; i < o.size(); i++) { 221 if(o[i] != at(i)) 222 return false; 223 } 224 225 return true; 226 } 227 228 Path Path::prependRoot() const 229 { 230 return m_attributes & Absolute ? *this : s_root + *this; 231 } 232 233 Path Path::removeRoot() const 234 { 235 Path copy(*this); 236 237 if(startsWith(s_root)) 238 copy.remove(0, s_root.size()); 239 240 return copy; 241 } 242 243 bool Path::operator==(const Path &o) const 244 { 245 return m_attributes == o.attributes() && m_parts == o.m_parts; 246 } 247 248 bool Path::operator!=(const Path &o) const 249 { 250 return !(*this == o); 251 } 252 253 bool Path::operator<(const Path &o) const 254 { 255 return m_parts < o.m_parts; 256 } 257 258 Path Path::operator+(const std::string &part) const 259 { 260 Path path(*this); 261 path.append(part); 262 263 return path; 264 } 265 266 Path Path::operator+(const Path &o) const 267 { 268 Path path(*this); 269 path.append(o); 270 271 return path; 272 } 273 274 const Path &Path::operator+=(const std::string &parts) 275 { 276 append(parts); 277 return *this; 278 } 279 280 const Path &Path::operator+=(const Path &o) 281 { 282 append(o); 283 return *this; 284 } 285 286 const std::string &Path::at(const size_t index) const 287 { 288 auto it = m_parts.begin(); 289 advance(it, index); 290 291 return *it; 292 } 293 294 std::string &Path::operator[](const size_t index) 295 { 296 return const_cast<std::string &>(at(index)); 297 } 298 299 const std::string &Path::operator[](const size_t index) const 300 { 301 return at(index); 302 } 303 304 UseRootPath::UseRootPath(const Path &path) 305 : m_backup(std::move(Path::s_root)) 306 { 307 Path::s_root = path; 308 } 309 310 UseRootPath::~UseRootPath() 311 { 312 Path::s_root = std::move(m_backup); 313 } 314 315 TempPath::TempPath(const Path &target) 316 : m_target(target), m_temp(target) 317 { 318 m_temp[m_temp.size() - 1] += ".part"; 319 }