#include #include #include #include #include #include "conflib.h" extern const char *__progname; static char errbuf[10240]; typedef struct confval CONFVAL; typedef struct confstate CONFSTATE; struct confval { CONFVAL *link; const char *tag; const char *val; const char *valpath; const char *fromfn; } ; struct confstate { int got_conf; CONFVAL *vals; CONFVAL **valtail; } ; static void (*errfn)(int, const char *); static jmp_buf cerr_jmp; static const char *curfile; static CONFSTATE *state; static char *scopy(const char *s) { char *t; t = malloc(strlen(s)+1); if (t) strcpy(t,s); return(t); } static volatile void cerr(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vsprintf(&errbuf[0],fmt,ap); va_end(ap); (*errfn)(1,&errbuf[0]); longjmp(cerr_jmp,1); } static void cwarn(const char *fmt, ...) { va_list ap; va_start(ap,fmt); vsprintf(&errbuf[0],fmt,ap); va_end(ap); (*errfn)(0,&errbuf[0]); } static void default_errfn(int fatal, const char *msg) { fprintf(stderr,"%s: %s%s\n",__progname,fatal?"":"warning: ",msg); if (fatal) exit(1); } static char *config_path(const char *path, const char *relativeto) { char *tmp; if ( (path[0] == '/') || ((path[0] == '.') && (path[1] == '/')) ) { tmp = scopy(path); } else { const char *rs; if (relativeto == 0) { relativeto = "."; rs = relativeto; } else { rs = rindex(relativeto,'/'); if (rs) rs++; else rs = relativeto; } tmp = malloc((rs-relativeto)+strlen(path)+1); bcopy(relativeto,tmp,rs-relativeto); strcpy(tmp+(rs-relativeto),path); } return(tmp); } static void add_value(const char *tag, int taglen, const char *value) { CONFVAL *cv; char *tagtmp; cv = malloc(sizeof(CONFVAL)); if (state->vals) *state->valtail = cv; else state->vals = cv; cv->link = 0; state->valtail = &cv->link; tagtmp = malloc(taglen+1); bcopy(tag,tagtmp,taglen); tagtmp[taglen] = '\0'; cv->tag = tagtmp; cv->val = scopy(value); cv->valpath = 0; cv->fromfn = curfile; } static void read_config(const char *); /* forward */ static void process_config_line(const char *line) { const char *eq; if (line[0] == '#') return; eq = index(line,'='); if (! eq) { if (curfile) { cerr("missing = on line from %s: %s",curfile,line); } else { cerr("missing = on line: %s",line); } } if ((eq == line+7) && !strncmp(line,"include",7)) { char *tmp; jmp_buf tj; /* argh...setjmp/longjmp need an unwind-protect */ bcopy(&cerr_jmp,&tj,sizeof(jmp_buf)); if (setjmp(cerr_jmp)) { free(tmp); bcopy(&tj,&cerr_jmp,sizeof(jmp_buf)); longjmp(cerr_jmp,1); } tmp = config_path(eq+1,curfile); read_config(tmp); free(tmp); bcopy(&tj,&cerr_jmp,sizeof(jmp_buf)); } else { add_value(line,eq-line,eq+1); } } static int read_config_line(FILE *f) { char *line; int have; int len; int c; line = malloc(1); have = 0; len = 0; while (1) { c = getc(f); if (c == EOF) { if (len != 0) cwarn("partial line at end of config file %s ignored",curfile); free(line); return(0); } if (c == '\0') continue; if (c == '\n') { line[len] = '\0'; process_config_line(line); free(line); return(1); } while (len >= have) line = realloc(line,(have+=8)+1); line[len++] = c; } } static void read_config(const char *file) { FILE *f; curfile = scopy(file); f = fopen(file,"r"); if (f == 0) cerr("can't open config file %s",file); while (read_config_line(f)) ; fclose(f); } void *conf_newstate(void) { CONFSTATE *s; s = malloc(sizeof(*s)); s->got_conf = 0; s->vals = 0; return(s); } void conf_read(void *statev, const char *path, void (*err)(int, const char *)) { state = statev; if (setjmp(cerr_jmp)) return; errfn = err ? err : default_errfn; read_config(path); state->got_conf = 1; } void conf_line(void *statev, const char *line, void (*err)(int, const char *)) { state = statev; if (setjmp(cerr_jmp)) return; errfn = err ? err : default_errfn; curfile = 0; process_config_line(line); } void conf_check_env(void *statev, const char *envvar, void (*err)(int, const char *)) { const char *env; state = statev; if (state->got_conf) return; if (setjmp(cerr_jmp)) return; errfn = err ? err : default_errfn; env = getenv(envvar); if (! env) { cerr("no $%s and no config file specified in its stead",envvar); } read_config(env); state->got_conf = 1; } const char *conf_value(void *statev, const char *key, const char **from) { CONFVAL *cv; for (cv=((CONFSTATE *)statev)->vals;cv;cv=cv->link) { if (!strcmp(cv->tag,key)) { if (from) *from = cv->fromfn; return(cv->val); } } return(0); } int conf_values(void *statev, const char *key, const char **values, const char **from, int maxn) { CONFVAL *cv; int i; i = 0; for (cv=((CONFSTATE *)statev)->vals;cv;cv=cv->link) { if (!strcmp(cv->tag,key)) { if (i < maxn) { values[i] = cv->val; if (from) from[i] = cv->fromfn; } i ++; } } return(i); } const char *conf_path(void *statev, const char *key, const char **from) { CONFVAL *cv; for (cv=((CONFSTATE *)statev)->vals;cv;cv=cv->link) { if (!strcmp(cv->tag,key)) { if (cv->valpath == 0) cv->valpath = config_path(cv->val,cv->fromfn); if (from) *from = cv->fromfn; return(cv->valpath); } } return(0); } int conf_paths(void *statev, const char *key, const char **paths, const char **from, int maxn) { CONFVAL *cv; int i; i = 0; for (cv=((CONFSTATE *)statev)->vals;cv;cv=cv->link) { if (!strcmp(cv->tag,key)) { if (i < maxn) { if (cv->valpath == 0) cv->valpath = config_path(cv->val,cv->fromfn); paths[i] = cv->valpath; if (from) from[i] = cv->fromfn; } i ++; } } return(i); }