Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r75761 - in branches/quickbook-dev/tools/quickbook: doc src
From: grafikrobot_at_[hidden]
Date: 2011-11-30 23:29:55


Author: grafik
Date: 2011-11-30 23:29:54 EST (Wed, 30 Nov 2011)
New Revision: 75761
URL: http://svn.boost.org/trac/boost/changeset/75761

Log:
Implement glob multi-file includes.
Added:
   branches/quickbook-dev/tools/quickbook/src/glob.hpp (contents, props changed)
Text files modified:
   branches/quickbook-dev/tools/quickbook/doc/1_6.qbk | 20 +++
   branches/quickbook-dev/tools/quickbook/src/actions.cpp | 201 ++++++++++++++++++++++++++++++---------
   2 files changed, 171 insertions(+), 50 deletions(-)

Modified: branches/quickbook-dev/tools/quickbook/doc/1_6.qbk
==============================================================================
--- branches/quickbook-dev/tools/quickbook/doc/1_6.qbk (original)
+++ branches/quickbook-dev/tools/quickbook/doc/1_6.qbk 2011-11-30 23:29:54 EST (Wed, 30 Nov 2011)
@@ -240,4 +240,24 @@
 
 [endsect]
 
+[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]
+
 [endsect] [/1_6]

Modified: branches/quickbook-dev/tools/quickbook/src/actions.cpp
==============================================================================
--- branches/quickbook-dev/tools/quickbook/src/actions.cpp (original)
+++ branches/quickbook-dev/tools/quickbook/src/actions.cpp 2011-11-30 23:29:54 EST (Wed, 30 Nov 2011)
@@ -12,6 +12,7 @@
 #include <functional>
 #include <vector>
 #include <map>
+#include <set>
 #include <boost/filesystem/v3/convenience.hpp>
 #include <boost/filesystem/v3/fstream.hpp>
 #include <boost/range/distance.hpp>
@@ -32,6 +33,7 @@
 #include "block_tags.hpp"
 #include "phrase_tags.hpp"
 #include "id_manager.hpp"
+#include "glob.hpp"
 
 namespace quickbook
 {
@@ -1688,6 +1690,12 @@
         std::string path_text = path.is_encoded() ? path.get_encoded() :
             path.get_quickbook();
 
+ if(qbk_version_n >= 106u && path_text.find_first_of("[]?*"))
+ {
+ // For a glob expression we don't correct '\' as it's could be
+ // an escape for the glob.
+ return detail::generic_to_path(path_text);
+ }
         if(path_text.find('\\') != std::string::npos)
         {
             (qbk_version_n >= 106u ?
@@ -1744,37 +1752,124 @@
 
             fs::path filename;
             fs::path filename_relative;
+
+ bool operator < (include_search_return const & other) const
+ {
+ if (filename_relative < other.filename_relative) return true;
+ else if (other.filename_relative < filename_relative) return false;
+ else return filename < other.filename;
+ }
         };
 
- include_search_return include_search(fs::path const& path,
- quickbook::actions const& actions)
+ #if QUICKBOOK_WIDE_PATHS
+ typedef std::wstring path_string_t;
+ inline path_string_t path_to_string(fs::path const & p)
         {
- fs::path current = actions.current_file->path.parent_path();
+ return p.generic_wstring();
+ }
+ #else
+ typedef std::string path_string_t;
+ inline path_string_t path_to_string(fs::path const & p)
+ {
+ return p.generic_string();
+ }
+ #endif
 
- // If the path is relative, try and resolve it.
- if (!path.has_root_directory() && !path.has_root_name())
+ void include_search_glob(std::set<include_search_return> & result,
+ fs::path dir, fs::path path, quickbook::actions const & actions)
+ {
+ // 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)
             {
- // See if it can be found locally first.
- if (fs::exists(current / path))
+ if (path_to_string(*i).find_first_of("[]?*") != path_string_t::npos)
+ {
+ glob = *i;
+ for (++i; i != e; ++i) rest /= *i;
+ break;
+ }
+ else
                 {
- return include_search_return(
- current / path,
- actions.filename_relative.parent_path() / path);
+ dir /= *i;
                 }
+ }
+ // Walk through the dir for matches.
+ fs::directory_iterator dir_i(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,
+ actions.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,actions);
+ }
+ }
+ }
+
+ std::set<include_search_return> include_search(fs::path const& path,
+ quickbook::actions const& actions)
+ {
+ std::set<include_search_return> result;
+ fs::path current = actions.current_file->path.parent_path();
 
- // Search in each of the include path locations.
- BOOST_FOREACH(fs::path full, include_path)
+ // If the path has some glob match characters
+ // we do a discovery of all the matches..
+ if (qbk_version_n >= 106u && path_to_string(path).find_first_of("[]?*") != path_string_t::npos)
+ {
+ // Search for the current dir accumulating to the result.
+ include_search_glob(result,current,path,actions);
+ // Search the include path dirs accumulating to the result.
+ BOOST_FOREACH(fs::path dir, include_path)
+ {
+ include_search_glob(result,dir,path,actions);
+ }
+ // Done.
+ return result;
+ }
+ else
+ {
+ // If the path is relative, try and resolve it.
+ if (!path.has_root_directory() && !path.has_root_name())
                 {
- full /= path;
- if (fs::exists(full))
+ // See if it can be found locally first.
+ if (fs::exists(current / path))
+ {
+ result.insert(include_search_return(
+ current / path,
+ actions.filename_relative.parent_path() / path));
+ return result;
+ }
+
+ // Search in each of the include path locations.
+ BOOST_FOREACH(fs::path full, include_path)
                     {
- return include_search_return(full, path);
+ full /= path;
+ if (fs::exists(full))
+ {
+ result.insert(include_search_return(full, path));
+ return result;
+ }
                     }
                 }
- }
 
- return include_search_return(path,
- actions.filename_relative.parent_path() / path);
+ result.insert(include_search_return(path,
+ actions.filename_relative.parent_path() / path));
+ return result;
+ }
         }
     }
     
