blob: 2cec7c80bb55a87a7ed0e4cc9a58b1f7ab6b2b03 [file] [log] [blame]
// Copyright (C) 1999-2005 Open Source Telecom Corporation.
// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
//
// This program 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.
//
// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// Common C++. If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way. To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//
#include <cc++/config.h>
#include <cc++/export.h>
#include <cc++/file.h>
#include <cc++/misc.h>
#include <sys/stat.h>
#include "private.h"
#include <cstdlib>
#include <cstdio>
#ifndef CAPE_REGISTRY_APPLICATIONS
#define CAPE_REGISTRY_APPLICATIONS "SOFTWARE\\CAPE Applications"
#define CAPE_REGISTRY_USERSETTINGS "Software\\CAPE Applications"
#endif
#ifdef WIN32
class Registry
{
public:
const char *configdir;
Registry();
};
Registry::Registry()
{
configdir = getenv("SystemRoot");
if(!configdir || !*configdir)
configdir = getenv("windir");
if(!configdir || !*configdir)
configdir = "C:\\WINDOWS";
}
static Registry registry;
#endif
#ifdef CCXX_NAMESPACES
namespace ost {
using namespace std;
#endif
int Keydata::count = 0;
int Keydata::sequence = 1;
ifstream *Keydata::cfgFile = NULL;
char Keydata::lastpath[KEYDATA_PATH_SIZE + 1];
void endKeydata();
static unsigned bitsize(void)
{
if(sizeof(void *) > 4)
return 2;
return 1;
}
Keydata::Keydata() :
MemPager(KEYDATA_PAGER_SIZE * bitsize())
{
link = 0;
memset(&keys, 0, sizeof(keys));
}
Keydata::Keydata(const char *path) :
MemPager(KEYDATA_PAGER_SIZE * bitsize())
{
link = 0;
memset(&keys, 0, sizeof(keys));
load(path);
}
Keydata::Keydata(Define *pairs, const char *path) :
MemPager(KEYDATA_PAGER_SIZE * bitsize())
{
link = 0;
memset(&keys, 0, sizeof(keys));
load(pairs);
if(path)
load(path);
}
Keydata::~Keydata()
{
clean();
unlink();
if(count < 1)
endKeydata();
}
Keydata::Keysym *Keydata::getSymbol(const char *sym, bool create)
{
unsigned path = getIndex(sym);
size_t len = strlen(sym) + 1;
Keysym *key = keys[path];
while(key) {
if(!stricmp(sym, key->sym))
return key;
key = key->next;
}
if(!create)
return NULL;
// note: keysym has 1 byte for null character already
key = (Keysym *)alloc(sizeof(Keysym) + len - 1);
setString(key->sym, len, sym);
key->count = 0;
key->next = keys[path];
key->data = NULL;
key->list = NULL;
keys[path] = key;
return key;
}
unsigned Keydata::getIndex(const char *str)
{
unsigned key = 0;
while(*str)
key = (key << 1) ^ (*(str++) & 0x1f);
return key % KEYDATA_INDEX_SIZE;
}
int Keydata::getCount(const char *sym)
{
Keysym *key = getSymbol(sym, false);
if(!key)
return 0;
return key->count;
}
const char *Keydata::getFirst(const char *sym)
{
Keysym *key = getSymbol(sym, false);
Keyval *val;
if(!key)
return NULL;
val = key->data;
if(!val)
return NULL;
while(val->next)
val = val->next;
return val->val;
}
const char *Keydata::getString(const char *sym, const char *def)
{
const char *cp = getLast(sym);
if(!cp)
return def;
return cp;
}
long Keydata::getLong(const char *sym, long def)
{
const char *cp = getLast(sym);
if(!cp)
return def;
return atol(cp);
}
double Keydata::getDouble(const char *sym, double def)
{
const char *cp = getLast(sym);
if(!cp)
return def;
return atof(cp);
}
bool Keydata::getBool(const char *sym)
{
const char *cp = getLast(sym);
if(!cp)
return false;
switch(*cp) {
case 'y':
case 'Y':
case 't':
case 'T':
return true;
default:
return false;
}
}
bool Keydata::isKey(const char *sym)
{
if(getLast(sym))
return true;
return false;
}
const char *Keydata::getLast(const char *sym)
{
Keysym *key = getSymbol(sym, false);
if(!key)
return NULL;
if(!key->data)
return NULL;
return key->data->val;
}
unsigned Keydata::getCount(void)
{
unsigned icount = 0;
Keysym *key;
int idx;
for(idx = 0; idx < KEYDATA_INDEX_SIZE; ++idx) {
key = keys[idx];
while(key) {
++icount;
key = key->next;
}
}
return icount;
}
unsigned Keydata::getIndex(char **data, unsigned max)
{
int idx;
Keysym *key;
unsigned icount = 0;
for(idx = 0; idx < KEYDATA_INDEX_SIZE; ++idx) {
if(icount >= max)
break;
key = keys[idx];
while(key && icount < max) {
*(data++) = key->sym;
++icount;
key = key->next;
}
}
*data = NULL;
return icount;
}
void Keydata::setValue(const char *sym, const char *data)
{
size_t len = strlen(data) + 1;
Keysym *key = getSymbol(sym, true);
Keyval *val;
if(!data)
data = "";
// note keyval has 1 byte for null already
val = (Keyval *)alloc(sizeof(Keyval) + len - 1);
++key->count;
key->list = NULL;
val->next = key->data;
key->data = val;
setString(val->val, len, data);
}
const char *const *Keydata::getList(const char *sym)
{
int icount;
Keysym *key = getSymbol(sym, false);
Keyval *data;
if(!key)
return NULL;
icount = key->count;
if(!icount)
return NULL;
++icount;
if(!key->list) {
key->list =(const char **)first(sizeof(const char**) * icount);
key->list[--icount] = NULL;
data = key->data;
while(icount && data) {
key->list[--icount] = data->val;
data = data->next;
}
while(icount)
key->list[--icount] = "";
}
return key->list;
}
void Keydata::clrValue(const char *sym)
{
Keysym *key = getSymbol(sym, false);
if(!key)
return;
key->count = 0;
key->list = NULL;
key->data = NULL;
}
void Keydata::load(Define *defs)
{
Keysym *key;
while(defs->keyword) {
key = getSymbol(defs->keyword, true);
if(!key->data)
setValue(defs->keyword, defs->value);
++defs;
}
}
void Keydata::load(const char *keypath)
{
loadPrefix(NULL, keypath);
}
void Keydata::loadPrefix(const char *pre, const char *keypath)
{
// FIXME: use string of dinamic allocation
char path[KEYDATA_PATH_SIZE];
char seek[33];
const char *prefix = NULL;
const char *ext;
#ifdef WIN32
const char *ccp;
#endif
char *cp;
bool etcpath = false, etctest = false;
#ifndef WIN32
struct stat ino;
#endif
path[0] = 0;
#ifdef WIN32
HKEY key;
LONG value;
DWORD keynamelen, keytextlen;
TCHAR keyname[256];
TCHAR keytext[256];
DWORD keyindex = 0;
char *regprefix = getenv("CONFIG_REGISTRY");
if(!regprefix)
regprefix="";
ccp = keypath;
if(*ccp == '~') {
++ccp;
if(*ccp == '/')
++ccp;
snprintf(path, sizeof(path), CAPE_REGISTRY_USERSETTINGS "/%s%s/", regprefix, ccp);
}
else {
if(*ccp == '/')
++ccp;
snprintf(path, sizeof(path), CAPE_REGISTRY_APPLICATIONS "/%s%s/", regprefix, ccp);
}
cp = path;
while(NULL != (cp = strchr(cp, '/')))
*cp = '\\';
if(*keypath == '~')
value = RegOpenKey(HKEY_CURRENT_USER, path, &key);
else
value = RegOpenKey(HKEY_LOCAL_MACHINE, path, &key);
// if registry key path is found, then we use registry values
// and ignore .ini files...
if(value == ERROR_SUCCESS) {
for(;;) {
keynamelen = 256;
value = RegEnumKeyEx(key, keyindex, keyname, &keynamelen, NULL, NULL, NULL, NULL);
if(value != ERROR_SUCCESS)
break;
keytextlen = 256;
keytext[0] = '\0';
value = RegEnumValue(key, keyindex++, keytext, &keytextlen, NULL, NULL, NULL, NULL);
if(value != ERROR_SUCCESS)
continue;
if(pre) {
snprintf(path, sizeof(path), "%s.%s", pre, keyname);
setValue(path, keytext);
}
else
setValue(keyname, keytext);
}
RegCloseKey(key);
return;
}
// windows will not support subdir .ini tree; now if not in
// registry, then assume unavailable
ccp = strchr(keypath + 3, '/');
if(ccp)
ccp = strchr(ccp, '/');
if(ccp)
return;
if(*keypath == '~') {
prefix = getenv("USERPROFILE");
if(!prefix)
return;
setString(path, sizeof(path) - 8, prefix);
addString(path, sizeof(path), "\\");
++keypath;
cp = path;
while(NULL != (cp = strchr(cp, '\\')))
*cp = '/';
}
#else
if(*keypath == '~') {
prefix = getenv("HOME");
if(!prefix)
return;
setString(path, sizeof(path) - 8, prefix);
addString(path, sizeof(path), "/.");
++keypath;
}
#endif
if(!prefix) {
#ifdef WIN32
if(!prefix || !*prefix)
prefix = registry.configdir;
#else
retry:
#ifdef ETC_CONFDIR
if(!prefix || !*prefix) {
if(etcpath)
prefix = ETC_PREFIX;
else
prefix = ETC_CONFDIR;
etctest = true;
if(!stricmp(ETC_PREFIX, ETC_CONFDIR))
etcpath = true;
}
#else
if(!prefix || !*prefix) {
etctest = true;
prefix = ETC_PREFIX;
}
#endif
#endif
setString(path, sizeof(path) - 8, prefix);
#ifdef WIN32
cp = path;
while(NULL != (cp = strchr(cp, '\\')))
*cp = '/';
cp = path + strlen(path) - 1;
if(*cp != '/') {
*(++cp) = '/';
*(++cp) = 0;
}
#endif
prefix = NULL;
}
if(*keypath == '/' || *keypath == '\\')
++keypath;
addString(path, sizeof(path), keypath);
cp = strrchr(path, '/');
setString(seek, sizeof(seek), cp + 1);
*cp = 0;
ext = strrchr(path, '/');
if(ext)
ext = strrchr(ext + 2, '.');
else
ext = strrchr(path + 1, '.');
#ifdef WIN32
if(!ext)
addString(path, sizeof(path), ".ini");
#else
if(!prefix && !ext)
addString(path, sizeof(path), ".conf");
else if(prefix && !ext)
addString(path, sizeof(path), "rc");
ino.st_uid = (unsigned)-1;
if(stat(path, &ino) < 0 && etctest && !etcpath) {
etcpath = true;
goto retry;
}
// if root, make sure root owned config...
if(!geteuid() && ino.st_uid)
return;
// if root, make sure from a etc path only...
if(!geteuid() && !etctest)
return;
#endif
loadFile(path, seek, pre);
}
void Keydata::loadFile(const char *path, const char *keys, const char *pre)
{
char seek[33];
char find[33];
char line[256];
char buffer[256];
char *cp, *ep;
int fpos;
if(keys)
setString(seek, sizeof(seek), keys);
else
seek[0] = 0;
if(strcmp(path, lastpath)) {
endKeydata();
if(canAccess(path))
cfgFile->open(path, ios::in);
else
return;
if(!cfgFile->is_open())
return;
setString(lastpath, sizeof(lastpath), path);
}
if(link != sequence) {
link = sequence;
++count;
}
find[0] = 0;
cfgFile->seekg(0);
while(keys && stricmp(seek, find)) {
cfgFile->getline(line, sizeof(line) - 1);
if(cfgFile->eof()) {
lastpath[0] = 0;
cfgFile->close();
cfgFile->clear();
return;
}
cp = line;
while(*cp == ' ' || *cp == '\n' || *cp == '\t')
++cp;
if(*cp != '[')
continue;
ep = strchr(cp, ']');
if(ep)
*ep = 0;
else
continue;
setString(find, 32, ++cp);
}
for(;;) {
if(cfgFile->eof()) {
lastpath[0] = 0;
cfgFile->close();
cfgFile->clear();
return;
}
cfgFile->getline(line, sizeof(line) - 1);
cp = line;
while(*cp == ' ' || *cp == '\t' || *cp == '\n')
++cp;
if(!*cp || *cp == '#' || *cp == ';' || *cp == '!')
continue;
if(*cp == '[')
return;
fpos = 0;
while(*cp && *cp != '=') {
if(*cp == ' ' || *cp == '\t') {
++cp;
continue;
}
find[fpos] = *(cp++);
if(fpos < 32)
++fpos;
}
find[fpos] = 0;
if(*cp != '=')
continue;
++cp;
while(*cp == ' ' || *cp == '\t' || *cp == '\n')
++cp;
ep = cp + strlen(cp);
while((--ep) > cp) {
if(*ep == ' ' || *ep == '\t' || *ep == '\n')
*ep = 0;
else
break;
}
if(*cp == *ep && (*cp == '\'' || *cp == '\"')) {
++cp;
*ep = 0;
}
if(pre) {
#ifdef HAVE_SNPRINTF
snprintf(buffer, 256, "%s.%s", pre, find);
#else
setString(buffer, 256, pre);
addString(buffer, 256, ".");
addString(buffer, 256, find);
#endif
setValue(buffer, cp);
}
else
setValue(find, cp);
}
}
void Keydata::unlink(void)
{
if(link != sequence) {
link = 0;
return;
}
link = 0;
--count;
}
void Keydata::end(void)
{
Keydata::count = 0;
++Keydata::sequence;
if(!Keydata::sequence)
++Keydata::sequence;
Keydata::lastpath[0] = 0;
if(!Keydata::cfgFile)
Keydata::cfgFile = new std::ifstream();
else if(Keydata::cfgFile->is_open()) {
Keydata::cfgFile->close();
Keydata::cfgFile->clear();
}
}
#ifdef CCXX_NAMESPACES
}
#endif
/** EMACS **
* Local variables:
* mode: c++
* c-basic-offset: 4
* End:
*/