Version 0.1.12 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                                 memset(*_tmpc, 0, sizeof(struct ast_category));
181                                 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
182                                 (*_tmpc)->root =  NULL;
183                                 (*_tmpc)->next = tmp->root;
184                                 tmp->root = *_tmpc;
185                                 *_last =  NULL;
186                         } else {
187                                 ast_log(LOG_WARNING, 
188                                         "parse error: no closing ']', line %d of %s\n", lineno, configfile);
189                         }
190                 } else if (cur[0] == '#') {
191                         /* A directive */
192                         cur++;
193                         c = cur;
194                         while(*c && (*c > 32)) c++;
195                         if (*c) {
196                                 *c = '\0';
197                                 c++;
198                                 /* Find real argument */
199                                 while(*c  && (*c < 33)) c++;
200                                 if (!*c)
201                                         c = NULL;
202                         } else 
203                                 c = NULL;
204                         if (!strcasecmp(cur, "include")) {
205                                 /* A #include */
206                                 if (c) {
207                                         while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
208                                         /* Get rid of leading mess */
209                                         cur = c;
210                                         while(strlen(cur)) {
211                                                 c = cur + strlen(cur) - 1;
212                                                 if ((*c == '>') || (*c == '<') || (*c == '\"'))
213                                                         *c = '\0';
214                                                 else
215                                                         break;
216                                         }
217                                         if (includelevel < MAX_INCLUDE_LEVEL) {
218                                                 __ast_load(cur, tmp, _tmpc, _last, includelevel + 1);
219                                         } else 
220                                                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
221                                 } else
222                                         ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
223                                 /* Strip off leading and trailing "'s and <>'s */
224                         } else 
225                                 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
226                 } else {
227                         /* Just a line (variable = value) */
228                         if (!*_tmpc) {
229                                 ast_log(LOG_WARNING,
230                                         "parse error: No category context for line %d of %s\n", lineno, configfile);
231                         }
232                         c = strchr(cur, '=');
233                         if (c) {
234                                 *c = 0;
235                                 c++;
236                                 /* Ignore > in => */
237                                 if (*c== '>')
238                                         c++;
239                                 v = malloc(sizeof(struct ast_variable));
240                                 if (v) {
241                                         memset(v, 0, sizeof(struct ast_variable));
242                                         v->next = NULL;
243                                         v->name = strdup(strip(cur));
244                                         v->value = strdup(strip(c));
245                                         v->lineno = lineno;
246                                         if (*_last)  
247                                                 (*_last)->next = v;
248                                         else
249                                                 (*_tmpc)->root = v;
250                                         *_last = v;
251                                 } else {
252                                         ast_destroy(tmp);
253                                         ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
254                                         return -1;
255                                 }
256                         } else {
257                                 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
258                         }
259                                                                                                                 
260                 }
261         }
262         return 0;
263 }
264
265 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel)
266 {
267         char fn[256];
268         char buf[256];
269         FILE *f;
270         int lineno=0;
271
272         if (configfile[0] == '/') {
273                 strncpy(fn, configfile, sizeof(fn)-1);
274         } else {
275                 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
276         }
277         if ((option_verbose > 1) && !option_debug) {
278                 ast_verbose(  VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
279                 fflush(stdout);
280         }
281         if ((f = fopen(fn, "r"))) {
282                 if (option_debug)
283                         ast_log(LOG_DEBUG, "Parsing %s\n", fn);
284                 else if (option_verbose > 1)
285                         ast_verbose( "Found\n");
286                 if (!tmp) {
287                         tmp = malloc(sizeof(struct ast_config));
288                         if (tmp)
289                                 memset(tmp, 0, sizeof(struct ast_config));
290                 }
291                 if (!tmp) {
292                         ast_log(LOG_WARNING, "Out of memory\n");
293                         fclose(f);
294                         return NULL;
295                 }
296                 while(!feof(f)) {
297                         fgets(buf, sizeof(buf), f);
298                         lineno++;
299                         if (!feof(f)) {
300                                 if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel)) {
301                                         fclose(f);
302                                         return NULL;
303                                 }
304                         }
305                 }
306                 fclose(f);              
307         } else {
308                 if (option_debug)
309                         ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
310                 else if (option_verbose > 1)
311                         ast_verbose( "Not found (%s)", strerror(errno));
312         }
313         return tmp;
314 }
315
316 struct ast_config *ast_load(char *configfile)
317 {
318         struct ast_category *tmpc=NULL;
319         struct ast_variable *last = NULL;
320         return __ast_load(configfile, NULL, &tmpc, &last, 0);
321 }
322
323 char *ast_category_browse(struct ast_config *config, char *prev)
324 {       
325         struct ast_category *cat;
326         if (!prev) {
327                 if (config->root)
328                         return config->root->name;
329                 else
330                         return NULL;
331         }
332         cat = config->root;
333         while(cat) {
334                 if (cat->name == prev) {
335                         if (cat->next)
336                                 return cat->next->name;
337                         else
338                                 return NULL;
339                 }
340                 cat = cat->next;
341         }
342         cat = config->root;
343         while(cat) {
344                 if (!strcasecmp(cat->name, prev)) {
345                         if (cat->next)
346                                 return cat->next->name;
347                         else
348                                 return NULL;
349                 }
350                 cat = cat->next;
351         }
352         return NULL;
353 }