1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
// SPDX-FileCopyrightInfo: Copyright © DUNE Project contributors, see file LICENSE.md in module root
// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
#include <algorithm>
#include <iterator>
#include <string>
#include <dune/common/exceptions.hh>
#include <dune/common/stringutility.hh>
#include <dune/common/path.hh>
namespace Dune {
/**
* @addtogroup Path Filesystem Paths
* @ingroup Common
* @{
*/
/**
* @file
* @author Jö Fahlke <jorrit@jorrit.de>
* @brief Utilities for handling filesystem paths
*/
//! concatenate two paths
std::string concatPaths(const std::string& base, const std::string& p) {
if(p == "") return base;
if(p[0] == '/') return p;
if(base == "") return p;
if(hasSuffix(base, "/")) return base+p;
else return base+'/'+p;
}
//! sanitize a path for further processing
std::string processPath(const std::string& p) {
std::string result = p;
std::string::size_type src, dst;
// append a '/' to non-empty paths
if(result != "") result += '/';
// each path component now has a trailing '/'
// collapse any occurrence of multiple '/' to a single '/'
dst = src = 0;
while(src < result.size()) {
result[dst] = result[src];
++src;
if(result[dst] == '/')
while(src < result.size() && result[src] == '/')
++src;
++dst;
}
result.resize(dst);
// the path is now free of multiple '/' in a row
// collapse any occurrence of "/./" to "/"
dst = src = 0;
while(src < result.size()) {
result[dst] = result[src];
++src;
if(result[dst] == '/')
while(src+1 < result.size() && result[src] == '.' &&
result[src+1] == '/')
src+=2;
++dst;
}
result.resize(dst);
// there may be at most one leading "./". If so, remove it
if(hasPrefix(result, "./")) result.erase(0, 2);
// the path is now free of "."-components
// remove any "<component>/../" pairs
src = 0;
while(true) {
src = result.find("/../", src);
if(src == std::string::npos)
break;
for(dst = src; dst > 0 && result[dst-1] != '/'; --dst) ;
if(result.substr(dst, src-dst) == "..") {
// don't remove "../../"
src += 3;
continue;
}
if(dst == src)
// special case: "<component>" is the empty component. This means we
// found a leading "/../" in an absolute path, remove "/.."
result.erase(0, 3);
else {
// remove "<component>/../".
result.erase(dst, src-dst+4);
src = dst;
// try to back up one character so we are at a '/' instead of at the
// beginning of a component
if(src > 0) --src;
}
}
// absolute paths are now free of ".." components, and relative paths
// contain only leading ".." components
return result;
}
//! check whether the given path indicates that it is a directory
bool pathIndicatesDirectory(const std::string& p) {
if(p == "") return true;
if(p == ".") return true;
if(p == "..") return true;
if(hasSuffix(p, "/")) return true;
if(hasSuffix(p, "/.")) return true;
if(hasSuffix(p, "/..")) return true;
else return false;
}
//! pretty print path
std::string prettyPath(const std::string& p, bool isDirectory) {
std::string result = processPath(p);
// current directory
if(result == "") return ".";
// root directory
if(result == "/") return result;
// remove the trailing '/' for now
result.resize(result.size()-1);
// if the result ends in "..", we don't need to append '/' to make clear
// it's a directory
if(result == ".." || hasSuffix(result, "/.."))
return result;
// if it's a directory, tuck the '/' back on
if(isDirectory) result += '/';
return result;
}
//! pretty print path
std::string prettyPath(const std::string& p) {
return prettyPath(p, pathIndicatesDirectory(p));
}
//! compute a relative path between two paths
std::string relativePath(const std::string& newbase, const std::string& p)
{
bool absbase = hasPrefix(newbase, "/");
bool absp = hasPrefix(p, "/");
if(absbase != absp)
DUNE_THROW(NotImplemented, "relativePath: paths must be either both "
"relative or both absolute: newbase=\"" << newbase << "\" "
"p=\"" << p << "\"");
std::string mybase = processPath(newbase);
std::string myp = processPath(p);
// remove as many matching leading components as possible
// determine prefix length
std::string::size_type preflen = 0;
while(preflen < mybase.size() && preflen < myp.size() &&
mybase[preflen] == myp[preflen])
++preflen;
// backup to the beginning of the component
while(preflen > 0 && myp[preflen-1] != '/')
--preflen;
mybase.erase(0, preflen);
myp.erase(0,preflen);
// if mybase contains leading ".." components, we're screwed
if(hasPrefix(mybase, "../"))
DUNE_THROW(NotImplemented, "relativePath: newbase has too many leading "
"\"..\" components: newbase=\"" << newbase << "\" "
"p=\"" << p << "\"");
// count the number of components in mybase
typedef std::iterator_traits<std::string::iterator>::difference_type
count_t;
count_t count = std::count(mybase.begin(), mybase.end(), '/');
std::string result;
// prefix with that many leading components
for(count_t i = 0; i < count; ++i)
result += "../";
// append what is left of p
result += myp;
return result;
}
/** @} group Path */
}
|