554 lines
11 KiB
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);
|
|
}
|
|
/*}}}*/
|