1 /**
2 * \file RMF/paths.cpp
3 * \brief Handle read/write of Model data from/to files.
4 *
5 * Copyright 2007-2021 IMP Inventors. All rights reserved.
6 *
7 */
8
9 #include <boost/filesystem/path.hpp>
10 #include <boost/filesystem/operations.hpp>
11 #include <boost/version.hpp>
12 #include <string>
13 #include <vector>
14 #if BOOST_VERSION <= 104100
15 #include <cstdio> // for tmpnam()
16 #endif
17
18 #include "RMF/compiler_macros.h"
19 #include "RMF/internal/paths.h"
20
21 RMF_ENABLE_WARNINGS
22
23 namespace {
24
25 #ifndef _MSC_VER
26
27 // Return true iff the provided path is an absolute one
isabspath(boost::filesystem::path path)28 bool isabspath(boost::filesystem::path path) {
29 return path.string()[0] == '/';
30 }
31
32 // Return an absolute path for a path (possibly) relative to basedir
abspath(boost::filesystem::path path,boost::filesystem::path basedir)33 boost::filesystem::path abspath(boost::filesystem::path path,
34 boost::filesystem::path basedir) {
35 if (isabspath(path)) {
36 return path;
37 } else {
38 return basedir / path;
39 }
40 }
41
42 // Get the number of pathname components common to both absolute paths
get_common_prefix(boost::filesystem::path p1,boost::filesystem::path p2)43 size_t get_common_prefix(boost::filesystem::path p1,
44 boost::filesystem::path p2) {
45 size_t common = 0;
46 for (boost::filesystem::path::iterator it1(p1.begin()), it1_end(p1.end()),
47 it2(p2.begin()), it2_end(p2.end());
48 it1 != it1_end && it2 != it2_end; ++it1, ++it2) {
49 if (*it1 == *it2) common++;
50 }
51 return common;
52 }
53
54 // Get the number of pathname components in the path
count_path_components(boost::filesystem::path p)55 size_t count_path_components(boost::filesystem::path p) {
56 size_t numcomp = 0;
57 for (boost::filesystem::path::iterator it(p.begin()), it_end(p.end());
58 it != it_end; ++it) {
59 numcomp++;
60 }
61 return numcomp;
62 }
63
64 // Remove extraneous . and .. entries from the path
normalize(const boost::filesystem::path & p)65 boost::filesystem::path normalize(const boost::filesystem::path& p) {
66 size_t comp = count_path_components(p);
67 std::vector<bool> keep(comp, true);
68
69 size_t i = 0;
70 for (boost::filesystem::path::iterator it(p.begin()), it_end(p.end());
71 it != it_end; ++it, ++i) {
72 if (*it == ".") {
73 keep[i] = false;
74 } else if (*it == "..") {
75 keep[i] = false;
76 for (int j = i; j >= 0; --j) {
77 if (keep[j]) {
78 keep[j] = false;
79 break;
80 }
81 }
82 }
83 }
84 boost::filesystem::path newp;
85 i = 0;
86 for (boost::filesystem::path::iterator it(p.begin()), it_end(p.end());
87 it != it_end; ++it, ++i) {
88 if (keep[i]) {
89 newp /= *it;
90 }
91 }
92 return newp;
93 }
94
95 // Return a path for p that is relative to the directory containing base
relpath(boost::filesystem::path p,boost::filesystem::path base)96 boost::filesystem::path relpath(boost::filesystem::path p,
97 boost::filesystem::path base) {
98 boost::filesystem::path cwd = boost::filesystem::current_path();
99 boost::filesystem::path absp = abspath(p, cwd);
100 boost::filesystem::path absbase = abspath(base.parent_path(), cwd);
101
102 size_t lenbase = count_path_components(absbase);
103 size_t common = get_common_prefix(absp, absbase);
104
105 boost::filesystem::path r;
106 for (size_t i = 0; i < lenbase - common; ++i) {
107 r /= "..";
108 }
109 size_t pcomp = 0;
110 for (boost::filesystem::path::iterator it(absp.begin()), it_end(absp.end());
111 it != it_end; ++it, ++pcomp) {
112 if (pcomp >= common) {
113 r /= *it;
114 }
115 }
116 return r;
117 }
118
119 #endif // _MSC_VER
120
121 } // anonymous namespace
122
123 namespace RMF {
124 namespace internal {
get_file_name(std::string path)125 std::string get_file_name(std::string path) {
126 #if BOOST_VERSION >= 104600
127 return boost::filesystem::path(path).filename().string();
128 #else
129 return boost::filesystem::path(path).filename();
130 #endif
131 }
132
get_relative_path(std::string base,std::string file)133 std::string get_relative_path(std::string base, std::string file) {
134 #ifdef _MSC_VER
135 return file;
136 #else
137 return relpath(boost::filesystem::path(file),
138 boost::filesystem::path(base)).string();
139 #endif
140 }
141
get_absolute_path(std::string base,std::string file)142 std::string get_absolute_path(std::string base, std::string file) {
143 #ifdef _MSC_VER
144 return file;
145 #else
146 boost::filesystem::path b(base);
147 boost::filesystem::path absb = abspath(b.parent_path(),
148 boost::filesystem::current_path());
149 boost::filesystem::path f(file);
150
151 return normalize(abspath(f, absb)).string();
152 #endif
153 }
154
get_unique_path()155 std::string get_unique_path() {
156 #if BOOST_VERSION > 104100
157 boost::filesystem::path temp = boost::filesystem::unique_path();
158 return temp.string();
159 #else
160 return tmpnam(NULL);
161 #endif
162 }
163
164 } // namespace internal
165 } /* namespace RMF */
166
167 RMF_DISABLE_WARNINGS
168