mirror of
				https://github.com/Icinga/icinga2.git
				synced 2025-11-04 13:45:04 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			277 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/******************************************************************************
 | 
						|
 * Icinga 2                                                                   *
 | 
						|
 * Copyright (C) 2012-2016 Icinga Development Team (https://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 "base/configwriter.hpp"
 | 
						|
#include "base/exception.hpp"
 | 
						|
#include <boost/regex.hpp>
 | 
						|
#include <boost/algorithm/string/replace.hpp>
 | 
						|
#include <boost/algorithm/string/split.hpp>
 | 
						|
#include <boost/algorithm/string/classification.hpp>
 | 
						|
#include <set>
 | 
						|
#include <iterator>
 | 
						|
 | 
						|
using namespace icinga;
 | 
						|
 | 
						|
void ConfigWriter::EmitBoolean(std::ostream& fp, bool val)
 | 
						|
{
 | 
						|
	fp << (val ? "true" : "false");
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitNumber(std::ostream& fp, double val)
 | 
						|
{
 | 
						|
	fp << std::fixed << val;
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitString(std::ostream& fp, const String& val)
 | 
						|
{
 | 
						|
	fp << "\"" << EscapeIcingaString(val) << "\"";
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitEmpty(std::ostream& fp)
 | 
						|
{
 | 
						|
	fp << "null";
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitArray(std::ostream& fp, int indentLevel, const Array::Ptr& val)
 | 
						|
{
 | 
						|
	fp << "[ ";
 | 
						|
	EmitArrayItems(fp, indentLevel, val);
 | 
						|
	if (val->GetLength() > 0)
 | 
						|
		fp << " ";
 | 
						|
	fp << "]";
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitArrayItems(std::ostream& fp, int indentLevel, const Array::Ptr& val)
 | 
						|
{
 | 
						|
	bool first = true;
 | 
						|
 | 
						|
	ObjectLock olock(val);
 | 
						|
	for (const Value& item : val) {
 | 
						|
		if (first)
 | 
						|
			first = false;
 | 
						|
		else
 | 
						|
			fp << ", ";
 | 
						|
 | 
						|
		EmitValue(fp, indentLevel, item);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitScope(std::ostream& fp, int indentLevel, const Dictionary::Ptr& val,
 | 
						|
    const Array::Ptr& imports, bool splitDot)
 | 
						|
{
 | 
						|
	fp << "{";
 | 
						|
 | 
						|
	if (imports && imports->GetLength() > 0) {
 | 
						|
		ObjectLock xlock(imports);
 | 
						|
		for (const Value& import : imports) {
 | 
						|
			fp << "\n";
 | 
						|
			EmitIndent(fp, indentLevel);
 | 
						|
			fp << "import \"" << import << "\"";
 | 
						|
		}
 | 
						|
 | 
						|
		fp << "\n";
 | 
						|
	}
 | 
						|
 | 
						|
	if (val) {
 | 
						|
		ObjectLock olock(val);
 | 
						|
		for (const Dictionary::Pair& kv : val) {
 | 
						|
			fp << "\n";
 | 
						|
			EmitIndent(fp, indentLevel);
 | 
						|
 | 
						|
			if (splitDot) {
 | 
						|
				std::vector<String> tokens;
 | 
						|
				boost::algorithm::split(tokens, kv.first, boost::is_any_of("."));
 | 
						|
 | 
						|
				EmitIdentifier(fp, tokens[0], true);
 | 
						|
 | 
						|
				for (std::vector<String>::size_type i = 1; i < tokens.size(); i++) {
 | 
						|
					fp << "[";
 | 
						|
					EmitString(fp, tokens[i]);
 | 
						|
					fp << "]";
 | 
						|
				}
 | 
						|
			} else
 | 
						|
				EmitIdentifier(fp, kv.first, true);
 | 
						|
 | 
						|
			fp << " = ";
 | 
						|
			EmitValue(fp, indentLevel + 1, kv.second);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	fp << "\n";
 | 
						|
	EmitIndent(fp, indentLevel - 1);
 | 
						|
	fp << "}";
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitValue(std::ostream& fp, int indentLevel, const Value& val)
 | 
						|
{
 | 
						|
	if (val.IsObjectType<Array>())
 | 
						|
		EmitArray(fp, indentLevel, val);
 | 
						|
	else if (val.IsObjectType<Dictionary>())
 | 
						|
		EmitScope(fp, indentLevel, val);
 | 
						|
	else if (val.IsObjectType<ConfigIdentifier>())
 | 
						|
		EmitIdentifier(fp, static_cast<ConfigIdentifier::Ptr>(val)->GetName(), false);
 | 
						|
	else if (val.IsString())
 | 
						|
		EmitString(fp, val);
 | 
						|
	else if (val.IsNumber())
 | 
						|
		EmitNumber(fp, val);
 | 
						|
	else if (val.IsBoolean())
 | 
						|
		EmitBoolean(fp, val);
 | 
						|
	else if (val.IsEmpty())
 | 
						|
		EmitEmpty(fp);
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitRaw(std::ostream& fp, const String& val)
 | 
						|
{
 | 
						|
	fp << val;
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitIndent(std::ostream& fp, int indentLevel)
 | 
						|
{
 | 
						|
	for (int i = 0; i < indentLevel; i++)
 | 
						|
		fp << "\t";
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitIdentifier(std::ostream& fp, const String& identifier, bool inAssignment)
 | 
						|
{
 | 
						|
	static std::set<String> keywords;
 | 
						|
	static boost::mutex mutex;
 | 
						|
 | 
						|
	{
 | 
						|
		boost::mutex::scoped_lock lock(mutex);
 | 
						|
		if (keywords.empty()) {
 | 
						|
			const std::vector<String>& vkeywords = GetKeywords();
 | 
						|
			std::copy(vkeywords.begin(), vkeywords.end(), std::inserter(keywords, keywords.begin()));
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (keywords.find(identifier) != keywords.end()) {
 | 
						|
		fp << "@" << identifier;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	boost::regex expr("^[a-zA-Z_][a-zA-Z0-9\\_]*$");
 | 
						|
	boost::smatch what;
 | 
						|
	if (boost::regex_search(identifier.GetData(), what, expr))
 | 
						|
		fp << identifier;
 | 
						|
	else if (inAssignment)
 | 
						|
		EmitString(fp, identifier);
 | 
						|
	else
 | 
						|
		BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid identifier"));
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitConfigItem(std::ostream& fp, const String& type, const String& name, bool isTemplate,
 | 
						|
    bool ignoreOnError, const Array::Ptr& imports, const Dictionary::Ptr& attrs)
 | 
						|
{
 | 
						|
	if (isTemplate)
 | 
						|
		fp << "template ";
 | 
						|
	else
 | 
						|
		fp << "object ";
 | 
						|
 | 
						|
	EmitIdentifier(fp, type, false);
 | 
						|
	fp << " ";
 | 
						|
	EmitString(fp, name);
 | 
						|
 | 
						|
	if (ignoreOnError)
 | 
						|
		fp << " ignore_on_error";
 | 
						|
 | 
						|
	fp << " ";
 | 
						|
	EmitScope(fp, 1, attrs, imports, true);
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitComment(std::ostream& fp, const String& text)
 | 
						|
{
 | 
						|
	fp << "/* " << text << " */\n";
 | 
						|
}
 | 
						|
 | 
						|
