1// Copyright 2010 The Kyua Authors. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above copyright 11// notice, this list of conditions and the following disclaimer in the 12// documentation and/or other materials provided with the distribution. 13// * Neither the name of Google Inc. nor the names of its contributors 14// may be used to endorse or promote products derived from this software 15// without specific prior written permission. 16// 17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29#include "utils/fs/path.hpp" 30 31#include "utils/fs/exceptions.hpp" 32#include "utils/fs/operations.hpp" 33#include "utils/sanity.hpp" 34 35namespace fs = utils::fs; 36 37 38namespace { 39 40 41/// Normalizes an input string to a valid path. 42/// 43/// A normalized path cannot have empty components; i.e. there can be at most 44/// one consecutive separator (/). 45/// 46/// \param in The string to normalize. 47/// 48/// \return The normalized string, representing a path. 49/// 50/// \throw utils::fs::invalid_path_error If the path is empty. 51static std::string 52normalize(const std::string& in) 53{ 54 if (in.empty()) 55 throw fs::invalid_path_error(in, "Cannot be empty"); 56 57 std::string out; 58 59 std::string::size_type pos = 0; 60 do { 61 const std::string::size_type next_pos = in.find('/', pos); 62 63 const std::string component = in.substr(pos, next_pos - pos); 64 if (!component.empty()) { 65 if (pos == 0) 66 out += component; 67 else if (component != ".") 68 out += "/" + component; 69 } 70 71 if (next_pos == std::string::npos) 72 pos = next_pos; 73 else 74 pos = next_pos + 1; 75 } while (pos != std::string::npos); 76 77 return out.empty() ? "/" : out; 78} 79 80 81} // anonymous namespace 82 83 84/// Creates a new path object from a textual representation of a path. 85/// 86/// \param text A valid representation of a path in textual form. 87/// 88/// \throw utils::fs::invalid_path_error If the input text does not represent a 89/// valid path. 90fs::path::path(const std::string& text) : 91 _repr(normalize(text)) 92{ 93} 94 95 96/// Gets a view of the path as an array of characters. 97/// 98/// \return A \code const char* \endcode representation for the object. 99const char* 100fs::path::c_str(void) const 101{ 102 return _repr.c_str(); 103} 104 105 106/// Gets a view of the path as a std::string. 107/// 108/// \return A \code std::string& \endcode representation for the object. 109const std::string& 110fs::path::str(void) const 111{ 112 return _repr; 113} 114 115 116/// Gets the branch path (directory name) of the path. 117/// 118/// The branch path of a path with just one component (no separators) is ".". 119/// 120/// \return A new path representing the branch path. 121fs::path 122fs::path::branch_path(void) const 123{ 124 const std::string::size_type end_pos = _repr.rfind('/'); 125 if (end_pos == std::string::npos) 126 return fs::path("."); 127 else if (end_pos == 0) 128 return fs::path("/"); 129 else 130 return fs::path(_repr.substr(0, end_pos)); 131} 132 133 134/// Gets the leaf name (base name) of the path. 135/// 136/// \return A new string representing the leaf name. 137std::string 138fs::path::leaf_name(void) const 139{ 140 const std::string::size_type beg_pos = _repr.rfind('/'); 141 142 if (beg_pos == std::string::npos) 143 return _repr; 144 else 145 return _repr.substr(beg_pos + 1); 146} 147 148 149/// Converts a relative path in the current directory to an absolute path. 150/// 151/// \pre The path is relative. 152/// 153/// \return The absolute representation of the relative path. 154fs::path 155fs::path::to_absolute(void) const 156{ 157 PRE(!is_absolute()); 158 return fs::current_path() / *this; 159} 160 161 162/// \return True if the representation of the path is absolute. 163bool 164fs::path::is_absolute(void) const 165{ 166 return _repr[0] == '/'; 167} 168 169 170/// Checks whether the path is a parent of another path. 171/// 172/// A path is considered to be a parent of itself. 173/// 174/// \return True if this path is a parent of p. 175bool 176fs::path::is_parent_of(path p) const 177{ 178 do { 179 if ((*this) == p) 180 return true; 181 p = p.branch_path(); 182 } while (p != fs::path(".") && p != fs::path("/")); 183 return false; 184} 185 186 187/// Counts the number of components in the path. 188/// 189/// \return The number of components. 190int 191fs::path::ncomponents(void) const 192{ 193 int count = 0; 194 if (_repr == "/") 195 return 1; 196 else { 197 for (std::string::const_iterator iter = _repr.begin(); 198 iter != _repr.end(); ++iter) { 199 if (*iter == '/') 200 count++; 201 } 202 return count + 1; 203 } 204} 205 206 207/// Less-than comparator for paths. 208/// 209/// This is provided to make identifiers useful as map keys. 210/// 211/// \param p The path to compare to. 212/// 213/// \return True if this identifier sorts before the other identifier; false 214/// otherwise. 215bool 216fs::path::operator<(const fs::path& p) const 217{ 218 return _repr < p._repr; 219} 220 221 222/// Compares two paths for equality. 223/// 224/// Given that the paths are internally normalized, input paths such as 225/// ///foo/bar and /foo///bar are exactly the same. However, this does NOT 226/// check for true equality: i.e. this does not access the file system to check 227/// if the paths actually point to the same object my means of links. 228/// 229/// \param p The path to compare to. 230/// 231/// \returns A boolean indicating whether the paths are equal. 232bool 233fs::path::operator==(const fs::path& p) const 234{ 235 return _repr == p._repr; 236} 237 238 239/// Compares two paths for inequality. 240/// 241/// See the description of operator==() for more details on the comparison 242/// performed. 243/// 244/// \param p The path to compare to. 245/// 246/// \returns A boolean indicating whether the paths are different. 247bool 248fs::path::operator!=(const fs::path& p) const 249{ 250 return _repr != p._repr; 251} 252 253 254/// Concatenates this path with one or more components. 255/// 256/// \param components The new components to concatenate to the path. These are 257/// normalized because, in general, they may come from user input. These 258/// components cannot represent an absolute path. 259/// 260/// \return A new path containing the concatenation of this path and the 261/// provided components. 262/// 263/// \throw utils::fs::invalid_path_error If components does not represent a 264/// valid path. 265/// \throw utils::fs::join_error If the join operation is invalid because the 266/// two paths are incompatible. 267fs::path 268fs::path::operator/(const std::string& components) const 269{ 270 return (*this) / fs::path(components); 271} 272 273 274/// Concatenates this path with another path. 275/// 276/// \param rest The path to concatenate to this one. Cannot be absolute. 277/// 278/// \return A new path containing the concatenation of this path and the other 279/// path. 280/// 281/// \throw utils::fs::join_error If the join operation is invalid because the 282/// two paths are incompatible. 283fs::path 284fs::path::operator/(const fs::path& rest) const 285{ 286 if (rest.is_absolute()) 287 throw fs::join_error(_repr, rest._repr, 288 "Cannot concatenate a path to an absolute path"); 289 return fs::path(_repr + '/' + rest._repr); 290} 291 292 293/// Formats a path for insertion on a stream. 294/// 295/// \param os The output stream. 296/// \param p The path to inject to the stream. 297/// 298/// \return The output stream os. 299std::ostream& 300fs::operator<<(std::ostream& os, const fs::path& p) 301{ 302 return (os << p.str()); 303} 304