]> git.lyx.org Git - lyx.git/blob - src/support/globbing.C
* lyxfunctional.h: delete compare_memfun and helper classes
[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 "globbing.h"
14
15 #include "gettext.h"
16
17 #include "lstrings.h"
18 #include "path.h"
19
20 #include <boost/regex.hpp>
21 #include <boost/tokenizer.hpp>
22
23 #include <glob.h>
24
25 #include <sstream>
26
27 using std::distance;
28 using std::ostringstream;
29 using std::string;
30 using std::vector;
31
32
33 namespace lyx {
34 namespace support {
35
36 string const convert_brace_glob(string const & glob)
37 {
38         // Matches " *.{abc,def,ghi}", storing "*." as group 1 and
39         // "abc,def,ghi" as group 2.
40         static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
41         // Matches "abc" and "abc,", storing "abc" as group 1.
42         static boost::regex const block_re("([^,}]+),?");
43
44         string pattern;
45
46         string::const_iterator it = glob.begin();
47         string::const_iterator const end = glob.end();
48         while (true) {
49                 boost::match_results<string::const_iterator> what;
50                 if (!boost::regex_search(it, end, what, glob_re)) {
51                         // Ensure that no information is lost.
52                         pattern += string(it, end);
53                         break;
54                 }
55
56                 // Everything from the start of the input to
57                 // the start of the match.
58                 pattern += string(what[-1].first, what[-1].second);
59
60                 // Given " *.{abc,def}", head == "*." and tail == "abc,def".
61                 string const head = string(what[1].first, what[1].second);
62                 string const tail = string(what[2].first, what[2].second);
63
64                 // Split the ','-separated chunks of tail so that
65                 // $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
66                 string const fmt = " " + head + "$1";
67                 pattern += boost::regex_merge(tail, block_re, fmt);
68
69                 // Increment the iterator to the end of the match.
70                 it += distance(it, what[0].second);
71         }
72
73         return pattern;
74 }
75
76
77 vector<string> const glob(string const & pattern, int flags)
78 {
79         glob_t glob_buffer;
80         glob_buffer.gl_offs = 0;
81         glob(pattern.c_str(), flags, 0, &glob_buffer);
82         vector<string> const matches(glob_buffer.gl_pathv,
83                                      glob_buffer.gl_pathv +
84                                      glob_buffer.gl_pathc);
85         globfree(&glob_buffer);
86         return matches;
87 }
88
89
90 vector<string> const expand_globs(string const & mask,
91                                   string const & directory)
92 {
93         typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
94         boost::char_separator<char> const separator(" ");
95
96         // Given "<glob> <glob> ... *.{abc,def} <glob>", expand to
97         //       "<glob> <glob> ... *.abc *.def <glob>"
98         string const converted_glob = convert_brace_glob(mask);
99
100         Path p(directory);
101
102         // Split into individual globs and then call 'glob' on each one.
103         vector<string> matches;
104         Tokenizer const tokens(converted_glob, separator);
105         Tokenizer::const_iterator it = tokens.begin();
106         Tokenizer::const_iterator const end = tokens.end();
107         for (; it != end; ++it) {
108                 vector<string> const tmp = glob(*it);
109                 matches.insert(matches.end(), tmp.begin(), tmp.end());
110         }
111         return matches;
112 }
113
114
115 FileFilterList::FileFilterList(string const & qt_style_filter)
116 {
117         string const filter = qt_style_filter
118                 + (qt_style_filter.empty() ? string() : ";;")
119                 + _("All files (*)");
120
121         // Split data such as "TeX documents (*.tex);;LyX Documents (*.lyx)"
122         // into individual filters.
123         static boost::regex const separator_re(";;");
124
125         string::const_iterator it = filter.begin();
126         string::const_iterator const end = filter.end();
127         while (true) {
128                 boost::match_results<string::const_iterator> what;
129
130                 if (!boost::regex_search(it, end, what, separator_re)) {
131                         parse_filter(string(it, end));
132                         break;
133                 }
134
135                 // Everything from the start of the input to
136                 // the start of the match.
137                 parse_filter(string(what[-1].first, what[-1].second));
138
139                 // Increment the iterator to the end of the match.
140                 it += distance(it, what[0].second);
141         }
142 }
143
144
145 void FileFilterList::parse_filter(string const & filter)
146 {
147         // Matches "TeX documents (*.tex)",
148         // storing "TeX documents " as group 1 and "*.tex" as group 2.
149         static boost::regex const filter_re("([^(]*)\\(([^)]+)\\) *$");
150
151         boost::match_results<string::const_iterator> what;
152         if (!boost::regex_search(filter, what, filter_re)) {
153                 // Just a glob, no description.
154                 filters_.push_back(Filter(string(), trim(filter)));
155         } else {
156                 string const desc = string(what[1].first, what[1].second);
157                 string const globs = string(what[2].first, what[2].second);
158                 filters_.push_back(Filter(trim(desc), trim(globs)));
159         }
160 }
161
162
163 string const FileFilterList::str(bool expand) const
164 {
165         ostringstream ss;
166
167         vector<Filter>::const_iterator const begin = filters_.begin();
168         vector<Filter>::const_iterator const end = filters_.end();
169         vector<Filter>::const_iterator it = begin;
170         for (; it != end; ++it) {
171                 string const globs = expand ?
172                         convert_brace_glob(it->globs()) : it->globs();
173                 if (it != begin)
174                         ss << ";;";
175                 bool const has_description = !it->description().empty();
176                 if (has_description)
177                         ss << it->description() << " (";
178                 ss << globs;
179                 if (has_description)
180                         ss << ')';
181         }
182
183         return ss.str();
184 }
185
186 } // namespace support
187 } // namespace lyx