Files
znc/src/Config.cpp
J-P Nurmi 70c0ffb10b Use nullptr (#816)
Changes applied by 'clang-modernize -use-nullptr [...]'
2015-02-25 09:20:20 +01:00

202 lines
5.3 KiB
C++

/*
* Copyright (C) 2004-2015 ZNC, see the NOTICE file for details.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <znc/Config.h>
#include <znc/FileUtils.h>
#include <stack>
#include <sstream>
struct ConfigStackEntry {
CString sTag;
CString sName;
CConfig Config;
ConfigStackEntry(const CString& Tag, const CString Name) {
sTag = Tag;
sName = Name;
}
};
CConfigEntry::CConfigEntry()
: m_pSubConfig(nullptr) {
}
CConfigEntry::CConfigEntry(const CConfig& Config)
: m_pSubConfig(new CConfig(Config)) {
}
CConfigEntry::CConfigEntry(const CConfigEntry& other)
: m_pSubConfig(nullptr) {
if (other.m_pSubConfig)
m_pSubConfig = new CConfig(*other.m_pSubConfig);
}
CConfigEntry::~CConfigEntry()
{
delete m_pSubConfig;
}
CConfigEntry& CConfigEntry::operator=(const CConfigEntry& other) {
delete m_pSubConfig;
if (other.m_pSubConfig)
m_pSubConfig = new CConfig(*other.m_pSubConfig);
else
m_pSubConfig = nullptr;
return *this;
}
bool CConfig::Parse(CFile& file, CString& sErrorMsg)
{
CString sLine;
unsigned int uLineNum = 0;
CConfig *pActiveConfig = this;
std::stack<ConfigStackEntry> ConfigStack;
bool bCommented = false; // support for /**/ style comments
if (!file.Seek(0)) {
sErrorMsg = "Could not seek to the beginning of the config.";
return false;
}
while (file.ReadLine(sLine)) {
uLineNum++;
#define ERROR(arg) do { \
std::stringstream stream; \
stream << "Error on line " << uLineNum << ": " << arg; \
sErrorMsg = stream.str(); \
m_SubConfigs.clear(); \
m_ConfigEntries.clear(); \
return false; \
} while (0)
// Remove all leading spaces and trailing line endings
sLine.TrimLeft();
sLine.TrimRight("\r\n");
if (bCommented || sLine.Left(2) == "/*") {
/* Does this comment end on the same line again? */
bCommented = (sLine.Right(2) != "*/");
continue;
}
if ((sLine.empty()) || (sLine[0] == '#') || (sLine.Left(2) == "//")) {
continue;
}
if ((sLine.Left(1) == "<") && (sLine.Right(1) == ">")) {
sLine.LeftChomp();
sLine.RightChomp();
sLine.Trim();
CString sTag = sLine.Token(0);
CString sValue = sLine.Token(1, true);
sTag.Trim();
sValue.Trim();
if (sTag.Left(1) == "/") {
sTag = sTag.substr(1);
if (!sValue.empty())
ERROR("Malformated closing tag. Expected \"</" << sTag << ">\".");
if (ConfigStack.empty())
ERROR("Closing tag \"" << sTag << "\" which is not open.");
const struct ConfigStackEntry& entry = ConfigStack.top();
CConfig myConfig(entry.Config);
CString sName(entry.sName);
if (!sTag.Equals(entry.sTag))
ERROR("Closing tag \"" << sTag << "\" which is not open.");
// This breaks entry
ConfigStack.pop();
if (ConfigStack.empty())
pActiveConfig = this;
else
pActiveConfig = &ConfigStack.top().Config;
SubConfig &conf = pActiveConfig->m_SubConfigs[sTag.AsLower()];
SubConfig::const_iterator it = conf.find(sName);
if (it != conf.end())
ERROR("Duplicate entry for tag \"" << sTag << "\" name \"" << sName << "\".");
conf[sName] = CConfigEntry(myConfig);
} else {
if (sValue.empty())
ERROR("Empty block name at begin of block.");
ConfigStack.push(ConfigStackEntry(sTag.AsLower(), sValue));
pActiveConfig = &ConfigStack.top().Config;
}
continue;
}
// If we have a regular line, figure out where it goes
CString sName = sLine.Token(0, false, "=");
CString sValue = sLine.Token(1, true, "=");
// Only remove the first space, people might want
// leading spaces (e.g. in the MOTD).
if (sValue.Left(1) == " ")
sValue.LeftChomp();
// We don't have any names with spaces, trim all
// leading/trailing spaces.
sName.Trim();
if (sName.empty() || sValue.empty())
ERROR("Malformed line");
CString sNameLower = sName.AsLower();
pActiveConfig->m_ConfigEntries[sNameLower].push_back(sValue);
}
if (bCommented)
ERROR("Comment not closed at end of file.");
if (!ConfigStack.empty()) {
const CString& sTag = ConfigStack.top().sTag;
ERROR("Not all tags are closed at the end of the file. Inner-most open tag is \"" << sTag << "\".");
}
return true;
}
void CConfig::Write(CFile& File, unsigned int iIndentation) {
CString sIndentation = CString(iIndentation, '\t');
for (EntryMapIterator it = m_ConfigEntries.begin(); it != m_ConfigEntries.end(); ++it) {
for (VCString::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
File.Write(sIndentation + it->first + " = " + *it2 + "\n");
}
}
for (SubConfigMapIterator it = m_SubConfigs.begin(); it != m_SubConfigs.end(); ++it) {
for (SubConfig::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
File.Write("\n");
File.Write(sIndentation + "<" + it->first + " " + it2->first + ">\n");
it2->second.m_pSubConfig->Write(File, iIndentation + 1);
File.Write(sIndentation + "</" + it->first + ">\n");
}
}
}