1 // Copyright 2010 Google Inc. 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 const char* 98 fs::path::c_str(void) const 99 { 100 return _repr.c_str(); 101 } 102 103 104 /// Gets a view of the path as a std::string. 105 const std::string& 106 fs::path::str(void) const 107 { 108 return _repr; 109 } 110 111 112 /// Gets the branch path (directory name) of the path. 113 /// 114 /// The branch path of a path with just one component (no separators) is ".". 115 /// 116 /// \return A new path representing the branch path. 117 fs::path 118 fs::path::branch_path(void) const 119 { 120 const std::string::size_type end_pos = _repr.rfind('/'); 121 if (end_pos == std::string::npos) 122 return fs::path("."); 123 else if (end_pos == 0) 124 return fs::path("/"); 125 else 126 return fs::path(_repr.substr(0, end_pos)); 127 } 128 129 130 /// Gets the leaf name (base name) of the path. 131 /// 132 /// \return A new string representing the leaf name. 133 std::string 134 fs::path::leaf_name(void) const 135 { 136 const std::string::size_type beg_pos = _repr.rfind('/'); 137 138 if (beg_pos == std::string::npos) 139 return _repr; 140 else 141 return _repr.substr(beg_pos + 1); 142 } 143 144 145 /// Converts a relative path in the current directory to an absolute path. 146 /// 147 /// \pre The path is relative. 148 /// 149 /// \return The absolute representation of the relative path. 150 fs::path 151 fs::path::to_absolute(void) const 152 { 153 PRE(!is_absolute()); 154 return fs::current_path() / *this; 155 } 156 157 158 /// Checks whether the path is absolute. 159 bool 160 fs::path::is_absolute(void) const 161 { 162 return _repr[0] == '/'; 163 } 164 165 166 /// Checks whether the path is a parent of another path. 167 /// 168 /// A path is considered to be a parent of itself. 169 /// 170 /// \return True if this path is a parent of p. 171 bool 172 fs::path::is_parent_of(path p) const 173 { 174 do { 175 if ((*this) == p) 176 return true; 177 p = p.branch_path(); 178 } while (p != fs::path(".") && p != fs::path("/")); 179 return false; 180 } 181 182 183 /// Counts the number of components in the path. 184 /// 185 /// \return The number of components. 186 int 187 fs::path::ncomponents(void) const 188 { 189 int count = 0; 190 if (_repr == "/") 191 return 1; 192 else { 193 for (std::string::const_iterator iter = _repr.begin(); 194 iter != _repr.end(); ++iter) { 195 if (*iter == '/') 196 count++; 197 } 198 return count + 1; 199 } 200 } 201 202 203 /// Less-than comparator for paths. 204 /// 205 /// This is provided to make identifiers useful as map keys. 206 /// 207 /// \param p The path to compare to. 208 /// 209 /// \return True if this identifier sorts before the other identifier; false 210 /// otherwise. 211 bool 212 fs::path::operator<(const fs::path& p) const 213 { 214 return _repr < p._repr; 215 } 216 217 218 /// Compares two paths for equality. 219 /// 220 /// Given that the paths are internally normalized, input paths such as 221 /// ///foo/bar and /foo///bar are exactly the same. However, this does NOT 222 /// check for true equality: i.e. this does not access the file system to check 223 /// if the paths actually point to the same object my means of links. 224 /// 225 /// \param p The path to compare to. 226 /// 227 /// \returns A boolean indicating whether the paths are equal. 228 bool 229 fs::path::operator==(const fs::path& p) const 230 { 231 return _repr == p._repr; 232 } 233 234 235 /// Compares two paths for inequality. 236 /// 237 /// See the description of operator==() for more details on the comparison 238 /// performed. 239 /// 240 /// \param p The path to compare to. 241 /// 242 /// \returns A boolean indicating whether the paths are different. 243 bool 244 fs::path::operator!=(const fs::path& p) const 245 { 246 return _repr != p._repr; 247 } 248 249 250 /// Concatenates this path with one or more components. 251 /// 252 /// \param components The new components to concatenate to the path. These are 253 /// normalized because, in general, they may come from user input. These 254 /// components cannot represent an absolute path. 255 /// 256 /// \return A new path containing the concatenation of this path and the 257 /// provided components. 258 /// 259 /// \throw utils::fs::invalid_path_error If components does not represent a 260 /// valid path. 261 /// \throw utils::fs::join_error If the join operation is invalid because the 262 /// two paths are incompatible. 263 fs::path 264 fs::path::operator/(const std::string& components) const 265 { 266 return (*this) / fs::path(components); 267 } 268 269 270 /// Concatenates this path with another path. 271 /// 272 /// \param rest The path to concatenate to this one. Cannot be absolute. 273 /// 274 /// \return A new path containing the concatenation of this path and the other 275 /// path. 276 /// 277 /// \throw utils::fs::join_error If the join operation is invalid because the 278 /// two paths are incompatible. 279 fs::path 280 fs::path::operator/(const fs::path& rest) const 281 { 282 if (rest.is_absolute()) 283 throw fs::join_error(_repr, rest._repr, 284 "Cannot concatenate a path to an absolute path"); 285 return fs::path(_repr + '/' + rest._repr); 286 } 287 288 289 /// Formats a path for insertion on a stream. 290 /// 291 /// \param os The output stream. 292 /// \param p The path to inject to the stream. 293 /// 294 /// \return The output stream os. 295 std::ostream& 296 fs::operator<<(std::ostream& os, const fs::path& p) 297 { 298 return (os << p.str()); 299 } 300