114d6584aa5edc6ebdb9da7081354b5e6839e5a5
[asterisk/asterisk.git] / config.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Configuration File Parser
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <asterisk/config.h>
20 #include <asterisk/options.h>
21 #include <asterisk/logger.h>
22 #include "asterisk.h"
23 #include "astconf.h"
24
25 #define MAX_INCLUDE_LEVEL 10
26
27 struct ast_category {
28         char name[80];
29         struct ast_variable *root;
30         struct ast_category *next;
31 };
32
33 struct ast_config {
34         /* Maybe this structure isn't necessary but we'll keep it
35            for now */
36         struct ast_category *root;
37 };
38
39 static char *strip(char *buf)
40 {
41         char *start;
42         /* Strip off trailing whitespace, returns, etc */
43         while(strlen(buf) && (buf[strlen(buf)-1]<33))
44                 buf[strlen(buf)-1] = '\0';
45         start = buf;
46         /* Strip off leading whitespace, returns, etc */
47         while(*start && (*start < 33))
48                 *start++ = '\0';
49         return start;
50 }
51
52 void ast_destroy(struct ast_config *ast)
53 {
54         struct ast_category *cat, *catn;
55         struct ast_variable *v, *vn;
56
57         if (!ast)
58                 return;
59
60         cat = ast->root;
61         while(cat) {
62                 v = cat->root;
63                 while(v) {
64                         vn = v;
65                         free(v->name);
66                         free(v->value);
67                         v = v->next;
68                         free(vn);
69                 }
70                 catn = cat;
71                 cat = cat->next;
72                 free(catn);
73         }
74         free(ast);
75 }
76
77 int ast_true(char *s)
78 {
79         if (!s)
80                 return 0;
81         /* Determine if this is a true value */
82         if (!strcasecmp(s, "yes") ||
83             !strcasecmp(s, "true") ||
84                 !strcasecmp(s, "y") ||
85                 !strcasecmp(s, "t") ||
86                 !strcasecmp(s, "1"))
87                         return -1;
88         return 0;
89 }
90
91 struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
92 {
93         struct ast_category *cat;
94         cat = config->root;
95         while(cat) {
96                 if (cat->name == category)
97                         return cat->root;
98                 cat = cat->next;
99         }
100         cat = config->root;
101         while(cat) {
102                 if (!strcasecmp(cat->name, category))
103                         return cat->root;
104                 cat = cat->next;
105         }
106         return NULL;
107 }
108
109 char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
110 {
111         struct ast_variable *v;
112         if (category) {
113                 v = ast_variable_browse(config, category);
114                 while (v) {
115                         if (value == v->name)
116                                 return v->value;
117                         v=v->next;
118                 }
119                 v = ast_variable_browse(config, category);
120                 while (v) {
121                         if (!strcasecmp(value, v->name))
122                                 return v->value;
123                         v=v->next;
124                 }
125         } else {
126                 struct ast_category *cat;
127                 cat = config->root;
128                 while(cat) {
129                         v = cat->root;
130                         while (v) {
131                                 if (!strcasecmp(value, v->name))
132                                         return v->value;
133                                 v=v->next;
134                         }
135                         cat = cat->next;
136                 }
137         }
138         return NULL;
139 }
140
141 int ast_category_exist(struct ast_config *config, char *category_name)
142 {
143         struct ast_category *category = NULL;
144
145         category = config->root;
146
147         while(category) {
148                 if (!strcasecmp(category->name,category_name)) 
149                         return 1;
150                 category = category->next;
151         } 
152
153         return 0;
154 }
155
156 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel);
157 static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, char *configfile, int includelevel)
158 {
159         char *c;
160         char *cur;
161         struct ast_variable *v;
162         /* Strip off lines using ; as comment */
163         c = strchr(buf, ';');
164         if (c)
165                 *c = '\0';
166         cur = strip(buf);
167         if (strlen(cur)) {
168                 /* Actually parse the entry */
169                 if (cur[0] == '[') {
170                         /* A category header */
171                         c = strchr(cur, ']');
172                         if (c) {
173                                 *c = 0;
174                                 *_tmpc = malloc(sizeof(struct ast_category));
175                                 if (!*_tmpc) {
176                                         ast_destroy(tmp);
177                                         ast_log(LOG_WARNING,
178                                                 "Out of memory, line %d\n", lineno);
179                                         return -1;
180                                 }
181                                 memset(*_tmpc, 0, sizeof(struct ast_category));
182                                 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
183                                 (*_tmpc)->root =  NULL;
184                                 (*_tmpc)->next = tmp->root;
185                                 tmp->root = *_tmpc;
186                                 *_last =  NULL;
187                         } else {
188                                 ast_log(LOG_WARNING, 
189                                         "parse error: no closing ']', line %d of %s\n", lineno, configfile);
190                         }
191                 } else if (cur[0] == '#') {
192                         /* A directive */
193                         cur++;
194                         c = cur;
195                         while(*c && (*c > 32)) c++;
196                         if (*c) {
197                                 *c = '\0';
198                                 c++;
199                                 /* Find real argument */
200                                 while(*c  && (*c < 33)) c++;
201                                 if (!*c)
202                                         c = NULL;
203                         } else 
204                                 c = NULL;
205                         if (!strcasecmp(cur, "include")) {
206                                 /* A #include */
207                                 if (c) {
208                                         while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
209                                         /* Get rid of leading mess */
210                                         cur = c;
211                                         while(strlen(cur)) {
212                                                 c = cur + strlen(cur) - 1;
213                                                 if ((*c == '>') || (*c == '<') || (*c == '\"'))
214                                                         *c = '\0';
215                                                 else
216                                                         break;
217                                         }
218                                         if (includelevel < MAX_INCLUDE_LEVEL) {
219                                                 __ast_load(cur, tmp, _tmpc, _last, includelevel + 1);
220                                         } else 
221                                                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
222                                 } else
223                                         ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
224                                 /* Strip off leading and trailing "'s and <>'s */
225                         } else 
226                                 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
227                 } else {
228                         /* Just a line (variable = value) */
229                         if (!*_tmpc) {
230                                 ast_log(LOG_WARNING,
231                                         "parse error: No category context for line %d of %s\n", lineno, configfile);
232                                 ast_destroy(tmp);
233                                 return -1;
234                         }
235                         c = strchr(cur, '=');
236                         if (c) {
237                                 *c = 0;
238                                 c++;
239                                 /* Ignore > in => */
240                                 if (*c== '>')
241                                         c++;
242                                 v = malloc(sizeof(struct ast_variable));
243                                 if (v) {
244                                         memset(v, 0, sizeof(struct ast_variable));
245                                         v->next = NULL;
246                                         v->name = strdup(strip(cur));
247                                         v->value = strdup(strip(c));
248                                         v->lineno = lineno;
249                                         if (*_last)  
250                                                 (*_last)->next = v;
251                                         else
252                                                 (*_tmpc)->root = v;
253                                         *_last = v;
254                                 } else {
255                                         ast_destroy(tmp);
256                                         ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
257                                         return -1;
258                                 }
259                         } else {
260                                 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
261                         }
262                                                                                                                 
263                 }
264         }
265         return 0;
266 }
267
268 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel)
269 {
270         char fn[256];
271         char buf[256];
272         FILE *f;
273         int lineno=0;
274
275         if (configfile[0] == '/') {
276                 strncpy(fn, configfile, sizeof(fn)-1);
277         } else {
278                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
279         }
280         if ((option_verbose > 1) && !option_debug) {
281                 ast_verbose(  VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
282                 fflush(stdout);
283         }
284         if ((f = fopen(fn, "r"))) {
285                 if (option_debug)
286                         ast_log(LOG_DEBUG, "Parsing %s\n", fn);
287                 else if (option_verbose > 1)
288                         ast_verbose( "Found\n");
289                 if (!tmp) {
290                         tmp = malloc(sizeof(struct ast_config));
291                         if (tmp)
292                                 memset(tmp, 0, sizeof(struct ast_config));
293                 }
294                 if (!tmp) {
295                         ast_log(LOG_WARNING, "Out of memory\n");
296                         fclose(f);
297                         return NULL;
298                 }
299                 while(!feof(f)) {
300                         fgets(buf, sizeof(buf), f);
301                         lineno++;
302                         if (!feof(f)) {
303                                 if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel)) {
304                                         fclose(f);
305                                         return NULL;
306                                 }
307                         }
308                 }
309                 fclose(f);              
310         } else {
311                 if (option_debug)
312                         ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
313                 else if (option_verbose > 1)
314                         ast_verbose( "Not found (%s)", strerror(errno));
315         }
316         return tmp;
317 }
318
319 struct ast_config *ast_load(char *configfile)
320 {
321         struct ast_category *tmpc=NULL;
322         struct ast_variable *last = NULL;
323         return __ast_load(configfile, NULL, &tmpc, &last, 0);
324 }
325
326 char *ast_category_browse(struct ast_config *config, char *prev)
327 {       
328         struct ast_category *cat;
329         if (!prev) {
330                 if (config->root)
331                         return config->root->name;
332                 else
333                         return NULL;
334         }
335         cat = config->root;
336         while(cat) {
337                 if (cat->name == prev) {
338                         if (cat->next)
339                                 return cat->next->name;
340                         else
341                                 return NULL;
342                 }
343                 cat = cat->next;
344         }
345         cat = config->root;
346         while(cat) {
347                 if (!strcasecmp(cat->name, prev)) {
348                         if (cat->next)
349                                 return cat->next->name;
350                         else
351                                 return NULL;
352                 }
353                 cat = cat->next;
354         }
355         return NULL;
356 }