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