Mercurial > code
diff C++/Parser.cpp @ 179:3648e9e6935b
Add parser
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 22 Sep 2013 19:57:59 +0200 |
parents | |
children | 2bcdee0fe8d4 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Parser.cpp Sun Sep 22 19:57:59 2013 +0200 @@ -0,0 +1,396 @@ +/* + * Parser.h -- config file parser + * + * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <iostream> +#include <fstream> + +#include "Parser.h" + +/* -------------------------------------------------------- + * Option public members + * -------------------------------------------------------- */ + +bool operator==(const Option &o1, const Option &o2) +{ + return o1.m_key == o2.m_key && + o1.m_value == o2.m_value; +} + +/* -------------------------------------------------------- + * Section public members + * -------------------------------------------------------- */ + +Section::Section() + :m_allowed(true) +{ +} + +Section::~Section() +{ +} + +Section::Section(const Section &s) +{ + m_name = s.m_name; + m_options = s.m_options; + m_allowed = s.m_allowed; +} + +const std::string Section::findOption(const std::string &name) const +{ + std::string ret; + + for (const Option &o : m_options) + if (o.m_key == name) { + ret = o.m_value; + break; + } + + return ret; +} + +template <> bool Section::getOption(const std::string &name) const +{ + bool result = false; + + if (hasOption(name)) { + std::string value = findOption(name); + + if (value == "yes" || value == "true"|| value == "1") + result = true; + else if (value == "no" || value == "false" || value == "0") + result = false; + } + + return result; +} + +template <> int Section::getOption(const std::string &name) const +{ + int result = -1; + + if (hasOption(name)) { + result = atoi(findOption(name).c_str()); + } + + return result; +} + +template <> std::string Section::getOption(const std::string &name) const +{ + std::string result; + + if (hasOption(name)) + result = findOption(name); + + return result; +} + +const std::string & Section::getName() const +{ + return m_name; +} + +const std::vector<Option> & Section::getOptions() const +{ + return m_options; +} + +bool Section::hasOption(const std::string &name) const +{ + for (const Option &o : m_options) + if (o.m_key == name) + return true; + + return false; +} + +bool operator==(const Section &s1, const Section &s2) +{ + if (s1.m_name != s2.m_name) + return false; + + return s1.m_options == s2.m_options; +} + +/* -------------------------------------------------------- + * Parser private members + * -------------------------------------------------------- */ + +void Parser::addSection(const std::string &name) +{ + Section section; + + section.m_name = name; + section.m_allowed = true; + + m_sections.push_back(section); +} + +void Parser::addOption(const std::string &key, const std::string &value) +{ + Option option; + Section ¤t = m_sections.back(); + + option.m_key = key; + option.m_value = value; + + current.m_options.push_back(option); +} + +void Parser::readSection(int lineno, const std::string &line) +{ + size_t end; + + if ((end = line.find_first_of(']')) != std::string::npos) { + if (end > 1) { + std::string name = line.substr(1, end - 1); + + /* + * Check if we can add a section, if redefinition is + * disabled, we must disable the previous section so the + * further read options should not be enabled until + * a correct section is found again. + */ + if (hasSection(name) && (m_tuning & DisableRedefinition)) { + if (!(m_tuning & DisableVerbosity)) + log(lineno, name, "redefinition not allowed"); + m_sections.back().m_allowed = false; + } else { + addSection(name); + } + } else if (!(m_tuning & DisableVerbosity)) { + /* + * Do not add options at this step because it will + * corrupt the previous one. + */ + m_sections.back().m_allowed = false; + log(lineno, "", "empty section name"); + } + } +} + +void Parser::readOption(int lineno, const std::string &line) +{ + size_t epos; + std::string key, value; + Section ¤t = m_sections.back(); + + // Error on last section? + if (!current.m_allowed) { + /* + * If it is the root section, this has been probably set by + * DisableRootSection flag, otherwise an error has occured + * so no need to log. + */ + if (current.m_name == "" && !(m_tuning == DisableVerbosity)) + log(lineno, "", "option not allowed in that scope"); + + return; + } + + if ((epos = line.find_first_of('=')) == std::string::npos) { + if (!(m_tuning & DisableVerbosity)) + log(lineno, current.m_name, "missing `=' keyword"); + return; + } + + if (epos > 0) { + size_t i, begin, last; + char c; + + key = line.substr(0, epos - 1); + value = line.substr(epos + 1); + + // clean option key + for (i = 0; !isspace(key[i]) && i < key.length(); ++i) + continue; + key = key.substr(0, i); + + // clean option value + for (begin = 0; isspace(value[begin]) && begin < value.length(); ++begin) + continue; + value = value.substr(begin); + + c = value[0]; + begin = 0; + if (c == '\'' || c == '"') { + for (last = begin = 1; value[last] != c && last < value.length(); ++last) + continue; + if (value[last] != c && !(m_tuning & DisableVerbosity)) + if (!(m_tuning & DisableVerbosity)) + log(lineno, current.m_name, "undisclosed std::string"); + } else { + for (last = begin; !isspace(value[last]) && last < value.length(); ++last) + continue; + } + + if (last - begin > 0) + value = value.substr(begin, last - begin); + else + value.clear(); + + // Add the option if the key is not empty + if (key.length() > 0) + addOption(key, value); + } +} + +void Parser::readLine(int lineno, const std::string &line) +{ + size_t i; + std::string buffer; + + // Skip default spaces + for (i = 0; isspace(line[i]) && i < line.length(); ++i) + continue; + + buffer = line.substr(i); + if (buffer.length() > 0) { + if (buffer[0] != m_commentChar) { + if (buffer[0] == '[') + readSection(lineno, buffer); + else + readOption(lineno, buffer); + } + } +} + +/* -------------------------------------------------------- + * Parser public methods + * -------------------------------------------------------- */ + +const char Parser::DEFAULT_COMMENT_CHAR = '#'; + +Parser::Parser(const std::string &path, int tuning, char commentToken) + :m_path(path), m_tuning(tuning), m_commentChar(commentToken) +{ + Section root; + + // Add a default root section + root.m_name = ""; + root.m_allowed = (tuning & DisableRootSection) ? false : true; + + m_sections.push_back(root); +} + +Parser::Parser() +{ +} + +Parser::~Parser() +{ +} + +bool Parser::open() +{ + std::ifstream file; + std::string line; + int lineno = 1; + + file.open(m_path.c_str()); + if (!file.is_open()) { + m_error = "could not open file " + m_path; // XXX: add a real error + return false; + } + + // Avoid use of C getline + while (std::getline(file, line)) { + readLine(lineno++, line); + } + + file.close(); + + return true; +} + +const std::string & Parser::getError() const +{ + return m_error; +} + +const std::vector<Section> & Parser::getSections() const +{ + return m_sections; +} + +std::vector<Section> Parser::findSections(const std::string &name) const +{ + std::vector<Section> list; + + for (const Section &s : m_sections) { + if (s.m_name == name) { + Section copy = s; + list.push_back(copy); + } + } + + return list; +} + +bool Parser::hasSection(const std::string &name) const +{ + for (const Section &s : m_sections) + if (s.m_name == name) + return true; + + return false; +} + +Section Parser::getSection(const std::string &name) const +{ + Section ret; + + for (const Section &s : m_sections) + if (s.m_name == name) + ret = s; + + return ret; +} + +Section Parser::requireSection(const std::string &name) const +{ + if (!hasSection(name)) + throw NotFoundException(name); + + return getSection(name); +} + +void Parser::log(int number, const std::string &, const std::string &message) +{ + std::cout << "line " << number << ": " << message << std::endl; +} + +void Parser::dump() +{ + for (auto s : m_sections) { + dumpSection(s); + for (auto o : s.m_options) + dumpOption(o); + } +} + +void Parser::dumpSection(const Section §ion) +{ + std::cout << "Section " << section.m_name << std::endl; +} + +void Parser::dumpOption(const Option &option) +{ + std::cout << " Option " << option.m_key << " = " << option.m_value << std::endl; +}