Re: [quickbook] Direct code import...
Daniel James <[email protected]>
| Newsgroups | gmane.comp.lib.boost.documentation |
|---|---|
| Message-ID | <CAHOE3ycgPo_KwobvLE3y9UKxFHuWVYk0Md1xXxOBHLYxj_DGTg@mail.gmail.com> |
On 5 January 2013 21:27, Rene Rivera <[email protected]> wrote: > After having my Mac's drive get trashed I lost a set of notes I had on what > needed to get finished for this. Does anyone have an idea on what needs to > get done to complete this feature on boostbook-dev branch? I'm not sure. The quickbook-dev branch was completely merged to trunk, but I reverted the glob feature because it didn't completely work. One problem was that it uses a bitset for all the possible character values, which is fine when using 8-bit characters, but not when using 16-bit characters (on windows). There might have been others, I can't remember. At the time I wanted to support unicode, but since then I've found out it's trickier than I thought, because different filesystems use different normalisation forms, or more typically no normalisation at all. Which makes consistent cross platform handling of unicode a bit tricky. For example in HFS+ 'é' is actually two code points. So a glob which is aware of UTF-8, but not aware of combining characters, won't match 'café.txt' with 'caf?.txt', or even 'ca?é.txt' (that's on HFS+), but will for other filesystems. I had hoped Boost.Locale might help out here, but I think it requires ICU for normalisation, which is IMO far too large a dependency. So... for the time being, it'd probably be best if the glob only supports ascii. If the glob contains non-ascii characters (i.e. anything > 127), it should be an error, if a file does, it should be ignored (actually could allow '*' to match non-ascii characters). This won't be that much of a problem since I expect most people don't really expect unicode filenames to work anyway (GNU find's glob has the same issue, although interestingly, GNU bash has fixed it in recent years). I've attached an updated patch to restore the glob implementation. Since the quickbook-dev branch was merged, I added support for tracking dependencies, which the glob implementation doesn't do. I will probably need to rework the dependency tracking a bit as it combines checking for a file's existence with tracking (i.e. every time quickbook looks to see if a file exists, it does it through the dependency tracker so that it will be recorded). I think I'll need to add glob support to the dependency tracker. Probably don't need to implement the glob in the glob in the dependency tracker, but just record it as a special type of checked path. Since no new quickbook developments are going to be included in the new boost release, and the next will use git, it probably makes sense to switch to using git now. I'm currently running git-svn to create a new repo, but it won't be the proper one (it's missing some branches, and I haven't mapped the users, and probably other issues, the kde svn2git might work better). It should be possible to copy new changes over to the final repo, so it'll be a good place to start. _______________________________________________ Boost-docs mailing list [email protected] http://lists.boost.org/mailman/listinfo.cgi/boost-docs
glob.patch
(application/octet-stream, 15.7 KB)
diff --git a/tools/quickbook/doc/1_6.qbk b/tools/quickbook/doc/1_6.qbk
index 89c41f2..4f40332 100644
--- a/tools/quickbook/doc/1_6.qbk
+++ b/tools/quickbook/doc/1_6.qbk
@@ -282,6 +282,26 @@ html.
[section:1_7 Quickbook 1.7]
+[section:glob Including multiple files with Globs]
+
+One can now include multiple files at once using a glob pattern for the
+file reference:
+
+ [include sub/*/*.qbk]
+ [include include/*.h]
+
+All the matching files, and intermediate irectories, will match and be
+included. The glob pattern can be "\*" for matching zero or more characters,
+"?" for matching a single character, "\[<c>-<c>\]" to match a character class,
+"\[\^<char>-<char>\]" to exclusive match a character class, "\\\\" to escape
+a glob special character which is then matched, and anything else is matched
+to the character.
+
+[note Because of the escaping in file references the "\\\\" glob escape is
+a double "\\"; i.e. and escaped back-slash.]
+
+[endsect]
+
[section:source_mode Source mode for single entities]
1.7 introduces a new `!` element type for setting the source mode of a single
diff --git a/tools/quickbook/src/actions.cpp b/tools/quickbook/src/actions.cpp
index 304663f..32efc91 100644
--- a/tools/quickbook/src/actions.cpp
+++ b/tools/quickbook/src/actions.cpp
@@ -33,6 +33,7 @@
#include "block_tags.hpp"
#include "phrase_tags.hpp"
#include "id_manager.hpp"
+#include "glob.hpp"
namespace quickbook
{
@@ -1764,8 +1765,8 @@ namespace quickbook
}
struct path_details {
- // Will possibly add 'url' and 'glob' to this list later:
- enum path_type { path };
+ // Will possibly add 'url' to this list later:
+ enum path_type { path, glob };
std::string value;
path_type type;
@@ -1787,7 +1788,10 @@ namespace quickbook
std::string path_text = qbk_version_n >= 106u || path.is_encoded() ?
path.get_encoded() : path.get_quickbook();
- if(path_text.find('\\') != std::string::npos)
+ bool is_glob = qbk_version_n >= 107u &&
+ path_text.find_first_of("[]?*") != std::string::npos;
+
+ if(!is_glob && path_text.find('\\') != std::string::npos)
{
quickbook::detail::ostream* err;
@@ -1807,13 +1811,24 @@ namespace quickbook
boost::replace(path_text, '\\', '/');
}
- return path_details(path_text, path_details::path);
+ return path_details(path_text,
+ is_glob ? path_details::glob : path_details::path);
}
xinclude_path calculate_xinclude_path(value const& p, quickbook::state& state)
{
path_details details = check_path(p, state);
+ if (details.type == path_details::glob) {
+ // TODO: Should know if this is an xinclude or an xmlbase.
+ // Would also help with implementation of 'check_path'.
+ detail::outerr(p.get_file(), p.get_position())
+ << "Glob used in xinclude/xmlbase."
+ << std::endl;
+ ++state.error_count;
+ return xinclude_path(state.current_file->path.parent_path(), "");
+ }
+
fs::path path = detail::generic_to_path(details.value);
fs::path full_path = path;
@@ -1861,54 +1876,136 @@ namespace quickbook
}
};
+ #if QUICKBOOK_WIDE_PATHS
+ typedef std::wstring path_string_t;
+ inline path_string_t path_to_string(fs::path const & p)
+ {
+ return p.generic_wstring();
+ }
+ static const path_string_t::value_type* glob_chars = L"[]?*";
+ #else
+ typedef std::string path_string_t;
+ inline path_string_t path_to_string(fs::path const & p)
+ {
+ return p.generic_string();
+ }
+ static const path_string_t::value_type* glob_chars = "[]?*";
+ #endif
+
+ void include_search_glob(std::set<include_search_return> & result,
+ fs::path dir, fs::path path, quickbook::state const & state)
+ {
+ // Split the glob into the current dir/glob/rest to search.
+ fs::path glob;
+ fs::path rest;
+ fs::path::iterator i = path.begin();
+ fs::path::iterator e = path.end();
+ for (; i != e; ++i)
+ {
+ if (path_to_string(*i).find_first_of(glob_chars) != path_string_t::npos)
+ {
+ glob = *i;
+ for (++i; i != e; ++i) rest /= *i;
+ break;
+ }
+ else
+ {
+ dir /= *i;
+ }
+ }
+ // Walk through the dir for matches.
+ fs::directory_iterator dir_i(dir.empty() ? fs::path(".") : dir);
+ fs::directory_iterator dir_e;
+ for (; dir_i != dir_e; ++dir_i)
+ {
+ fs::path f = dir_i->path().filename();
+ // Skip if the dir item doesn't match.
+ if (!quickbook::glob(path_to_string(glob).c_str(),path_to_string(f).c_str())) continue;
+ // If it's a file we add it to the results.
+ if (fs::is_regular_file(dir_i->status()))
+ {
+ result.insert(include_search_return(
+ dir/f,
+ state.filename_relative.parent_path()/dir/f
+ ));
+ }
+ // If it's a matching dir, we recurse looking for more files.
+ else
+ {
+ include_search_glob(result,dir,f/rest,state);
+ }
+ }
+ }
+
std::set<include_search_return> include_search(path_details const& details,
quickbook::state& state, string_iterator pos)
{
std::set<include_search_return> result;
- fs::path path = detail::generic_to_path(details.value);
-
- // If the path is relative, try and resolve it.
- if (!path.has_root_directory() && !path.has_root_name())
+ // If the path has some glob match characters
+ // we do a discovery of all the matches..
+ if (details.type == path_details::glob)
{
- fs::path local_path =
- state.current_file->path.parent_path() / path;
+ fs::path current = state.current_file->path.parent_path();
+ fs::path path(details.value);
- // See if it can be found locally first.
- if (state.add_dependency(local_path))
+ // Search for the current dir accumulating to the result.
+ include_search_glob(result,current,path,state);
+ // Search the include path dirs accumulating to the result.
+ BOOST_FOREACH(fs::path dir, include_path)
{
- result.insert(include_search_return(
- local_path,
- state.filename_relative.parent_path() / path));
- return result;
+ include_search_glob(result,dir,path,state);
}
+ // Done.
+ return result;
+ }
+ else
+ {
+ fs::path path = detail::generic_to_path(details.value);
- BOOST_FOREACH(fs::path full, include_path)
+ // If the path is relative, try and resolve it.
+ if (!path.has_root_directory() && !path.has_root_name())
{
- full /= path;
+ fs::path local_path =
+ state.current_file->path.parent_path() / path;
- if (state.add_dependency(full))
+ // See if it can be found locally first.
+ if (state.add_dependency(local_path))
{
- result.insert(include_search_return(full, path));
+ result.insert(include_search_return(
+ local_path,
+ state.filename_relative.parent_path() / path));
return result;
}
+
+ // Search in each of the include path locations.
+ BOOST_FOREACH(fs::path full, include_path)
+ {
+ full /= path;
+
+ if (state.add_dependency(full))
+ {
+ result.insert(include_search_return(full, path));
+ return result;
+ }
+ }
}
- }
- else
- {
- if (state.add_dependency(path)) {
- result.insert(include_search_return(path, path));
- return result;
+ else
+ {
+ if (state.add_dependency(path)) {
+ result.insert(include_search_return(path, path));
+ return result;
+ }
}
- }
- detail::outerr(state.current_file, pos)
- << "Unable to find file: "
- << details.value
- << std::endl;
- ++state.error_count;
+ detail::outerr(state.current_file, pos)
+ << "Unable to find file: "
+ << details.value
+ << std::endl;
+ ++state.error_count;
- return result;
+ return result;
+ }
}
}
diff --git a/tools/quickbook/src/glob.hpp b/tools/quickbook/src/glob.hpp
new file mode 100644
index 0000000..1cb4132
--- /dev/null
+++ b/tools/quickbook/src/glob.hpp
@@ -0,0 +1,227 @@
+#ifndef BOOST_QUICKBOOK_GLOB_HPP
+#define BOOST_QUICKBOOK_GLOB_HPP
+/*
+ Copyright Redshift Software Inc 2011
+ Distributed under 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)
+ */
+
+/*
+ * Copyright 1994 Christopher Seiwald. All rights reserved.
+ *
+ * This file is part of Jam - see jam.c for Copyright information.
+ */
+
+/*
+ * glob.c - match a string against a simple pattern
+ *
+ * Understands the following patterns:
+ *
+ * * any number of characters
+ * ? any single character
+ * [a-z] any single character in the range a-z
+ * [^a-z] any single character not in the range a-z
+ * \x match x
+ *
+ * External functions:
+ *
+ * glob() - match a string against a simple pattern
+ *
+ * Internal functions:
+ *
+ * globchars() - build a bitlist to check for character group match
+ */
+
+#include <boost/cstdint.hpp>
+
+namespace quickbook
+{
+ namespace glob_detail
+ {
+ template < typename Char >
+ struct bit_list
+ {
+ /* bytes used for [chars] in compiled expr */
+ enum bit_list_size_t
+ {
+ bit_list_size = sizeof(Char)/8
+ };
+
+ boost::uint8_t tab[bit_list_size];
+
+ bit_list()
+ {
+ for (unsigned i = 0; i<bit_list_size; ++i)
+ tab[i] = 0;
+ }
+
+ bool operator[](unsigned bit)
+ {
+ return (tab[bit/8]&(1<<(bit%8)));
+ }
+
+ void set(unsigned bit)
+ {
+ /* `bit != 0` :: Do not include \0 in either $[chars] or $[^chars]. */
+ if (bit!=0)
+ tab[bit/8] |= (1<<(bit%8) );
+ }
+
+ void negate()
+ {
+ for (unsigned i = 0; i<bit_list_size; ++i)
+ tab[i] ^= 255;
+ }
+ };
+
+ /*
+ * globchars() - build a bitlist to check for character group match.
+ */
+ template < typename Char >
+ bit_list<Char> globchars(const Char * s, const Char * e)
+ {
+ bit_list<Char> result;
+
+ bool neg = false;
+
+ if (*s==Char('^'))
+ {
+ neg = true;
+ ++s;
+ }
+
+ while (s<e)
+ {
+ Char c;
+
+ if ((s+2<e)&&(s[1]==Char('-')))
+ {
+ for (c = s[0]; c<=s[2]; ++c)
+ result.set(c);
+ s += 3;
+ }
+ else
+ {
+ c = *s++;
+ result.set(c);
+ }
+ }
+
+ if (neg)
+ result.negate();
+
+ return result;
+ }
+
+ /*
+ * glob() - match a string against a simple pattern.
+ */
+ template < typename Char >
+ bool glob(const Char * c, const Char * s, bool & fail)
+ {
+ const Char eos = Char('\0');
+
+ fail = false;
+
+ while (true)
+ {
+ if (eos==*c)
+ {
+ fail = eos!=*s;
+ return !fail;
+ }
+ else if (Char('?')==*c)
+ {
+ ++c;
+ if (eos==*s++)
+ return false;
+ }
+ else if (Char('[')==*c)
+ {
+ ++c;
+ /* Scan for matching ]. */
+
+ const Char * here = c;
+ do
+ {
+ if (eos==*c++)
+ return false;
+ }
+ while ((here==c)||(*c!=Char(']')));
+ ++c;
+
+ /* Build character class bitlist. */
+
+ glob_detail::bit_list<Char> bitlist =
+ glob_detail::globchars(here, c);
+
+ if (!bitlist[*s])
+ return false;
+ ++s;
+ }
+ else if (Char('*')==*c)
+ {
+ ++c;
+ const Char * here = s;
+
+ while (eos!=*s)
+ ++s;
+
+ /* Try to match the rest of the pattern in a recursive */
+ /* call. If the match fails we'll back up chars, retrying. */
+
+ while (s!=here)
+ {
+ bool r = false;
+
+ /* A fast path for the last token in a pattern. */
+ if (eos!=*c)
+ r = glob(c, s, fail);
+ else if (eos!=*s)
+ {
+ fail = true;
+ r = false;
+ }
+ else
+ r = true;
+
+ if (r)
+ return true;
+ if (fail)
+ return false;
+ --s;
+ }
+ }
+ else if (Char('\\')==*c)
+ {
+ ++c;
+ /* Force literal match of next char. */
+ if (eos==*c||(*s++!=*c++))
+ return false;
+ }
+ else
+ {
+ ++c;
+ if (*s++!=c[-1])
+ return false;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /*
+ * glob() - match a string against a simple pattern.
+ */
+ template < typename Char >
+ bool glob(const Char * pattern, const Char * s)
+ {
+ bool fail = false;
+ bool result = glob_detail::glob(pattern, s, fail);
+ return result;
+ }
+}
+
+#endif