$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
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