xref: /netbsd/external/bsd/atf/dist/tools/atffile.cpp (revision de996de9)
1ee44dd6cSjmmv //
2ee44dd6cSjmmv // Automated Testing Framework (atf)
3ee44dd6cSjmmv //
4ee44dd6cSjmmv // Copyright (c) 2007 The NetBSD Foundation, Inc.
5ee44dd6cSjmmv // All rights reserved.
6ee44dd6cSjmmv //
7ee44dd6cSjmmv // Redistribution and use in source and binary forms, with or without
8ee44dd6cSjmmv // modification, are permitted provided that the following conditions
9ee44dd6cSjmmv // are met:
10ee44dd6cSjmmv // 1. Redistributions of source code must retain the above copyright
11ee44dd6cSjmmv //    notice, this list of conditions and the following disclaimer.
12ee44dd6cSjmmv // 2. Redistributions in binary form must reproduce the above copyright
13ee44dd6cSjmmv //    notice, this list of conditions and the following disclaimer in the
14ee44dd6cSjmmv //    documentation and/or other materials provided with the distribution.
15ee44dd6cSjmmv //
16ee44dd6cSjmmv // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17ee44dd6cSjmmv // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18ee44dd6cSjmmv // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19ee44dd6cSjmmv // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20ee44dd6cSjmmv // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21ee44dd6cSjmmv // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22ee44dd6cSjmmv // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23ee44dd6cSjmmv // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24ee44dd6cSjmmv // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25ee44dd6cSjmmv // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26ee44dd6cSjmmv // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27ee44dd6cSjmmv // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28ee44dd6cSjmmv //
29ee44dd6cSjmmv 
30ee44dd6cSjmmv #include <cassert>
31ee44dd6cSjmmv #include <cstdlib>
32ee44dd6cSjmmv #include <fstream>
33ee44dd6cSjmmv 
34ee44dd6cSjmmv #include "atffile.hpp"
35ee44dd6cSjmmv #include "exceptions.hpp"
36ee44dd6cSjmmv #include "expand.hpp"
37ee44dd6cSjmmv #include "parser.hpp"
38ee44dd6cSjmmv 
39ee44dd6cSjmmv namespace impl = tools;
40ee44dd6cSjmmv namespace detail = tools::detail;
41ee44dd6cSjmmv 
42ee44dd6cSjmmv namespace {
43ee44dd6cSjmmv 
44ee44dd6cSjmmv typedef std::map< std::string, std::string > vars_map;
45ee44dd6cSjmmv 
46ee44dd6cSjmmv } // anonymous namespace
47ee44dd6cSjmmv 
48ee44dd6cSjmmv // ------------------------------------------------------------------------
49ee44dd6cSjmmv // The "atf_atffile" auxiliary parser.
50ee44dd6cSjmmv // ------------------------------------------------------------------------
51ee44dd6cSjmmv 
52ee44dd6cSjmmv namespace atf_atffile {
53ee44dd6cSjmmv 
54ee44dd6cSjmmv static const tools::parser::token_type eof_type = 0;
55ee44dd6cSjmmv static const tools::parser::token_type nl_type = 1;
56ee44dd6cSjmmv static const tools::parser::token_type text_type = 2;
57ee44dd6cSjmmv static const tools::parser::token_type colon_type = 3;
58ee44dd6cSjmmv static const tools::parser::token_type conf_type = 4;
59ee44dd6cSjmmv static const tools::parser::token_type dblquote_type = 5;
60ee44dd6cSjmmv static const tools::parser::token_type equal_type = 6;
61ee44dd6cSjmmv static const tools::parser::token_type hash_type = 7;
62ee44dd6cSjmmv static const tools::parser::token_type prop_type = 8;
63ee44dd6cSjmmv static const tools::parser::token_type tp_type = 9;
64ee44dd6cSjmmv static const tools::parser::token_type tp_glob_type = 10;
65ee44dd6cSjmmv 
66ee44dd6cSjmmv class tokenizer : public tools::parser::tokenizer< std::istream > {
67ee44dd6cSjmmv public:
tokenizer(std::istream & is,size_t curline)68ee44dd6cSjmmv     tokenizer(std::istream& is, size_t curline) :
69ee44dd6cSjmmv         tools::parser::tokenizer< std::istream >
70ee44dd6cSjmmv             (is, true, eof_type, nl_type, text_type, curline)
71ee44dd6cSjmmv     {
72ee44dd6cSjmmv         add_delim(':', colon_type);
73ee44dd6cSjmmv         add_delim('=', equal_type);
74ee44dd6cSjmmv         add_delim('#', hash_type);
75ee44dd6cSjmmv         add_quote('"', dblquote_type);
76ee44dd6cSjmmv         add_keyword("conf", conf_type);
77ee44dd6cSjmmv         add_keyword("prop", prop_type);
78ee44dd6cSjmmv         add_keyword("tp", tp_type);
79ee44dd6cSjmmv         add_keyword("tp-glob", tp_glob_type);
80ee44dd6cSjmmv     }
81ee44dd6cSjmmv };
82ee44dd6cSjmmv 
83ee44dd6cSjmmv } // namespace atf_atffile
84ee44dd6cSjmmv 
85ee44dd6cSjmmv // ------------------------------------------------------------------------
86ee44dd6cSjmmv // The "atf_atffile_reader" class.
87ee44dd6cSjmmv // ------------------------------------------------------------------------
88ee44dd6cSjmmv 
atf_atffile_reader(std::istream & is)89ee44dd6cSjmmv detail::atf_atffile_reader::atf_atffile_reader(std::istream& is) :
90ee44dd6cSjmmv     m_is(is)
91ee44dd6cSjmmv {
92ee44dd6cSjmmv }
93ee44dd6cSjmmv 
~atf_atffile_reader(void)94ee44dd6cSjmmv detail::atf_atffile_reader::~atf_atffile_reader(void)
95ee44dd6cSjmmv {
96ee44dd6cSjmmv }
97ee44dd6cSjmmv 
98ee44dd6cSjmmv void
got_conf(const std::string & name,const std::string & val)99ee44dd6cSjmmv detail::atf_atffile_reader::got_conf(
100*de996de9Sjmmv     const std::string& name __attribute__((__unused__)),
101*de996de9Sjmmv     const std::string& val __attribute__((__unused__)))
102ee44dd6cSjmmv {
103ee44dd6cSjmmv }
104ee44dd6cSjmmv 
105ee44dd6cSjmmv void
got_prop(const std::string & name,const std::string & val)106ee44dd6cSjmmv detail::atf_atffile_reader::got_prop(
107*de996de9Sjmmv     const std::string& name __attribute__((__unused__)),
108*de996de9Sjmmv     const std::string& val __attribute__((__unused__)))
109ee44dd6cSjmmv {
110ee44dd6cSjmmv }
111ee44dd6cSjmmv 
112ee44dd6cSjmmv void
got_tp(const std::string & name,bool isglob)113ee44dd6cSjmmv detail::atf_atffile_reader::got_tp(
114*de996de9Sjmmv     const std::string& name __attribute__((__unused__)),
115*de996de9Sjmmv     bool isglob __attribute__((__unused__)))
116ee44dd6cSjmmv {
117ee44dd6cSjmmv }
118ee44dd6cSjmmv 
119ee44dd6cSjmmv void
got_eof(void)120ee44dd6cSjmmv detail::atf_atffile_reader::got_eof(void)
121ee44dd6cSjmmv {
122ee44dd6cSjmmv }
123ee44dd6cSjmmv 
124ee44dd6cSjmmv void
read(void)125ee44dd6cSjmmv detail::atf_atffile_reader::read(void)
126ee44dd6cSjmmv {
127ee44dd6cSjmmv     using tools::parser::parse_error;
128ee44dd6cSjmmv     using namespace atf_atffile;
129ee44dd6cSjmmv 
130ee44dd6cSjmmv     std::pair< size_t, tools::parser::headers_map > hml =
131ee44dd6cSjmmv         tools::parser::read_headers(m_is, 1);
132ee44dd6cSjmmv     tools::parser::validate_content_type(hml.second,
133ee44dd6cSjmmv         "application/X-atf-atffile", 1);
134ee44dd6cSjmmv 
135ee44dd6cSjmmv     tokenizer tkz(m_is, hml.first);
136ee44dd6cSjmmv     tools::parser::parser< tokenizer > p(tkz);
137ee44dd6cSjmmv 
138ee44dd6cSjmmv     for (;;) {
139ee44dd6cSjmmv         try {
140ee44dd6cSjmmv             tools::parser::token t =
141ee44dd6cSjmmv                 p.expect(conf_type, hash_type, prop_type, tp_type,
142ee44dd6cSjmmv                          tp_glob_type, nl_type, eof_type,
143ee44dd6cSjmmv                          "conf, #, prop, tp, tp-glob, a new line or eof");
144ee44dd6cSjmmv             if (t.type() == eof_type)
145ee44dd6cSjmmv                 break;
146ee44dd6cSjmmv 
147ee44dd6cSjmmv             if (t.type() == conf_type) {
148ee44dd6cSjmmv                 t = p.expect(colon_type, "`:'");
149ee44dd6cSjmmv 
150ee44dd6cSjmmv                 t = p.expect(text_type, "variable name");
151ee44dd6cSjmmv                 std::string var = t.text();
152ee44dd6cSjmmv 
153ee44dd6cSjmmv                 t = p.expect(equal_type, "equal sign");
154ee44dd6cSjmmv 
155ee44dd6cSjmmv                 t = p.expect(text_type, "word or quoted string");
156ee44dd6cSjmmv                 ATF_PARSER_CALLBACK(p, got_conf(var, t.text()));
157ee44dd6cSjmmv             } else if (t.type() == hash_type) {
158ee44dd6cSjmmv                 (void)p.rest_of_line();
159ee44dd6cSjmmv             } else if (t.type() == prop_type) {
160ee44dd6cSjmmv                 t = p.expect(colon_type, "`:'");
161ee44dd6cSjmmv 
162ee44dd6cSjmmv                 t = p.expect(text_type, "property name");
163ee44dd6cSjmmv                 std::string name = t.text();
164ee44dd6cSjmmv 
165ee44dd6cSjmmv                 t = p.expect(equal_type, "equale sign");
166ee44dd6cSjmmv 
167ee44dd6cSjmmv                 t = p.expect(text_type, "word or quoted string");
168ee44dd6cSjmmv                 ATF_PARSER_CALLBACK(p, got_prop(name, t.text()));
169ee44dd6cSjmmv             } else if (t.type() == tp_type) {
170ee44dd6cSjmmv                 t = p.expect(colon_type, "`:'");
171ee44dd6cSjmmv 
172ee44dd6cSjmmv                 t = p.expect(text_type, "word or quoted string");
173ee44dd6cSjmmv                 ATF_PARSER_CALLBACK(p, got_tp(t.text(), false));
174ee44dd6cSjmmv             } else if (t.type() == tp_glob_type) {
175ee44dd6cSjmmv                 t = p.expect(colon_type, "`:'");
176ee44dd6cSjmmv 
177ee44dd6cSjmmv                 t = p.expect(text_type, "word or quoted string");
178ee44dd6cSjmmv                 ATF_PARSER_CALLBACK(p, got_tp(t.text(), true));
179ee44dd6cSjmmv             } else if (t.type() == nl_type) {
180ee44dd6cSjmmv                 continue;
181ee44dd6cSjmmv             } else
182ee44dd6cSjmmv                 std::abort();
183ee44dd6cSjmmv 
184ee44dd6cSjmmv             t = p.expect(nl_type, hash_type, eof_type,
185ee44dd6cSjmmv                          "new line or comment");
186ee44dd6cSjmmv             if (t.type() == hash_type) {
187ee44dd6cSjmmv                 (void)p.rest_of_line();
188ee44dd6cSjmmv                 t = p.next();
189ee44dd6cSjmmv             } else if (t.type() == eof_type)
190ee44dd6cSjmmv                 break;
191ee44dd6cSjmmv         } catch (const parse_error& pe) {
192ee44dd6cSjmmv             p.add_error(pe);
193ee44dd6cSjmmv             p.reset(nl_type);
194ee44dd6cSjmmv         }
195ee44dd6cSjmmv     }
196ee44dd6cSjmmv 
197ee44dd6cSjmmv     ATF_PARSER_CALLBACK(p, got_eof());
198ee44dd6cSjmmv }
199ee44dd6cSjmmv 
200ee44dd6cSjmmv // ------------------------------------------------------------------------
201ee44dd6cSjmmv // The "reader" helper class.
202ee44dd6cSjmmv // ------------------------------------------------------------------------
203ee44dd6cSjmmv 
204ee44dd6cSjmmv class reader : public detail::atf_atffile_reader {
205ee44dd6cSjmmv     const tools::fs::directory& m_dir;
206ee44dd6cSjmmv     vars_map m_conf, m_props;
207ee44dd6cSjmmv     std::vector< std::string > m_tps;
208ee44dd6cSjmmv 
209ee44dd6cSjmmv     void
got_tp(const std::string & name,bool isglob)210ee44dd6cSjmmv     got_tp(const std::string& name, bool isglob)
211ee44dd6cSjmmv     {
212ee44dd6cSjmmv         if (isglob) {
213ee44dd6cSjmmv             std::vector< std::string > ms =
214ee44dd6cSjmmv                 tools::expand::expand_glob(name, m_dir.names());
215ee44dd6cSjmmv             // Cannot use m_tps.insert(iterator, begin, end) here because it
216ee44dd6cSjmmv             // does not work under Solaris.
217ee44dd6cSjmmv             for (std::vector< std::string >::const_iterator iter = ms.begin();
218ee44dd6cSjmmv                  iter != ms.end(); iter++)
219ee44dd6cSjmmv                 m_tps.push_back(*iter);
220ee44dd6cSjmmv         } else {
221ee44dd6cSjmmv             if (m_dir.find(name) == m_dir.end())
222ee44dd6cSjmmv                 throw tools::not_found_error< tools::fs::path >
223ee44dd6cSjmmv                     ("Cannot locate the " + name + " file",
224ee44dd6cSjmmv                      tools::fs::path(name));
225ee44dd6cSjmmv             m_tps.push_back(name);
226ee44dd6cSjmmv         }
227ee44dd6cSjmmv     }
228ee44dd6cSjmmv 
229ee44dd6cSjmmv     void
got_prop(const std::string & name,const std::string & val)230ee44dd6cSjmmv     got_prop(const std::string& name, const std::string& val)
231ee44dd6cSjmmv     {
232ee44dd6cSjmmv         m_props[name] = val;
233ee44dd6cSjmmv     }
234ee44dd6cSjmmv 
235ee44dd6cSjmmv     void
got_conf(const std::string & var,const std::string & val)236ee44dd6cSjmmv     got_conf(const std::string& var, const std::string& val)
237ee44dd6cSjmmv     {
238ee44dd6cSjmmv         m_conf[var] = val;
239ee44dd6cSjmmv     }
240ee44dd6cSjmmv 
241ee44dd6cSjmmv public:
reader(std::istream & is,const tools::fs::directory & dir)242ee44dd6cSjmmv     reader(std::istream& is, const tools::fs::directory& dir) :
243ee44dd6cSjmmv         detail::atf_atffile_reader(is),
244ee44dd6cSjmmv         m_dir(dir)
245ee44dd6cSjmmv     {
246ee44dd6cSjmmv     }
247ee44dd6cSjmmv 
248ee44dd6cSjmmv     const vars_map&
conf(void) const249ee44dd6cSjmmv     conf(void)
250ee44dd6cSjmmv         const
251ee44dd6cSjmmv     {
252ee44dd6cSjmmv         return m_conf;
253ee44dd6cSjmmv     }
254ee44dd6cSjmmv 
255ee44dd6cSjmmv     const vars_map&
props(void) const256ee44dd6cSjmmv     props(void)
257ee44dd6cSjmmv         const
258ee44dd6cSjmmv     {
259ee44dd6cSjmmv         return m_props;
260ee44dd6cSjmmv     }
261ee44dd6cSjmmv 
262ee44dd6cSjmmv     const std::vector< std::string >&
tps(void) const263ee44dd6cSjmmv     tps(void)
264ee44dd6cSjmmv         const
265ee44dd6cSjmmv     {
266ee44dd6cSjmmv         return m_tps;
267ee44dd6cSjmmv     }
268ee44dd6cSjmmv };
269ee44dd6cSjmmv 
270ee44dd6cSjmmv // ------------------------------------------------------------------------
271ee44dd6cSjmmv // The "atffile" class.
272ee44dd6cSjmmv // ------------------------------------------------------------------------
273ee44dd6cSjmmv 
atffile(const vars_map & config_vars,const std::vector<std::string> & test_program_names,const vars_map & properties)274ee44dd6cSjmmv impl::atffile::atffile(const vars_map& config_vars,
275ee44dd6cSjmmv                        const std::vector< std::string >& test_program_names,
276ee44dd6cSjmmv                        const vars_map& properties) :
277ee44dd6cSjmmv     m_conf(config_vars),
278ee44dd6cSjmmv     m_tps(test_program_names),
279ee44dd6cSjmmv     m_props(properties)
280ee44dd6cSjmmv {
281ee44dd6cSjmmv     assert(properties.find("test-suite") != properties.end());
282ee44dd6cSjmmv }
283ee44dd6cSjmmv 
284ee44dd6cSjmmv const std::vector< std::string >&
tps(void) const285ee44dd6cSjmmv impl::atffile::tps(void)
286ee44dd6cSjmmv     const
287ee44dd6cSjmmv {
288ee44dd6cSjmmv     return m_tps;
289ee44dd6cSjmmv }
290ee44dd6cSjmmv 
291ee44dd6cSjmmv const vars_map&
conf(void) const292ee44dd6cSjmmv impl::atffile::conf(void)
293ee44dd6cSjmmv     const
294ee44dd6cSjmmv {
295ee44dd6cSjmmv     return m_conf;
296ee44dd6cSjmmv }
297ee44dd6cSjmmv 
298ee44dd6cSjmmv const vars_map&
props(void) const299ee44dd6cSjmmv impl::atffile::props(void)
300ee44dd6cSjmmv     const
301ee44dd6cSjmmv {
302ee44dd6cSjmmv     return m_props;
303ee44dd6cSjmmv }
304ee44dd6cSjmmv 
305ee44dd6cSjmmv // ------------------------------------------------------------------------
306ee44dd6cSjmmv // Free functions.
307ee44dd6cSjmmv // ------------------------------------------------------------------------
308ee44dd6cSjmmv 
309ee44dd6cSjmmv // XXX Glob expansion and file existance checks certainly do not belong in
310ee44dd6cSjmmv // a *parser*.  This needs to be taken out...
311ee44dd6cSjmmv impl::atffile
read_atffile(const tools::fs::path & filename)312ee44dd6cSjmmv impl::read_atffile(const tools::fs::path& filename)
313ee44dd6cSjmmv {
314ee44dd6cSjmmv     // Scan the directory where the atffile lives in to gather a list of
315ee44dd6cSjmmv     // all possible test programs in it.
316ee44dd6cSjmmv     tools::fs::directory dir(filename.branch_path());
317ee44dd6cSjmmv     dir.erase(filename.leaf_name());
318ee44dd6cSjmmv     tools::fs::directory::iterator iter = dir.begin();
319ee44dd6cSjmmv     while (iter != dir.end()) {
320ee44dd6cSjmmv         const std::string& name = (*iter).first;
321ee44dd6cSjmmv         const tools::fs::file_info& fi = (*iter).second;
322ee44dd6cSjmmv 
323ee44dd6cSjmmv         // Discard hidden files and non-executable ones so that they are
324ee44dd6cSjmmv         // not candidates for glob matching.
325ee44dd6cSjmmv         if (name[0] == '.' || (!fi.is_owner_executable() &&
326ee44dd6cSjmmv                                !fi.is_group_executable()))
327ee44dd6cSjmmv             dir.erase(iter++);
328ee44dd6cSjmmv         else
329ee44dd6cSjmmv             iter++;
330ee44dd6cSjmmv     }
331ee44dd6cSjmmv 
332ee44dd6cSjmmv     // Parse the atffile.
333ee44dd6cSjmmv     std::ifstream is(filename.c_str());
334ee44dd6cSjmmv     if (!is)
335ee44dd6cSjmmv         throw tools::not_found_error< tools::fs::path >
336ee44dd6cSjmmv             ("Cannot open Atffile", filename);
337ee44dd6cSjmmv     reader r(is, dir);
338ee44dd6cSjmmv     r.read();
339ee44dd6cSjmmv     is.close();
340ee44dd6cSjmmv 
341ee44dd6cSjmmv     // Sanity checks.
342ee44dd6cSjmmv     if (r.props().find("test-suite") == r.props().end())
343ee44dd6cSjmmv         throw tools::not_found_error< std::string >
344ee44dd6cSjmmv             ("Undefined property `test-suite'", "test-suite");
345ee44dd6cSjmmv 
346ee44dd6cSjmmv     return atffile(r.conf(), r.tps(), r.props());
347ee44dd6cSjmmv }
348