+int pushgconfdata(struct gconffile **cf, const char *data) {
+ int i;
+ struct gconffile *newcf;
+
+ if (!*cf) {
+ newcf = malloc(sizeof(struct gconffile) * 2);
+ if (!newcf)
+ return 0;
+ memset(newcf, 0, sizeof(struct gconffile) * 2);
+ } else {
+ for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
+ newcf = realloc(*cf, sizeof(struct gconffile) * (i + 2));
+ if (!newcf)
+ return 0;
+ memmove(newcf + 1, newcf, sizeof(struct gconffile) * (i + 1));
+ memset(newcf, 0, sizeof(struct gconffile));
+ }
+ newcf[0].data = data;
+ *cf = newcf;
+ return 1;
+}
+
+FILE *pushgconffile(struct gconffile **cf, FILE *file, const char *description) {
+ int i;
+ struct gconffile *newcf;
+ char *desc;
+
+ if (!file) {
+ debug(DBG_INFO, "could not read config from %s", description);
+ return NULL;
+ }
+ debug(DBG_DBG, "reading config from %s", description);
+
+ desc = stringcopy(description, 0);
+ if (!desc)
+ goto errmalloc;
+
+ if (!*cf) {
+ newcf = malloc(sizeof(struct gconffile) * 2);
+ if (!newcf)
+ goto errmalloc;
+ memset(newcf, 0, sizeof(struct gconffile) * 2);
+ } else {
+ for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
+ newcf = realloc(*cf, sizeof(struct gconffile) * (i + 2));
+ if (!newcf)
+ goto errmalloc;
+ memmove(newcf + 1, newcf, sizeof(struct gconffile) * (i + 1));
+ memset(newcf, 0, sizeof(struct gconffile));
+ }
+ newcf[0].file = file;
+ newcf[0].path = desc;
+ *cf = newcf;
+ return file;
+
+errmalloc:
+ free(desc);
+ fclose(file);
+ debug(DBG_ERR, "malloc failed");
+ return NULL;
+}
+
+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;
+ else {
+ /* dirname may modify its argument */
+ curfile = stringcopy((*cf)->path, 0);
+ if (!curfile) {
+ debug(DBG_ERR, "malloc failed");
+ goto exit;
+ }
+ dir = dirname(curfile);
+ path = malloc(strlen(dir) + strlen(cfgpath) + 2);
+ if (!path) {
+ debug(DBG_ERR, "malloc failed");
+ goto exit;
+ }
+ strcpy(path, dir);
+ path[strlen(dir)] = '/';
+ strcpy(path + strlen(dir) + 1, cfgpath);
+ }
+ memset(&globbuf, 0, sizeof(glob_t));
+ if (glob(path, 0, NULL, &globbuf)) {
+ debug(DBG_WARN, "could not glob %s", path);
+ goto exit;
+ }
+
+ for (i = globbuf.gl_pathc - 1; i >= 0; i--) {
+ f = pushgconfpath(cf, globbuf.gl_pathv[i]);
+ if (!f)
+ break;
+ }
+ globfree(&globbuf);
+
+exit:
+ if (curfile) {
+ free(curfile);
+ free(path);
+ }
+ return f;
+}
+
+int popgconf(struct gconffile **cf) {
+ int i;
+
+ if (!*cf)
+ return 0;
+ for (i = 0; (*cf)[i].data || (*cf)[i].path; i++);
+ if (i && (*cf)[0].file) {
+ fclose((*cf)[0].file);
+ if ((*cf)[0].path) {
+ debug(DBG_DBG, "closing config file %s", (*cf)[0].path);
+ free((*cf)[0].path);
+ }
+ }
+ if (i < 2) {
+ free(*cf);
+ *cf = NULL;
+ return 0;
+ }
+ memmove(*cf, *cf + 1, sizeof(struct gconffile) * i);
+ 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);
+ if ((*cf)[i].path) {
+ debug(DBG_DBG, "closing config file %s", (*cf)[i].path);
+ free((*cf)[i].path);
+ }
+ }
+ }
+ free(*cf);
+ *cf = NULL;
+}
+
+struct gconffile *openconfigfile(const char *file) {
+ struct gconffile *cf = NULL;
+
+ if (!pushgconfpath(&cf, file)) {
+ debug(DBG_ERR, "could not read config file %s\n%s", file, strerror(errno));
+ return NULL;
+ }
+ debug(DBG_DBG, "reading config file %s", file);
+ return cf;
+}
+