]> git.lyx.org Git - lyx.git/blob - src/support/globbing.C
Give FileFilterList its own .[Ch] files.
[lyx.git] / src / support / globbing.C
1 /**
2  * \file globbing.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "support/globbing.h"
14 #include "support/lstrings.h"
15 #include "support/path.h"
16
17 #include <boost/regex.hpp>
18 #include <boost/tokenizer.hpp>
19
20 #include <glob.h>
21
22 using std::distance;
23 using std::string;
24 using std::vector;
25
26
27 namespace lyx {
28 namespace support {
29
30 string const convert_brace_glob(string const & glob)
31 {
32         // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
33         // "abc,def,ghi" as group 2.
34         static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
35         // Matches "abc" and "abc,", storing "abc" as group 1.
36         static boost::regex const block_re("([^,}]+),?");
37
38         string pattern;
39
40         string::const_iterator it = glob.begin();
41         string::const_iterator const end = glob.end();
42         while (true) {
43                 boost::match_results<string::const_iterator> what;
44                 if (!boost::regex_search(it, end, what, glob_re)) {
45                         // Ensure that no information is lost.
46                         pattern += string(it, end);
47                         break;
48                 }
49
50                 // Everything from the start of the input to
51                 // the start of the match.
52                 pattern += string(what[-1].first, what[-1].second);
53
54                 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
55                 string const head = string(what[1].first, what[1].second);
56                 string const tail = string(what[2].first, what[2].second);
57
58                 // Split the ','-separated chunks of tail so that
59                 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
60                 string const fmt = " " + head + "$1";
61                 pattern += boost::regex_merge(tail, block_re, fmt);
62
63                 // Increment the iterator to the end of the match.
64                 it += distance(it, what[0].second);
65         }
66
67         return pattern;
68 }
69
70
71 vector<string> const glob(string const & pattern, int flags)
72 {
73         glob_t glob_buffer;
74         glob_buffer.gl_offs = 0;
75         glob(pattern.c_str(), flags, 0, &glob_buffer);
76         vector<string> const matches(glob_buffer.gl_pathv,
77                                      glob_buffer.gl_pathv +
78                                      glob_buffer.gl_pathc);
79         globfree(&glob_buffer);
80         return matches;
81 }
82
83
84 vector<string> const expand_globs(string const & mask,
85                                   string const & directory)
86 {
87         typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
88         boost::char_separator<char> const separator(" ");
89
90         // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
91         //       "<glob> <glob> ... *.abc *.def <glob>"
92         string const converted_glob = convert_brace_glob(mask);
93
94         Path p(directory);
95
96         // Split into individual globs and then call 'glob' on each one.
97         vector<string> matches;
98         Tokenizer const tokens(converted_glob, separator);
99         Tokenizer::const_iterator it = tokens.begin();
100         Tokenizer::const_iterator const end = tokens.end();
101         for (; it != end; ++it) {
102                 vector<string> const tmp = glob(*it);
103                 matches.insert(matches.end(), tmp.begin(), tmp.end());
104         }
105         return matches;
106 }
107
108 } // namespace support
109 } // namespace lyx