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 <boost/filesystem/fstream.hpp>
11 #include <boost/filesystem/operations.hpp>
12 #include "for.hpp"
13 #include "path.hpp"
14 
15 namespace quickbook
16 {
17     static char const* control_escapes[16] = {
18         "\\000", "\\001", "\\002", "\\003", "\\004", "\\005", "\\006", "\\a",
19         "\\b",   "\\t",   "\\n",   "\\v",   "\\f",   "\\r",   "\\016", "\\017"};
20 
escaped_path(std::string const & generic)21     static std::string escaped_path(std::string const& generic)
22     {
23         std::string result;
24         result.reserve(generic.size());
25 
26         QUICKBOOK_FOR (char c, generic) {
27             if (c >= 0 && c < 16) {
28                 result += control_escapes[(unsigned int)c];
29             }
30             else if (c == '\\') {
31                 result += "\\\\";
32             }
33             else if (c == 127) {
34                 result += "\\177";
35             }
36             else {
37                 result += c;
38             }
39         }
40 
41         return result;
42     }
43 
get_path(fs::path const & path,dependency_tracker::flags f)44     static std::string get_path(
45         fs::path const& path, dependency_tracker::flags f)
46     {
47         std::string generic = quickbook::detail::path_to_generic(path);
48 
49         if (f & dependency_tracker::escaped) {
50             generic = escaped_path(generic);
51         }
52 
53         return generic;
54     }
55 
dependency_tracker()56     dependency_tracker::dependency_tracker()
57         : dependencies()
58         , glob_dependencies()
59         , last_glob(glob_dependencies.end())
60     {
61     }
62 
add_dependency(fs::path const & f)63     bool dependency_tracker::add_dependency(fs::path const& f)
64     {
65         bool found = fs::exists(fs::status(f));
66         dependencies[f] |= found;
67         return found;
68     }
69 
add_glob(fs::path const & f)70     void dependency_tracker::add_glob(fs::path const& f)
71     {
72         std::pair<glob_list::iterator, bool> r = glob_dependencies.insert(
73             std::make_pair(f, glob_list::mapped_type()));
74         last_glob = r.first;
75     }
76 
add_glob_match(fs::path const & f)77     void dependency_tracker::add_glob_match(fs::path const& f)
78     {
79         assert(last_glob != glob_dependencies.end());
80         last_glob->second.insert(f);
81     }
82 
write_dependencies(fs::path const & file_out,flags f)83     void dependency_tracker::write_dependencies(
84         fs::path const& file_out, flags f)
85     {
86         fs::ofstream out(file_out);
87 
88         if (out.fail()) {
89             throw std::runtime_error(
90                 "Error opening dependency file " +
91                 quickbook::detail::path_to_generic(file_out));
92         }
93 
94         out.exceptions(std::ios::badbit);
95         write_dependencies(out, f);
96     }
97 
write_dependencies(std::ostream & out,flags f)98     void dependency_tracker::write_dependencies(std::ostream& out, flags f)
99     {
100         if (f & checked) {
101             QUICKBOOK_FOR (dependency_list::value_type const& d, dependencies) {
102                 out << (d.second ? "+ " : "- ") << get_path(d.first, f)
103                     << std::endl;
104             }
105 
106             QUICKBOOK_FOR (glob_list::value_type const& g, glob_dependencies) {
107                 out << "g " << get_path(g.first, f) << std::endl;
108 
109                 QUICKBOOK_FOR (fs::path const& p, g.second) {
110                     out << "+ " << get_path(p, f) << std::endl;
111                 }
112             }
113         }
114         else {
115             std::set<std::string> paths;
116 
117             QUICKBOOK_FOR (dependency_list::value_type const& d, dependencies) {
118                 if (d.second) {
119                     paths.insert(get_path(d.first, f));
120                 }
121             }
122 
123             QUICKBOOK_FOR (glob_list::value_type const& g, glob_dependencies) {
124                 QUICKBOOK_FOR (fs::path const& p, g.second) {
125                     paths.insert(get_path(p, f));
126                 }
127             }
128 
129             QUICKBOOK_FOR (std::string const& p, paths) {
130                 out << p << std::endl;
131             }
132         }
133     }
134 }
135