void ConfigWriter::EmitFunctionCall(std::ostream& fp, const String& name, const Array::Ptr& arguments)
 | 
						|
{
 | 
						|
	EmitIdentifier(fp, name, false);
 | 
						|
	fp << "(";
 | 
						|
	EmitArrayItems(fp, 0, arguments);
 | 
						|
	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;
 | 
						|
}
 | 
						|
 | 
						|
const std::vector<String>& ConfigWriter::GetKeywords(void)
 | 
						|
{
 | 
						|
	static std::vector<String> keywords;
 | 
						|
	static boost::mutex mutex;
 | 
						|
	boost::mutex::scoped_lock lock(mutex);
 | 
						|
 | 
						|
	if (keywords.empty()) {
 | 
						|
		keywords.push_back("object");
 | 
						|
		keywords.push_back("template");
 | 
						|
		keywords.push_back("include");
 | 
						|
		keywords.push_back("include_recursive");
 | 
						|
		keywords.push_back("include_zones");
 | 
						|
		keywords.push_back("library");
 | 
						|
		keywords.push_back("null");
 | 
						|
		keywords.push_back("true");
 | 
						|
		keywords.push_back("false");
 | 
						|
		keywords.push_back("const");
 | 
						|
		keywords.push_back("var");
 | 
						|
		keywords.push_back("this");
 | 
						|
		keywords.push_back("globals");
 | 
						|
		keywords.push_back("locals");
 | 
						|
		keywords.push_back("use");
 | 
						|
		keywords.push_back("__using");
 | 
						|
		keywords.push_back("ignore_on_error");
 | 
						|
		keywords.push_back("current_filename");
 | 
						|
		keywords.push_back("current_line");
 | 
						|
		keywords.push_back("apply");
 | 
						|
		keywords.push_back("to");
 | 
						|
		keywords.push_back("where");
 | 
						|
		keywords.push_back("import");
 | 
						|
		keywords.push_back("assign");
 | 
						|
		keywords.push_back("ignore");
 | 
						|
		keywords.push_back("function");
 | 
						|
		keywords.push_back("return");
 | 
						|
		keywords.push_back("break");
 | 
						|
		keywords.push_back("continue");
 | 
						|
		keywords.push_back("for");
 | 
						|
		keywords.push_back("if");
 | 
						|
		keywords.push_back("else");
 | 
						|
		keywords.push_back("while");
 | 
						|
		keywords.push_back("throw");
 | 
						|
	}
 | 
						|
 | 
						|
	return keywords;
 | 
						|
}
 | 
						|
 | 
						|
ConfigIdentifier::ConfigIdentifier(const String& identifier)
 | 
						|
    : m_Name(identifier)
 | 
						|
{ }
 | 
						|
 | 
						|
String ConfigIdentifier::GetName(void) const
 | 
						|
{
 | 
						|
	return m_Name;
 | 
						|
}
 |