]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /*============================================================================= |
2 | Copyright (c) 2013 Daniel James | |
3 | ||
4 | Use, modification and distribution is subject to the Boost Software | |
5 | License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
6 | http://www.boost.org/LICENSE_1_0.txt) | |
7 | =============================================================================*/ | |
8 | ||
9 | #include "dependency_tracker.hpp" | |
10 | #include "native_text.hpp" | |
11 | #include <boost/filesystem/operations.hpp> | |
12 | #include <boost/filesystem/fstream.hpp> | |
13 | #include <boost/foreach.hpp> | |
14 | ||
15 | namespace quickbook | |
16 | { | |
17 | // Convert the path to its canonical representation if it exists. | |
18 | // Or something close if it doesn't. | |
19 | static fs::path normalize_path(fs::path const& path) | |
20 | { | |
21 | fs::path p = fs::absolute(path); // The base of the path. | |
22 | fs::path extra; // The non-existant part of the path. | |
23 | int parent_count = 0; // Number of active '..' sections | |
24 | ||
25 | // Invariant: path is equivalent to: p / ('..' * parent_count) / extra | |
26 | // i.e. if parent_count == 0: p/extra | |
27 | // if parent_count == 2: p/../../extra | |
28 | ||
29 | // Pop path sections from path until we find an existing | |
30 | // path, adjusting for any dot path sections. | |
31 | while (!fs::exists(fs::status(p))) { | |
32 | fs::path name = p.filename(); | |
33 | p = p.parent_path(); | |
34 | if (name == "..") { | |
35 | ++parent_count; | |
36 | } | |
37 | else if (name == ".") { | |
38 | } | |
39 | else if (parent_count) { | |
40 | --parent_count; | |
41 | } | |
42 | else { | |
43 | extra = name / extra; | |
44 | } | |
45 | } | |
46 | ||
47 | // If there are any left over ".." sections, then add them | |
48 | // on to the end of the real path, and trust Boost.Filesystem | |
49 | // to sort them out. | |
50 | while (parent_count) { | |
51 | p = p / ".."; | |
52 | --parent_count; | |
53 | } | |
54 | ||
55 | // Cannoicalize the existing part of the path, and add 'extra' back to | |
56 | // the end. | |
57 | return fs::canonical(p) / extra; | |
58 | } | |
59 | ||
60 | static char const* control_escapes[16] = { | |
61 | "\\000", "\\001", "\\002", "\\003", | |
62 | "\\004", "\\005", "\\006", "\\a", | |
63 | "\\b", "\\t", "\\n", "\\v", | |
64 | "\\f", "\\r", "\\016", "\\017" | |
65 | }; | |
66 | ||
67 | static std::string escaped_path(std::string const& generic) | |
68 | { | |
69 | std::string result; | |
70 | result.reserve(generic.size()); | |
71 | ||
72 | BOOST_FOREACH(char c, generic) | |
73 | { | |
74 | if (c >= 0 && c < 16) { | |
75 | result += control_escapes[(unsigned int) c]; | |
76 | } | |
77 | else if (c == '\\') { | |
78 | result += "\\\\"; | |
79 | } | |
80 | else if (c == 127) { | |
81 | result += "\\177"; | |
82 | } | |
83 | else { | |
84 | result += c; | |
85 | } | |
86 | } | |
87 | ||
88 | return result; | |
89 | } | |
90 | ||
91 | static std::string get_path(fs::path const& path, | |
92 | dependency_tracker::flags f) | |
93 | { | |
94 | std::string generic = quickbook::detail::path_to_generic(path); | |
95 | ||
96 | if (f & dependency_tracker::escaped) { | |
97 | generic = escaped_path(generic); | |
98 | } | |
99 | ||
100 | return generic; | |
101 | } | |
102 | ||
103 | dependency_tracker::dependency_tracker() : | |
104 | dependencies(), glob_dependencies(), | |
105 | last_glob(glob_dependencies.end()) {} | |
106 | ||
107 | bool dependency_tracker::add_dependency(fs::path const& f) { | |
108 | bool found = fs::exists(fs::status(f)); | |
109 | dependencies[normalize_path(f)] |= found; | |
110 | return found; | |
111 | } | |
112 | ||
113 | void dependency_tracker::add_glob(fs::path const& f) { | |
114 | std::pair<glob_list::iterator, bool> r = glob_dependencies.insert( | |
115 | std::make_pair(normalize_path(f), glob_list::mapped_type())); | |
116 | last_glob = r.first; | |
117 | } | |
118 | ||
119 | void dependency_tracker::add_glob_match(fs::path const& f) { | |
120 | assert(last_glob != glob_dependencies.end()); | |
121 | last_glob->second.insert(normalize_path(f)); | |
122 | } | |
123 | ||
124 | void dependency_tracker::write_dependencies(fs::path const& file_out, | |
125 | flags f) | |
126 | { | |
127 | fs::ofstream out(file_out); | |
128 | ||
129 | if (out.fail()) { | |
130 | throw std::runtime_error( | |
131 | "Error opening dependency file " + | |
132 | quickbook::detail::path_to_generic(file_out)); | |
133 | } | |
134 | ||
135 | out.exceptions(std::ios::badbit); | |
136 | write_dependencies(out, f); | |
137 | } | |
138 | ||
139 | void dependency_tracker::write_dependencies(std::ostream& out, | |
140 | flags f) | |
141 | { | |
142 | if (f & checked) { | |
143 | BOOST_FOREACH(dependency_list::value_type const& d, dependencies) | |
144 | { | |
145 | out << (d.second ? "+ " : "- ") | |
146 | << get_path(d.first, f) << std::endl; | |
147 | } | |
148 | ||
149 | BOOST_FOREACH(glob_list::value_type const& g, glob_dependencies) | |
150 | { | |
151 | out << "g " | |
152 | << get_path(g.first, f) << std::endl; | |
153 | ||
154 | BOOST_FOREACH(fs::path const& p, g.second) | |
155 | { | |
156 | out << "+ " << get_path(p, f) << std::endl; | |
157 | } | |
158 | } | |
159 | } | |
160 | else { | |
161 | std::set<std::string> paths; | |
162 | ||
163 | BOOST_FOREACH(dependency_list::value_type const& d, dependencies) | |
164 | { | |
165 | if (d.second) { | |
166 | paths.insert(get_path(d.first, f)); | |
167 | } | |
168 | } | |
169 | ||
170 | BOOST_FOREACH(glob_list::value_type const& g, glob_dependencies) | |
171 | { | |
172 | BOOST_FOREACH(fs::path const& p, g.second) | |
173 | { | |
174 | paths.insert(get_path(p, f)); | |
175 | } | |
176 | } | |
177 | ||
178 | BOOST_FOREACH(std::string const& p, paths) | |
179 | { | |
180 | out << p << std::endl; | |
181 | } | |
182 | } | |
183 | } | |
184 | } |