485 lines
10 KiB
C++
485 lines
10 KiB
C++
|
#include <ctype.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <ctype.h>
|
||
|
#include <stdio.h>
|
||
|
#include <pthread.h>
|
||
|
#include <sys/mman.h>
|
||
|
|
||
|
#include "memcheck.h"
|
||
|
#include "config.h"
|
||
|
#include "log.h"
|
||
|
|
||
|
int str2int (const char *strval, int def)
|
||
|
{
|
||
|
int ret_code = def;
|
||
|
if (isdigit (strval[0]) || (strval[0] == '-' && isdigit(strval[1])))
|
||
|
return atoi (strval);
|
||
|
|
||
|
if (!strcasecmp (strval, "On"))
|
||
|
ret_code = 1;
|
||
|
else if (!strcasecmp (strval, "Off"))
|
||
|
ret_code = 0;
|
||
|
else if (!strcasecmp (strval, "Yes"))
|
||
|
ret_code = 1;
|
||
|
else if (!strcasecmp (strval, "No"))
|
||
|
ret_code = 0;
|
||
|
else if (!strcasecmp (strval, "True"))
|
||
|
ret_code = 1;
|
||
|
else if (!strcasecmp (strval, "False"))
|
||
|
ret_code = 0;
|
||
|
else if (!strcasecmp (strval, "Enable"))
|
||
|
ret_code = 1;
|
||
|
else if (!strcasecmp (strval, "Disable"))
|
||
|
ret_code = 0;
|
||
|
else if (!strcasecmp (strval, "Enabled"))
|
||
|
ret_code = 1;
|
||
|
else if (!strcasecmp (strval, "Disabled"))
|
||
|
ret_code = 0;
|
||
|
|
||
|
return ret_code;
|
||
|
}
|
||
|
|
||
|
void CConfig::Dump (FILE *fp, bool dec)
|
||
|
{
|
||
|
fprintf(fp, "##### ORIGINAL FILE %s #####\n", filename.c_str());
|
||
|
for(secmap_t::iterator i = smap.begin(); i != smap.end(); i++)
|
||
|
{
|
||
|
const char *sec = i->first.c_str();
|
||
|
keymap_t &kmap = i->second;
|
||
|
if(kmap.size()<=0)
|
||
|
continue;
|
||
|
fprintf(fp, "\n[%s]\n", sec);
|
||
|
for(keymap_t::iterator j = kmap.begin(); j != kmap.end(); j++)
|
||
|
{
|
||
|
const char *key = j->first.c_str();
|
||
|
const char *val = j->second.c_str();
|
||
|
char flag = val[0];
|
||
|
if(flag=='\0')
|
||
|
{
|
||
|
if(dec==true)
|
||
|
fprintf(fp, "# %s NOT SET\n", key);
|
||
|
} else {
|
||
|
val++;
|
||
|
if(flag==' ' || dec==false)
|
||
|
fprintf(fp, "%s = %s\n", key, val);
|
||
|
else
|
||
|
fprintf(fp, "%c %s = %s\n", flag, key, val);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int CConfig::Dump (const char *fn, bool dec)
|
||
|
{
|
||
|
FILE *fp = fopen(fn, "w");
|
||
|
if(fp==NULL)
|
||
|
return -1;
|
||
|
Dump(fp, dec);
|
||
|
fclose(fp);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static char *skip_blank(char *p)
|
||
|
{
|
||
|
while(isblank(*p))
|
||
|
p++;
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
int CConfig::ParseBufferedConfig(char *buf, const char *fn, const char *defsec, bool bakconfig)
|
||
|
{
|
||
|
int len = strlen(buf), ln = 0, ret_code = 0;
|
||
|
char *start = buf, *end = buf;
|
||
|
|
||
|
|
||
|
if(defsec==NULL)
|
||
|
defsec = "";
|
||
|
if (fn == NULL)
|
||
|
fn = "buffered config";
|
||
|
keymap_t *kmap = &smap[defsec];
|
||
|
|
||
|
for (start=buf, ln=0; end && buf + len > start; start = end + 1)
|
||
|
{
|
||
|
end = strchr (start, '\n');
|
||
|
if(end)
|
||
|
{
|
||
|
if (*end)
|
||
|
*end = '\0';
|
||
|
if(end>start && end[-1]=='\r')
|
||
|
end[-1] = '\0';
|
||
|
}
|
||
|
|
||
|
std::string val("?");
|
||
|
keymap_t *m = kmap;
|
||
|
|
||
|
ln++;
|
||
|
|
||
|
char *v = NULL;
|
||
|
char *key = skip_blank(start);
|
||
|
// blank or comment line
|
||
|
if(key[0]=='\0' || key[0]=='#' || key[0]=='?' || key[0]==';')
|
||
|
continue;
|
||
|
// key must printable
|
||
|
if(!isprint(key[0]))
|
||
|
{
|
||
|
log_warning("%s(%d): invalid line: %s", fn, ln, key);
|
||
|
ret_code = -1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// find the equation
|
||
|
start = key + strcspn(key, "= \t");
|
||
|
if(*start!='\0')
|
||
|
{
|
||
|
char *p = start[0]=='=' ? start : skip_blank(start+1);
|
||
|
if(*p == '=')
|
||
|
{
|
||
|
v = skip_blank(p+1);
|
||
|
}
|
||
|
else if(key[0]=='[')
|
||
|
/*OK*/;
|
||
|
else {
|
||
|
log_warning("%s(%d): invalid line: %s", fn, ln, key);
|
||
|
ret_code = -1;
|
||
|
continue;
|
||
|
}
|
||
|
*start = '\0';
|
||
|
} else if(key[0] != '[')
|
||
|
{
|
||
|
log_warning("%s(%d): invalid line: %s", fn, ln, key);
|
||
|
ret_code = -1;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if(key[0]=='[')
|
||
|
{
|
||
|
char *r = strchr(key, ']');
|
||
|
if(!r)
|
||
|
{
|
||
|
log_warning("%s(%d): invalid section: %s", fn, ln, key);
|
||
|
ret_code = -1;
|
||
|
continue;
|
||
|
} else if(!((r[1]=='\0'&&v==NULL)||(r[1]=='.'&&v!=NULL)))
|
||
|
{
|
||
|
log_warning("%s(%d): invalid section: %s", fn, ln, key);
|
||
|
ret_code = -1;
|
||
|
continue;
|
||
|
} else {
|
||
|
*r = '\0';
|
||
|
m = &smap[key+1];
|
||
|
if(r[1]=='\0')
|
||
|
{
|
||
|
kmap = m;
|
||
|
continue;
|
||
|
}
|
||
|
key = r + 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(v==NULL)
|
||
|
continue;
|
||
|
|
||
|
switch(v[0])
|
||
|
{
|
||
|
case '(':
|
||
|
start = strchr(v, ')');
|
||
|
if(start==NULL) goto error;
|
||
|
break;
|
||
|
case '[':
|
||
|
start = strchr(v, ']');
|
||
|
if(start==NULL) goto error;
|
||
|
break;
|
||
|
case '{':
|
||
|
start = strchr(v, '}');
|
||
|
if(start==NULL) goto error;
|
||
|
break;
|
||
|
|
||
|
case '"':
|
||
|
start = strrchr(v+1, '"');
|
||
|
if(start==NULL) goto error;
|
||
|
break;
|
||
|
case '\'':
|
||
|
start = strrchr(v+1, '\'');
|
||
|
if(start==NULL) goto error;
|
||
|
break;
|
||
|
default:
|
||
|
start = end ? end-1 : v+strlen(v)-1;
|
||
|
if(*start=='\0') start--;
|
||
|
while(start > v && isblank(*start))
|
||
|
start--;
|
||
|
break;
|
||
|
error:
|
||
|
log_warning("%s(%d): unended quote or bracket", fn, ln);
|
||
|
ret_code = -1;
|
||
|
continue;
|
||
|
}
|
||
|
start[1] = '\0';
|
||
|
|
||
|
if(v[0])
|
||
|
val.append(v);
|
||
|
|
||
|
(*m)[key] = val;
|
||
|
}
|
||
|
|
||
|
if (bakconfig)
|
||
|
{
|
||
|
/*add by foryzhou write config file to stat directory,ignore error*/
|
||
|
char bak_config[1024];
|
||
|
snprintf(bak_config,sizeof(bak_config),"cp %s ../stat/",fn);
|
||
|
system(bak_config);
|
||
|
}
|
||
|
return ret_code;
|
||
|
}
|
||
|
|
||
|
int CConfig::ParseConfig (const char *fn, const char *defsec,bool bakconfig)
|
||
|
{
|
||
|
char *buf;
|
||
|
int len, fd, ret_code = -1;
|
||
|
|
||
|
if(fn == NULL)
|
||
|
fn = filename.c_str();
|
||
|
else if(filename.size() <= 0)
|
||
|
{
|
||
|
filename = fn;
|
||
|
}
|
||
|
if(filename[0]=='\0')
|
||
|
return -1;
|
||
|
|
||
|
fd = open (fn, O_RDONLY);
|
||
|
if (fd < 0) {
|
||
|
log_warning ("open %s error, cwd=%s, %m\n", fn, get_current_dir_name ());
|
||
|
return -1;
|
||
|
}
|
||
|
len = lseek (fd, 0L, SEEK_END);
|
||
|
lseek (fd, 0L, SEEK_SET);
|
||
|
buf = (char *)MALLOC(len+1);
|
||
|
read(fd, buf, len);
|
||
|
buf[len] = '\0';
|
||
|
ret_code = len + 1;
|
||
|
close(fd);
|
||
|
ret_code = ParseBufferedConfig(buf, fn, defsec, bakconfig);
|
||
|
FREE(buf);
|
||
|
return ret_code;
|
||
|
}
|
||
|
|
||
|
bool CConfig::HasSection (const char *sec)
|
||
|
{
|
||
|
secmap_t::iterator n = smap.find(sec);
|
||
|
return n!=smap.end() && n->second.size()>0 ? true : false;
|
||
|
}
|
||
|
|
||
|
bool CConfig::HasKey (const char *sec, const char *key)
|
||
|
{
|
||
|
secmap_t::iterator n = smap.find(sec);
|
||
|
if(n==smap.end())
|
||
|
return false;
|
||
|
keymap_t &kmap = n->second;
|
||
|
keymap_t::iterator m = kmap.find(key);
|
||
|
return m != kmap.end() && m->second.size()>0 ? true : false;
|
||
|
}
|
||
|
|
||
|
const char* CConfig::GetStrVal (const char *sec, const char* key)
|
||
|
{
|
||
|
keymap_t &kmap = smap[sec];
|
||
|
std::string &v = kmap[key];
|
||
|
if(v.size()==0)
|
||
|
return NULL;
|
||
|
v[0] = ' ';
|
||
|
return v.c_str() + 1;
|
||
|
}
|
||
|
|
||
|
int CConfig::GetIntVal (const char *sec, const char* key, int def)
|
||
|
{
|
||
|
const char* val = GetStrVal(sec, key);
|
||
|
if (val == NULL)
|
||
|
return def;
|
||
|
|
||
|
return str2int (val, def);
|
||
|
}
|
||
|
|
||
|
unsigned long long CConfig::GetSizeVal(
|
||
|
const char *sec,
|
||
|
const char* key,
|
||
|
unsigned long long def,
|
||
|
char unit)
|
||
|
{
|
||
|
const char* val = GetStrVal(sec, key);
|
||
|
if (val == NULL || !isdigit(val[0]))
|
||
|
return def;
|
||
|
|
||
|
const char *p;
|
||
|
double a = strtod(val, (char **)&p);
|
||
|
if(*p) unit = *p;
|
||
|
switch(unit)
|
||
|
{
|
||
|
case 'b': break;
|
||
|
case 'B': break;
|
||
|
case 'k': a *= 1000; break;
|
||
|
case 'K': a *= 1<<10; break;
|
||
|
case 'm': a *= 1000000; break;
|
||
|
case 'M': a *= 1<<20; break;
|
||
|
case 'g': a *= 1000000000; break;
|
||
|
case 'G': a *= 1<<30; break;
|
||
|
case 't': a *= 1000000000000LL; break;
|
||
|
case 'T': a *= 1ULL<<40; break;
|
||
|
}
|
||
|
|
||
|
return (unsigned long long)a;
|
||
|
}
|
||
|
|
||
|
int CConfig::GetIdxVal(
|
||
|
const char *sec, const char *key,
|
||
|
const char * const * array, int nDefault)
|
||
|
{
|
||
|
const char *val = GetStrVal(sec, key);
|
||
|
if(val == NULL)
|
||
|
return nDefault;
|
||
|
|
||
|
if(isdigit(val[0]))
|
||
|
{
|
||
|
char *p;
|
||
|
int n = strtol(val, &p, 0);
|
||
|
if(*p=='\0')
|
||
|
{
|
||
|
for(int i=0; array[i]; i++) {
|
||
|
if(n==i)
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for(int i=0; array[i]; i++) {
|
||
|
if(!strcasecmp(val, array[i]))
|
||
|
return i;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
class CAutoConfigSection: public CAutoConfig {
|
||
|
private:
|
||
|
static pthread_mutex_t glock;
|
||
|
void GlobalLock(void) { pthread_mutex_lock(&glock); }
|
||
|
void GlobalUnlock(void) { pthread_mutex_unlock(&glock); }
|
||
|
private:
|
||
|
CConfig *parent;
|
||
|
char *section;
|
||
|
// buf must have enough room place composed key name
|
||
|
char buf[256];
|
||
|
char *last;
|
||
|
public:
|
||
|
CAutoConfigSection(CConfig *p, const char *sec);
|
||
|
~CAutoConfigSection();
|
||
|
|
||
|
virtual int GetIntVal(const char *key, const char *inst, int def=0);
|
||
|
virtual unsigned long long GetSizeVal(const char *key, const char *inst, unsigned long long def=0, char unit=0);
|
||
|
virtual int GetIdxVal(const char *, const char *, const char * const *, int=0);
|
||
|
virtual const char *GetStrVal (const char *key, const char *inst);
|
||
|
private:
|
||
|
// return composed key, or vanilla key, always non-null
|
||
|
const char *findkey(const char *key, const char *inst);
|
||
|
// strip suffix digits
|
||
|
int stripnumber(void);
|
||
|
// strip suffix alphaphetic
|
||
|
int stripalpha(void);
|
||
|
// strip suffix punct
|
||
|
int strippunct(void);
|
||
|
};
|
||
|
|
||
|
pthread_mutex_t CAutoConfigSection::glock = PTHREAD_MUTEX_INITIALIZER;
|
||
|
|
||
|
CAutoConfigSection::CAutoConfigSection(CConfig *p, const char *sec)
|
||
|
{
|
||
|
this->parent = p;
|
||
|
this->section = STRDUP(sec);
|
||
|
}
|
||
|
|
||
|
CAutoConfigSection::~CAutoConfigSection()
|
||
|
{
|
||
|
FREE_CLEAR(section);
|
||
|
}
|
||
|
|
||
|
int CAutoConfigSection::stripnumber(void) {
|
||
|
int n = 0;
|
||
|
while(last>=buf && isdigit(*last)) {
|
||
|
last--;
|
||
|
n++;
|
||
|
}
|
||
|
last[1] = 0;
|
||
|
strippunct();
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
int CAutoConfigSection::stripalpha(void) {
|
||
|
int n = 0;
|
||
|
while(last>=buf && isalpha(*last)) {
|
||
|
last--;
|
||
|
n++;
|
||
|
}
|
||
|
last[1] = 0;
|
||
|
strippunct();
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
int CAutoConfigSection::strippunct(void) {
|
||
|
int n = 0;
|
||
|
while(last>=buf && *last!='@' && !isalnum(*last)) {
|
||
|
last--;
|
||
|
n++;
|
||
|
}
|
||
|
last[1] = 0;
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
const char *CAutoConfigSection::findkey(const char *key, const char *inst) {
|
||
|
snprintf(buf, sizeof(buf), "%s@%s", key, inst);
|
||
|
last = buf + strlen(buf) - 1;
|
||
|
strippunct();
|
||
|
|
||
|
do {
|
||
|
if(parent->GetStrVal(section, buf) != NULL) {
|
||
|
return buf;
|
||
|
}
|
||
|
} while(isdigit(*last) ? stripnumber() : stripalpha());
|
||
|
|
||
|
return key;
|
||
|
}
|
||
|
|
||
|
int CAutoConfigSection::GetIntVal(const char *key, const char *inst, int def) {
|
||
|
int ret;
|
||
|
GlobalLock();
|
||
|
ret = parent->GetIntVal(section, findkey(key, inst), def);
|
||
|
GlobalUnlock();
|
||
|
return ret;
|
||
|
}
|
||
|
unsigned long long CAutoConfigSection::GetSizeVal(const char *key, const char *inst, unsigned long long def, char unit) {
|
||
|
unsigned long long ret;
|
||
|
GlobalLock();
|
||
|
ret = parent->GetSizeVal(section, findkey(key, inst), def, unit);
|
||
|
GlobalUnlock();
|
||
|
return ret;
|
||
|
}
|
||
|
int CAutoConfigSection::GetIdxVal(const char *key, const char *inst, const char * const *idxval, int def) {
|
||
|
int ret;
|
||
|
GlobalLock();
|
||
|
ret = parent->GetIdxVal(section, findkey(key, inst), idxval, def);
|
||
|
GlobalUnlock();
|
||
|
return ret;
|
||
|
}
|
||
|
const char *CAutoConfigSection::GetStrVal (const char *key, const char *inst) {
|
||
|
const char *ret;
|
||
|
GlobalLock();
|
||
|
ret = parent->GetStrVal(section, findkey(key, inst));
|
||
|
GlobalUnlock();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
CAutoConfig *CConfig::GetAutoConfigInstance(const char *section) {
|
||
|
CAutoConfigSection *inst;
|
||
|
NEW (CAutoConfigSection(this, section), inst);
|
||
|
return inst;
|
||
|
}
|
||
|
|