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