Skip to content

Instantly share code, notes, and snippets.

@FurryHead
Created June 27, 2011 16:21
Show Gist options
  • Save FurryHead/1049203 to your computer and use it in GitHub Desktop.
Save FurryHead/1049203 to your computer and use it in GitHub Desktop.
Code file for ConfigParser
/*
* configparser.cxx
*
* Copyright 2011 FurryHead <[email protected]>
*
* ConfigParser 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.
*
* ConfigParser 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 ConfigParser; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <map>
#include <vector>
#include <fstream>
#include <sstream>
#include <string>
#include "configparser.hxx"
using namespace std;
Section::Section(string section_name) : s_name(section_name) {
}
string Section::name() {
return s_name;
}
const map<string,string>& Section::items() {
return values;
}
vector<string> Section::options() {
vector<string> opts;
for (map<string,string>::iterator i = values.begin(); i != values.end(); ++i)
opts.push_back(i->first);
return opts;
}
void Section::remove_option(string option) {
if (!has_option(option))
return;
for (map<string,string>::iterator i = values.begin(); i != values.end(); ++i)
if (i->first == option) {
values.erase(i);
break;
}
}
void Section::set(string option, string value) {
values[option] = value;
}
string Section::get(string option) {
for (map<string, string>::iterator i = values.begin(); i != values.end(); ++i)
if (i->first == option)
return i->second;
throw SyntaxError("No such option "+option);
}
bool Section::has_option(string option) {
for (map<string, string>::iterator i = values.begin(); i != values.end(); ++i)
if (i->first == option)
return true;
return false;
}
ConfigParser::ConfigParser(string filename) : current_section("DEFAULT") {
read(filename);
}
ConfigParser::ConfigParser() : current_section("DEFAULT") {
}
string ConfigParser::get_vars(string value) {
stringstream err; err << "Line " << current_line_no;
string lineNum = err.str();
unsigned int cutAt = value.find_first_not_of(" "), cutAtEnd = value.find_last_not_of(" ");
if (cutAt == string::npos)
return "";
string result = "";
value = value.substr(cutAt, cutAtEnd-cutAt+1);
bool in_var = false;
string varName = "";
// PreviousChar, CurrentChar, NextChar
char pc, cc, nc;
for (int i = 0; i < value.size(); i++) {
pc = value[(i > 0 ? i-1 : i)], cc = value[i], nc = value[(i < value.size() ? i+1 : i)];
if (cc == '{') {
if (in_var) {
if (pc != '\\') {
throw SyntaxError(lineNum + " unexpected {");
}
} else {
if (pc != '\\') {
in_var = true;
}
}
} else if (cc == '}') {
if (!in_var) {
if (pc != '\\') {
throw SyntaxError(lineNum + " unexpected }");
}
} else {
if (pc != '\\') {
in_var = false;
unsigned int start = varName.find_first_not_of(" "), end = varName.find_last_not_of(" ");
if (start == string::npos) {
varName = "";
continue;
}
varName = varName.substr(start, end-start+1);
if (current_section.has_option(varName))
result += current_section.get(varName);
varName = "";
}
}
} else if (cc == '\\' && nc != '\'' && nc != '\"') {
result += nc;
i++;
} else {
if (in_var) {
varName += cc;
} else {
result += cc;
}
}
}
if (in_var)
throw SyntaxError(lineNum + " missing end }");
return result;
}
string ConfigParser::parse_line(string line) {
stringstream err; err << "Line " << current_line_no;
string lineNum = err.str();
unsigned int first = line.find_first_not_of(" "), last = line.find_last_not_of(" ");
if (first == string::npos)
return "";
line = line.substr(first, last-first+1);
if (line.find("\\") == line.size()-1)
return line;
bool in_section = false;
bool reading_key = true;
bool reading_value = false;
bool any_more_chars_is_bad = false;
string value = "", key = "", section = "";
char pc, cc, nc;
for (int i = 0; i < line.size(); i++) {
pc = line[(i > 0 ? i-1 : i)], cc = line[i], nc = line[(i < line.size() ? i+1 : i)];
if (any_more_chars_is_bad && cc != ' ' && cc != ';' && cc != '#')
throw SyntaxError(lineNum + " expected end of line");
if (cc == '[') {
if (i == 0) {
in_section = true;
reading_key = false;
} else {
throw SyntaxError(lineNum + " unexpected [");
}
} else if (cc == ']') {
if (pc != '\\') {
in_section = false;
unsigned int start = section.find_first_not_of(" "), end = section.find_last_not_of(" ");
if (start == string::npos) return "";
section = section.substr(start, end-start+1);
if (section.find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-") != string::npos)
throw SyntaxError(lineNum + " invalid section name (only alphanumberic chars including - and _ are allowed)");
_sections.push_back(current_section);
current_section = Section(section);
any_more_chars_is_bad = true;
}
} else if (cc == '=' || cc == ':') {
if (pc != '\\') {
reading_value = true;
reading_key = false;
}
} else if (cc == ';' || cc == '#') {
if (pc != '\\') {
if (key.size() == 0)
return "";
else
break;
}
} else {
bool test = (cc == '\\' && nc != '{' && nc != '}' && nc != '\'' && nc != '\"');
char c_add = (test ? nc : cc);
int i_add = (test ? 1 : 0);
if (reading_key) {
key += c_add;
i += i_add;
} else if (reading_value) {
value += c_add;
i += i_add;
} else if (in_section) {
section += c_add;
i += i_add;
}
}
}
if (in_section)
throw SyntaxError(lineNum + " missing end ]");
else if (reading_key)
throw SyntaxError(lineNum + " expected '=' or ':'");
else if (reading_value) {
// everything went OK, and we're reading a key/value not a section
unsigned int start = key.find_first_not_of(" ");
unsigned int end = key.find_last_not_of(" ");
if (start == string::npos)
key = "";
else
key = key.substr(start, end-start+1);
start = value.find_first_not_of(" ");
end = value.find_last_not_of(" ");
if (start == string::npos)
value = "";
else
value = value.substr(start, end-start+1);
value = get_vars(value);
if (key.find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-") != string::npos)
throw SyntaxError(lineNum + " invalid key name (only alphanumberic chars including - and _ are allowed)");
unsigned int oq = value.find("'");
unsigned int tq = value.find("\"");
if (oq != string::npos || tq != string::npos) {
char qt = (oq == string::npos ? value[tq] : value[oq]);
char pc, cc, nc;
string result = "";
bool in_str = false;
bool chars_found = false;
bool any_more_chars_is_bad = false;
for (int i = 0; i < value.size(); i++) {
pc = value[(i > 0 ? i-1 : i)], cc = value[i], nc = value[(i < value.size() ? i+1 : i)];
if (any_more_chars_is_bad)
throw SyntaxError(lineNum + " unexpected characters after end-of-string");
if (chars_found && in_str)
throw SyntaxError(lineNum + " unexpected characters before string");
if (cc == qt) {
if (pc != '\\') {
if (in_str) {
in_str = false;
any_more_chars_is_bad = true;
} else {
in_str = true;
if (i == value.size()-1) {
if (chars_found) {
throw SyntaxError(lineNum + " unexpected characters before string");
}
}
}
}
} else {
if (cc == '\\') {
result += nc;
i++;
} else {
result += cc;
}
if (!in_str)
chars_found = true;
}
}
if (in_str)
throw SyntaxError(lineNum + " missing end-of-string");
value = result;
}
current_section.set(key, value);
}
return "";
}
void ConfigParser::read(string filename) {
ifstream ini(filename.c_str(), ios::in);
if (!ini.is_open())
throw SyntaxError("Unable to open "+filename);
read(ini);
ini.close();
}
void ConfigParser::read(istream& file) {
string line = "", data = "";
current_line_no = 0;
current_section = Section("DEFAULT");
_sections.clear();
while (file.good()) {
current_line_no += 1;
getline(file, data);
line += data;
line = parse_line(line);
}
_sections.push_back(current_section);
}
void ConfigParser::write(string filename) {
ofstream ini(filename.c_str(), ios::out | ios::trunc);
if (!ini.is_open())
throw SyntaxError("Unable to open "+filename);
write(ini);
ini.close();
}
void ConfigParser::write(ostream& file) {
//todo: save code.
}
string ConfigParser::get(string section, string key) {
if (!has_section(section))
throw SyntaxError("No such section "+section);
return get_section(section)->get(key);
}
bool ConfigParser::valid_key(string key) {
if (key.find_last_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-") == string::npos)
return true;
else
return false;
}
string ConfigParser::set(string section, string key, string value) {
if (!has_section(section))
throw SyntaxError("No such section "+section);
if (key.find(" ") != string::npos)
throw SyntaxError("Options may not contain spaces.");
else if (key.size() < 1)
throw SyntaxError("Options may not be zero in length.");
else if (!valid_key(key))
throw SyntaxError("Options must contain only alphanumerical characters (_ and - are included).");
else {
get_section(section)->set(key,value);
}
}
Section* ConfigParser::get_section(string section) {
if (!has_section(section))
throw SyntaxError("No such section "+section);
else
for (vector<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i)
if (section == i->name())
return &(*i);
}
const map<string, string>& ConfigParser::items(string section) {
return get_section(section)->items();
}
bool ConfigParser::has_section(string section) {
for (vector<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i)
if (section == i->name())
return true;
return false;
}
bool ConfigParser::has_option(string section, string option) {
if (!has_section(section))
return false;
for (vector<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i)
if (section == i->name())
if (i->has_option(option))
return true;
return false;
}
void ConfigParser::add_section(string section) {
if (has_section(section))
throw SyntaxError("Section already exists.");
_sections.push_back(Section(section));
}
void ConfigParser::remove_section(string section) {
if (!has_section(section))
throw SyntaxError("No such section "+section);
for (vector<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i)
if (section == i->name()) {
_sections.erase(i);
break;
}
}
void ConfigParser::remove_option(string section, string option) {
if (!has_section(section))
throw SyntaxError("No such section "+section);
get_section(section)->remove_option(option);
}
vector<string> ConfigParser::sections() {
vector<string> sects;
for (vector<Section>::iterator i = _sections.begin(); i != _sections.end(); ++i)
if (i->name() != "DEFAULT")
sects.push_back(i->name());
return sects;
}
vector<string> ConfigParser::options(string section) {
if (!has_section(section))
throw SyntaxError("No such section "+section);
return get_section(section)->options();
}
SyntaxError::SyntaxError(string error) {
estr = error;
}
string SyntaxError::what() {
return estr;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment