1
// -*- tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
// vi: set ts=8 sw=2 et sts=2:
12
#include <dune/common/exceptions.hh>
13
#include <dune/common/misc.hh>
14
#include <dune/common/path.hh>
18
* @addtogroup Path Filesystem Paths
25
* @author Jö Fahlke <jorrit@jorrit.de>
26
* @brief Utilites for handling filesystem paths
29
//! concatenate two paths
30
std::string concatPaths(const std::string& base, const std::string& p) {
31
if(p == "") return base;
32
if(p[0] == '/') return p;
33
if(base == "") return p;
34
if(hasSuffix(base, "/")) return base+p;
35
else return base+'/'+p;
38
//! sanitize a path for further processing
39
std::string processPath(const std::string& p) {
40
std::string result = p;
41
std::string::size_type src, dst;
43
// append a '/' to non-empty paths
44
if(result != "") result += '/';
46
// each path component now has a trailing '/'
48
// collapse any occurance of multiple '/' to a single '/'
50
while(src < result.size()) {
51
result[dst] = result[src];
53
if(result[dst] == '/')
54
while(src < result.size() && result[src] == '/')
60
// the path is now free of multiple '/' in a row
62
// collapse any occurance of "/./" to "/"
64
while(src < result.size()) {
65
result[dst] = result[src];
67
if(result[dst] == '/')
68
while(src+1 < result.size() && result[src] == '.' &&
75
// there may be at most one leading "./". If so, remove it
76
if(hasPrefix(result, "./")) result.erase(0, 2);
78
// the path is now free of "."-components
80
// remove any "<component>/../" pairs
83
src = result.find("/../", src);
84
if(src == std::string::npos)
86
for(dst = src; dst > 0 && result[dst-1] != '/'; --dst);
87
if(result.substr(dst, src-dst) == "..") {
88
// don't remove "../../"
93
// special case: "<component>" is the empty component. This means we
94
// found a leading "/../" in an absolute path, remove "/.."
97
// remove "<component>/../".
98
result.erase(dst, src-dst+4);
100
// try to back up one character so we are at a '/' instead of at the
101
// beginning of a component
106
// absolute paths are now free of ".." components, and relative paths
107
// contain only leading ".." components
112
//! check whether the given path indicates that it is a directory
113
bool pathIndicatesDirectory(const std::string& p) {
114
if(p == "") return true;
115
if(p == ".") return true;
116
if(p == "..") return true;
117
if(hasSuffix(p, "/")) return true;
118
if(hasSuffix(p, "/.")) return true;
119
if(hasSuffix(p, "/..")) return true;
123
//! pretty print path
124
std::string prettyPath(const std::string& p, bool isDirectory) {
125
std::string result = processPath(p);
127
if(result == "") return ".";
129
if(result == "/") return result;
131
// remove the trailing '/' for now
132
result.resize(result.size()-1);
134
// if the result ends in "..", we don't need to append '/' to make clear
136
if(result == ".." || hasSuffix(result, "/.."))
139
// if it's a directory, tuck the '/' back on
140
if(isDirectory) result += '/';
145
//! pretty print path
146
std::string prettyPath(const std::string& p) {
147
return prettyPath(p, pathIndicatesDirectory(p));
150
//! compute a relative path between two paths
151
std::string relativePath(const std::string& newbase, const std::string& p)
153
bool absbase = hasPrefix(newbase, "/");
154
bool absp = hasPrefix(p, "/");
156
DUNE_THROW(NotImplemented, "relativePath: paths must be either both "
157
"relative or both absolute: newbase=\"" << newbase << "\" "
158
"p=\"" << p << "\"");
160
std::string mybase = processPath(newbase);
161
std::string myp = processPath(p);
163
// remove as many matching leading components as possible
164
// determine prefix length
165
std::string::size_type preflen = 0;
166
while(preflen < mybase.size() && preflen < myp.size() &&
167
mybase[preflen] == myp[preflen])
169
// backup to the beginning of the component
170
while(preflen > 0 && myp[preflen-1] != '/')
172
mybase.erase(0, preflen);
173
myp.erase(0,preflen);
175
// if mybase contains leading ".." components, we're screwed
176
if(hasPrefix(mybase, "../"))
177
DUNE_THROW(NotImplemented, "relativePath: newbase has too many leading "
178
"\"..\" components: newbase=\"" << newbase << "\" "
179
"p=\"" << p << "\"");
181
// count the number of components in mybase
182
typedef std::iterator_traits<std::string::iterator>::difference_type
184
count_t count = std::count(mybase.begin(), mybase.end(), '/');
187
// prefix with that many leading components
188
for(count_t i = 0; i < count; ++i)
190
// append what is left of p