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
|
#include "boosty.h"
#include "boost-utils.h"
#include <stdio.h>
#include <iostream>
// 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 = boosty::absolute(boostfs_normalize(path));
fs::path r = boosty::absolute(relative_to);
// if root paths are different, return absolute path
if (p.root_path() != r.root_path())
return p;
// initialize relative path
fs::path result;
// find out where the two paths diverge
fs::path::const_iterator itr_path = p.begin();
fs::path::const_iterator itr_relative_to = r.begin();
while (*itr_path == *itr_relative_to && itr_path != p.end() && itr_relative_to != r.end()) {
++itr_path;
++itr_relative_to;
}
// add "../" for each remaining token in relative_to
if (itr_relative_to != r.end()) {
++itr_relative_to;
while (itr_relative_to != r.end()) {
result /= "..";
++itr_relative_to;
}
}
// add remaining path
while (itr_path != p.end()) {
result /= *itr_path;
++itr_path;
}
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 = boosty::absolute(path);
fs::path::iterator it = absPath.begin();
fs::path result = *it;
if (it!=absPath.end()) it++;
// Get canonical version of the existing part
for(;exists(result) && it != absPath.end(); ++it) {
result /= *it;
}
result = boosty::canonical(result.parent_path());
if (it!=absPath.begin()) 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 = boosty::absolute(boostfs_normalize(p));
fs::path abs_base = boosty::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;
}
|