moved genericconfig to gconfig.c and reorganised header files
[radsecproxy.git] / gconfig.c
1 /*
2  * Copyright (C) 2007 Stig Venaas <venaas@uninett.no>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  */
8
9 #include <string.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/types.h>
14 #include "debug.h"
15 #include "util.h"
16 #include "gconfig.h"
17
18 /* returns NULL on error, where to continue parsing if token and ok. E.g. "" will return token with empty string */
19 char *strtokenquote(char *s, char **token, char *del, char *quote, char *comment) {
20     char *t = s, *q, *r;
21
22     if (!t || !token || !del)
23         return NULL;
24     while (*t && strchr(del, *t))
25         t++;
26     if (!*t || (comment && strchr(comment, *t))) {
27         *token = NULL;
28         return t + 1; /* needs to be non-NULL, but value doesn't matter */
29     }
30     if (quote && (q = strchr(quote, *t))) {
31         t++;
32         r = t;
33         while (*t && *t != *q)
34             t++;
35         if (!*t || (t[1] && !strchr(del, t[1])))
36             return NULL;
37         *t = '\0';
38         *token = r;
39         return t + 1;
40     }
41     *token = t;
42     t++;
43     while (*t && !strchr(del, *t))
44         t++;
45     *t = '\0';
46     return t + 1;
47 }
48
49 /* Parses config with following syntax:
50  * One of these:
51  * option-name value
52  * option-name = value
53  * Or:
54  * option-name value {
55  *     option-name [=] value
56  *     ...
57  * }
58  */
59 void getgenericconfig(FILE *f, char *block, ...) {
60     va_list ap;
61     char line[1024];
62     /* initialise lots of stuff to avoid stupid compiler warnings */
63     char *tokens[3], *s, *opt = NULL, *val = NULL, *word, *optval, **str = NULL, ***mstr = NULL;
64     int type = 0, tcount, conftype = 0, n;
65     void (*cbk)(FILE *, char *, char *, char *) = NULL;
66         
67     while (fgets(line, 1024, f)) {
68         s = line;
69         for (tcount = 0; tcount < 3; tcount++) {
70             s = strtokenquote(s, &tokens[tcount], " \t\r\n", "\"'", tcount ? NULL : "#");
71             if (!s)
72                 debugx(1, DBG_ERR, "Syntax error in line starting with: %s", line);
73             if (!tokens[tcount])
74                 break;
75         }
76         if (!tcount || **tokens == '#')
77             continue;
78
79         if (**tokens == '}') {
80             if (block)
81                 return;
82             debugx(1, DBG_ERR, "configuration error, found } with no matching {");
83         }
84
85         switch (tcount) {
86         case 2:
87             opt = tokens[0];
88             val = tokens[1];
89             conftype = CONF_STR;
90             break;
91         case 3:
92             if (tokens[1][0] == '=' && tokens[1][1] == '\0') {
93                 opt = tokens[0];
94                 val = tokens[2];
95                 conftype = CONF_STR;
96                 break;
97             }
98             if (tokens[2][0] == '{' && tokens[2][1] == '\0') {
99                 opt = tokens[0];
100                 val = tokens[1];
101                 conftype = CONF_CBK;
102                 break;
103             }
104             /* fall through */
105         default:
106             if (block)
107                 debugx(1, DBG_ERR, "configuration error in block %s, line starting with %s", block, tokens[0]);
108             debugx(1, DBG_ERR, "configuration error, syntax error in line starting with %s", tokens[0]);
109         }
110
111         if (!*val)
112             debugx(1, DBG_ERR, "configuration error, option %s needs a non-empty value", opt);
113         
114         va_start(ap, block);
115         while ((word = va_arg(ap, char *))) {
116             type = va_arg(ap, int);
117             switch (type) {
118             case CONF_STR:
119                 str = va_arg(ap, char **);
120                 if (!str)
121                     debugx(1, DBG_ERR, "getgeneralconfig: internal parameter error");
122                 break;
123             case CONF_MSTR:
124                 mstr = va_arg(ap, char ***);
125                 if (!mstr)
126                     debugx(1, DBG_ERR, "getgeneralconfig: internal parameter error");
127                 break;
128             case CONF_CBK:
129                 cbk = va_arg(ap, void (*)(FILE *, char *, char *, char *));
130                 break;
131             default:
132                 debugx(1, DBG_ERR, "getgeneralconfig: internal parameter error");
133             }
134             if (!strcasecmp(opt, word))
135                 break;
136         }
137         va_end(ap);
138         
139         if (!word) {
140             if (block)
141                 debugx(1, DBG_ERR, "configuration error in block %s, unknown option %s", block, opt);
142             debugx(1, DBG_ERR, "configuration error, unknown option %s", opt);
143         }
144
145         if (((type == CONF_STR || type == CONF_MSTR) && conftype != CONF_STR) ||
146             (type == CONF_CBK && conftype != CONF_CBK)) {
147             if (block)
148                 debugx(1, DBG_ERR, "configuration error in block %s, wrong syntax for option %s", block, opt);
149             debugx(1, DBG_ERR, "configuration error, wrong syntax for option %s", opt);
150         }
151
152         switch (type) {
153         case CONF_STR:
154             if (block)
155                 debug(DBG_DBG, "getgeneralconfig: block %s: %s = %s", block, opt, val);
156             else 
157                 debug(DBG_DBG, "getgeneralconfig: %s = %s", opt, val);
158             if (*str)
159                 debugx(1, DBG_ERR, "configuration error, option %s already set to %s", opt, *str);
160             *str = stringcopy(val, 0);
161             if (!*str)
162                 debugx(1, DBG_ERR, "malloc failed");
163             break;
164         case CONF_MSTR:
165             if (block)
166                 debug(DBG_DBG, "getgeneralconfig: block %s: %s = %s", block, opt, val);
167             else 
168                 debug(DBG_DBG, "getgeneralconfig: %s = %s", opt, val);
169             if (*mstr)
170                 for (n = 0; (*mstr)[n]; n++);
171             else
172                 n = 0;
173             *mstr = realloc(*mstr, sizeof(char *) * (n + 2));
174             if (!*mstr)
175                 debugx(1, DBG_ERR, "malloc failed");
176             (*mstr)[n] = stringcopy(val, 0);
177             (*mstr)[n + 1] = NULL;
178             break;
179         case CONF_CBK:
180             optval = malloc(strlen(opt) + strlen(val) + 2);
181             if (!optval)
182                 debugx(1, DBG_ERR, "malloc failed");
183             sprintf(optval, "%s %s", opt, val);
184             cbk(f, optval, opt, val);
185             free(optval);
186             break;
187         default:
188             debugx(1, DBG_ERR, "getgeneralconfig: internal parameter error");
189         }
190     }
191 }