]> granicus.if.org Git - icinga2/commitdiff
Implement support for writing configuration files
authorMichael Friedrich <michael.friedrich@netways.de>
Mon, 3 Aug 2015 12:15:05 +0000 (14:15 +0200)
committerMichael Friedrich <michael.friedrich@netways.de>
Mon, 3 Aug 2015 13:07:38 +0000 (15:07 +0200)
fixes #9099

lib/config/CMakeLists.txt
lib/config/configwriter.cpp [new file with mode: 0644]
lib/config/configwriter.hpp [new file with mode: 0644]

index 102f6e6d1dd2bc74b6df68d44a4f27a7edb6d915..f60b1b8525c581a4b7a14eab32f056155e992dae 100644 (file)
@@ -37,6 +37,7 @@ set(config_SOURCES
   applyrule.cpp
   configcompilercontext.cpp configcompiler.cpp configitembuilder.cpp
   configitem.cpp ${FLEX_config_lexer_OUTPUTS} ${BISON_config_parser_OUTPUTS}
+  configwriter.cpp
   expression.cpp objectrule.cpp
 )
 
diff --git a/lib/config/configwriter.cpp b/lib/config/configwriter.cpp
new file mode 100644 (file)
index 0000000..278aae1
--- /dev/null
@@ -0,0 +1,218 @@
+/******************************************************************************
+ * Icinga 2                                                                   *
+ * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
+ *                                                                            *
+ * This program is free software; you can redistribute it and/or              *
+ * modify it under the terms of the GNU General Public License                *
+ * as published by the Free Software Foundation; either version 2             *
+ * of the License, or (at your option) any later version.                     *
+ *                                                                            *
+ * This program is distributed in the hope that it will be useful,            *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
+ * GNU General Public License for more details.                               *
+ *                                                                            *
+ * You should have received a copy of the GNU General Public License          *
+ * along with this program; if not, write to the Free Software Foundation     *
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
+ ******************************************************************************/
+
+#include "config/configwriter.hpp"
+#include "base/exception.hpp"
+#include <boost/foreach.hpp>
+#include <boost/regex.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+using namespace icinga;
+
+ConfigWriter::ConfigWriter(const String& fileName)
+    : m_FP(fileName.CStr(), std::ofstream::out | std::ostream::trunc)
+{ }
+
+void ConfigWriter::EmitBoolean(bool val)
+{
+       m_FP << (val ? "true" : "false");
+}
+
+void ConfigWriter::EmitNumber(double val)
+{
+       m_FP << val;
+}
+
+void ConfigWriter::EmitString(const String& val)
+{
+       m_FP << "\"" << EscapeIcingaString(val) << "\"";
+}
+
+void ConfigWriter::EmitEmpty(void)
+{
+       m_FP << "null";
+}
+
+void ConfigWriter::EmitArray(const Array::Ptr& val)
+{
+       m_FP << "[ ";
+       EmitArrayItems(val);
+       m_FP << " ]";
+}
+
+void ConfigWriter::EmitArrayItems(const Array::Ptr& val)
+{
+       bool first = true;
+
+       ObjectLock olock(val);
+       BOOST_FOREACH(const Value& item, val) {
+               if (first)
+                       first = false;
+               else
+                       m_FP << ", ";
+
+               EmitValue(0, item);
+       }
+}
+
+void ConfigWriter::EmitScope(int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports)
+{
+       m_FP << "{";
+
+       if (imports && imports->GetLength() > 0) {
+               ObjectLock xlock(imports);
+               BOOST_FOREACH(const Value& import, imports) {
+                       m_FP << "\n";
+                       EmitIndent(indentLevel);
+                       m_FP << "import \"" << import << "\"";
+               }
+
+               m_FP << "\n";
+       }
+
+       ObjectLock olock(val);
+       BOOST_FOREACH(const Dictionary::Pair& kv, val) {
+               m_FP << "\n";
+               EmitIndent(indentLevel);
+               EmitIdentifier(kv.first, true);
+               m_FP << " = ";
+               EmitValue(indentLevel + 1, kv.second);
+       }
+
+       m_FP << "\n";
+       EmitIndent(indentLevel - 1);
+       m_FP << "}";
+}
+
+void ConfigWriter::EmitValue(int indentLevel, const Value& val)
+{
+       if (val.IsObjectType<Array>())
+               EmitArray(val);
+       else if (val.IsObjectType<Dictionary>())
+               EmitScope(indentLevel, val);
+       else if (val.IsString())
+               EmitString(val);
+       else if (val.IsNumber())
+               EmitNumber(val);
+       else if (val.IsBoolean())
+               EmitBoolean(val);
+       else if (val.IsEmpty())
+               EmitEmpty();
+}
+
+void ConfigWriter::EmitRaw(const String& val)
+{
+       m_FP << val;
+}
+
+void ConfigWriter::EmitIndent(int indentLevel)
+{
+       for (int i = 0; i < indentLevel; i++)
+               m_FP << "\t";
+}
+
+void ConfigWriter::EmitIdentifier(const String& identifier, bool inAssignment)
+{
+       static std::set<String> keywords;
+       if (keywords.empty()) {
+               keywords.insert("object");
+               keywords.insert("template");
+               keywords.insert("include");
+               keywords.insert("include_recursive");
+               keywords.insert("library");
+               keywords.insert("null");
+               keywords.insert("true");
+               keywords.insert("false");
+               keywords.insert("const");
+               keywords.insert("var");
+               keywords.insert("this");
+               keywords.insert("globals");
+               keywords.insert("locals");
+               keywords.insert("use");
+               keywords.insert("apply");
+               keywords.insert("to");
+               keywords.insert("where");
+               keywords.insert("import");
+               keywords.insert("assign");
+               keywords.insert("ignore");
+               keywords.insert("function");
+               keywords.insert("return");
+               keywords.insert("break");
+               keywords.insert("continue");
+               keywords.insert("for");
+               keywords.insert("if");
+               keywords.insert("else");
+               keywords.insert("while");
+       }
+
+       if (keywords.find(identifier) != keywords.end()) {
+               m_FP << "@" << identifier;
+               return;
+       }
+
+       boost::regex expr("^[a-zA-Z_][a-zA-Z0-9\\_]*$");
+       boost::smatch what;
+       if (boost::regex_search(identifier.GetData(), what, expr))
+               m_FP << identifier;
+       else if (inAssignment)
+               EmitString(identifier);
+       else
+               BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid identifier"));
+}
+
+void ConfigWriter::EmitConfigItem(const String& type, const String& name, bool isTemplate,
+    const Array::Ptr& imports, const Dictionary::Ptr& attrs)
+{
+       if (isTemplate)
+               m_FP << "template ";
+       else
+               m_FP << "object ";
+
+       EmitIdentifier(type, false);
+       m_FP << " ";
+       EmitString(name);
+       m_FP << " ";
+       EmitScope(1, attrs, imports);
+}
+
+void ConfigWriter::EmitComment(const String& text)
+{
+       m_FP << "/* " << text << " */\n";
+}
+
+void ConfigWriter::EmitFunctionCall(const String& name, const Array::Ptr& arguments)
+{
+       EmitIdentifier(name, false);
+       m_FP << "(";
+       EmitArrayItems(arguments);
+       m_FP << ")";
+}
+
+String ConfigWriter::EscapeIcingaString(const String& str)
+{
+       String result = str;
+       boost::algorithm::replace_all(result, "\\", "\\\\");
+       boost::algorithm::replace_all(result, "\n", "\\n");
+       boost::algorithm::replace_all(result, "\t", "\\t");
+       boost::algorithm::replace_all(result, "\r", "\\r");
+       boost::algorithm::replace_all(result, "\b", "\\b");
+       boost::algorithm::replace_all(result, "\f", "\\f");
+       boost::algorithm::replace_all(result, "\"", "\\\"");
+       return result;
+}
diff --git a/lib/config/configwriter.hpp b/lib/config/configwriter.hpp
new file mode 100644 (file)
index 0000000..e27af01
--- /dev/null
@@ -0,0 +1,70 @@
+/******************************************************************************
+ * Icinga 2                                                                   *
+ * Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org)    *
+ *                                                                            *
+ * This program is free software; you can redistribute it and/or              *
+ * modify it under the terms of the GNU General Public License                *
+ * as published by the Free Software Foundation; either version 2             *
+ * of the License, or (at your option) any later version.                     *
+ *                                                                            *
+ * This program is distributed in the hope that it will be useful,            *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
+ * GNU General Public License for more details.                               *
+ *                                                                            *
+ * You should have received a copy of the GNU General Public License          *
+ * along with this program; if not, write to the Free Software Foundation     *
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
+ ******************************************************************************/
+
+#ifndef CONFIGWRITER_H
+#define CONFIGWRITER_H
+
+#include "config/i2-config.hpp"
+#include "base/object.hpp"
+#include "base/array.hpp"
+#include "base/dictionary.hpp"
+#include <fstream>
+
+namespace icinga
+{
+
+/**
+ * A configuration writer.
+ *
+ * @ingroup config
+ */
+class I2_CONFIG_API ConfigWriter : public Object {
+public:
+       DECLARE_PTR_TYPEDEFS(ConfigWriter);
+
+       ConfigWriter(const String& fileName);
+
+       void EmitBoolean(bool val);
+       void EmitNumber(double val);
+       void EmitString(const String& val);
+       void EmitEmpty(void);
+       void EmitArray(const Array::Ptr& val);
+       void EmitArrayItems(const Array::Ptr& val);
+       void EmitDictionary(const Dictionary::Ptr& val);
+       void EmitScope(int indentLevel, const Dictionary::Ptr& val, const Array::Ptr& imports = Array::Ptr());
+       void EmitValue(int indentLevel, const Value& val);
+       void EmitRaw(const String& val);
+       void EmitIndent(int indentLevel);
+
+       void EmitIdentifier(const String& identifier, bool inAssignment);
+       void EmitConfigItem(const String& type, const String& name, bool isTemplate,
+           const Array::Ptr& imports, const Dictionary::Ptr& attrs);
+
+       void EmitComment(const String& text);
+       void EmitFunctionCall(const String& name, const Array::Ptr& arguments);
+
+private:
+       std::ofstream m_FP;
+
+       static String EscapeIcingaString(const String& str);
+};
+
+}
+
+#endif /* CONFIGWRITER_H */