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