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 35 namespace fs = utils::fs; 36 37 38 namespace { 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. 51 static std::string 52 normalize(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. 90 fs::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. 99 const char* 100 fs::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. 109 const std::string& 110 fs::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. 121 fs::path 122 fs::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. 137 std::string 138 fs::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. 154 fs::path 155 fs::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. 163 bool 164 fs::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. 175 bool 176 fs::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. 190 int 191 fs::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. 215 bool 216 fs::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. 232 bool 233 fs::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. 247 bool 248 fs::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. 267 fs::path 268 fs::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. 283 fs::path 284 fs::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. 299 std::ostream& 300 fs::operator<<(std::ostream& os, const fs::path& p) 301 { 302 return (os << p.str()); 303 } 304