Implement support for writing configuration files

fixes #9099
This commit is contained in:
Michael Friedrich 2015-08-03 14:15:05 +02:00
parent 08b1808397
commit 973db46d40
3 changed files with 289 additions and 0 deletions

View 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
)

218
lib/config/configwriter.cpp Normal file
View File

@ -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;
}

View File

@ -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 */