310 lines
7.5 KiB
C++
Executable File
310 lines
7.5 KiB
C++
Executable File
//
|
|
// Copyright (c) 2006 Alexis Wilke
|
|
//
|
|
// Boost Software License - Version 1.0 - August 17th, 2003
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person or organization
|
|
// obtaining a copy of the software and accompanying documentation covered by
|
|
// this license (the "Software") to use, reproduce, display, distribute,
|
|
// execute, and transmit the Software, and to prepare derivative works of the
|
|
// Software, and to permit third-parties to whom the Software is furnished to
|
|
// do so, all subject to the following:
|
|
//
|
|
// The copyright notices in the Software and this entire statement, including
|
|
// the above license grant, this restriction and the following disclaimer,
|
|
// must be included in all copies of the Software, in whole or in part, and
|
|
// all derivative works of the Software, unless such copies or derivative
|
|
// works are solely in the form of machine-executable object code generated by
|
|
// a source language processor.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// Find documentation on the home website:
|
|
//
|
|
// http://boost-extras.sourceforge.net/
|
|
// http://boost-extras.sourceforge.net/ini_file/ini_file.html
|
|
//
|
|
#include "ini_file.hpp"
|
|
#include <iostream>
|
|
|
|
namespace ini_file
|
|
{
|
|
const param param_map::m_empty("");
|
|
const section section_map::m_empty("");
|
|
|
|
std::istream& operator >> (std::istream& in, section_map& _section_map)
|
|
{
|
|
boost::shared_ptr<section> sec;
|
|
boost::shared_ptr<param> par;
|
|
std::string result, comment, name;
|
|
char c, quote;
|
|
const char *s, *e;
|
|
int line, i;
|
|
|
|
line = 0;
|
|
while(!in.eof() && !!in) {
|
|
++line;
|
|
getline(in, result);
|
|
//std::cout << "Line = <<" << result << ">>" << std::endl;
|
|
if(result.size() == 0) continue;
|
|
switch(c = result[0]) {
|
|
case ';':
|
|
// comment
|
|
if(comment.size() > 0) {
|
|
comment += "\n";
|
|
}
|
|
s = result.c_str() + 1;
|
|
while(isspace(*s)) {
|
|
++s;
|
|
}
|
|
comment += s;
|
|
break;
|
|
|
|
case '[':
|
|
// section
|
|
s = result.c_str() + 1;
|
|
e = result.c_str() + result.size();
|
|
while(s < e && isspace(s[0])) {
|
|
++s;
|
|
}
|
|
while(e > s && isspace(e[-1])) {
|
|
--e;
|
|
}
|
|
if(e[-1] != ']') {
|
|
// name must end with ']' + optional spaces
|
|
throw ini_exceptions::invalid_section_name();
|
|
}
|
|
--e;
|
|
while(e > s && isspace(e[-1])) {
|
|
--e;
|
|
}
|
|
if(e == s) {
|
|
// name mustn't be empty
|
|
throw ini_exceptions::invalid_section_name();
|
|
}
|
|
// valid section name, create section
|
|
sec.reset(new section(result.substr(s - result.c_str(), e - s)));
|
|
sec->set_comment(comment);
|
|
comment.clear();
|
|
_section_map.insert(sec);
|
|
break;
|
|
|
|
default:
|
|
// make sure the variable starts with a letter or _
|
|
// (there is no spec. for that, but it feels sensical;
|
|
// should that apply to section names?)
|
|
if((c >= 'a' || c <= 'z')
|
|
&& (c >= 'A' || c <= 'Z')
|
|
&& c == '_') {
|
|
if(isspace(c)) {
|
|
// note the loop does not test 'result[0]'
|
|
// since we just checked it (it's in 'c')
|
|
i = result.size() - 1;
|
|
while(i > 0 && isspace(result[i])) {
|
|
--i;
|
|
}
|
|
if(i == 0) {
|
|
// empty line with just spaces
|
|
continue;
|
|
}
|
|
}
|
|
// unsure what this is
|
|
throw ini_exceptions::invalid_param_name();
|
|
}
|
|
if(!sec) {
|
|
// variables before a section is not valid
|
|
throw ini_exceptions::param_without_section();
|
|
}
|
|
s = result.c_str();
|
|
e = s + 1;
|
|
while(!isspace(*e) && *e != '=') {
|
|
++e;
|
|
}
|
|
// This case cannot happen since we start with e = s + 1.
|
|
//if(e == s) {
|
|
// // empty variable names are not acceptable
|
|
// throw ini_exceptions::invalid_param_name();
|
|
//}
|
|
par.reset(new param(result.substr(0, e - s)));
|
|
while(isspace(*e)) {
|
|
++e;
|
|
}
|
|
if(*e != '=') {
|
|
// variable names must be followed by '='
|
|
throw ini_exceptions::invalid_parameter();
|
|
}
|
|
++e;
|
|
while(isspace(*e)) {
|
|
++e;
|
|
}
|
|
quote = *e;
|
|
if(quote == '"' || quote == '\'') {
|
|
// quoted value
|
|
++e;
|
|
s = e;
|
|
while(*e != quote && *e != '\0') {
|
|
// allow escaping, but keep the backslashes
|
|
// TODO: which is wrong!
|
|
if(*e == '\\' && e[1] == quote) {
|
|
++e;
|
|
}
|
|
++e;
|
|
}
|
|
if(*e != quote) {
|
|
throw ini_exceptions::invalid_quotation();
|
|
}
|
|
// warning: we ignore what follows
|
|
}
|
|
else {
|
|
s = e;
|
|
e = result.c_str() + result.size();
|
|
while(e > s && isspace(e[-1])) {
|
|
--e;
|
|
}
|
|
}
|
|
par->set_value(result.substr(s - result.c_str(), e - s));
|
|
par->set_comment(comment);
|
|
comment.clear();
|
|
sec->insert(par);
|
|
break;
|
|
|
|
}
|
|
}
|
|
return in;
|
|
}
|
|
|
|
namespace details
|
|
{
|
|
std::ostream& operator << (std::ostream& out, const name& _name)
|
|
{
|
|
return out << _name.get_name();
|
|
}
|
|
std::ostream& operator << (std::ostream& out, const comment& _comment)
|
|
{
|
|
// TODO:
|
|
// we would need a filter for comments, but I'm not too sure we can
|
|
// do that (i.e. add a filter, write the comment, remove the filter)
|
|
bool new_line = true;
|
|
std::string::const_iterator i;
|
|
std::string::const_iterator end = _comment.get_comment().end();
|
|
for(i = _comment.get_comment().begin(); i != end; ++i) {
|
|
if(*i == '\r' || *i == '\n') {
|
|
out << *i;
|
|
new_line = true;
|
|
}
|
|
else {
|
|
if(new_line) {
|
|
out << "; ";
|
|
}
|
|
new_line = false;
|
|
out << *i;
|
|
}
|
|
}
|
|
if(!new_line) {
|
|
out << std::endl;
|
|
}
|
|
return out;
|
|
}
|
|
}
|
|
|
|
std::ostream& operator << (std::ostream& out, const param& _param)
|
|
{
|
|
if(_param.get_name().size() == 0) {
|
|
if(_param.get_value().size() == 0) {
|
|
return out;
|
|
}
|
|
throw ini_exceptions::param_name_missing();
|
|
}
|
|
|
|
// output <comment> and then '<name>='
|
|
out << static_cast<const details::comment&>(_param)
|
|
<< static_cast<const details::name&>(_param)
|
|
<< '=';
|
|
|
|
// output <value>, check whether we need quotation
|
|
const std::string& value = _param.get_value();
|
|
const char *s = value.c_str();
|
|
const char *e = s + value.size();
|
|
if(*s == '"' || *s == '\'' || isspace(*s) || (e > s && isspace(e[-1]))) {
|
|
// need quotation to not lose any data
|
|
char quote = '\0';
|
|
while(*s != '\0') {
|
|
if(*s == '\'') {
|
|
quote = '"';
|
|
break;
|
|
}
|
|
if(*s == '"') {
|
|
quote = '\'';
|
|
break;
|
|
}
|
|
++s;
|
|
}
|
|
s = value.c_str();
|
|
out << quote;
|
|
while(*s != '\0') {
|
|
if(*s == quote) {
|
|
out << "\\";
|
|
}
|
|
out << *s;
|
|
++s;
|
|
}
|
|
out << quote;
|
|
}
|
|
else {
|
|
// no quotation necessary
|
|
out << value;
|
|
}
|
|
|
|
out << std::endl;
|
|
}
|
|
|
|
|
|
std::ostream& operator << (std::ostream& out, const param_map& _param_map)
|
|
{
|
|
param_map::const_iterator i = _param_map.begin();
|
|
param_map::const_iterator end = _param_map.end();
|
|
while(i != end) {
|
|
out << *(*i).second;
|
|
++i;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
std::ostream& operator << (std::ostream& out, const section& _section)
|
|
{
|
|
if(_section.get_name().size() == 0) {
|
|
if(_section.size() == 0) {
|
|
return out;
|
|
}
|
|
throw ini_exceptions::section_name_missing();
|
|
}
|
|
|
|
return out
|
|
<< static_cast<const details::comment&>(_section)
|
|
<< '[' << static_cast<const details::name&>(_section) << ']' << std::endl
|
|
<< static_cast<const param_map&>(_section);
|
|
}
|
|
std::ostream& operator << (std::ostream& out, const section_map& _section_map)
|
|
{
|
|
section_map::const_iterator i = _section_map.begin();
|
|
section_map::const_iterator end = _section_map.end();
|
|
while(i != end) {
|
|
out << *(*i).second;
|
|
out << std::endl;
|
|
++i;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace ini_file
|
|
// vim: ts=4
|