@@ -1880,48 +1975,54 @@
 
         value_consumer values = include;
         value include_doc_id = values.optional_consume(general_tags::include_id);
- include_search_return paths = include_search(
+ std::set<include_search_return> search = include_search(
             check_path(values.consume(), actions), actions);
         values.finish();
 
- try {
- if (qbk_version_n >= 106)
- {
- if (actions.imported && include.get_tag() == block_tags::include)
- return;
-
- std::string ext = paths.filename.extension().generic_string();
-
- if (ext == ".qbk" || ext == ".quickbook")
+ std::set<include_search_return>::iterator i = search.begin();
+ std::set<include_search_return>::iterator e = search.end();
+ for (; i != e; ++i)
+ {
+ include_search_return const & paths = *i;
+ try {
+ if (qbk_version_n >= 106)
                 {
- load_quickbook(actions, paths, include.get_tag(), include_doc_id);
- }
- else
- {
- load_source_file(actions, paths, include.get_tag(), first, include_doc_id);
- }
- }
- else
- {
- if (include.get_tag() == block_tags::include)
- {
- load_quickbook(actions, paths, include.get_tag(), include_doc_id);
+ if (actions.imported && include.get_tag() == block_tags::include)
+ return;
+
+ std::string ext = paths.filename.extension().generic_string();
+
+ if (ext == ".qbk" || ext == ".quickbook")
+ {
+ load_quickbook(actions, paths, include.get_tag(), include_doc_id);
+ }
+ else
+ {
+ load_source_file(actions, paths, include.get_tag(), first, include_doc_id);
+ }
                 }
                 else
                 {
- load_source_file(actions, paths, include.get_tag(), first, include_doc_id);
+ if (include.get_tag() == block_tags::include)
+ {
+ load_quickbook(actions, paths, include.get_tag(), include_doc_id);
+ }
+ else
+ {
+ load_source_file(actions, paths, include.get_tag(), first, include_doc_id);
+ }
                 }
             }
- }
- catch (load_error& e) {
- ++actions.error_count;
+ catch (load_error& e) {
+ ++actions.error_count;
 
- detail::outerr(actions.current_file, first)
- << "Loading file:"
- << paths.filename
- << ": "
- << detail::utf8(e.what())
- << std::endl;
+ detail::outerr(actions.current_file, first)
+ << "Loading file:"
+ << paths.filename
+ << ": "
+ << detail::utf8(e.what())
+ << std::endl;
+ }
         }
     }
 

Added: branches/quickbook-dev/tools/quickbook/src/glob.hpp
==============================================================================
--- (empty file)
+++ branches/quickbook-dev/tools/quickbook/src/glob.hpp 2011-11-30 23:29:54 EST (Wed, 30 Nov 2011)
@@ -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


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk