diff options
author | Marius Kintel <marius@kintel.net> | 2013-03-12 16:35:32 (GMT) |
---|---|---|
committer | Marius Kintel <marius@kintel.net> | 2013-03-12 16:35:32 (GMT) |
commit | 5628a18f6a0fcf2dd170cac31e386f1ad4f97c0c (patch) | |
tree | baf407ed0a29cca328236ad2cc15e985ad44bf34 | |
parent | dccc039211eebe3f69c935557bd56abae323c26f (diff) |
boostfs_relative_path was buggy, replaced with boostfs_uncomplete for now
-rw-r--r-- | src/boost-utils.cc | 128 | ||||
-rw-r--r-- | src/boost-utils.h | 7 | ||||
-rw-r--r-- | src/dxfdata.cc | 4 | ||||
-rw-r--r-- | src/value.cc | 2 |
4 files changed, 135 insertions, 6 deletions
diff --git a/src/boost-utils.cc b/src/boost-utils.cc index f81917a..5127047 100644 --- a/src/boost-utils.cc +++ b/src/boost-utils.cc @@ -2,10 +2,12 @@ #include <stdio.h> #include <iostream> -fs::path relativePath(const fs::path &path, const fs::path &relative_to) +// If the given (absolute) path is relative to the relative_to path, return a new +// relative path. Will normalize the given path first +fs::path boostfs_relative_path(const fs::path &path, const fs::path &relative_to) { // create absolute paths - fs::path p = fs::absolute(path); + fs::path p = fs::absolute(boostfs_normalize(path)); fs::path r = fs::absolute(relative_to); // if root paths are different, return absolute path @@ -40,3 +42,125 @@ fs::path relativePath(const fs::path &path, const fs::path &relative_to) return result; } + +// Will normalize the given path, i.e. remove any redundant ".." path elements. +fs::path boostfs_normalize(const fs::path &path) +{ + fs::path absPath = absolute(path); + fs::path::iterator it = absPath.begin(); + fs::path result = *it++; + + // Get canonical version of the existing part + for(;exists(result) && it != absPath.end(); ++it) { + result /= *it; + } + result = canonical(result.parent_path()); + --it; + + // For the rest remove ".." and "." in a path with no symlinks + for (; it != absPath.end(); ++it) { + // Just move back on ../ + if (*it == "..") { + result = result.parent_path(); + } + // Ignore "." + else if (*it != ".") { + // Just cat other path entries + result /= *it; + } + } + + return result; +} + +/** + * https://svn.boost.org/trac/boost/ticket/1976#comment:2 + * + * "The idea: uncomplete(/foo/new, /foo/bar) => ../new + * The use case for this is any time you get a full path (from an open dialog, perhaps) + * and want to store a relative path so that the group of files can be moved to a different + * directory without breaking the paths. An IDE would be a simple example, so that the + * project file could be safely checked out of subversion." + * + * ALGORITHM: + * iterate path and base + * compare all elements so far of path and base + * whilst they are the same, no write to output +x2 * when they change, or one runs out: + * write to output, ../ times the number of remaining elements in base + * write to output, the remaining elements in path + */ +fs::path +boostfs_uncomplete(fs::path const p, fs::path const base) +{ + if (p == base) return "./"; + /*!! this breaks stuff if path is a filename rather than a directory, + which it most likely is... but then base shouldn't be a filename so... */ + + // create absolute paths + fs::path abs_p = fs::absolute(boostfs_normalize(p)); + fs::path abs_base = fs::absolute(base); + + fs::path from_path, from_base, output; + + fs::path::iterator path_it = abs_p.begin(), path_end = abs_p.end(); + fs::path::iterator base_it = abs_base.begin(), base_end = abs_base.end(); + + // check for emptiness + if ((path_it == path_end) || (base_it == base_end)) { + throw std::runtime_error("path or base was empty; couldn't generate relative path"); + } + +#ifdef WIN32 + // drive letters are different; don't generate a relative path + if (*path_it != *base_it) return p; + + // now advance past drive letters; relative paths should only go up + // to the root of the drive and not past it + ++path_it, ++base_it; +#endif + + // Cache system-dependent dot, double-dot and slash strings + const std::string _dot = "."; + const std::string _dots = ".."; + const std::string _sep = "/"; + + // iterate over path and base + while (true) { + + // compare all elements so far of path and base to find greatest common root; + // when elements of path and base differ, or run out: + if ((path_it == path_end) || (base_it == base_end) || (*path_it != *base_it)) { + + // write to output, ../ times the number of remaining elements in base; + // this is how far we've had to come down the tree from base to get to the common root + for (; base_it != base_end; ++base_it) { + if (*base_it == _dot) + continue; + else if (*base_it == _sep) + continue; + + output /= "../"; + } + + // write to output, the remaining elements in path; + // this is the path relative from the common root + fs::path::iterator path_it_start = path_it; + for (; path_it != path_end; ++path_it) { + if (path_it != path_it_start) output /= "/"; + if (*path_it == _dot) continue; + if (*path_it == _sep) continue; + output /= *path_it; + } + break; + } + + // add directory level to both paths and continue iteration + from_path /= fs::path(*path_it); + from_base /= fs::path(*base_it); + + ++path_it, ++base_it; + } + + return output; +} diff --git a/src/boost-utils.h b/src/boost-utils.h index c431857..3678ecc 100644 --- a/src/boost-utils.h +++ b/src/boost-utils.h @@ -3,6 +3,11 @@ #include <boost/filesystem.hpp> namespace fs = boost::filesystem; -fs::path relativePath(const fs::path &path, const fs::path &relative_to); + +// FIXME: boostfs_relative_path() has been replaced by +// boostfs_uncomplete(), but kept around for now. +fs::path boostfs_relative_path(const fs::path &path, const fs::path &relative_to); +fs::path boostfs_normalize(const fs::path &path); +fs::path boostfs_uncomplete(fs::path const p, fs::path const base); #endif diff --git a/src/dxfdata.cc b/src/dxfdata.cc index be982ff..8415228 100644 --- a/src/dxfdata.cc +++ b/src/dxfdata.cc @@ -390,10 +390,10 @@ DxfData::DxfData(double fn, double fs, double fa, BOOST_FOREACH(const EntityList::value_type &i, unsupported_entities_list) { if (layername.empty()) { PRINTB("WARNING: Unsupported DXF Entity '%s' (%x) in %s.", - i.first % i.second % QuotedString(boosty::stringy(relativePath(filename, fs::current_path())))); + i.first % i.second % QuotedString(boosty::stringy(boostfs_uncomplete(filename, fs::current_path())))); } else { PRINTB("WARNING: Unsupported DXF Entity '%s' (%x) in layer '%s' of %s.", - i.first % i.second % layername % QuotedString(boosty::stringy(relativePath(filename, fs::current_path())))); + i.first % i.second % layername % QuotedString(boosty::stringy(boostfs_uncomplete(filename, fs::current_path())))); } } diff --git a/src/value.cc b/src/value.cc index bec803a..ae810a2 100644 --- a/src/value.cc +++ b/src/value.cc @@ -39,7 +39,7 @@ std::ostream &operator<<(std::ostream &stream, const Filename &filename) { - stream << QuotedString(boosty::stringy(relativePath(filename, fs::current_path()))); + stream << QuotedString(boosty::stringy(boostfs_uncomplete(filename, fs::current_path()))); return stream; } |