blob: 12b2a757a9266165ae8c242a7c02baa43587c7dd [file] [log] [blame]
/*=============================================================================
Copyright (c) 2013 Daniel James
Use, modification and distribution is subject to the Boost Software
License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
#include "dependency_tracker.hpp"
#include "native_text.hpp"
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/foreach.hpp>
namespace quickbook
{
// Convert the path to its canonical representation if it exists.
// Or something close if it doesn't.
static fs::path normalize_path(fs::path const& path)
{
fs::path p = fs::absolute(path); // The base of the path.
fs::path extra; // The non-existant part of the path.
int parent_count = 0; // Number of active '..' sections
// Invariant: path is equivalent to: p / ('..' * parent_count) / extra
// i.e. if parent_count == 0: p/extra
// if parent_count == 2: p/../../extra
// Pop path sections from path until we find an existing
// path, adjusting for any dot path sections.
while (!fs::exists(fs::status(p))) {
fs::path name = p.filename();
p = p.parent_path();
if (name == "..") {
++parent_count;
}
else if (name == ".") {
}
else if (parent_count) {
--parent_count;
}
else {
extra = name / extra;
}
}
// If there are any left over ".." sections, then add them
// on to the end of the real path, and trust Boost.Filesystem
// to sort them out.
while (parent_count) {
p = p / "..";
--parent_count;
}
// Cannoicalize the existing part of the path, and add 'extra' back to
// the end.
return fs::canonical(p) / extra;
}
static char const* control_escapes[16] = {
"\\000", "\\001", "\\002", "\\003",
"\\004", "\\005", "\\006", "\\a",
"\\b", "\\t", "\\n", "\\v",
"\\f", "\\r", "\\016", "\\017"
};
static std::string escaped_path(std::string const& generic)
{
std::string result;
result.reserve(generic.size());
BOOST_FOREACH(char c, generic)
{
if (c >= 0 && c < 16) {
result += control_escapes[(unsigned int) c];
}
else if (c == '\\') {
result += "\\\\";
}
else if (c == 127) {
result += "\\177";
}
else {
result += c;
}
}
return result;
}
static std::string get_path(fs::path const& path,
dependency_tracker::flags f)
{
std::string generic = quickbook::detail::path_to_generic(path);
if (f & dependency_tracker::escaped) {
generic = escaped_path(generic);
}
return generic;
}
dependency_tracker::dependency_tracker() :
dependencies(), glob_dependencies(),
last_glob(glob_dependencies.end()) {}
bool dependency_tracker::add_dependency(fs::path const& f) {
bool found = fs::exists(fs::status(f));
dependencies[normalize_path(f)] |= found;
return found;
}
void dependency_tracker::add_glob(fs::path const& f) {
std::pair<glob_list::iterator, bool> r = glob_dependencies.insert(
std::make_pair(normalize_path(f), glob_list::mapped_type()));
last_glob = r.first;
}
void dependency_tracker::add_glob_match(fs::path const& f) {
assert(last_glob != glob_dependencies.end());
last_glob->second.insert(normalize_path(f));
}
void dependency_tracker::write_dependencies(fs::path const& file_out,
flags f)
{
fs::ofstream out(file_out);
if (out.fail()) {
throw std::runtime_error(
"Error opening dependency file " +
quickbook::detail::path_to_generic(file_out));
}
out.exceptions(std::ios::badbit);
write_dependencies(out, f);
}
void dependency_tracker::write_dependencies(std::ostream& out,
flags f)
{
if (f & checked) {
BOOST_FOREACH(dependency_list::value_type const& d, dependencies)
{
out << (d.second ? "+ " : "- ")
<< get_path(d.first, f) << std::endl;
}
BOOST_FOREACH(glob_list::value_type const& g, glob_dependencies)
{
out << "g "
<< get_path(g.first, f) << std::endl;
BOOST_FOREACH(fs::path const& p, g.second)
{
out << "+ " << get_path(p, f) << std::endl;
}
}
}
else {
std::set<std::string> paths;
BOOST_FOREACH(dependency_list::value_type const& d, dependencies)
{
if (d.second) {
paths.insert(get_path(d.first, f));
}
}
BOOST_FOREACH(glob_list::value_type const& g, glob_dependencies)
{
BOOST_FOREACH(fs::path const& p, g.second)
{
paths.insert(get_path(p, f));
}
}
BOOST_FOREACH(std::string const& p, paths)
{
out << p << std::endl;
}
}
}
}