#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
+#include <limits.h>
#include <glob.h>
#include <sys/types.h>
+#include <ctype.h>
#include <libgen.h>
#include <errno.h>
#include "debug.h"
return 1;
}
-FILE *pushgconffile(struct gconffile **cf, const char *path) {
+FILE *pushgconffile(struct gconffile **cf, FILE *file, const char *description) {
int i;
struct gconffile *newcf;
- char *pathcopy;
- FILE *f;
+ char *desc;
- f = fopen(path, "r");
- if (!f) {
- debug(DBG_INFO, "could not read config file %s", path);
+ if (!file) {
+ debug(DBG_INFO, "could not read config from %s", description);
return NULL;
}
- debug(DBG_DBG, "opened config file %s", path);
+ debug(DBG_DBG, "reading config from %s", description);
- pathcopy = stringcopy(path, 0);
- if (!pathcopy)
+ desc = stringcopy(description, 0);
+ if (!desc)
goto errmalloc;
-
+
if (!*cf) {
newcf = malloc(sizeof(struct gconffile) * 2);
if (!newcf)
memmove(newcf + 1, newcf, sizeof(struct gconffile) * (i + 1));
memset(newcf, 0, sizeof(struct gconffile));
}
- newcf[0].file = f;
- newcf[0].path = pathcopy;
+ newcf[0].file = file;
+ newcf[0].path = desc;
*cf = newcf;
- return f;
-
- errmalloc:
- free(pathcopy);
- fclose(f);
+ return file;
+
+errmalloc:
+ free(desc);
+ fclose(file);
debug(DBG_ERR, "malloc failed");
return NULL;
}
-FILE *pushgconffiles(struct gconffile **cf, const char *cfgpath) {
+FILE *pushgconfpath(struct gconffile **cf, const char *path) {
+ FILE *f;
+
+ f = fopen(path, "r");
+ return pushgconffile(cf, f, path);
+}
+
+FILE *pushgconfpaths(struct gconffile **cf, const char *cfgpath) {
int i;
FILE *f = NULL;
glob_t globbuf;
char *path, *curfile = NULL, *dir;
-
+
/* if cfgpath is relative, make it relative to current config */
if (*cfgpath == '/')
path = (char *)cfgpath;
}
memset(&globbuf, 0, sizeof(glob_t));
if (glob(path, 0, NULL, &globbuf)) {
- debug(DBG_INFO, "could not glob %s", path);
+ debug(DBG_WARN, "could not glob %s", path);
goto exit;
}
for (i = globbuf.gl_pathc - 1; i >= 0; i--) {
- f = pushgconffile(cf, globbuf.gl_pathv[i]);
+ f = pushgconfpath(cf, globbuf.gl_pathv[i]);
if (!f)
break;
- }
+ }
globfree(&globbuf);
- exit:
+exit:
if (curfile) {
free(curfile);
free(path);
return f;
}
-int popgconffile(struct gconffile **cf) {
+int popgconf(struct gconffile **cf) {
int i;
if (!*cf)
return 1;
}
+void freegconfmstr(char **mstr) {
+ int i;
+
+ if (mstr) {
+ for (i = 0; mstr[i]; i++)
+ free(mstr[i]);
+ free(mstr);
+ }
+}
+
void freegconf(struct gconffile **cf) {
int i;
if (!*cf)
return;
-
+
for (i = 0; (*cf)[i].data || (*cf)[i].path; i++) {
if ((*cf)[i].file) {
fclose((*cf)[i].file);
struct gconffile *openconfigfile(const char *file) {
struct gconffile *cf = NULL;
- if (!pushgconffile(&cf, file)) {
+ if (!pushgconfpath(&cf, file)) {
debug(DBG_ERR, "could not read config file %s\n%s", file, strerror(errno));
return NULL;
}
int getlinefromcf(struct gconffile *cf, char *line, const size_t size) {
size_t i, pos;
-
+
if (!cf)
return 0;
-
+
if (cf->file)
return fgets(line, size, cf->file) ? 1 : 0;
else if (cf->data) {
char line[1024];
char *tokens[3], *s;
int tcount;
-
+
*opt = NULL;
*val = NULL;
*conftype = 0;
-
+
if (!cf || !*cf || (!(*cf)->file && !(*cf)->data))
return 1;
for (;;) {
if (!getlinefromcf(*cf, line, 1024)) {
- if (popgconffile(cf))
+ if (popgconf(cf))
continue;
return 1;
}
}
break;
}
-
+
switch (tcount) {
case 2:
*opt = stringcopy(tokens[0], 0);
if (**val)
return 1;
-
+
debug(DBG_ERR, "configuration error, option %s needs a non-empty value", *opt);
goto errexit;
- errmalloc:
+errmalloc:
debug(DBG_ERR, "malloc failed");
- errexit:
+errexit:
free(*opt);
*opt = NULL;
free(*val);
return 0;
}
+uint8_t hexdigit2int(char d) {
+ if (d >= '0' && d <= '9')
+ return d - '0';
+ if (d >= 'a' && d <= 'f')
+ return 10 + d - 'a';
+ if (d >= 'A' && d <= 'F')
+ return 10 + d - 'A';
+ return 0;
+}
+
+void unhex(char *s) {
+ char *t;
+ for (t = s; *t; s++) {
+ if (*t == '%' && isxdigit((int)t[1]) && isxdigit((int)t[2])) {
+ *s = 16 * hexdigit2int(t[1]) + hexdigit2int(t[2]);
+ t += 3;
+ } else
+ *s = *t++;
+ }
+ *s = '\0';
+}
+
+typedef int (*t_fptr)(struct gconffile **, void *, char *, char *, char *);
+
/* returns 1 if ok, 0 on error */
/* caller must free returned values also on error */
int getgenericconfig(struct gconffile **cf, char *block, ...) {
va_list ap;
- char *opt = NULL, *val, *word, *optval, **str = NULL, ***mstr = NULL, **newmstr;
- uint8_t *bln;
+ char *opt = NULL, *val, *word, *optval, **str = NULL, ***mstr = NULL, **newmstr, *endptr;
+ uint8_t *bln = NULL;
+ long int *lint = NULL;
int type = 0, conftype = 0, n;
- void (*cbk)(struct gconffile **, void *, char *, char *, char *) = NULL;
+ t_fptr cbk = NULL;
void *cbkarg = NULL;
for (;;) {
return 1;
if (conftype == CONF_STR && !strcasecmp(opt, "include")) {
- if (!pushgconffiles(cf, val)) {
+ if (!pushgconfpaths(cf, val)) {
debug(DBG_ERR, "failed to include config file %s", val);
goto errexit;
}
free(val);
continue;
}
-
+
va_start(ap, block);
while ((word = va_arg(ap, char *))) {
type = va_arg(ap, int);
if (!bln)
goto errparam;
break;
+ case CONF_LINT:
+ lint = va_arg(ap, long int *);
+ if (!lint)
+ goto errparam;
+ break;
case CONF_CBK:
- cbk = va_arg(ap, void (*)(struct gconffile **, void *, char *, char *, char *));
+ cbk = va_arg(ap, t_fptr);
if (!cbk)
goto errparam;
cbkarg = va_arg(ap, void *);
break;
}
va_end(ap);
-
+
if (!word) {
if (block)
debug(DBG_ERR, "configuration error in block %s, unknown option %s", block, opt);
goto errexit;
}
- if (((type == CONF_STR || type == CONF_MSTR || type == CONF_BLN) && conftype != CONF_STR) ||
+ if (((type == CONF_STR || type == CONF_MSTR || type == CONF_BLN || type == CONF_LINT) && conftype != CONF_STR) ||
(type == CONF_CBK && conftype != CONF_CBK)) {
if (block)
debug(DBG_ERR, "configuration error in block %s, wrong syntax for option %s", block, opt);
debug(DBG_ERR, "configuration error, option %s already set to %s", opt, *str);
goto errexit;
}
+ unhex(val);
*str = val;
break;
case CONF_MSTR:
debug(DBG_ERR, "malloc failed");
goto errexit;
}
+ unhex(val);
newmstr[n] = val;
newmstr[n + 1] = NULL;
*mstr = newmstr;
goto errexit;
}
break;
+ case CONF_LINT:
+ endptr = NULL;
+ *lint = strtol(val, &endptr, 0);
+ if (*lint == LONG_MIN || *lint == LONG_MAX || !endptr || endptr == val || *endptr != '\0') {
+ if (block)
+ debug(DBG_ERR, "configuration error in block %s, value for option %s must be an integer, not %s", block, opt, val);
+ else
+ debug(DBG_ERR, "configuration error, value for option %s must be an integer, not %s", opt, val);
+ goto errexit;
+ }
+ break;
case CONF_CBK:
optval = malloc(strlen(opt) + strlen(val) + 2);
if (!optval) {
goto errexit;
}
sprintf(optval, "%s %s", opt, val);
- cbk(cf, cbkarg, optval, opt, val);
+ if (!cbk(cf, cbkarg, optval, opt, val)) {
+ free(optval);
+ goto errexit;
+ }
free(val);
free(optval);
continue;
}
if (block)
debug(DBG_DBG, "getgenericconfig: block %s: %s = %s", block, opt, val);
- else
+ else
debug(DBG_DBG, "getgenericconfig: %s = %s", opt, val);
- if (type == CONF_BLN)
+ if (type == CONF_BLN || type == CONF_LINT)
free(val);
}
- errparam:
+errparam:
debug(DBG_ERR, "getgenericconfig: internal parameter error");
- errexit:
+errexit:
free(opt);
free(val);
return 0;
}
+
+/* Local Variables: */
+/* c-file-style: "stroustrup" */
+/* End: */