/* * vim: softtabstop=4 shiftwidth=4 cindent foldmethod=marker expandtab * * $LastChangedDate$ * $Revision$ * $LastChangedBy$ * $URL$ * * Copyright 2010-2011 Bob Shaffer II * * This file is part of Mangler. * * Mangler 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 3 of the License, or * (at your option) any later version. * * Mangler 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 Mangler. If not, see . */ #include "inilib.h" #include #include #include #include using namespace std; bool iniCaselessCmp::operator()(const string &left, const string &right) const { string::const_iterator p = left.begin(); string::const_iterator q = right.begin(); while (p != left.end() && q != right.end() && tolower(*p) == tolower(*q)) { ++p; ++q; } if (p == left.end()) { return q != right.end(); } if (q == right.end()) { return false; } return tolower(*p) < tolower(*q); } iniVariant::iniVariant() : mValue("") {} iniVariant::iniVariant(const string &s) : mValue(s) {} iniVariant::iniVariant(const char *s) : mValue(s) {} iniVariant::iniVariant(int n) { ostringstream temp; temp << n; mValue = temp.str(); } iniVariant::iniVariant(unsigned n) { ostringstream temp; temp << n; mValue = temp.str(); } iniVariant::iniVariant(long n) { ostringstream temp; temp << n; mValue = temp.str(); } iniVariant::iniVariant(unsigned long n) { ostringstream temp; temp << n; mValue = temp.str(); } iniVariant::iniVariant(long long n) { ostringstream temp; temp << n; mValue = temp.str(); } iniVariant::iniVariant(double d) { ostringstream temp; temp << d; mValue = temp.str(); } iniVariant::iniVariant(bool b) { mValue = b ? "True" : "False"; } #if ADD_GLIB_SUPPORT iniVariant::iniVariant(const Glib::ustring &s) : mValue( Glib::locale_from_utf8(s) ) {} Glib::ustring iniVariant::toUString() const { return Glib::locale_to_utf8(mValue); } #endif int iniVariant::toInt() const { return atoi(mValue.c_str()); } unsigned iniVariant::toUInt() const { return (unsigned)atol(mValue.c_str()); } long iniVariant::toLong() const { return atol(mValue.c_str()); } unsigned long iniVariant::toULong() const { return strtoul(mValue.c_str(), NULL, 10); } long long iniVariant::toLLong() const { return atoll(mValue.c_str()); } double iniVariant::toDouble() const { return strtod(mValue.c_str(), NULL); } bool iniVariant::toBool() const { string temp = toLower(); return (temp == "yes" || temp == "true" || temp == "1"); } string::size_type iniVariant::length() const { return mValue.length(); } iniVariant::operator string &() { return mValue; } iniVariant::operator const string &() { return mValue; } string iniVariant::toString() const { return mValue; } string iniVariant::toUpper() const { string ret; int len = mValue.length(); for (int i = 0; i < len; ++i) { char c = toupper(mValue[i]); ret += c; } return ret; } string iniVariant::toLower() const { string ret; int len = mValue.length(); for (int i = 0; i < len; ++i) { char c = tolower(mValue[i]); ret += c; } return ret; } const char *iniVariant::toCString() const { return mValue.c_str(); } iniVariant iniVariant::mNULLvariant; iniValue::iniValue(const iniVariant &v) { append(v); } bool iniVariant::operator==(const iniVariant &v) const { return (mValue == v.mValue); } iniValue::operator iniVariant &() { // make the vector size be 1, and return a reference to // that element if (size() == 0) { insert(end(), iniVariant()); } if (size() > 1) { erase(begin(), end() - 2); } return at(0); } iniValue::operator const iniVariant &() { if (size() == 0) { return iniVariant::null(); } return at(0); } iniVariant iniValue::value() const { if (size() == 0) { return iniVariant(); } return at(0); } iniValue::size_type iniValue::count() const { return size(); } void iniValue::append(const iniVariant &v) { push_back(v); } iniValue &iniValue::operator=(const iniVariant &v) { ((iniVariant&)(*this)) = v; return (*this); } iniValue &iniValue::operator+=(const iniVariant &v) { push_back(v); return (*this); } bool iniValue::operator==(const iniVariant &v) const { return (size() == 1 && at(0) == v); } string iniValue::toString() const { return value().toString(); } string iniValue::toUpper() const { return value().toUpper(); } string iniValue::toLower() const { return value().toLower(); } const char *iniValue::toCString() const { return value().toCString(); } Glib::ustring iniValue::toUString() const { return value().toUString(); } int iniValue::toInt() const { return value().toInt(); } unsigned iniValue::toUInt() const { return value().toUInt(); } long iniValue::toLong() const { return value().toLong(); } unsigned long iniValue::toULong() const { return value().toULong(); } long long iniValue::toLLong() const { return value().toLLong(); } double iniValue::toDouble() const { return value().toDouble(); } bool iniValue::toBool() const { return value().toBool(); } string::size_type iniValue::length() const { return value().length(); } bool iniSection::contains(const string &s) const { return (find(s) != end()); } iniValue::size_type iniSection::count(const string &s) const { if (contains(s)) { return at(s).size(); } else return 0; } ostream &iniSection::save(ostream &out, bool quotes) const { map::const_iterator iter; for (iter = begin(); iter != end(); iter++) { int vcnt = iter->second.size(); for (int i = 0; i < vcnt; ++i) { saveLine(out, iter->first, iter->second.at(i).toString(), quotes); } } return out; } ostream &iniSection::saveLine(ostream &out, const string &keyName, const string &value, bool quotes) { // this is lame, and i've never seen it used outside of mysql's config //if (value.empty()) { // out << quoteString(keyName) << endl; //} else { out << ((quotes) ? quoteString(keyName) : keyName) << " = " << ((quotes) ? quoteString(value) : value) << endl; //} return out; } string iniSection::quoteString(const string &s) { string ret; int len = s.length(); bool needs_quotes( false ); for (int i = 0; i < len; ++i) { char c( s[i] ); if (c == ' ' || c == '\t' || c == '=' || c == '#' || c == ';') { needs_quotes = true; } if (c == '\"' || c == '\\') { needs_quotes = true; ret.append("\\"); } ret += c; } if (needs_quotes) { ret.insert(0, "\""); ret.append("\""); } return ret; } void iniSection::trimString(string &s) { if (s.empty()) { return; } while (s.length() && (s[0] == ' ' || s[0] == '\t')) { s.erase(0, 1); } if (s.empty()) { return; } for (int i = s.length() - 1; i >= 0; --i) { if (s[i] == '\n') { continue; } if (s[i] != ' ' && s[i] != '\t') { break; } s.erase(i, 1); } } void iniSection::unquoteString(string &s) { if (s.empty()) { return; } for (int i = 0, len = s.length(); i < len; ++i) { if (s[i] != '\"') { continue; } s.erase(i, 1); --len; while (i < len) { if (s[i] == '\\') { s.erase(i, 1); --len; } if (++i < len && s[i] == '\"') { s.erase(i, 1); --len; } } } } vector iniSection::parseLine(string s, bool quotes) { vector ret; trimString(s); if (s.empty() || s[0] == '=') { return ret; } for (int i = 0, len = s.length(); i < len; ++i) { if (s[i] != '=') { continue; } string temp = s.substr(0, i); trimString(temp); if (quotes) { unquoteString(temp); } ret.push_back(temp); temp = s.substr(i + 1); trimString(temp); if (quotes) { unquoteString(temp); } ret.push_back(temp); break; } return ret; } iniFile::iniFile() { pthread_mutex_init(&mymutex, NULL); } iniFile::iniFile(const string &filename, bool rdonly, bool quotes) : mFilename( filename ) { this->rdonly = rdonly; this->quotes = quotes; pthread_mutex_init(&mymutex, NULL); reload(); } void iniFile::setFilename(const string &filename) { mFilename = filename; } string iniFile::getFilename() const { return mFilename; } istream &iniFile::load(istream &in) { // this is the big ugly shit that reads the file clear(); string curSect( "" ); string temp; for (;;) { getline(in, temp); if (in.eof()) { break; } cleanLine(temp); if (temp.empty()) { continue; } if (temp[0] == '[') { // new section removeBrackets(temp); curSect = temp; } else { vector kvPair( iniSection::parseLine(temp, quotes) ); if (kvPair.size() == 2 && curSect.length()) { (*this)[curSect][kvPair[0]].append(kvPair[1]); } // else print parse error message } } return in; } ostream &iniFile::save(ostream &out) const { map::const_iterator iter; for (iter = begin(); iter != end(); iter++) { out << '[' << iter->first << ']' << endl; iter->second.save(out, quotes); out << endl; } return out; } void iniFile::save() const { Glib::ustring tmpfile = ""; if (rdonly) { return; } pthread_mutex_lock((pthread_mutex_t *)&mymutex); if (! mFilename.empty()) { tmpfile = mFilename + "~"; ofstream fout( tmpfile.c_str() ); if (fout) { save(fout); } fout.close(); rename(tmpfile.c_str(), mFilename.c_str()); } pthread_mutex_unlock((pthread_mutex_t *)&mymutex); } void iniFile::reload() { pthread_mutex_lock(&mymutex); ifstream fin( mFilename.c_str() ); if (fin) { load(fin); fin.close(); } pthread_mutex_unlock(&mymutex); } iniFile::~iniFile() { save(); pthread_mutex_destroy(&mymutex); } bool iniFile::contains(const string &s) const { return (find(s) != end()); } void iniFile::cleanLine(string &s) { bool in_quotes( false ); for (int i = 0, len = s.length(); i < len; ++i) { if (s[i] == '\r') { s.erase(i--, 1); --len; } } for (int i = 0, len = s.length(); i < len; ++i) { if (s[i] == '\"') { in_quotes = ! in_quotes; } else if (! in_quotes && (s[i] == '#' || s[i] == ';')) { s.erase(i, s.npos); break; } } iniSection::trimString(s); } void iniFile::removeBrackets(string &s) { // assume string is trimmed already and has brackets around it s.erase(0, 1); s.erase(s.length() - 1, s.npos); iniSection::trimString(s); } istream &operator>>(istream &in, iniFile &f) { return f.load(in); } ostream &operator<<(ostream &out, iniFile &f) { return f.save(out); }