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