Home | History | Annotate | Line # | Download | only in fs
      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