capi4yaps/cfg.c

554 lines
11 KiB
C

/* -*- mode: c; mode: fold -*- */
# include "config.h"
# include <stdio.h>
# include <stdlib.h>
# include <ctype.h>
# include <string.h>
# include "pager.h"
/*{{{ typedefs */
typedef unsigned long hash_t;
typedef struct _entry {
hash_t hval;
char *var;
char *val;
struct _entry
*next;
} entry;
typedef struct _charc {
hash_t hval;
char *name;
struct _charc
*next;
} charc;
typedef struct _block {
# ifndef NDEBUG
# define MAGIC MKMAGIC ('c', 'f', 'g', '\0')
long magic;
# endif /* NDEBUG */
hash_t hval;
char *name;
charc *use;
Bool done;
entry *e;
char *sep;
int seplen;
struct _block
*next;
} block;
typedef struct _fifo {
void *dat;
struct _fifo
*next;
} fifo;
typedef struct _fpstack {
FILE *fp;
struct _fpstack
*up;
} fpstack;
/*}}}*/
/*{{{ support routines */
static hash_t
calc_hash (char *str)
{
hash_t hval;
for (hval = 0; *str; ) {
hval <<= 2;
hval += *str++ & 0xff;
hval >>= 1;
}
return hval;
}
static Bool
issame (hash_t h1, char *s1, hash_t h2, char *s2)
{
if (h1 == h2)
if ((! s1) && (! s2))
return True;
else if (s1 && s2 && (s1[0] == s2[0]) && (! strcmp (s1, s2)))
return True;
return False;
}
static block *
new_block (char *bname, hash_t hval, char *sep)
{
block *b;
if (b = (block *) malloc (sizeof (block))) {
b -> name = NULL;
if ((! bname) || (b -> name = strdup (bname))) {
b -> sep = NULL;
b -> seplen = 0;
if ((! sep) || (b -> sep = strdup (sep))) {
# ifndef NDEBUG
b -> magic = MAGIC;
# endif /* NDEBUG */
b -> hval = hval;
b -> use = NULL;
b -> done = False;
b -> e = NULL;
b -> next = NULL;
} else {
if (b -> name)
free (b -> name);
free (b);
b = NULL;
}
} else {
free (b);
b = NULL;
}
}
return b;
}
static void
add_entry (block *base, block *b, char *var, char *val)
{
hash_t hval;
entry *e, *etmp;
Bool add;
char *sav;
if (b && var) {
if (*var == '+') {
++var;
add = True;
} else
add = False;
hval = calc_hash (var);
for (e = b -> e; e; e = e -> next)
if (issame (hval, var, e -> hval, e -> var))
break;
if (e) {
if (e -> val)
if (add) {
if (val && *val) {
sav = e -> val;
if (e -> val = malloc (strlen (sav) + strlen (val) + base -> seplen + 2)) {
sprintf (e -> val, "%s%s%s", sav, (base -> sep ? base -> sep : ""), val);
free (sav);
val = NULL;
} else
e -> val = sav;
}
} else {
free (e -> val);
e -> val = NULL;
}
} else if (e = (entry *) malloc (sizeof (entry)))
if (e -> var = strdup (var)) {
e -> hval = hval;
e -> val = NULL;
e -> next = b -> e;
b -> e = e;
} else {
free (e);
e = NULL;
}
if (e && (! e -> val) && val && *val) {
if (add && (base != b)) {
for (etmp = base -> e; etmp; etmp = etmp -> next)
if (issame (hval, var, etmp -> hval, etmp -> var))
break;
if (etmp && etmp -> val)
if (e -> val = malloc (strlen (etmp -> val) + strlen (val) + base -> seplen + 2))
sprintf (e -> val, "%s%s%s", etmp -> val, (base -> sep ? base -> sep : ""), val);
}
if (! e -> val)
e -> val = strdup (val);
}
}
}
/*}}}*/
/*{{{ read configuration */
void *
cfg_new (char *sep)
{
return (void *) new_block (NULL, 0, sep);
}
void *
cfg_read (char *fname, void *bp, char *sep)
{
FILE *fp;
fpstack *fcur, *ftmp;
char *gline, *line;
char *ptr, *use;
charc *prv, *tmp;
block *base, *prev, *cur;
hash_t hval;
char *var, *val;
int plen;
Bool done;
int siz, len;
char *temp;
if (bp)
base = (block *) bp;
else
if (! (base = new_block (NULL, 0, sep)))
return NULL;
cur = base;
if (fp = fopen (fname, "r"))
if (fcur = (fpstack *) malloc (sizeof (fpstack))) {
fcur -> fp = fp;
fcur -> up = NULL;
while (fcur) {
while (gline = getline (fcur -> fp, True)) {
for (line = gline; isspace (*line); ++line)
;
if ((! *line) || (*line == '#')) {
free (gline);
continue;
}
if (*line == '[') {
use = NULL;
if (ptr = strchr (line, ']')) {
*ptr++ = '\0';
while (isspace (*ptr))
++ptr;
if (*ptr)
use = ptr;
}
ptr = line + 1;
if (! *ptr)
cur = base;
else {
hval = calc_hash (ptr);
for (cur = base, prev = NULL; cur; cur = cur -> next)
if (issame (hval, ptr, cur -> hval, cur -> name))
break;
else
prev = cur;
if ((! cur) && (cur = new_block (ptr, hval, NULL)))
if (prev)
prev -> next = cur;
if (cur && use) {
if (cur -> use)
for (prv = cur -> use; prv -> next; prv = prv -> next)
;
else
prv = NULL;
while (*use) {
ptr = use;
use = skipch (ptr, ',');
if (tmp = (charc *) malloc (sizeof (charc)))
if (tmp -> name = strdup (ptr)) {
tmp -> hval = 0;
tmp -> next = NULL;
if (prv)
prv -> next = tmp;
else
cur -> use = tmp;
prv = tmp;
} else
free (tmp);
}
}
}
if (! cur) {
free (gline);
break;
}
} else if (*line == '|') {
++line;
while (isspace (*line))
++line;
if (fp = fopen (line, "r"))
if (ftmp = (fpstack *) malloc (sizeof (fpstack))) {
ftmp -> fp = fp;
ftmp -> up = fcur;
fcur = ftmp;
} else
fclose (fp);
} else {
var = line;
val = skip (var);
if (var = strdup (var)) {
temp = NULL;
if ((*val == '{') && (! *(val + 1))) {
done = False;
siz = 0;
len = 0;
while (ptr = getline (fcur -> fp, False)) {
if ((*ptr != '}') || *(ptr + 1)) {
plen = strlen (ptr);
if (len + plen + 2 >= siz) {
siz = len + plen + 256;
if (! (temp = Realloc (temp, siz + 2))) {
siz = 0;
done = True;
}
}
if (len + plen < siz) {
if (len)
temp[len++] = '\n';
strcpy (temp + len, ptr);
len += plen;
}
} else
done = True;
free (ptr);
if (done)
break;
}
if (temp) {
temp[len] = '\0';
val = temp;
} else
val = NULL;
} else if (*val == '\\')
++val;
add_entry (base, cur, var, val);
if (temp)
free (temp);
free (var);
}
}
free (gline);
}
ftmp = fcur;
fcur = fcur -> up;
fclose (ftmp -> fp);
free (ftmp);
}
} else
fclose (fp);
return (void *) base;
}
/*}}}*/
/*{{{ add/change configuration */
void
cfg_modify (void *bp, char *bname, char *var, char *val)
{
block *base = (block *) bp;
block *use, *prv;
hash_t hval;
MCHK (base);
if (base) {
if (! bname)
use = base;
else {
hval = calc_hash (bname);
for (use = base -> next, prv = base; use; use = use -> next)
if (issame (hval, bname, use -> hval, use -> name))
break;
else
prv = use;
if (! use)
use = new_block (bname, hval, NULL);
}
if (use)
add_entry (base, use, var, val);
}
}
/*}}}*/
/*{{{ end (free up) configuration */
void *
cfg_end (void *bp)
{
block *b = (block *) bp;
block *tmp;
charc *run;
entry *e;
MCHK (b);
while (b) {
if (b -> name)
free (b -> name);
while (b -> use) {
run = b -> use;
b -> use = b -> use -> next;
if (run -> name)
free (run -> name);
free (run);
}
while (b -> e) {
e = b -> e;
b -> e = b -> e -> next;
if (e -> var)
free (e -> var);
if (e -> val)
free (e -> val);
free (e);
}
if (b -> sep)
free (b -> sep);
tmp = b;
b = b -> next;
free (tmp);
}
return NULL;
}
/*}}}*/
/*{{{ retrieving */
static char *
do_get (void *bp, char *bname, char *var, Bool glob, char *dflt)
{
block *b = (block *) bp;
block *run, *tmp;
entry *e;
fifo *f, *l, *ft;
charc *use;
char *ptr, *sav;
charc *vars, *vprv, *vtmp, *vrun;
Bool first;
hash_t hval;
MCHK (b);
if (! (var = strdup (var)))
return NULL;
vars = NULL;
vprv = NULL;
for (ptr = var; *ptr; ) {
sav = ptr;
ptr = skipch (ptr, ',');
if (vtmp = (charc *) malloc (sizeof (charc)))
if (vtmp -> name = strdup (sav)) {
vtmp -> hval = calc_hash (vtmp -> name);
vtmp -> next = NULL;
if (vprv)
vprv -> next = vtmp;
else
vars = vtmp;
vprv = vtmp;
} else
free (vtmp);
}
free (var);
if (! vars)
return NULL;
e = NULL;
f = NULL;
l = NULL;
first = True;
while ((! e) && bname) {
hval = calc_hash (bname);
for (run = b -> next; run; run = run -> next)
if (issame (hval, bname, run -> hval, run -> name))
break;
bname = NULL;
if (run && (first || (! run -> done))) {
for (vrun = vars; vrun; vrun = vrun -> next) {
for (e = run -> e; e; e = e -> next)
if (issame (vrun -> hval, vrun -> name, e -> hval, e -> var))
break;
if (e)
break;
}
if (! e) {
if (use = run -> use) {
if (first) {
for (tmp = b -> next; tmp; tmp = tmp -> next)
tmp -> done = False;
first = False;
}
for (; use; use = use -> next)
if (ft = (fifo *) malloc (sizeof (fifo))) {
ft -> dat = (void *) use;
ft -> next = NULL;
if (l)
l -> next = ft;
else {
f = ft;
l = ft;
}
} else
break;
}
if (f) {
ft = f;
f = f -> next;
if (! f)
l = NULL;
use = (charc *) ft -> dat;
bname = use -> name;
free (ft);
} else
bname = NULL;
run -> done = True;
}
}
}
if ((! e) && glob)
for (vrun = vars; vrun; vrun = vrun -> next) {
for (e = b -> e; e; e = e -> next)
if (issame (vrun -> hval, vrun -> name, e -> hval, e -> var))
break;
if (e)
break;
}
while (vars) {
vtmp = vars;
vars = vars -> next;
free (vtmp -> name);
free (vtmp);
}
return e ? e -> val : dflt;
}
static int
do_iget (void *bp, char *bname, char *var, Bool glob, int dflt)
{
char *ret;
ret = do_get (bp, bname, var, glob, NULL);
return ret ? atoi (ret) : dflt;
}
static Bool
do_bget (void *bp, char *bname, char *var, Bool glob, int dflt)
{
char *ret;
ret = do_get (bp, bname, var, glob, NULL);
return ret ? ((*ret && strchr ("TtYy1+", *ret)) ? True : False) : dflt;
}
char *
cfg_get (void *bp, char *bname, char *var, char *dflt)
{
return do_get (bp, bname, var, True, dflt);
}
int
cfg_iget (void *bp, char *bname, char *var, int dflt)
{
return do_iget (bp, bname, var, True, dflt);
}
Bool
cfg_bget (void *bp, char *bname, char *var, Bool dflt)
{
return do_bget (bp, bname, var, True, dflt);
}
char *
cfg_block_get (void *bp, char *bname, char *var, char *dflt)
{
return do_get (bp, bname, var, False, dflt);
}
int
cfg_block_iget (void *bp, char *bname, char *var, int dflt)
{
return do_iget (bp, bname, var, False, dflt);
}
Bool
cfg_block_bget (void *bp, char *bname, char *var, Bool dflt)
{
return do_bget (bp, bname, var, False, dflt);
}
/*}}}*/