1//
2// Automated Testing Framework (atf)
3//
4// Copyright (c) 2007 The NetBSD Foundation, Inc.
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions
9// are met:
10// 1. Redistributions of source code must retain the above copyright
11//    notice, this list of conditions and the following disclaimer.
12// 2. Redistributions in binary form must reproduce the above copyright
13//    notice, this list of conditions and the following disclaimer in the
14//    documentation and/or other materials provided with the distribution.
15//
16// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29
30#include <fstream>
31#include <vector>
32
33#include "atf-c/defs.h"
34
35#include "atf-c++/config.hpp"
36
37#include "atf-c++/detail/env.hpp"
38#include "atf-c++/detail/fs.hpp"
39#include "atf-c++/detail/sanity.hpp"
40#include "atf-c++/detail/parser.hpp"
41
42#include "config.hpp"
43
44namespace impl = atf::atf_run;
45namespace detail = atf::atf_run::detail;
46
47namespace {
48
49namespace atf_config {
50
51static const atf::parser::token_type eof_type = 0;
52static const atf::parser::token_type nl_type = 1;
53static const atf::parser::token_type text_type = 2;
54static const atf::parser::token_type dblquote_type = 3;
55static const atf::parser::token_type equal_type = 4;
56static const atf::parser::token_type hash_type = 5;
57
58class tokenizer : public atf::parser::tokenizer< std::istream > {
59public:
60    tokenizer(std::istream& is, size_t curline) :
61        atf::parser::tokenizer< std::istream >
62            (is, true, eof_type, nl_type, text_type, curline)
63    {
64        add_delim('=', equal_type);
65        add_delim('#', hash_type);
66        add_quote('"', dblquote_type);
67    }
68};
69
70} // namespace atf_config
71
72class config_reader : public detail::atf_config_reader {
73    atf::tests::vars_map m_vars;
74
75    void
76    got_var(const std::string& var, const std::string& name)
77    {
78        m_vars[var] = name;
79    }
80
81public:
82    config_reader(std::istream& is) :
83        atf_config_reader(is)
84    {
85    }
86
87    const atf::tests::vars_map&
88    get_vars(void)
89        const
90    {
91        return m_vars;
92    }
93};
94
95template< class K, class V >
96static
97void
98merge_maps(std::map< K, V >& dest, const std::map< K, V >& src)
99{
100    for (typename std::map< K, V >::const_iterator iter = src.begin();
101         iter != src.end(); iter++)
102        dest[(*iter).first] = (*iter).second;
103}
104
105static
106void
107merge_config_file(const atf::fs::path& config_path,
108                  atf::tests::vars_map& config)
109{
110    std::ifstream is(config_path.c_str());
111    if (is) {
112        config_reader reader(is);
113        reader.read();
114        merge_maps(config, reader.get_vars());
115    }
116}
117
118static
119std::vector< atf::fs::path >
120get_config_dirs(void)
121{
122    std::vector< atf::fs::path > dirs;
123    dirs.push_back(atf::fs::path(atf::config::get("atf_confdir")));
124    if (atf::env::has("HOME"))
125        dirs.push_back(atf::fs::path(atf::env::get("HOME")) / ".atf");
126    return dirs;
127}
128
129} // anonymous namespace
130
131detail::atf_config_reader::atf_config_reader(std::istream& is) :
132    m_is(is)
133{
134}
135
136detail::atf_config_reader::~atf_config_reader(void)
137{
138}
139
140void
141detail::atf_config_reader::got_var(
142    const std::string& var ATF_DEFS_ATTRIBUTE_UNUSED,
143    const std::string& val ATF_DEFS_ATTRIBUTE_UNUSED)
144{
145}
146
147void
148detail::atf_config_reader::got_eof(void)
149{
150}
151
152void
153detail::atf_config_reader::read(void)
154{
155    using atf::parser::parse_error;
156    using namespace atf_config;
157
158    std::pair< size_t, atf::parser::headers_map > hml =
159        atf::parser::read_headers(m_is, 1);
160    atf::parser::validate_content_type(hml.second,
161        "application/X-atf-config", 1);
162
163    tokenizer tkz(m_is, hml.first);
164    atf::parser::parser< tokenizer > p(tkz);
165
166    for (;;) {
167        try {
168            atf::parser::token t = p.expect(eof_type, hash_type, text_type,
169                                            nl_type,
170                                            "eof, #, new line or text");
171            if (t.type() == eof_type)
172                break;
173
174            if (t.type() == hash_type) {
175                (void)p.rest_of_line();
176                t = p.expect(nl_type, "new line");
177            } else if (t.type() == text_type) {
178                std::string name = t.text();
179
180                t = p.expect(equal_type, "equal sign");
181
182                t = p.expect(text_type, "word or quoted string");
183                ATF_PARSER_CALLBACK(p, got_var(name, t.text()));
184
185                t = p.expect(nl_type, hash_type, "new line or comment");
186                if (t.type() == hash_type) {
187                    (void)p.rest_of_line();
188                    t = p.expect(nl_type, "new line");
189                }
190            } else if (t.type() == nl_type) {
191            } else
192                UNREACHABLE;
193        } catch (const parse_error& pe) {
194            p.add_error(pe);
195            p.reset(nl_type);
196        }
197    }
198
199    ATF_PARSER_CALLBACK(p, got_eof());
200}
201
202atf::tests::vars_map
203impl::merge_configs(const atf::tests::vars_map& lower,
204                    const atf::tests::vars_map& upper)
205{
206    atf::tests::vars_map merged = lower;
207    merge_maps(merged, upper);
208    return merged;
209}
210
211atf::tests::vars_map
212impl::read_config_files(const std::string& test_suite_name)
213{
214    atf::tests::vars_map config;
215
216    const std::vector< atf::fs::path > dirs = get_config_dirs();
217    for (std::vector< atf::fs::path >::const_iterator iter = dirs.begin();
218         iter != dirs.end(); iter++) {
219        merge_config_file((*iter) / "common.conf", config);
220        merge_config_file((*iter) / (test_suite_name + ".conf"), config);
221    }
222
223    return config;
224}
225