Created
June 27, 2011 16:21
-
-
Save FurryHead/1049203 to your computer and use it in GitHub Desktop.
Code file for ConfigParser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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