]> granicus.if.org Git - icinga2/blob - lib/base/configwriter.cpp
Update copyright headers for 2016
[icinga2] / lib / base / configwriter.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/)  *
4  *                                                                            *
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.                     *
9  *                                                                            *
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.                               *
14  *                                                                            *
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  ******************************************************************************/
19
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>
28 #include <set>
29 #include <iterator>
30
31 using namespace icinga;
32
33 void ConfigWriter::EmitBoolean(std::ostream& fp, bool val)
34 {
35         fp << (val ? "true" : "false");
36 }
37
38 void ConfigWriter::EmitNumber(std::ostream& fp, double val)
39 {
40         fp << std::fixed << val;
41 }
42
43 void ConfigWriter::EmitString(std::ostream& fp, const String& val)
44 {
45         fp << "\"" << EscapeIcingaString(val) << "\"";
46 }
47
48 void ConfigWriter::EmitEmpty(std::ostream& fp)
49 {
50         fp << "null";
51 }
52
53 void ConfigWriter::EmitArray(std::ostream& fp, int indentLevel, const Array::Ptr& val)
54 {
55         fp << "[ ";
56         EmitArrayItems(fp, indentLevel, val);
57         if (val->GetLength() > 0)
58                 fp << " ";
59         fp << "]";
60 }
61
62 void ConfigWriter::EmitArrayItems(std::ostream& fp, int indentLevel, const Array::Ptr& val)
63 {
64         bool first = true;
65
66         ObjectLock olock(val);
67         BOOST_FOREACH(const Value& item, val) {
68                 if (first)
69                         first = false;
70                 else
71                         fp << ", ";
72
73                 EmitValue(fp, indentLevel, item);
74         }
75 }
76
77 void ConfigWriter::EmitScope(std::ostream& fp, int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports)
78 {
79         fp << "{";
80
81         if (imports && imports->GetLength() > 0) {
82                 ObjectLock xlock(imports);
83                 BOOST_FOREACH(const Value& import, imports) {
84                         fp << "\n";
85                         EmitIndent(fp, indentLevel);
86                         fp << "import \"" << import << "\"";
87                 }
88
89                 fp << "\n";
90         }
91
92         if (val) {
93                 ObjectLock olock(val);
94                 BOOST_FOREACH(const Dictionary::Pair& kv, val) {
95                         fp << "\n";
96                         EmitIndent(fp, indentLevel);
97                         
98                         std::vector<String> tokens;
99                         boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
100                         
101                         EmitIdentifier(fp, tokens[0], true);
102                         
103                         for (std::vector<String>::size_type i = 1; i < tokens.size(); i++) {
104                                 fp << "[";
105                                 EmitString(fp, tokens[i]);
106                                 fp << "]";
107                         }
108                         
109                         fp << " = ";
110                         EmitValue(fp, indentLevel + 1, kv.second);
111                 }
112         }
113
114         fp << "\n";
115         EmitIndent(fp, indentLevel - 1);
116         fp << "}";
117 }
118
119 void ConfigWriter::EmitValue(std::ostream& fp, int indentLevel, const Value& val)
120 {
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())
128                 EmitString(fp, val);
129         else if (val.IsNumber())
130                 EmitNumber(fp, val);
131         else if (val.IsBoolean())
132                 EmitBoolean(fp, val);
133         else if (val.IsEmpty())
134                 EmitEmpty(fp);
135 }
136
137 void ConfigWriter::EmitRaw(std::ostream& fp, const String& val)
138 {
139         fp << val;
140 }
141
142 void ConfigWriter::EmitIndent(std::ostream& fp, int indentLevel)
143 {
144         for (int i = 0; i < indentLevel; i++)
145                 fp << "\t";
146 }
147
148 void ConfigWriter::EmitIdentifier(std::ostream& fp, const String& identifier, bool inAssignment)
149 {
150         static std::set<String> keywords;
151         static boost::mutex mutex;
152
153         {
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()));
158                 }
159         }
160
161         if (keywords.find(identifier) != keywords.end()) {
162                 fp << "@" << identifier;
163                 return;
164         }
165
166         boost::regex expr("^[a-zA-Z_][a-zA-Z0-9\\_]*$");
167         boost::smatch what;
168         if (boost::regex_search(identifier.GetData(), what, expr))
169                 fp << identifier;
170         else if (inAssignment)
171                 EmitString(fp, identifier);
172         else
173                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid identifier"));
174 }
175
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)
178 {
179         if (isTemplate)
180                 fp << "template ";
181         else
182                 fp << "object ";
183
184         EmitIdentifier(fp, type, false);
185         fp << " ";
186         EmitString(fp, name);
187
188         if (ignoreOnError)
189                 fp << " ignore_on_error";
190
191         fp << " ";
192         EmitScope(fp, 1, attrs, imports);
193 }
194
195 void ConfigWriter::EmitComment(std::ostream& fp, const String& text)
196 {
197         fp << "/* " << text << " */\n";
198 }
199
200 void ConfigWriter::EmitFunctionCall(std::ostream& fp, const String& name, const Array::Ptr& arguments)
201 {
202         EmitIdentifier(fp, name, false);
203         fp << "(";
204         EmitArrayItems(fp, 0, arguments);
205         fp << ")";
206 }
207
208 String ConfigWriter::EscapeIcingaString(const String& str)
209 {
210         String result = 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, "\"", "\\\"");
218         return result;
219 }
220
221 const std::vector<String>& ConfigWriter::GetKeywords(void)
222 {
223         static std::vector<String> keywords;
224         static boost::mutex mutex;
225         boost::mutex::scoped_lock lock(mutex);
226
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");
261         }
262
263         return keywords;
264 }
265
266 ConfigIdentifier::ConfigIdentifier(const String& identifier)
267     : m_Name(identifier)
268 { }
269
270 String ConfigIdentifier::GetName(void) const
271 {
272         return m_Name;
273 }