Skip to content
Snippets Groups Projects
Commit 90fec5bb authored by Jorrit Fahlke's avatar Jorrit Fahlke
Browse files

Utilities for the parsing and processing of filesystem paths.

[[Imported from SVN: r6039]]
parent 3ee8758a
Branches
Tags
No related merge requests found
......@@ -9,6 +9,7 @@ libcommon_la_SOURCES = \
configparser.cc \
ios_state.cc \
parametertree.cc \
path.cc \
stdstreams.cc
commonincludedir = $(includedir)/dune/common
......@@ -53,6 +54,7 @@ commoninclude_HEADERS = \
mpitraits.hh \
nullptr.hh \
parametertree.hh \
path.hh \
polyallocator.hh \
poolallocator.hh \
precision.hh \
......
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <algorithm>
#include <iterator>
#include <string>
#include <dune/common/exceptions.hh>
#include <dune/common/misc.hh>
#include <dune/common/path.hh>
namespace Dune {
/**
* @addtogroup Path Filesystem Paths
* @ingroup Common
* @{
*/
/**
* @file
* @author Jö Fahlke <jorrit@jorrit.de>
* @brief Utilites 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 occurance 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 occurance 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 */
}
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#ifndef DUNE_COMMON_PATH_HH
#define DUNE_COMMON_PATH_HH
#include <string>
namespace Dune {
/**
* @addtogroup Path Filesystem Paths
* @ingroup Common
* @{
*/
/**
* @file
* @author Jö Fahlke <jorrit@jorrit.de>
* @brief Utilites for handling filesystem paths
*/
//! concatenate two paths
/**
* \param base The base path.
* \param p The path to concatenate onto base.
*
* If p is an absolute path, return p. Otherwise return the
* string-concatenation of base and path, possibly with a '/' inbetween, if
* necessary.
*
* Some examples:
* <table>
* <tr><th> base </th><th> p </th><th> result </th></tr>
* <tr><td> anything </td><td> "/abs/path" </td><td> "/abs/path" </td></tr>
* <tr><td> "a" </td><td> "b" </td><td> "a/b" </td></tr>
* <tr><td> "/a" </td><td> "b" </td><td> "/a/b" </td></tr>
* <tr><td> "a/" </td><td> "b" </td><td> "a/b" </td></tr>
* <tr><td> "a" </td><td> "b/" </td><td> "a/b/" </td></tr>
* <tr><td> ".." </td><td> "b" </td><td> "../b" </td></tr>
* <tr><td> "a" </td><td> ".." </td><td> "a/.." </td></tr>
* <tr><td> "." </td><td> "b" </td><td> "./b" </td></tr>
* <tr><td> "a" </td><td> "." </td><td> "a/." </td></tr>
* <tr><td> "" </td><td> "b" </td><td> "b" </td></tr>
* <tr><td> "a" </td><td> "" </td><td> "a" </td></tr>
* <tr><td> "" </td><td> "" </td><td> "" </td></tr>
* </table>
*
* If both base and p are sanitized as per processPath(), and if p does not
* contain any leading "../", then the result will also be sanitized.
*/
std::string concatPaths(const std::string& base, const std::string& p);
//! sanitize a path for further processing
/**
* Sanitize the path as far as possible to make further processing easier.
* The resulting path has the following properties:
* <ul>
* <li> The path is a series of components, each followed by a single '/'.
* <li> An absolute path starts with an empty component followed by a '/',
* so its first charachter will be '/'. This is the only case where an
* empty component can occur.
* <li> The path does not contain any component ".". Any such component in
* the input is removed.
* <li> A ".." component may only occur in the following case: A relative
* path may contain a series of ".." in the beginning. Any other
* occurances of ".." int eh input is collapsed with a preceding
* component or simply removed if it is at the beginning of an absolute
* path.
* </ul>
*
* \note The result is really meant for processing only since it has two
* unusual properties: First, any path denoting the current directory
* in the input, such as "." will result in an empty path "". Second,
* any non-empty result path will have a trailing '/'. For other
* uses, prettyPath() may be more appropriate.
*
* Some examples:
* <table>
* <tr><th> p </th><th> result </th></tr>
* <tr><td> "" </td><td> "" </td></tr>
* <tr><td> "." </td><td> "" </td></tr>
* <tr><td> "./" </td><td> "" </td></tr>
* <tr><td> "a/.." </td><td> "" </td></tr>
* <tr><td> ".." </td><td> "../" </td></tr>
* <tr><td> "../a" </td><td> "../a/" </td></tr>
* <tr><td> "a" </td><td> "a/" </td></tr>
* <tr><td> "a//" </td><td> "a/" </td></tr>
* <tr><td> "a///b" </td><td> "a/b/" </td></tr>
* <tr><td> "/" </td><td> "/" </td></tr>
* <tr><td> "/." </td><td> "/" </td></tr>
* <tr><td> "/.." </td><td> "/" </td></tr>
* <tr><td> "/a/.." </td><td> "/" </td></tr>
* <tr><td> "/a" </td><td> "/a/" </td></tr>
* <tr><td> "/a/" </td><td> "/a/" </td></tr>
* <tr><td> "/../a/" </td><td> "/a/" </td></tr>
* </table>
*/
std::string processPath(const std::string& p);
//! check whether the given path indicates that it is a directory
/**
* In particular the following kinds of paths indicate a directory:
* <ul>
* <li> The empty path (denotes the current directory),
* <li> any path with a trailing '/',
* <li> any path whose last component is "." or "..".
* </ul>
*/
bool pathIndicatesDirectory(const std::string& p);
//! pretty print path
/**
* \param p Path to pretty-print.
* \param isDirectory Whether to append a '/' to make clear this is a
* directory.
*
* Pretty print the path. This removes any duplicate '/' and any
* superfluous occurances of ".." and ".". The resulting path will have a
* trailing '/' if it is the root path or if isDirectory is true. It will
* however not have a trailing '/' if it is otherwise clear that it is a
* directory -- i.e. if its last component is "." or "..".
*
* Some examples:
* <table>
* <tr><th> p </th><th> isDirectory </th><th> result </th></tr>
* <tr><td> "" </td><td> anything </td><td> "." </td></tr>
* <tr><td> "." </td><td> anything </td><td> "." </td></tr>
* <tr><td> "./" </td><td> anything </td><td> "." </td></tr>
* <tr><td> "a/.." </td><td> anything </td><td> "." </td></tr>
* <tr><td> ".." </td><td> anything </td><td> ".." </td></tr>
* <tr><td> "../a" </td><td> true </td><td> "../a/" </td></tr>
* <tr><td> "../a" </td><td> false </td><td> "../a" </td></tr>
* <tr><td> "a" </td><td> true </td><td> "a/" </td></tr>
* <tr><td> "a" </td><td> false </td><td> "a" </td></tr>
* <tr><td> "a//" </td><td> true </td><td> "a/" </td></tr>
* <tr><td> "a//" </td><td> false </td><td> "a" </td></tr>
* <tr><td> "a///b" </td><td> true </td><td> "a/b/" </td></tr>
* <tr><td> "a///b" </td><td> false </td><td> "a/b" </td></tr>
* <tr><td> "/" </td><td> anything </td><td> "/" </td></tr>
* <tr><td> "/." </td><td> anything </td><td> "/" </td></tr>
* <tr><td> "/.." </td><td> anything </td><td> "/" </td></tr>
* <tr><td> "/a/.." </td><td> anything </td><td> "/" </td></tr>
* <tr><td> "/a" </td><td> true </td><td> "/a/" </td></tr>
* <tr><td> "/a" </td><td> false </td><td> "/a" </td></tr>
* <tr><td> "/a/" </td><td> true </td><td> "/a/" </td></tr>
* <tr><td> "/a/" </td><td> false </td><td> "/a" </td></tr>
* <tr><td> "/../a/" </td><td> true </td><td> "/a/" </td></tr>
* <tr><td> "/../a/" </td><td> false </td><td> "/a" </td></tr>
* </table>
*/
std::string prettyPath(const std::string& p, bool isDirectory);
//! pretty print path
/**
* \param p Path to pretty-print.
*
* This is like prettyPath(const std::string& p, bool isDirectory) with
* isDirectory automatically determined using pathIndicatesDirectory(p).
*/
std::string prettyPath(const std::string& p);
//! compute a relative path between two paths
/**
* \param newbase Base path for the resulting relative path.
* \param p Path re sulting path should resolve to, when taken
* reltively to newbase.
*
* Compute a relative path from newbase to p. newbase is assumed to be a
* directory. p and newbase should either both be absolute, or both be
* relative. In the latter case they are assumed to both be relative to
* the same unspecified directory. The has the form of something sanitized
* by processPath().
*
* \throw NotImplemented The condition that newbase and p must both be
* relative or both be absolute does not hold.
* \throw NotImplemented After sanitization newbase has more leading ".."
* components than p.
*/
std::string relativePath(const std::string& newbase, const std::string& p);
/** @} group Path */
}
#endif // DUNE_COMMON_PATH_HH
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment