1 /******************************************************************************
3 * Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/) *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License *
7 * as published by the Free Software Foundation; either version 2 *
8 * of the License, or (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the Free Software Foundation *
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ******************************************************************************/
20 #include "base/configwriter.hpp"
21 #include "config/configcompiler.hpp"
22 #include "base/exception.hpp"
23 #include <boost/foreach.hpp>
24 #include <boost/regex.hpp>
25 #include <boost/algorithm/string/replace.hpp>
26 #include <boost/algorithm/string/split.hpp>
27 #include <boost/algorithm/string/classification.hpp>
31 using namespace icinga;
33 void ConfigWriter::EmitBoolean(std::ostream& fp, bool val)
35 fp << (val ? "true" : "false");
38 void ConfigWriter::EmitNumber(std::ostream& fp, double val)
40 fp << std::fixed << val;
43 void ConfigWriter::EmitString(std::ostream& fp, const String& val)
45 fp << "\"" << EscapeIcingaString(val) << "\"";
48 void ConfigWriter::EmitEmpty(std::ostream& fp)
53 void ConfigWriter::EmitArray(std::ostream& fp, int indentLevel, const Array::Ptr& val)
56 EmitArrayItems(fp, indentLevel, val);
57 if (val->GetLength() > 0)
62 void ConfigWriter::EmitArrayItems(std::ostream& fp, int indentLevel, const Array::Ptr& val)
66 ObjectLock olock(val);
67 BOOST_FOREACH(const Value& item, val) {
73 EmitValue(fp, indentLevel, item);
77 void ConfigWriter::EmitScope(std::ostream& fp, int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports)
81 if (imports && imports->GetLength() > 0) {
82 ObjectLock xlock(imports);
83 BOOST_FOREACH(const Value& import, imports) {
85 EmitIndent(fp, indentLevel);
86 fp << "import \"" << import << "\"";
93 ObjectLock olock(val);
94 BOOST_FOREACH(const Dictionary::Pair& kv, val) {
96 EmitIndent(fp, indentLevel);
98 std::vector<String> tokens;
99 boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
101 EmitIdentifier(fp, tokens[0], true);
103 for (std::vector<String>::size_type i = 1; i < tokens.size(); i++) {
105 EmitString(fp, tokens[i]);
110 EmitValue(fp, indentLevel + 1, kv.second);
115 EmitIndent(fp, indentLevel - 1);
119 void ConfigWriter::EmitValue(std::ostream& fp, int indentLevel, const Value& val)
121 if (val.IsObjectType<Array>())
122 EmitArray(fp, indentLevel, val);
123 else if (val.IsObjectType<Dictionary>())
124 EmitScope(fp, indentLevel, val);
125 else if (val.IsObjectType<ConfigIdentifier>())
126 EmitIdentifier(fp, static_cast<ConfigIdentifier::Ptr>(val)->GetName(), false);
127 else if (val.IsString())
129 else if (val.IsNumber())
131 else if (val.IsBoolean())
132 EmitBoolean(fp, val);
133 else if (val.IsEmpty())
137 void ConfigWriter::EmitRaw(std::ostream& fp, const String& val)
142 void ConfigWriter::EmitIndent(std::ostream& fp, int indentLevel)
144 for (int i = 0; i < indentLevel; i++)
148 void ConfigWriter::EmitIdentifier(std::ostream& fp, const String& identifier, bool inAssignment)
150 static std::set<String> keywords;
151 static boost::mutex mutex;
154 boost::mutex::scoped_lock lock(mutex);
155 if (keywords.empty()) {
156 const std::vector<String>& vkeywords = GetKeywords();
157 std::copy(vkeywords.begin(), vkeywords.end(), std::inserter(keywords, keywords.begin()));
161 if (keywords.find(identifier) != keywords.end()) {
162 fp << "@" << identifier;
166 boost::regex expr("^[a-zA-Z_][a-zA-Z0-9\\_]*$");
168 if (boost::regex_search(identifier.GetData(), what, expr))
170 else if (inAssignment)
171 EmitString(fp, identifier);
173 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid identifier"));
176 void ConfigWriter::EmitConfigItem(std::ostream& fp, const String& type, const String& name, bool isTemplate,
177 bool ignoreOnError, const Array::Ptr& imports, const Dictionary::Ptr& attrs)
184 EmitIdentifier(fp, type, false);
186 EmitString(fp, name);
189 fp << " ignore_on_error";
192 EmitScope(fp, 1, attrs, imports);
195 void ConfigWriter::EmitComment(std::ostream& fp, const String& text)
197 fp << "/* " << text << " */\n";
200 void ConfigWriter::EmitFunctionCall(std::ostream& fp, const String& name, const Array::Ptr& arguments)
202 EmitIdentifier(fp, name, false);
204 EmitArrayItems(fp, 0, arguments);
208 String ConfigWriter::EscapeIcingaString(const String& str)
211 boost::algorithm::replace_all(result, "\\", "\\\\");
212 boost::algorithm::replace_all(result, "\n", "\\n");
213 boost::algorithm::replace_all(result, "\t", "\\t");
214 boost::algorithm::replace_all(result, "\r", "\\r");
215 boost::algorithm::replace_all(result, "\b", "\\b");
216 boost::algorithm::replace_all(result, "\f", "\\f");
217 boost::algorithm::replace_all(result, "\"", "\\\"");
221 const std::vector<String>& ConfigWriter::GetKeywords(void)
223 static std::vector<String> keywords;
224 static boost::mutex mutex;
225 boost::mutex::scoped_lock lock(mutex);
227 if (keywords.empty()) {
228 keywords.push_back("object");
229 keywords.push_back("template");
230 keywords.push_back("include");
231 keywords.push_back("include_recursive");
232 keywords.push_back("include_zones");
233 keywords.push_back("library");
234 keywords.push_back("null");
235 keywords.push_back("true");
236 keywords.push_back("false");
237 keywords.push_back("const");
238 keywords.push_back("var");
239 keywords.push_back("this");
240 keywords.push_back("globals");
241 keywords.push_back("locals");
242 keywords.push_back("use");
243 keywords.push_back("ignore_on_error");
244 keywords.push_back("current_filename");
245 keywords.push_back("current_line");
246 keywords.push_back("apply");
247 keywords.push_back("to");
248 keywords.push_back("where");
249 keywords.push_back("import");
250 keywords.push_back("assign");
251 keywords.push_back("ignore");
252 keywords.push_back("function");
253 keywords.push_back("return");
254 keywords.push_back("break");
255 keywords.push_back("continue");
256 keywords.push_back("for");
257 keywords.push_back("if");
258 keywords.push_back("else");
259 keywords.push_back("while");
260 keywords.push_back("throw");
266 ConfigIdentifier::ConfigIdentifier(const String& identifier)
270 String ConfigIdentifier::GetName(void) const