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