// Copyright 2010 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/fs/path.hpp"2930#include "utils/fs/exceptions.hpp"31#include "utils/fs/operations.hpp"32#include "utils/sanity.hpp"3334namespace fs = utils::fs;353637namespace {383940/// Normalizes an input string to a valid path.41///42/// A normalized path cannot have empty components; i.e. there can be at most43/// one consecutive separator (/).44///45/// \param in The string to normalize.46///47/// \return The normalized string, representing a path.48///49/// \throw utils::fs::invalid_path_error If the path is empty.50static std::string51normalize(const std::string& in)52{53if (in.empty())54throw fs::invalid_path_error(in, "Cannot be empty");5556std::string out;5758std::string::size_type pos = 0;59do {60const std::string::size_type next_pos = in.find('/', pos);6162const std::string component = in.substr(pos, next_pos - pos);63if (!component.empty()) {64if (pos == 0)65out += component;66else if (component != ".")67out += "/" + component;68}6970if (next_pos == std::string::npos)71pos = next_pos;72else73pos = next_pos + 1;74} while (pos != std::string::npos);7576return out.empty() ? "/" : out;77}787980} // anonymous namespace818283/// Creates a new path object from a textual representation of a path.84///85/// \param text A valid representation of a path in textual form.86///87/// \throw utils::fs::invalid_path_error If the input text does not represent a88/// valid path.89fs::path::path(const std::string& text) :90_repr(normalize(text))91{92}939495/// Gets a view of the path as an array of characters.96///97/// \return A \code const char* \endcode representation for the object.98const char*99fs::path::c_str(void) const100{101return _repr.c_str();102}103104105/// Gets a view of the path as a std::string.106///107/// \return A \code std::string& \endcode representation for the object.108const std::string&109fs::path::str(void) const110{111return _repr;112}113114115/// Gets the branch path (directory name) of the path.116///117/// The branch path of a path with just one component (no separators) is ".".118///119/// \return A new path representing the branch path.120fs::path121fs::path::branch_path(void) const122{123const std::string::size_type end_pos = _repr.rfind('/');124if (end_pos == std::string::npos)125return fs::path(".");126else if (end_pos == 0)127return fs::path("/");128else129return fs::path(_repr.substr(0, end_pos));130}131132133/// Gets the leaf name (base name) of the path.134///135/// \return A new string representing the leaf name.136std::string137fs::path::leaf_name(void) const138{139const std::string::size_type beg_pos = _repr.rfind('/');140141if (beg_pos == std::string::npos)142return _repr;143else144return _repr.substr(beg_pos + 1);145}146147148/// Converts a relative path in the current directory to an absolute path.149///150/// \pre The path is relative.151///152/// \return The absolute representation of the relative path.153fs::path154fs::path::to_absolute(void) const155{156PRE(!is_absolute());157return fs::current_path() / *this;158}159160161/// \return True if the representation of the path is absolute.162bool163fs::path::is_absolute(void) const164{165return _repr[0] == '/';166}167168169/// Checks whether the path is a parent of another path.170///171/// A path is considered to be a parent of itself.172///173/// \return True if this path is a parent of p.174bool175fs::path::is_parent_of(path p) const176{177do {178if ((*this) == p)179return true;180p = p.branch_path();181} while (p != fs::path(".") && p != fs::path("/"));182return false;183}184185186/// Counts the number of components in the path.187///188/// \return The number of components.189int190fs::path::ncomponents(void) const191{192int count = 0;193if (_repr == "/")194return 1;195else {196for (std::string::const_iterator iter = _repr.begin();197iter != _repr.end(); ++iter) {198if (*iter == '/')199count++;200}201return count + 1;202}203}204205206/// Less-than comparator for paths.207///208/// This is provided to make identifiers useful as map keys.209///210/// \param p The path to compare to.211///212/// \return True if this identifier sorts before the other identifier; false213/// otherwise.214bool215fs::path::operator<(const fs::path& p) const216{217return _repr < p._repr;218}219220221/// Compares two paths for equality.222///223/// Given that the paths are internally normalized, input paths such as224/// ///foo/bar and /foo///bar are exactly the same. However, this does NOT225/// check for true equality: i.e. this does not access the file system to check226/// if the paths actually point to the same object my means of links.227///228/// \param p The path to compare to.229///230/// \returns A boolean indicating whether the paths are equal.231bool232fs::path::operator==(const fs::path& p) const233{234return _repr == p._repr;235}236237238/// Compares two paths for inequality.239///240/// See the description of operator==() for more details on the comparison241/// performed.242///243/// \param p The path to compare to.244///245/// \returns A boolean indicating whether the paths are different.246bool247fs::path::operator!=(const fs::path& p) const248{249return _repr != p._repr;250}251252253/// Concatenates this path with one or more components.254///255/// \param components The new components to concatenate to the path. These are256/// normalized because, in general, they may come from user input. These257/// components cannot represent an absolute path.258///259/// \return A new path containing the concatenation of this path and the260/// provided components.261///262/// \throw utils::fs::invalid_path_error If components does not represent a263/// valid path.264/// \throw utils::fs::join_error If the join operation is invalid because the265/// two paths are incompatible.266fs::path267fs::path::operator/(const std::string& components) const268{269return (*this) / fs::path(components);270}271272273/// Concatenates this path with another path.274///275/// \param rest The path to concatenate to this one. Cannot be absolute.276///277/// \return A new path containing the concatenation of this path and the other278/// path.279///280/// \throw utils::fs::join_error If the join operation is invalid because the281/// two paths are incompatible.282fs::path283fs::path::operator/(const fs::path& rest) const284{285if (rest.is_absolute())286throw fs::join_error(_repr, rest._repr,287"Cannot concatenate a path to an absolute path");288return fs::path(_repr + '/' + rest._repr);289}290291292/// Formats a path for insertion on a stream.293///294/// \param os The output stream.295/// \param p The path to inject to the stream.296///297/// \return The output stream os.298std::ostream&299fs::operator<<(std::ostream& os, const fs::path& p)300{301return (os << p.str());302}303304305