Allow on/off (bug #2233)
[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 <time.h>
20 #include <asterisk/config.h>
21 #include <asterisk/config_pvt.h>
22 #include <asterisk/cli.h>
23 #include <asterisk/lock.h>
24 #include <asterisk/options.h>
25 #include <asterisk/logger.h>
26 #include <asterisk/utils.h>
27 #include "asterisk.h"
28 #include "astconf.h"
29
30
31 static int ast_cust_config=0;
32 struct ast_config *(*global_load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
33 #ifdef PRESERVE_COMMENTS                                                                                                                                     
34 ,struct ast_comment_struct *
35 #endif
36 );
37
38 AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
39 static struct ast_config_reg *ast_cust_config_list;
40 static char *config_conf_file = "extconfig.conf";
41
42 static char *strip(char *buf)
43 {
44         char *start;
45         /* Strip off trailing whitespace, returns, etc */
46         while (!ast_strlen_zero(buf) && (buf[strlen(buf)-1]<33))
47                 buf[strlen(buf)-1] = '\0';
48         start = buf;
49         /* Strip off leading whitespace, returns, etc */
50         while (*start && (*start < 33))
51                 *start++ = '\0';
52         return start;
53 }
54
55 #ifdef PRESERVE_COMMENTS
56 static void free_comments(struct ast_comment *com)
57 {
58         struct ast_comment *l;
59         while (com) {
60                 l = com;
61                 com = com->next;
62                 free(l);
63         }
64 }
65 #endif
66
67 void ast_destroy(struct ast_config *ast)
68 {
69         struct ast_category *cat, *catn;
70         struct ast_variable *v, *vn;
71
72         if (!ast)
73                 return;
74
75         cat = ast->root;
76         while(cat) {
77                 v = cat->root;
78                 while(v) {
79                         vn = v;
80                         free(v->name);
81                         free(v->value);
82 #ifdef PRESERVE_COMMENTS
83                         free_comments(v->precomments);
84                         free_comments(v->sameline);
85 #endif                  
86                         v = v->next;
87                         free(vn);
88                 }
89                 catn = cat;
90 #ifdef PRESERVE_COMMENTS
91                 free_comments(cat->precomments);
92                 free_comments(cat->sameline);
93 #endif          
94                 cat = cat->next;
95                 free(catn);
96         }
97 #ifdef PRESERVE_COMMENTS
98         free_comments(ast->trailingcomments);
99 #endif  
100         free(ast);
101 }
102
103 int ast_true(char *s)
104 {
105         if (!s)
106                 return 0;
107         /* Determine if this is a true value */
108         if (!strcasecmp(s, "yes") ||
109             !strcasecmp(s, "true") ||
110                 !strcasecmp(s, "y") ||
111                 !strcasecmp(s, "t") ||
112                 !strcasecmp(s, "1") ||
113                 !strcasecmp(s, "on"))
114                         return -1;
115         return 0;
116 }
117
118 int ast_false(char *s)
119 {
120         if (!s)
121                 return 0;
122         /* Determine if this is a false value */
123         if (!strcasecmp(s, "no") ||
124             !strcasecmp(s, "false") ||
125                 !strcasecmp(s, "n") ||
126                 !strcasecmp(s, "f") ||
127                 !strcasecmp(s, "0") ||
128                 !strcasecmp(s, "off"))
129                         return -1;
130         return 0;
131 }
132
133 struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
134 {
135         struct ast_category *cat;
136         cat = config->root;
137         while(cat) {
138                 if (cat->name == category)
139                         return cat->root;
140                 cat = cat->next;
141         }
142         cat = config->root;
143         while(cat) {
144                 if (!strcasecmp(cat->name, category))
145                         return cat->root;
146                 cat = cat->next;
147         }
148         return NULL;
149 }
150
151 char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
152 {
153         struct ast_variable *v;
154         if (category) {
155                 v = ast_variable_browse(config, category);
156                 while (v) {
157                         if (value == v->name)
158                                 return v->value;
159                         v=v->next;
160                 }
161                 v = ast_variable_browse(config, category);
162                 while (v) {
163                         if (!strcasecmp(value, v->name))
164                                 return v->value;
165                         v=v->next;
166                 }
167         } else {
168                 struct ast_category *cat;
169                 cat = config->root;
170                 while(cat) {
171                         v = cat->root;
172                         while (v) {
173                                 if (!strcasecmp(value, v->name))
174                                         return v->value;
175                                 v=v->next;
176                         }
177                         cat = cat->next;
178                 }
179         }
180         return NULL;
181 }
182
183 #ifdef PRESERVE_COMMENTS
184 int ast_variable_delete(struct ast_config *cfg, char *category, char *variable, char *value)
185 {
186         struct ast_variable *v, *pv, *bv, *bpv;
187         struct ast_category *cat;
188         cat = cfg->root;
189         while(cat) {
190                 if (cat->name == category) {
191                         break;
192                 }
193                 cat = cat->next;
194         }
195         if (!cat) {
196                 cat = cfg->root;
197                 while(cat) {
198                         if (!strcasecmp(cat->name, category)) {
199                                 break;
200                         }
201                         cat = cat->next;
202                 }
203         }
204         if (!cat)
205                 return -1;
206         v = cat->root;
207         pv = NULL;
208         while (v) {
209                 if ((variable == v->name) && (!value || !strcmp(v->value, value)))
210                         break;
211                 pv = v;
212                 v=v->next;
213         }
214         if (!v) {
215                 /* Get the last one that looks like it */
216                 bv = NULL;
217                 bpv = NULL;
218                 v = cat->root;
219                 pv = NULL;
220                 while (v) {
221                         if (!strcasecmp(variable, v->name) && (!value || !strcmp(v->value, value))) {
222                                 bv = v;
223                                 bpv = pv;
224                         }
225                         pv = v;
226                         v=v->next;
227                 }
228                 v = bv;
229         }
230
231         if (v) {
232                 /* Unlink from original position */
233                 if (pv) 
234                         pv->next = v->next;
235                 else
236                         cat->root = v->next;
237                 v->next = NULL;
238                 free(v->name);
239                 if (v->value)
240                         free(v->value);
241                 free_comments(v->sameline);
242                 free_comments(v->precomments);
243                 return 0;
244         }
245         return -1;
246 }
247
248 int ast_category_delete(struct ast_config *cfg, char *category)
249 {
250         struct ast_variable *v, *pv;
251         struct ast_category *cat, *cprev;
252         cat = cfg->root;
253         cprev = NULL;
254         while(cat) {
255                 if (cat->name == category) {
256                         break;
257                 }
258                 cprev = cat;
259                 cat = cat->next;
260         }
261         if (!cat) {
262                 cat = cfg->root;
263                 cprev = NULL;
264                 while(cat) {
265                         if (!strcasecmp(cat->name, category)) {
266                                 break;
267                         }
268                         cprev = cat;
269                         cat = cat->next;
270                 }
271         }
272         if (!cat)
273                 return -1;
274         /* Unlink it */
275         if (cprev)
276                 cprev->next = cat->next;
277         else
278                 cfg->root = cat->next;
279         v = cat->root;
280         while (v) {
281                 pv = v;
282                 v=v->next;
283                 if (pv->value)
284                         free(pv->value);
285                 if (pv->name)
286                         free(pv->name);
287                 free_comments(pv->sameline);
288                 free_comments(pv->precomments);
289                 free(pv);
290         }
291         free_comments(cat->sameline);
292         free_comments(cat->precomments);
293         free(cat);
294         return 0;
295 }
296
297 struct ast_variable *ast_variable_append_modify(struct ast_config *config, char *category, char *variable, char *value, int newcat, int newvar, int move)
298 {
299         struct ast_variable *v, *pv=NULL, *bv, *bpv;
300         struct ast_category *cat, *pcat;
301         cat = config->root;
302         if (!newcat) {
303                 while(cat) {
304                         if (cat->name == category) {
305                                 break;
306                         }
307                         cat = cat->next;
308                 }
309                 if (!cat) {
310                         cat = config->root;
311                         while(cat) {
312                                 if (!strcasecmp(cat->name, category)) {
313                                         break;
314                                 }
315                                 cat = cat->next;
316                         }
317                 }
318         }
319         if (!cat) {
320                 cat = malloc(sizeof(struct ast_category));
321                 if (!cat)
322                         return NULL;
323                 memset(cat, 0, sizeof(struct ast_category));
324                 strncpy(cat->name, category, sizeof(cat->name) - 1);
325                 if (config->root) {
326                         /* Put us at the end */
327                         pcat = config->root;
328                         while(pcat->next)
329                                 pcat = pcat->next;
330                         pcat->next = cat;
331                 } else {
332                         /* We're the first one */
333                         config->root = cat;
334                 }
335                         
336         }
337         if (!newvar) {
338                 v = cat->root;
339                 pv = NULL;
340                 while (v) {
341                         if (variable == v->name)
342                                 break;
343                         pv = v;
344                         v=v->next;
345                 }
346                 if (!v) {
347                         /* Get the last one that looks like it */
348                         bv = NULL;
349                         bpv = NULL;
350                         v = cat->root;
351                         pv = NULL;
352                         while (v) {
353                                 if (!strcasecmp(variable, v->name)) {
354                                         bv = v;
355                                         bpv = pv;
356                                 }
357                                 pv = v;
358                                 v=v->next;
359                         }
360                         v = bv;
361                 }
362         } else v = NULL;
363         if (v && move) {
364                 /* Unlink from original position */
365                 if (pv) 
366                         pv->next = v->next;
367                 else
368                         cat->root = v->next;
369                 v->next = NULL;
370         }
371         if (!v) {
372                 v = malloc(sizeof(struct ast_variable));
373                 if (!v)
374                         return NULL;
375                 memset(v, 0, sizeof(struct ast_variable));
376                 v->name = strdup(variable);
377                 move = 1;
378         }
379         if (v->value)
380                 free(v->value);
381         if (value)
382                 v->value = strdup(value);
383         else
384                 v->value = strdup("");
385         if (move) {
386                 if (cat->root) {
387                         pv = cat->root;
388                         while (pv->next) 
389                                 pv = pv->next;
390                         pv->next = v;
391                 } else {
392                         cat->root = v;
393                 }
394         }
395         return v;
396 }
397 #endif          
398
399 int ast_category_exist(struct ast_config *config, char *category_name)
400 {
401         struct ast_category *category = NULL;
402
403         category = config->root;
404
405         while(category) {
406                 if (!strcasecmp(category->name,category_name)) 
407                         return 1;
408                 category = category->next;
409         } 
410
411         return 0;
412 }
413
414 #ifdef PRESERVE_COMMENTS
415 static struct ast_comment *build_comment(char *cmt)
416 {
417         struct ast_comment *c;
418         int len = strlen(cmt) + 1;
419         c = malloc(sizeof(struct ast_comment) + len);
420         if (c) {
421                 /* Memset the header */
422                 memset(c, 0, sizeof(struct ast_comment));
423                 /* Copy the rest */
424                 strcpy(c->cmt, cmt);
425         }
426         return c;
427 }
428 #endif
429
430 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
431 #ifdef PRESERVE_COMMENTS
432 , struct ast_comment_struct *acs
433 #endif
434 );
435
436 static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, char *configfile, int includelevel 
437 #ifdef PRESERVE_COMMENTS
438 ,struct ast_comment_struct *acs
439 #endif
440 )
441 {
442         char *c;
443         char *cur;
444         char *arg=NULL;
445         struct ast_config_reg *reg=NULL;
446         struct ast_config *(*load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
447 #ifdef PRESERVE_COMMENTS
448 ,struct ast_comment_struct *
449 #endif
450         );
451         struct ast_variable *v;
452 #ifdef PRESERVE_COMMENTS
453         struct ast_comment *com = NULL;
454 #endif  
455         int object;
456         /* Strip off lines using ; as comment */
457         c = strchr(buf, ';');
458         if (c) {
459                 *c = '\0';
460 #ifdef PRESERVE_COMMENTS
461                 c++;
462                 if (*c != '!')
463                         com = build_comment(c);
464 #endif                  
465         }
466         cur = strip(buf);
467         if (!ast_strlen_zero(cur)) {
468                 /* Actually parse the entry */
469                 if (cur[0] == '[') {
470                         /* A category header */
471                         c = strchr(cur, ']');
472                         if (c) {
473                                 *c = 0;
474                                 *_tmpc = malloc(sizeof(struct ast_category));
475                                 if (!*_tmpc) {
476                                         ast_destroy(tmp);
477                                         ast_log(LOG_WARNING,
478                                                 "Out of memory, line %d\n", lineno);
479                                         return -1;
480                                 }
481                                 memset(*_tmpc, 0, sizeof(struct ast_category));
482                                 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
483                                 (*_tmpc)->root =  NULL;
484 #ifdef PRESERVE_COMMENTS
485                                 (*_tmpc)->precomments = acs->root;
486                                 (*_tmpc)->sameline = com;
487 #endif                          
488                                 if (!tmp->prev)
489                                         tmp->root = *_tmpc;
490                                 else
491                                         tmp->prev->next = *_tmpc;
492
493                                 tmp->prev = *_tmpc;
494 #ifdef PRESERVE_COMMENTS
495                                 acs->root = NULL;
496                                 acs->prev = NULL;
497 #endif                          
498                                 *_last =  NULL;
499                         } else {
500                                 ast_log(LOG_WARNING, 
501                                         "parse error: no closing ']', line %d of %s\n", lineno, configfile);
502                         }
503                 } else if (cur[0] == '#') {
504                         /* A directive */
505                         cur++;
506                         c = cur;
507                         while(*c && (*c > 32)) c++;
508                         if (*c) {
509                                 *c = '\0';
510                                 c++;
511                                 /* Find real argument */
512                                 while(*c  && (*c < 33)) c++;
513                                 if (!*c)
514                                         c = NULL;
515                         } else 
516                                 c = NULL;
517                         if (!strcasecmp(cur, "include")) {
518                                 /* A #include */
519                                 if (c) {
520                                         while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
521                                         /* Get rid of leading mess */
522                                         cur = c;
523                                         while (!ast_strlen_zero(cur)) {
524                                                 c = cur + strlen(cur) - 1;
525                                                 if ((*c == '>') || (*c == '<') || (*c == '\"'))
526                                                         *c = '\0';
527                                                 else
528                                                         break;
529                                         }
530                                         
531                                         if((c = strchr(cur,':'))) {
532                                                 *c = '\0';
533                                                 c++;
534                                                 arg = c;
535                                         }
536                                         
537                                         if (includelevel < MAX_INCLUDE_LEVEL) {
538                                                 if(arg && cur) {
539                                                         load_func = NULL;
540                                                         if(ast_cust_config_list)
541                                                                 reg = get_ast_cust_config(cur);
542                                                         if(reg && reg->func)
543                                                                 load_func = reg->func;
544                                                         if(load_func) { 
545                                                                 ast_log(LOG_NOTICE,"External Include '%s' via '%s' config engine\n",arg,cur);
546                                                                 load_func(arg,tmp, _tmpc, _last, includelevel
547 #ifdef PRESERVE_COMMENTS
548                                                                                   ,&acs
549 #endif
550                                                                                   );
551                                                         }
552                                                         else 
553                                                                 ast_log(LOG_WARNING,"Cant Find Registered Config Engine [%s] for [%s]\n",cur,arg);
554                                                 }
555                                                 else {
556                                                         __ast_load(cur, tmp, _tmpc, _last, includelevel + 1
557 #ifdef PRESERVE_COMMENTS
558                                                                            ,acs
559 #endif
560                                                                            );
561                                                 }
562                                         } else
563                                                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
564                                 } else
565                                         ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
566                                 /* Strip off leading and trailing "'s and <>'s */
567                         }
568                         else 
569                                 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
570                 } else {
571                         /* Just a line (variable = value) */
572                         if (!*_tmpc) {
573                                 ast_log(LOG_WARNING,
574                                         "parse error: No category context for line %d of %s\n", lineno, configfile);
575                                 ast_destroy(tmp);
576                                 return -1;
577                         }
578                         c = strchr(cur, '=');
579                         if (c) {
580                                 *c = 0;
581                                 c++;
582                                 /* Ignore > in => */
583                                 if (*c== '>') {
584                                         object = 1;
585                                         c++;
586                                 } else
587                                         object = 0;
588                                 v = malloc(sizeof(struct ast_variable));
589                                 if (v) {
590                                         memset(v, 0, sizeof(struct ast_variable));
591                                         v->next = NULL;
592                                         v->name = strdup(strip(cur));
593                                         v->value = strdup(strip(c));
594                                         v->lineno = lineno;
595                                         v->object = object;
596                                         /* Put and reset comments */
597 #ifdef PRESERVE_COMMENTS
598                                         v->precomments = acs->root;
599                                         v->sameline = com;
600                                         acs->prev = NULL;
601                                         acs->root = NULL;
602 #endif                                  
603                                         v->blanklines = 0;
604                                         if (*_last)
605                                                 (*_last)->next = v;
606                                         else
607                                                 (*_tmpc)->root = v;
608                                         *_last = v;
609                                 } else {
610                                         ast_destroy(tmp);
611                                         ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
612                                         return -1;
613                                 }
614                         } else {
615                                 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
616                         }
617                                                                                                                 
618                 }
619         } else {
620                 /* store any comments if there are any */
621 #ifdef PRESERVE_COMMENTS
622                 if (com) {
623                         if (acs->prev)
624                                 acs->prev->next = com;
625                         else
626                                 acs->root = com;
627                         acs->prev = com;
628                 } else {
629                         if (*_last) 
630                                 (*_last)->blanklines++;
631                 }
632 #endif
633         }
634         return 0;
635 }
636
637 #ifdef PRESERVE_COMMENTS
638 static void dump_comments(FILE *f, struct ast_comment *comment)
639 {
640         while (comment) {
641                 fprintf(f, ";%s", comment->cmt);
642                 comment = comment->next;
643         }
644 }
645 #endif
646
647 int ast_save(char *configfile, struct ast_config *cfg, char *generator)
648 {
649         FILE *f;
650         char fn[256];
651         char date[256]="";
652         time_t t;
653         struct ast_variable *var;
654         struct ast_category *cat;
655         int blanklines = 0;
656         if (configfile[0] == '/') {
657                 strncpy(fn, configfile, sizeof(fn)-1);
658         } else {
659                 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
660         }
661         time(&t);
662         strncpy(date, ctime(&t), sizeof(date) - 1);
663         if ((f = fopen(fn, "w"))) {
664                 if ((option_verbose > 1) && !option_debug)
665                         ast_verbose(  VERBOSE_PREFIX_2 "Saving '%s': ", fn);
666                 fprintf(f, ";!\n");
667                 fprintf(f, ";! Automatically generated configuration file\n");
668                 fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
669                 fprintf(f, ";! Generator: %s\n", generator);
670                 fprintf(f, ";! Creation Date: %s", date);
671                 fprintf(f, ";!\n");
672                 cat = cfg->root;
673                 while(cat) {
674 #ifdef PRESERVE_COMMENTS
675                         /* Dump any precomments */
676                         dump_comments(f, cat->precomments);
677 #endif
678                         /* Dump section with any appropriate comment */
679 #ifdef PRESERVE_COMMENTS
680                         if (cat->sameline) 
681                                 fprintf(f, "[%s]  ; %s\n", cat->name, cat->sameline->cmt);
682                         else
683 #endif
684                                 fprintf(f, "[%s]\n", cat->name);
685                         var = cat->root;
686                         while(var) {
687 #ifdef PRESERVE_COMMENTS
688                                 dump_comments(f, var->precomments);
689 #endif                          
690                                 if (var->sameline) 
691                                         fprintf(f, "%s %s %s  ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
692                                 else    
693                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
694                                 if (var->blanklines) {
695                                         blanklines = var->blanklines;
696                                         while (blanklines) {
697                                                 fprintf(f, "\n");
698                                                 blanklines--;
699                                         }
700                                 }
701                                         
702                                 var = var->next;
703                         }
704 #if 0
705                         /* Put an empty line */
706                         fprintf(f, "\n");
707 #endif
708                         cat = cat->next;
709                 }
710 #ifdef PRESERVE_COMMENTS
711                 dump_comments(f, cfg->trailingcomments);
712 #endif          
713         } else {
714                 if (option_debug)
715                         printf("Unable to open for writing: %s\n", fn);
716                 else if (option_verbose > 1)
717                         printf( "Unable to write (%s)", strerror(errno));
718                 return -1;
719         }
720         fclose(f);
721         return 0;
722 }
723
724 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
725 #ifdef PRESERVE_COMMENTS
726 , struct ast_comment_struct *acs
727 #endif
728 )
729 {
730         char fn[256];
731         char buf[8192];
732         FILE *f;
733         int lineno=0;
734         int master=0;
735         struct ast_config_reg *reg=NULL;
736         struct ast_config *(*load_func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
737 #ifdef PRESERVE_COMMENTS
738 ,struct ast_comment_struct *
739 #endif
740 );
741
742         load_func=NULL;
743         if (strcmp(configfile,config_conf_file) && strcmp(configfile,"asterisk.conf") && ast_cust_config_list) {
744                 if (global_load_func) {
745                         load_func = global_load_func;
746                 } else {
747                         reg = get_ast_cust_config_keyword(configfile);
748                         if (reg && reg->func) {
749                                 load_func = reg->func;
750                         } else {
751                                 reg = get_ast_cust_config_keyword("global");
752                                 if (reg && reg->func)
753                                         global_load_func = load_func = reg->func;
754                         }
755                 }
756
757                 if (load_func) {
758                         ast_log(LOG_NOTICE,"Loading Config %s via %s engine\n",configfile,reg && reg->name ? reg->name : "global");
759                         tmp = load_func(configfile,tmp, _tmpc, _last, includelevel
760 #ifdef PRESERVE_COMMENTS
761 ,&acs
762 #endif
763 );
764             
765                         if (tmp)
766                                 return tmp;
767                 }
768         }
769
770         if (configfile[0] == '/') {
771                 strncpy(fn, configfile, sizeof(fn)-1);
772         } else {
773                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
774         }
775         if ((option_verbose > 1) && !option_debug) {
776                 ast_verbose(  VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
777                 fflush(stdout);
778         }
779         if ((f = fopen(fn, "r"))) {
780                 if (option_debug)
781                         ast_log(LOG_DEBUG, "Parsing %s\n", fn);
782                 else if (option_verbose > 1)
783                         ast_verbose( "Found\n");
784                 if (!tmp) {
785                         tmp = malloc(sizeof(struct ast_config));
786                         if (tmp)
787                                 memset(tmp, 0, sizeof(struct ast_config));
788
789                         master = 1;
790                 }
791                 if (!tmp) {
792                         ast_log(LOG_WARNING, "Out of memory\n");
793                         fclose(f);
794                         return NULL;
795                 }
796                 while(!feof(f)) {
797                         lineno++;
798                         if (fgets(buf, sizeof(buf), f)) {
799                                 if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel
800 #ifdef PRESERVE_COMMENTS
801                                 , acs
802 #endif
803                                 )) {
804                                         fclose(f);
805                                         return NULL;
806                                 }
807                         }
808                 }
809                 fclose(f);              
810         } else {
811                 if (option_debug)
812                         ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
813                 else if (option_verbose > 1)
814                         ast_verbose( "Not found (%s)\n", strerror(errno));
815         }
816 #ifdef PRESERVE_COMMENTS
817         if (master) {
818                 /* Keep trailing comments */
819                 tmp->trailingcomments = acs->root;
820                 acs->root = NULL;
821                 acs->prev = NULL;
822         }
823 #endif
824         return tmp;
825 }
826
827 struct ast_config_reg *get_ast_cust_config_keyword(char *name) 
828 {
829         struct ast_config_reg *reg,*ret=NULL;
830         int x=0;
831         ast_mutex_lock(&ast_cust_config_lock);
832         for (reg=ast_cust_config_list;reg && !ret;reg=reg->next) {
833                 for (x=0;x<reg->keycount && !ret ;x++) {
834                         if (!strcmp(reg->keywords[x],name))
835                                 ret=reg;
836                 }
837         }
838         ast_mutex_unlock(&ast_cust_config_lock);
839         return ret;
840 }
841
842 struct ast_config_reg *get_ast_cust_config(char *name) 
843 {
844         struct ast_config_reg *ptr=NULL;
845         ast_mutex_lock(&ast_cust_config_lock);
846         for (ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
847                 if (!strcmp(name,ptr->name))
848                         break;
849         }
850         ast_mutex_unlock(&ast_cust_config_lock);
851         return ptr;
852 }
853
854 void ast_config_destroy_all(void) 
855 {
856         struct ast_config_reg *key;
857         ast_mutex_lock(&ast_cust_config_lock);
858         for (key=ast_cust_config_list;key;key=key->next) {
859                 ast_config_deregister(key);
860         }
861         ast_cust_config_list = NULL;
862         ast_mutex_unlock(&ast_cust_config_lock);
863 }
864
865 struct ast_config_reg *get_config_registrations(void) 
866 {
867         return ast_cust_config_list;
868 }
869
870 int ast_config_register(struct ast_config_reg *new) 
871 {
872         struct ast_config_reg *ptr;
873         ast_mutex_lock(&ast_cust_config_lock);
874         new->keycount = 0;
875         if (!ast_cust_config_list) {
876                 ast_cust_config_list = new;
877         } else {
878                 for(ptr=ast_cust_config_list;ptr->next;ptr=ptr->next);
879                         ptr->next = new;
880         }
881         ast_mutex_unlock(&ast_cust_config_lock);
882         ast_log(LOG_NOTICE,"Registered Config Engine %s\n",new->name);
883         return 1;
884 }
885
886 int ast_config_deregister(struct ast_config_reg *del) 
887 {
888         struct ast_config_reg *ptr=NULL,*last=NULL;
889         ast_mutex_lock(&ast_cust_config_lock);
890         for (ptr=ast_cust_config_list;ptr;ptr=ptr->next) {
891                 if (ptr == del) {
892                         if (last && ptr->next) {
893                                 last->next = ptr->next;
894                         } else if (last && ! ptr->next) {
895                                 last->next = NULL;
896                         } else if (!last && ptr->next) {
897                                 ast_cust_config_list = ptr->next;
898                         } else if (!last && !ptr->next) {
899                                 ast_cust_config_list = NULL;
900                         }
901                 }
902                 last = ptr;
903         }
904         ast_mutex_unlock(&ast_cust_config_lock);
905         return 0;
906 }
907
908 int ast_cust_config_active(void) {
909         return (ast_cust_config >0) ? 1 : 0;
910 }
911
912 struct ast_config *ast_load(char *configfile)
913 {
914         struct ast_category *tmpc=NULL;
915         struct ast_variable *last = NULL;
916
917
918 #ifdef PRESERVE_COMMENTS
919         struct ast_comment_struct acs = { NULL, NULL };
920 #endif  
921
922
923         return __ast_load(configfile, NULL, &tmpc, &last, 0 
924 #ifdef PRESERVE_COMMENTS
925         ,&acs
926 #endif
927         );
928 }
929
930 char *ast_category_browse(struct ast_config *config, char *prev)
931 {       
932         struct ast_category *cat;
933         if (!prev) {
934                 if (config->root)
935                         return config->root->name;
936                 else
937                         return NULL;
938         }
939         cat = config->root;
940         while(cat) {
941                 if (cat->name == prev) {
942                         if (cat->next)
943                                 return cat->next->name;
944                         else
945                                 return NULL;
946                 }
947                 cat = cat->next;
948         }
949         cat = config->root;
950         while(cat) {
951                 if (!strcasecmp(cat->name, prev)) {
952                         if (cat->next)
953                                 return cat->next->name;
954                         else
955                                 return NULL;
956                 }
957                 cat = cat->next;
958         }
959         return NULL;
960 }
961
962
963 struct ast_config *ast_new_config(void) 
964 {
965         struct ast_config *config;
966         config = malloc(sizeof(struct ast_config));
967         memset(config,0,sizeof(struct ast_config));
968         return config;
969 }
970
971
972
973 struct ast_category *ast_new_category(char *name) 
974 {
975         struct ast_category *category;
976         category = malloc(sizeof(struct ast_category));
977         if (category) {
978                 memset(category,0,sizeof(struct ast_category));
979                 strncpy(category->name,name,sizeof(category->name) - 1);
980         }
981         return category;
982 }
983
984
985 struct ast_variable *ast_new_variable(char *name, char *value) 
986 {
987         struct ast_variable *variable;
988         variable = malloc(sizeof(struct ast_variable));
989         if (variable) {
990                 memset(variable,0,sizeof(struct ast_variable));
991                 variable->object=0;
992                 variable->name = malloc(strlen(name)+1);
993                 if (variable->name) {
994                         strcpy(variable->name,name);
995                         variable->value = malloc(strlen(value)+1);
996                         if (variable->value) {
997                                 strcpy(variable->value,value);
998                         } else {
999                                 free(variable->name);
1000                                 variable->name = NULL;
1001                         }
1002                 }
1003         }
1004         if (!variable->value) {
1005                 free(variable);
1006                 variable = NULL;
1007         }
1008                 
1009         return variable;
1010 }
1011
1012 int ast_cust_config_register(struct ast_config_reg *new) 
1013 {
1014         ast_config_register(new);
1015         read_ast_cust_config();
1016         return 1;
1017 }
1018 int ast_cust_config_deregister(struct ast_config_reg *new) 
1019 {
1020         ast_config_deregister(new);
1021         read_ast_cust_config();
1022         return 1;
1023 }
1024
1025 static void clear_cust_keywords(void) 
1026 {
1027         struct ast_config_reg *key;
1028         int x;
1029         ast_mutex_lock(&ast_cust_config_lock);
1030         for (key=get_config_registrations();key;key=key->next) {
1031                 for (x=0;x<key->keycount;x++) {
1032                         key->keywords[x][0] = '\0';
1033                 }
1034                 key->keycount=0;
1035         }
1036         ast_mutex_unlock(&ast_cust_config_lock);
1037 }
1038
1039 static int config_command(int fd, int argc, char **argv) 
1040 {
1041         struct ast_config_reg *key;
1042         int x;
1043         
1044         ast_cli(fd,"\n\n");
1045         ast_mutex_lock(&ast_cust_config_lock);
1046         for (key=get_config_registrations();key;key=key->next) {
1047                 ast_cli(fd,"\nConfig Engine: %s\n",key->name);
1048                 for (x=0;x<key->keycount;x++)
1049                         ast_cli(fd,"===>%s\n",key->keywords[x]);
1050         }
1051         ast_mutex_unlock(&ast_cust_config_lock);
1052         ast_cli(fd,"\n\n");
1053         
1054         return 0;
1055 }
1056
1057 static struct ast_cli_entry config_command_struct = {
1058   { "show","config","handles", NULL }, config_command,
1059   "Show Config Handles", NULL };
1060
1061 int register_config_cli() 
1062 {
1063         return ast_cli_register(&config_command_struct);
1064 }
1065
1066 int read_ast_cust_config(void) 
1067 {
1068         char *cfg = config_conf_file;
1069         struct ast_config *config;
1070         struct ast_variable *v;
1071         struct ast_config_reg *ptr;
1072         struct ast_config_reg *test = NULL;
1073
1074         clear_cust_keywords();
1075         config = ast_load(cfg);
1076         if (config) {
1077                 for (v = ast_variable_browse(config,"settings");v;v=v->next) {
1078                         
1079                         ptr = get_ast_cust_config(v->value);
1080                         if (ptr) {
1081                                 if (ptr->keycount >= CONFIG_KEYWORD_ARRAYLEN) {
1082                                         ast_log(LOG_WARNING,"Max Number of Bindings exceeded for %s->%s %d/%d\n",v->name,v->value,ptr->keycount,CONFIG_KEYWORD_ARRAYLEN);
1083                                 } else {
1084                                         if (strcmp(v->name,config_conf_file) && strcmp(v->name,"asterisk.conf")) {
1085                                                 if (!(test = get_ast_cust_config_keyword(v->name))) {
1086                                                         ast_log(LOG_NOTICE,"Binding: %s to %s\n",v->name,v->value);
1087                                                         strncpy(ptr->keywords[ptr->keycount],v->name,sizeof(ptr->keywords[ptr->keycount]) - 1);
1088                                                         ptr->keywords[ptr->keycount][sizeof(ptr->keywords[ptr->keycount])-1] = '\0';
1089                                                         ptr->keycount++;
1090                                                 }
1091                                         } else {
1092                                                 ast_log(LOG_WARNING,"Cannot bind %s, Permission Denied\n",v->name);
1093                                         }
1094                                 }
1095                         }
1096                 }
1097                 
1098                 ast_destroy(config);
1099         }
1100
1101         return 0;
1102 }