//
// Configuration.cc
//
// Configuration: This class provides an object lookup table. Each object
// in the Configuration is indexed with a string. The objects
// can be returned by mentioning their string index. Values may
// include files with `/path/to/file` or other configuration
// variables with ${variable}
//
// Part of the ht://Dig package
// Copyright (c) 1999-2004 The ht://Dig Group
// For copyright details, see the file COPYING in your distribution
// or the GNU Library General Public License (LGPL) version 2 or later
//
//
// $Id: Configuration.cc,v 1.20 2004/05/28 13:15:20 lha Exp $
//
#ifdef HAVE_CONFIG_H
#include "htconfig.h"
#endif /* HAVE_CONFIG_H */
#include
#include "Configuration.h"
#include "htString.h"
#include "ParsedString.h"
#include
#include
#include
//*********************************************************************
// Configuration::Configuration()
//
Configuration::Configuration() : separators("=:"), allow_multiple(0)
{
}
//*********************************************************************
// void Configuration::NameValueSeparators(char *s)
//
void Configuration::NameValueSeparators(const String& s)
{
separators = s;
}
//*********************************************************************
// Add an entry to the configuration table.
//
void Configuration::Add(const String& str_arg)
{
const char* str = str_arg;
String name, value;
while (str && *str)
{
while (isspace(*str))
str++;
name = 0;
if (!isalpha(*str))
break;
// Some isalnum() implementations don't allow all the letters that
// isalpha() does, e.g. accented ones. They're not POSIX.2 compliant
// but we won't punish them with an infinite loop...
if (!isalnum(*str))
break;
while (isalnum(*str) || *str == '-' || *str == '_')
name << *str++;
name.lowercase();
//
// We have the name. Let's see if we will get a value
//
while (isspace(*str))
str++;
if (!*str)
{
//
// End of string. We need to store the name as a boolean TRUE
//
Add(name, "true");
return;
}
if (!strchr((char*)separators, *str))
{
//
// We are now at a new name. The previous one needs to be set
// to boolean TRUE
//
Add(name, "true");
continue;
}
//
// We now need to deal with the value
//
str++; // Skip the separator
while (isspace(*str))
str++;
if (!*str)
{
//
// End of string reached. The value must be blank
//
Add(name, "");
break;
}
value = 0;
if (*str == '"')
{
//
// Ah! A quoted value. This should be easy to deal with...
// (Just kidding!)
//
str++;
while (*str && *str != '"')
{
value << *str++;
}
Add(name, value);
if (*str == '"')
str++;
continue;
}
else if (*str == '\'')
{
// A single quoted value.
str++;
while (*str && *str != '\'')
{
value << *str++;
}
Add(name, value);
if (*str == '\'')
str++;
continue;
}
else
{
//
// A non-quoted string. This string will terminate at the
// next blank
//
while (*str && !isspace(*str))
{
value << *str++;
}
Add(name, value);
continue;
}
}
}
//*********************************************************************
// Add an entry to the configuration table, without allowing variable
// or file expansion of the value.
//
void Configuration::Add(const String& name, const String& value)
{
String escaped;
const char *s = value.get();
while (*s)
{
if (strchr("$`\\", *s))
escaped << '\\';
escaped << *s++;
}
ParsedString *ps = new ParsedString(escaped);
dcGlobalVars.Add(name, ps);
}
//*********************************************************************
// Add an entry to the configuration table, allowing parsing for variable
// or file expansion of the value.
//
void Configuration::AddParsed(const String& name, const String& value)
{
ParsedString *ps = new ParsedString(value);
if (mystrcasecmp(name, "locale") == 0)
{
String str(setlocale(LC_ALL, ps->get(dcGlobalVars)));
ps->set(str);
//
// Set time format to standard to avoid sending If-Modified-Since
// http headers in native format which http servers can't
// understand
//
setlocale(LC_TIME, "C");
}
dcGlobalVars.Add(name, ps);
}
//*********************************************************************
// Remove an entry from both the hash table and from the list of keys.
//
int Configuration::Remove(const String& name)
{
return dcGlobalVars.Remove(name);
}
//*********************************************************************
// char *Configuration::Find(const char *name) const
// Retrieve a variable from the configuration database. This variable
// will be parsed and a new String object will be returned.
//
const String Configuration::Find(const String& name) const
{
ParsedString *ps = (ParsedString *) dcGlobalVars[name];
if (ps)
{
return ps->get(dcGlobalVars);
}
else
{
#ifdef DEBUG
fprintf (stderr, "Could not find configuration option %s\n", (const char*)name);
#endif
return 0;
}
}
//-
// Return 1 if the value of configuration attribute name has
// been set, 0 otherwise
int Configuration::Exists(const String& name) const
{
return dcGlobalVars.Exists(name);
}
//*********************************************************************
Object *Configuration::Get_Object(char *name) {
return dcGlobalVars[name];
}
//*********************************************************************
//
int Configuration::Value(const String& name, int default_value) const
{
return Find(name).as_integer(default_value);
}
//*********************************************************************
//
double Configuration::Double(const String& name, double default_value) const
{
return Find(name).as_double(default_value);
}
//*********************************************************************
// int Configuration::Boolean(char *name, int default_value)
//
int Configuration::Boolean(const String& name, int default_value) const
{
int value = default_value;
const String s = Find(name);
if (s[0])
{
if (s.nocase_compare("true") == 0 ||
s.nocase_compare("yes") == 0 ||
s.nocase_compare("1") == 0)
value = 1;
else if (s.nocase_compare("false") == 0 ||
s.nocase_compare("no") == 0 ||
s.nocase_compare("0") == 0)
value = 0;
}
return value;
}
//*********************************************************************
//
const String Configuration::operator[](const String& name) const
{
return Find(name);
}
//*********************************************************************
//
int Configuration::Read(const String& filename)
{
FILE* in = fopen((const char*)filename, "r");
if(!in) {
fprintf(stderr, "Configuration::Read: cannot open %s for reading : ", (const char*)filename);
perror("");
return NOTOK;
}
#define CONFIG_BUFFER_SIZE (50*1024)
//
// Make the line buffer large so that we can read long lists of start
// URLs.
//
char buffer[CONFIG_BUFFER_SIZE + 1];
char *current;
String line;
String name;
char *value;
int len;
while (fgets(buffer, CONFIG_BUFFER_SIZE, in))
{
line << buffer;
line.chop("\r\n");
if (line.last() == '\\')
{
line.chop(1);
continue; // Append the next line to this one
}
current = line.get();
if (*current == '#' || *current == '\0')
{
line = 0;
continue; // Comments and blank lines are skipped
}
name = strtok(current, ": =\t");
value = strtok(0, "\r\n");
if (!value)
value = ""; // Blank value
//
// Skip any whitespace before the actual text
//
while (*value == ' ' || *value == '\t')
value++;
len = strlen(value) - 1;
//
// Skip any whitespace after the actual text
//
while (len >= 0 && (value[len] == ' ' || value[len] == '\t'))
{
value[len] = '\0';
len--;
}
if (mystrcasecmp((char*)name, "include") == 0)
{
ParsedString ps(value);
String str(ps.get(dcGlobalVars));
if (str[0] != '/') // Given file name not fully qualified
{
str = filename; // so strip dir. name from current one
len = str.lastIndexOf('/') + 1;
if (len > 0)
str.chop(str.length() - len);
else
str = ""; // No slash in current filename
str << ps.get(dcGlobalVars);
}
Read(str);
line = 0;
continue;
}
AddParsed(name, value);
line = 0;
}
fclose(in);
return OK;
}
//*********************************************************************
// void Configuration::Defaults(ConfigDefaults *array)
//
void Configuration::Defaults(const ConfigDefaults *array)
{
for (int i = 0; array[i].name; i++)
{
AddParsed(array[i].name, array[i].value);
}
}