/*
* Properties.cpp
*
* Copyright 2002, Log4cpp Project. All rights reserved.
*
* See the COPYING file for the terms of usage and distribution.
*/
#include "Properties.hh"
#include <cstdlib>
#include "StringUtil.hh"
namespace log4cpp {
Properties::Properties() {
}
Properties::~Properties() {
}
void Properties::load(std::istream& in) {
clear();
std::string fullLine, command;
std::string leftSide, rightSide;
char line[256];
std::string::size_type length;
bool partiallyRead(false); // fix for bug#137, for strings longer than 256 chars
while (in) {
if (in.getline(line, 256) || !in.bad()) {
// either string is read fully or only partially (logical but not read/write error)
if (partiallyRead)
fullLine.append(line);
else
fullLine = line;
partiallyRead = (in.fail() && !in.bad());
if (partiallyRead && !in.eof()) {
in.clear(in.rdstate() & ~std::ios::failbit);
continue; // to get full line
}
} else {
break;
}
/* if the line contains a # then it is a comment
if we find it anywhere other than the beginning, then we assume
there is a command on that line, and it we don't find it at all
we assume there is a command on the line (we test for valid
command later) if neither is true, we continue with the next line
*/
length = fullLine.find('#');
if (length == std::string::npos) {
command = fullLine;
} else if (length > 0) {
command = fullLine.substr(0, length);
} else {
continue;
}
// check the command and handle it
length = command.find('=');
if (length != std::string::npos) {
leftSide = StringUtil::trim(command.substr(0, length));
rightSide = StringUtil::trim(command.substr(length + 1, command.size() - length));
_substituteVariables(rightSide);
} else {
continue;
}
/* handle the command by determining what object the left side
refers to and setting the value given on the right
ASSUMPTIONS:
1. first object given on left side is "log4j" or "log4cpp"
2. all class names on right side are ignored because we
probably cannot resolve them anyway.
*/
// strip off the "log4j" or "log4cpp"
length = leftSide.find('.');
if (leftSide.substr(0, length) == "log4j" ||
leftSide.substr(0, length) == "log4cpp")
leftSide = leftSide.substr(length + 1);
// add to the map of properties
insert(value_type(leftSide, rightSide));
}
}
void Properties::save(std::ostream& out) {
for(const_iterator i = begin(); i != end(); ++i) {
out << (*i).first << "=" << (*i).second << std::endl;
}
}
int Properties::getInt(const std::string& property, int defaultValue) {
const_iterator key = find(property);
return (key == end()) ? defaultValue : std::atoi((*key).second.c_str());
}
bool Properties::getBool(const std::string& property, bool defaultValue) {
const_iterator key = find(property);
return (key == end()) ? defaultValue : ((*key).second == "true");
}
std::string Properties::getString(const std::string& property,
const char* defaultValue) {
const_iterator key = find(property);
return (key == end()) ? std::string(defaultValue) : (*key).second;
}
void Properties::_substituteVariables(std::string& value) {
std::string result;
std::string::size_type left = 0;
std::string::size_type right = value.find("${", left);
if (right == std::string::npos) {
// bail out early for 99% of cases
return;
}
while(true) {
result += value.substr(left, right - left);
if (right == std::string::npos) {
break;
}
left = right + 2;
right = value.find('}', left);
if (right == std::string::npos) {
// no close tag, use string literally
result += value.substr(left - 2);
break;
} else {
const std::string key = value.substr(left, right - left);
if (key == "${") {
result += "${";
} else {
char* env_value = std::getenv(key.c_str());
if (env_value) {
result += env_value;
} else {
const_iterator it = find(key);
if (it == end()) {
// not found assume empty;
} else {
result += (*it).second;
}
}
}
left = right + 1;
}
right = value.find("${", left);
}
value = result;
}
}