This fix closes issue #10642 -- it's not perfect, but should retain most blank lines...
[asterisk/asterisk.git] / main / config.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Configuration File Parser
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * Includes the Asterisk Realtime API - ARA
26  * See doc/realtime.txt and doc/extconfig.txt
27  */
28
29 #include "asterisk.h"
30
31 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <time.h>
39 #include <sys/stat.h>
40 #include <sys/socket.h>         /* for AF_INET */
41 #define AST_INCLUDE_GLOB 1
42 #ifdef AST_INCLUDE_GLOB
43 #if defined(__Darwin__) || defined(__CYGWIN__)
44 #define GLOB_ABORTED GLOB_ABEND
45 #endif
46 # include <glob.h>
47 #endif
48
49 #include "asterisk/config.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/options.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/channel.h"
56 #include "asterisk/app.h"
57 #include "asterisk/astobj2.h"
58
59 #define MAX_NESTED_COMMENTS 128
60 #define COMMENT_START ";--"
61 #define COMMENT_END "--;"
62 #define COMMENT_META ';'
63 #define COMMENT_TAG '-'
64
65 static char *extconfig_conf = "extconfig.conf";
66
67
68 /*! \brief Structure to keep comments for rewriting configuration files */
69 struct ast_comment {
70         struct ast_comment *next;
71         char cmt[0];
72 };
73
74 /*! \brief Hold the mtime for config files, so if we don't need to reread our config, don't. */
75 struct cache_file_include {
76         AST_LIST_ENTRY(cache_file_include) list;
77         char include[0];
78 };
79
80 struct cache_file_mtime {
81         AST_LIST_ENTRY(cache_file_mtime) list;
82         AST_LIST_HEAD(includes, cache_file_include) includes;
83         unsigned int has_exec:1;
84         time_t mtime;
85         char filename[0];
86 };
87
88 static AST_LIST_HEAD_STATIC(cfmtime_head, cache_file_mtime);
89
90 #define CB_INCR 250
91
92 static void CB_INIT(char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
93 {
94         if (!(*comment_buffer)) {
95                 *comment_buffer = ast_malloc(CB_INCR);
96                 if (!(*comment_buffer))
97                         return;
98                 (*comment_buffer)[0] = 0;
99                 *comment_buffer_size = CB_INCR;
100                 *lline_buffer = ast_malloc(CB_INCR);
101                 if (!(*lline_buffer))
102                         return;
103                 (*lline_buffer)[0] = 0;
104                 *lline_buffer_size = CB_INCR;
105         } else {
106                 (*comment_buffer)[0] = 0;
107                 (*lline_buffer)[0] = 0;
108         }
109 }
110
111 static void  CB_ADD(char **comment_buffer, int *comment_buffer_size, char *str)
112 {
113         int rem = *comment_buffer_size - strlen(*comment_buffer) - 1;
114         int siz = strlen(str);
115         if (rem < siz+1) {
116                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + siz + 1);
117                 if (!(*comment_buffer))
118                         return;
119                 *comment_buffer_size += CB_INCR+siz+1;
120         }
121         strcat(*comment_buffer,str);
122 }
123
124 static void  CB_ADD_LEN(char **comment_buffer, int *comment_buffer_size, char *str, int len)
125 {
126         int cbl = strlen(*comment_buffer) + 1;
127         int rem = *comment_buffer_size - cbl;
128         if (rem < len+1) {
129                 *comment_buffer = ast_realloc(*comment_buffer, *comment_buffer_size + CB_INCR + len + 1);
130                 if (!(*comment_buffer))
131                         return;
132                 *comment_buffer_size += CB_INCR+len+1;
133         }
134         strncat(*comment_buffer,str,len);
135         (*comment_buffer)[cbl+len-1] = 0;
136 }
137
138 static void  LLB_ADD(char **lline_buffer, int *lline_buffer_size, char *str)
139 {
140         int rem = *lline_buffer_size - strlen(*lline_buffer) - 1;
141         int siz = strlen(str);
142         if (rem < siz+1) {
143                 *lline_buffer = ast_realloc(*lline_buffer, *lline_buffer_size + CB_INCR + siz + 1);
144                 if (!(*lline_buffer)) 
145                         return;
146                 *lline_buffer_size += CB_INCR + siz + 1;
147         }
148         strcat(*lline_buffer,str);
149 }
150
151 static void CB_RESET(char **comment_buffer, char **lline_buffer)  
152
153         (*comment_buffer)[0] = 0; 
154         (*lline_buffer)[0] = 0;
155 }
156
157
158 static struct ast_comment *ALLOC_COMMENT(const char *buffer)
159
160         struct ast_comment *x;
161         x = ast_calloc(1, sizeof(*x)+strlen(buffer)+1);
162         strcpy(x->cmt, buffer);
163         return x;
164 }
165
166 /* I need to keep track of each config file, and all its inclusions,
167    so that we can track blank lines in each */
168
169 struct inclfile
170 {
171         char *fname;
172         int lineno;
173 };
174
175 static int hash_string(const void *obj, const int flags)
176 {
177         char *str = ((struct inclfile*)obj)->fname;
178         int total;
179
180         for (total=0; *str; str++)
181         {
182                 unsigned int tmp = total;
183                 total <<= 1; /* multiply by 2 */
184                 total += tmp; /* multiply by 3 */
185                 total <<= 2; /* multiply by 12 */
186                 total += tmp; /* multiply by 13 */
187         
188                 total += ((unsigned int)(*str));
189         }
190         if (total < 0)
191                 total = -total;
192         return total;
193 }
194
195 static int hashtab_compare_strings(void *a, void *b, int flags)
196 {
197         const struct inclfile *ae = a, *be = b;
198         return !strcmp(ae->fname, be->fname) ? CMP_MATCH : 0;
199 }
200
201 static struct ast_config_map {
202         struct ast_config_map *next;
203         char *name;
204         char *driver;
205         char *database;
206         char *table;
207         char stuff[0];
208 } *config_maps = NULL;
209
210 AST_MUTEX_DEFINE_STATIC(config_lock);
211 static struct ast_config_engine *config_engine_list;
212
213 #define MAX_INCLUDE_LEVEL 10
214
215 struct ast_category {
216         char name[80];
217         int ignored;                    /*!< do not let user of the config see this category */
218         int include_level;
219         char *file;                /*!< the file name from whence this declaration was read */
220         int lineno;
221         struct ast_comment *precomments;
222         struct ast_comment *sameline;
223         struct ast_comment *trailing; /*!< the last object in the list will get assigned any trailing comments when EOF is hit */
224         struct ast_variable *root;
225         struct ast_variable *last;
226         struct ast_category *next;
227 };
228
229 struct ast_config {
230         struct ast_category *root;
231         struct ast_category *last;
232         struct ast_category *current;
233         struct ast_category *last_browse;     /*!< used to cache the last category supplied via category_browse */
234         int include_level;
235         int max_include_level;
236         struct ast_config_include *includes;  /*!< a list of inclusions, which should describe the entire tree */
237 };
238
239 struct ast_config_include {
240         char *include_location_file;     /*!< file name in which the include occurs */
241         int  include_location_lineno;    /*!< lineno where include occurred */
242         int  exec;                       /*!< set to non-zero if itsa #exec statement */
243         char *exec_file;                 /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
244         char *included_file;             /*!< file name included */
245         int inclusion_count;             /*!< if the file is included more than once, a running count thereof -- but, worry not,
246                                               we explode the instances and will include those-- so all entries will be unique */
247         int output;                      /*!< a flag to indicate if the inclusion has been output */
248         struct ast_config_include *next; /*!< ptr to next inclusion in the list */
249 };
250
251 struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename) 
252 {
253         struct ast_variable *variable;
254         int name_len = strlen(name) + 1;        
255
256         if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
257                 variable->name = variable->stuff;
258                 variable->value = variable->stuff + name_len;           
259                 variable->file = variable->stuff + name_len + strlen(value) + 1;
260                 strcpy(variable->name,name);
261                 strcpy(variable->value,value);
262                 strcpy(variable->file,filename);
263         }
264         return variable;
265 }
266
267 struct ast_config_include *ast_include_new(struct ast_config *conf, const char *from_file, const char *included_file, int is_exec, const char *exec_file, int from_lineno, char *real_included_file_name, int real_included_file_name_size)
268 {
269         /* a file should be included ONCE. Otherwise, if one of the instances is changed,
270        then all be changed. -- how do we know to include it? -- Handling modified 
271        instances is possible, I'd have
272        to create a new master for each instance. */
273         struct ast_config_include *inc;
274         struct stat statbuf;
275         
276         inc = ast_include_find(conf, included_file);
277         if (inc) {
278                 do {
279                         inc->inclusion_count++;
280                         snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
281                 } while (stat(real_included_file_name, &statbuf) == 0);
282                 ast_log(LOG_WARNING,"'%s', line %d:  Same File included more than once! This data will be saved in %s if saved back to disk.\n", from_file, from_lineno, real_included_file_name);
283         } else
284                 *real_included_file_name = 0;
285         
286         inc = ast_calloc(1,sizeof(struct ast_config_include));
287         inc->include_location_file = ast_strdup(from_file);
288         inc->include_location_lineno = from_lineno;
289         if (!ast_strlen_zero(real_included_file_name))
290                 inc->included_file = ast_strdup(real_included_file_name);
291         else
292                 inc->included_file = ast_strdup(included_file);
293         
294         inc->exec = is_exec;
295         if (is_exec)
296                 inc->exec_file = ast_strdup(exec_file);
297         
298         /* attach this new struct to the conf struct */
299         inc->next = conf->includes;
300         conf->includes = inc;
301         
302         return inc;
303 }
304
305 void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
306 {
307         struct ast_config_include *incl;
308         struct ast_category *cat;
309         struct ast_variable *v;
310         
311         int from_len = strlen(from_file);
312         int to_len = strlen(to_file);
313         
314         if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
315                 return;
316         
317         /* the manager code allows you to read in one config file, then
318        write it back out under a different name. But, the new arrangement
319            ties output lines to the file name. So, before you try to write
320        the config file to disk, better riffle thru the data and make sure
321        the file names are changed.
322         */
323         /* file names are on categories, includes (of course), and on variables. So,
324            traverse all this and swap names */
325
326         for (incl = conf->includes; incl; incl=incl->next) {
327                 if (strcmp(incl->include_location_file,from_file) == 0) {
328                         if (from_len >= to_len)
329                                 strcpy(incl->include_location_file, to_file);
330                         else {
331                                 free(incl->include_location_file);
332                                 incl->include_location_file = strdup(to_file);
333                         }
334                 }
335         }
336         for (cat = conf->root; cat; cat = cat->next) {
337                 if (strcmp(cat->file,from_file) == 0) {
338                         if (from_len >= to_len)
339                                 strcpy(cat->file, to_file);
340                         else {
341                                 free(cat->file);
342                                 cat->file = strdup(to_file);
343                         }
344                 }
345                 for (v = cat->root; v; v = v->next) {
346                         if (strcmp(v->file,from_file) == 0) {
347                                 if (from_len >= to_len)
348                                         strcpy(v->file, to_file);
349                                 else {
350                                         free(v->file);
351                                         v->file = strdup(to_file);
352                                 }
353                         }
354                 }
355         }
356 }
357
358 struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
359 {
360         struct ast_config_include *x;
361         for (x=conf->includes;x;x=x->next)
362         {
363                 if (strcmp(x->included_file,included_file) == 0)
364                         return x;
365         }
366         return 0;
367 }
368
369
370 void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
371 {
372         if (!variable)
373                 return;
374         if (category->last)
375                 category->last->next = variable;
376         else
377                 category->root = variable;
378         category->last = variable;
379         while (category->last->next)
380                 category->last = category->last->next;
381 }
382
383 void ast_variables_destroy(struct ast_variable *v)
384 {
385         struct ast_variable *vn;
386
387         while (v) {
388                 vn = v;
389                 v = v->next;
390                 ast_free(vn);
391         }
392 }
393
394 struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category)
395 {
396         struct ast_category *cat = NULL;
397
398         if (category && config->last_browse && (config->last_browse->name == category))
399                 cat = config->last_browse;
400         else
401                 cat = ast_category_get(config, category);
402
403         return (cat) ? cat->root : NULL;
404 }
405
406 const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
407 {
408         const char *tmp;
409         tmp = ast_variable_retrieve(cfg, cat, var);
410         if (!tmp)
411                 tmp = ast_variable_retrieve(cfg, "general", var);
412         return tmp;
413 }
414
415
416 const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
417 {
418         struct ast_variable *v;
419
420         if (category) {
421                 for (v = ast_variable_browse(config, category); v; v = v->next) {
422                         if (!strcasecmp(variable, v->name))
423                                 return v->value;
424                 }
425         } else {
426                 struct ast_category *cat;
427
428                 for (cat = config->root; cat; cat = cat->next)
429                         for (v = cat->root; v; v = v->next)
430                                 if (!strcasecmp(variable, v->name))
431                                         return v->value;
432         }
433
434         return NULL;
435 }
436
437 static struct ast_variable *variable_clone(const struct ast_variable *old)
438 {
439         struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
440
441         if (new) {
442                 new->lineno = old->lineno;
443                 new->object = old->object;
444                 new->blanklines = old->blanklines;
445                 /* TODO: clone comments? */
446         }
447
448         return new;
449 }
450  
451 static void move_variables(struct ast_category *old, struct ast_category *new)
452 {
453         struct ast_variable *var = old->root;
454         old->root = NULL;
455 #if 1
456         /* we can just move the entire list in a single op */
457         ast_variable_append(new, var);
458 #else
459         while (var) {
460                 struct ast_variable *next = var->next;
461                 var->next = NULL;
462                 ast_variable_append(new, var);
463                 var = next;
464         }
465 #endif
466 }
467
468 struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno) 
469 {
470         struct ast_category *category;
471
472         if ((category = ast_calloc(1, sizeof(*category))))
473                 ast_copy_string(category->name, name, sizeof(category->name));
474         category->file = strdup(in_file);
475         category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
476         return category;
477 }
478
479 static struct ast_category *category_get(const struct ast_config *config, const char *category_name, int ignored)
480 {
481         struct ast_category *cat;
482
483         /* try exact match first, then case-insensitive match */
484         for (cat = config->root; cat; cat = cat->next) {
485                 if (cat->name == category_name && (ignored || !cat->ignored))
486                         return cat;
487         }
488
489         for (cat = config->root; cat; cat = cat->next) {
490                 if (!strcasecmp(cat->name, category_name) && (ignored || !cat->ignored))
491                         return cat;
492         }
493
494         return NULL;
495 }
496
497 struct ast_category *ast_category_get(const struct ast_config *config, const char *category_name)
498 {
499         return category_get(config, category_name, 0);
500 }
501
502 int ast_category_exist(const struct ast_config *config, const char *category_name)
503 {
504         return !!ast_category_get(config, category_name);
505 }
506
507 void ast_category_append(struct ast_config *config, struct ast_category *category)
508 {
509         if (config->last)
510                 config->last->next = category;
511         else
512                 config->root = category;
513         category->include_level = config->include_level;
514         config->last = category;
515         config->current = category;
516 }
517
518 void ast_category_destroy(struct ast_category *cat)
519 {
520         ast_variables_destroy(cat->root);
521         if (cat->file)
522                 free(cat->file);
523         
524         ast_free(cat);
525 }
526
527 static void ast_includes_destroy(struct ast_config_include *incls)
528 {
529         struct ast_config_include *incl,*inclnext;
530         
531         for (incl=incls; incl; incl = inclnext) {
532                 inclnext = incl->next;
533                 if (incl->include_location_file)
534                         free(incl->include_location_file);
535                 if (incl->exec_file)
536                         free(incl->exec_file);
537                 if (incl->included_file)
538                         free(incl->included_file);
539                 free(incl);
540         }
541 }
542
543 static struct ast_category *next_available_category(struct ast_category *cat)
544 {
545         for (; cat && cat->ignored; cat = cat->next);
546
547         return cat;
548 }
549
550 struct ast_variable *ast_category_root(struct ast_config *config, char *cat)
551 {
552         struct ast_category *category = ast_category_get(config, cat);
553         if (category)
554                 return category->root;
555         return NULL;
556 }
557
558 char *ast_category_browse(struct ast_config *config, const char *prev)
559 {       
560         struct ast_category *cat = NULL;
561
562         if (prev && config->last_browse && (config->last_browse->name == prev))
563                 cat = config->last_browse->next;
564         else if (!prev && config->root)
565                 cat = config->root;
566         else if (prev) {
567                 for (cat = config->root; cat; cat = cat->next) {
568                         if (cat->name == prev) {
569                                 cat = cat->next;
570                                 break;
571                         }
572                 }
573                 if (!cat) {
574                         for (cat = config->root; cat; cat = cat->next) {
575                                 if (!strcasecmp(cat->name, prev)) {
576                                         cat = cat->next;
577                                         break;
578                                 }
579                         }
580                 }
581         }
582         
583         if (cat)
584                 cat = next_available_category(cat);
585
586         config->last_browse = cat;
587         return (cat) ? cat->name : NULL;
588 }
589
590 struct ast_variable *ast_category_detach_variables(struct ast_category *cat)
591 {
592         struct ast_variable *v;
593
594         v = cat->root;
595         cat->root = NULL;
596         cat->last = NULL;
597
598         return v;
599 }
600
601 void ast_category_rename(struct ast_category *cat, const char *name)
602 {
603         ast_copy_string(cat->name, name, sizeof(cat->name));
604 }
605
606 static void inherit_category(struct ast_category *new, const struct ast_category *base)
607 {
608         struct ast_variable *var;
609
610         for (var = base->root; var; var = var->next)
611                 ast_variable_append(new, variable_clone(var));
612 }
613
614 struct ast_config *ast_config_new(void) 
615 {
616         struct ast_config *config;
617
618         if ((config = ast_calloc(1, sizeof(*config))))
619                 config->max_include_level = MAX_INCLUDE_LEVEL;
620         return config;
621 }
622
623 int ast_variable_delete(struct ast_category *category, const char *variable, const char *match)
624 {
625         struct ast_variable *cur, *prev=NULL, *curn;
626         int res = -1;
627         cur = category->root;
628         while (cur) {
629                 if (cur->name == variable) {
630                         if (prev) {
631                                 prev->next = cur->next;
632                                 if (cur == category->last)
633                                         category->last = prev;
634                         } else {
635                                 category->root = cur->next;
636                                 if (cur == category->last)
637                                         category->last = NULL;
638                         }
639                         cur->next = NULL;
640                         ast_variables_destroy(cur);
641                         return 0;
642                 }
643                 prev = cur;
644                 cur = cur->next;
645         }
646
647         prev = NULL;
648         cur = category->root;
649         while (cur) {
650                 curn = cur->next;
651                 if (!strcasecmp(cur->name, variable) && (ast_strlen_zero(match) || !strcasecmp(cur->value, match))) {
652                         if (prev) {
653                                 prev->next = cur->next;
654                                 if (cur == category->last)
655                                         category->last = prev;
656                         } else {
657                                 category->root = cur->next;
658                                 if (cur == category->last)
659                                         category->last = NULL;
660                         }
661                         cur->next = NULL;
662                         ast_variables_destroy(cur);
663                         res = 0;
664                 } else
665                         prev = cur;
666
667                 cur = curn;
668         }
669         return res;
670 }
671
672 int ast_variable_update(struct ast_category *category, const char *variable, 
673                                                 const char *value, const char *match, unsigned int object)
674 {
675         struct ast_variable *cur, *prev=NULL, *newer=NULL;
676
677         for (cur = category->root; cur; prev = cur, cur = cur->next) {
678                 if (strcasecmp(cur->name, variable) ||
679                         (!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
680                         continue;
681
682                 if (!(newer = ast_variable_new(variable, value, cur->file)))
683                         return -1;
684         
685                 newer->next = cur->next;
686                 newer->object = cur->object || object;
687                 if (prev)
688                         prev->next = newer;
689                 else
690                         category->root = newer;
691                 if (category->last == cur)
692                         category->last = newer;
693
694                 cur->next = NULL;
695                 ast_variables_destroy(cur);
696
697                 return 0;
698         }
699
700         if (prev)
701                 prev->next = newer;
702         else
703                 category->root = newer;
704
705         return 0;
706 }
707
708 int ast_category_delete(struct ast_config *cfg, const char *category)
709 {
710         struct ast_category *prev=NULL, *cat;
711         cat = cfg->root;
712         while (cat) {
713                 if (cat->name == category) {
714                         ast_variables_destroy(cat->root);
715                         if (prev) {
716                                 prev->next = cat->next;
717                                 if (cat == cfg->last)
718                                         cfg->last = prev;
719                         } else {
720                                 cfg->root = cat->next;
721                                 if (cat == cfg->last)
722                                         cfg->last = NULL;
723                         }
724                         ast_free(cat);
725                         return 0;
726                 }
727                 prev = cat;
728                 cat = cat->next;
729         }
730
731         prev = NULL;
732         cat = cfg->root;
733         while (cat) {
734                 if (!strcasecmp(cat->name, category)) {
735                         ast_variables_destroy(cat->root);
736                         if (prev) {
737                                 prev->next = cat->next;
738                                 if (cat == cfg->last)
739                                         cfg->last = prev;
740                         } else {
741                                 cfg->root = cat->next;
742                                 if (cat == cfg->last)
743                                         cfg->last = NULL;
744                         }
745                         ast_free(cat);
746                         return 0;
747                 }
748                 prev = cat;
749                 cat = cat->next;
750         }
751         return -1;
752 }
753
754 void ast_config_destroy(struct ast_config *cfg)
755 {
756         struct ast_category *cat, *catn;
757
758         if (!cfg)
759                 return;
760
761         ast_includes_destroy(cfg->includes);
762
763         cat = cfg->root;
764         while (cat) {
765                 ast_variables_destroy(cat->root);
766                 catn = cat;
767                 cat = cat->next;
768                 ast_free(catn);
769         }
770         ast_free(cfg);
771 }
772
773 struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
774 {
775         return cfg->current;
776 }
777
778 void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
779 {
780         /* cast below is just to silence compiler warning about dropping "const" */
781         cfg->current = (struct ast_category *) cat;
782 }
783
784 enum config_cache_attribute_enum {
785         ATTRIBUTE_INCLUDE = 0,
786         ATTRIBUTE_EXEC = 1,
787 };
788
789 static void config_cache_attribute(const char *configfile, enum config_cache_attribute_enum attrtype, const char *filename)
790 {
791         struct cache_file_mtime *cfmtime;
792         struct cache_file_include *cfinclude;
793         struct stat statbuf = { 0, };
794
795         /* Find our cached entry for this configuration file */
796         AST_LIST_LOCK(&cfmtime_head);
797         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
798                 if (!strcmp(cfmtime->filename, configfile))
799                         break;
800         }
801         if (!cfmtime) {
802                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(configfile) + 1);
803                 if (!cfmtime) {
804                         AST_LIST_UNLOCK(&cfmtime_head);
805                         return;
806                 }
807                 AST_LIST_HEAD_INIT(&cfmtime->includes);
808                 strcpy(cfmtime->filename, configfile);
809                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
810                 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
811         }
812
813         if (!stat(configfile, &statbuf))
814                 cfmtime->mtime = 0;
815         else
816                 cfmtime->mtime = statbuf.st_mtime;
817
818         switch (attrtype) {
819         case ATTRIBUTE_INCLUDE:
820                 cfinclude = ast_calloc(1, sizeof(*cfinclude) + strlen(filename) + 1);
821                 if (!cfinclude) {
822                         AST_LIST_UNLOCK(&cfmtime_head);
823                         return;
824                 }
825                 strcpy(cfinclude->include, filename);
826                 AST_LIST_INSERT_TAIL(&cfmtime->includes, cfinclude, list);
827                 break;
828         case ATTRIBUTE_EXEC:
829                 cfmtime->has_exec = 1;
830                 break;
831         }
832         AST_LIST_UNLOCK(&cfmtime_head);
833 }
834
835 static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, struct ast_flags flags,
836                                                          char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size, const char *suggested_include_file, struct ast_category **last_cat, struct ast_variable **last_var)
837 {
838         char *c;
839         char *cur = buf;
840         struct ast_variable *v;
841         char cmd[512], exec_file[512];
842         int object, do_exec, do_include;
843
844         /* Actually parse the entry */
845         if (cur[0] == '[') {
846                 struct ast_category *newcat = NULL;
847                 char *catname;
848
849                 /* A category header */
850                 c = strchr(cur, ']');
851                 if (!c) {
852                         ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
853                         return -1;
854                 }
855                 *c++ = '\0';
856                 cur++;
857                 if (*c++ != '(')
858                         c = NULL;
859                 catname = cur;
860                 if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
861                         return -1;
862                 }
863                 (*cat)->lineno = lineno;
864                 *last_var = 0;
865                 *last_cat = newcat;
866                 
867                 /* add comments */
868                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
869                         newcat->precomments = ALLOC_COMMENT(*comment_buffer);
870                 }
871                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
872                         newcat->sameline = ALLOC_COMMENT(*lline_buffer);
873                 }
874                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
875                         CB_RESET(comment_buffer, lline_buffer);
876                 
877                 /* If there are options or categories to inherit from, process them now */
878                 if (c) {
879                         if (!(cur = strchr(c, ')'))) {
880                                 ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
881                                 return -1;
882                         }
883                         *cur = '\0';
884                         while ((cur = strsep(&c, ","))) {
885                                 if (!strcasecmp(cur, "!")) {
886                                         (*cat)->ignored = 1;
887                                 } else if (!strcasecmp(cur, "+")) {
888                                         *cat = category_get(cfg, catname, 1);
889                                         if (!(*cat)) {
890                                                 if (newcat)
891                                                         ast_category_destroy(newcat);
892                                                 ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
893                                                 return -1;
894                                         }
895                                         if (newcat) {
896                                                 move_variables(newcat, *cat);
897                                                 ast_category_destroy(newcat);
898                                                 newcat = NULL;
899                                         }
900                                 } else {
901                                         struct ast_category *base;
902                                 
903                                         base = category_get(cfg, cur, 1);
904                                         if (!base) {
905                                                 ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
906                                                 return -1;
907                                         }
908                                         inherit_category(*cat, base);
909                                 }
910                         }
911                 }
912                 if (newcat)
913                         ast_category_append(cfg, *cat);
914         } else if (cur[0] == '#') {
915                 /* A directive */
916                 cur++;
917                 c = cur;
918                 while (*c && (*c > 32)) c++;
919                 if (*c) {
920                         *c = '\0';
921                         /* Find real argument */
922                         c = ast_skip_blanks(c + 1);
923                         if (!(*c))
924                                 c = NULL;
925                 } else 
926                         c = NULL;
927                 do_include = !strcasecmp(cur, "include");
928                 if (!do_include)
929                         do_exec = !strcasecmp(cur, "exec");
930                 else
931                         do_exec = 0;
932                 if (do_exec && !ast_opt_exec_includes) {
933                         ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
934                         do_exec = 0;
935                 }
936                 if (do_include || do_exec) {
937                         if (c) {
938                                 char *cur2;
939                                 char real_inclusion_name[256];
940                                 struct ast_config_include *inclu;
941                                 
942                                 /* Strip off leading and trailing "'s and <>'s */
943                                 while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
944                                 /* Get rid of leading mess */
945                                 cur = c;
946                                 cur2 = cur;
947                                 while (!ast_strlen_zero(cur)) {
948                                         c = cur + strlen(cur) - 1;
949                                         if ((*c == '>') || (*c == '<') || (*c == '\"'))
950                                                 *c = '\0';
951                                         else
952                                                 break;
953                                 }
954                                 /* #exec </path/to/executable>
955                                    We create a tmp file, then we #include it, then we delete it. */
956                                 if (do_exec) {
957                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
958                                                 config_cache_attribute(configfile, ATTRIBUTE_EXEC, NULL);
959                                         snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
960                                         snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file);
961                                         ast_safe_system(cmd);
962                                         cur = exec_file;
963                                 } else {
964                                         if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
965                                                 config_cache_attribute(configfile, ATTRIBUTE_INCLUDE, cur);
966                                         exec_file[0] = '\0';
967                                 }
968                                 /* A #include */
969                                 /* record this inclusion */
970                                 inclu = ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
971
972                                 do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name) ? 1 : 0;
973                                 if (!ast_strlen_zero(exec_file))
974                                         unlink(exec_file);
975                                 if (!do_include)
976                                         return 0;
977
978                         } else {
979                                 ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n", 
980                                                 do_exec ? "exec" : "include",
981                                                 do_exec ? "/path/to/executable" : "filename",
982                                                 lineno,
983                                                 configfile);
984                         }
985                 }
986                 else 
987                         ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
988         } else {
989                 /* Just a line (variable = value) */
990                 if (!(*cat)) {
991                         ast_log(LOG_WARNING,
992                                 "parse error: No category context for line %d of %s\n", lineno, configfile);
993                         return -1;
994                 }
995                 c = strchr(cur, '=');
996                 if (c) {
997                         *c = 0;
998                         c++;
999                         /* Ignore > in => */
1000                         if (*c== '>') {
1001                                 object = 1;
1002                                 c++;
1003                         } else
1004                                 object = 0;
1005                         if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
1006                                 v->lineno = lineno;
1007                                 v->object = object;
1008                                 *last_cat = 0;
1009                                 *last_var = v;
1010                                 /* Put and reset comments */
1011                                 v->blanklines = 0;
1012                                 ast_variable_append(*cat, v);
1013                                 /* add comments */
1014                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
1015                                         v->precomments = ALLOC_COMMENT(*comment_buffer);
1016                                 }
1017                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *lline_buffer && (*lline_buffer)[0] ) {
1018                                         v->sameline = ALLOC_COMMENT(*lline_buffer);
1019                                 }
1020                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1021                                         CB_RESET(comment_buffer, lline_buffer);
1022                                 
1023                         } else {
1024                                 return -1;
1025                         }
1026                 } else {
1027                         ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
1028                 }
1029         }
1030         return 0;
1031 }
1032
1033 static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1034 {
1035         char fn[256];
1036         char buf[8192];
1037         char *new_buf, *comment_p, *process_buf;
1038         FILE *f;
1039         int lineno=0;
1040         int comment = 0, nest[MAX_NESTED_COMMENTS];
1041         struct ast_category *cat = NULL;
1042         int count = 0;
1043         struct stat statbuf;
1044         struct cache_file_mtime *cfmtime = NULL;
1045         struct cache_file_include *cfinclude;
1046         struct ast_variable *last_var = 0;
1047         struct ast_category *last_cat = 0;
1048         /*! Growable string buffer */
1049         char *comment_buffer=0;   /*!< this will be a comment collector.*/
1050         int   comment_buffer_size=0;  /*!< the amount of storage so far alloc'd for the comment_buffer */
1051
1052         char *lline_buffer=0;    /*!< A buffer for stuff behind the ; */
1053         int  lline_buffer_size=0;
1054
1055         if (cfg)
1056                 cat = ast_config_get_current_category(cfg);
1057
1058         if (filename[0] == '/') {
1059                 ast_copy_string(fn, filename, sizeof(fn));
1060         } else {
1061                 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, filename);
1062         }
1063
1064         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1065                 CB_INIT(&comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size);
1066                 if (!lline_buffer || !comment_buffer) {
1067                         ast_log(LOG_ERROR, "Failed to initialize the comment buffer!\n");
1068                         return NULL;
1069                 }
1070         }
1071 #ifdef AST_INCLUDE_GLOB
1072         {
1073                 int glob_ret;
1074                 glob_t globbuf;
1075                 globbuf.gl_offs = 0;    /* initialize it to silence gcc */
1076 #ifdef SOLARIS
1077                 glob_ret = glob(fn, GLOB_NOCHECK, NULL, &globbuf);
1078 #else
1079                 glob_ret = glob(fn, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
1080 #endif
1081                 if (glob_ret == GLOB_NOSPACE)
1082                         ast_log(LOG_WARNING,
1083                                 "Glob Expansion of pattern '%s' failed: Not enough memory\n", fn);
1084                 else if (glob_ret  == GLOB_ABORTED)
1085                         ast_log(LOG_WARNING,
1086                                 "Glob Expansion of pattern '%s' failed: Read error\n", fn);
1087                 else  {
1088                         /* loop over expanded files */
1089                         int i;
1090                         for (i=0; i<globbuf.gl_pathc; i++) {
1091                                 ast_copy_string(fn, globbuf.gl_pathv[i], sizeof(fn));
1092 #endif
1093         do {
1094                 if (stat(fn, &statbuf))
1095                         continue;
1096
1097                 if (!S_ISREG(statbuf.st_mode)) {
1098                         ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
1099                         continue;
1100                 }
1101
1102                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE)) {
1103                         /* Find our cached entry for this configuration file */
1104                         AST_LIST_LOCK(&cfmtime_head);
1105                         AST_LIST_TRAVERSE(&cfmtime_head, cfmtime, list) {
1106                                 if (!strcmp(cfmtime->filename, fn))
1107                                         break;
1108                         }
1109                         if (!cfmtime) {
1110                                 cfmtime = ast_calloc(1, sizeof(*cfmtime) + strlen(fn) + 1);
1111                                 if (!cfmtime)
1112                                         continue;
1113                                 AST_LIST_HEAD_INIT(&cfmtime->includes);
1114                                 strcpy(cfmtime->filename, fn);
1115                                 /* Note that the file mtime is initialized to 0, i.e. 1970 */
1116                                 AST_LIST_INSERT_TAIL(&cfmtime_head, cfmtime, list);
1117                         }
1118                 }
1119
1120                 if (cfmtime && (!cfmtime->has_exec) && (cfmtime->mtime == statbuf.st_mtime) && ast_test_flag(&flags, CONFIG_FLAG_FILEUNCHANGED)) {
1121                         /* File is unchanged, what about the (cached) includes (if any)? */
1122                         int unchanged = 1;
1123                         AST_LIST_TRAVERSE(&cfmtime->includes, cfinclude, list) {
1124                                 /* We must glob here, because if we did not, then adding a file to globbed directory would
1125                                  * incorrectly cause no reload to be necessary. */
1126                                 char fn2[256];
1127 #ifdef AST_INCLUDE_GLOB
1128                                 int glob_ret;
1129                                 glob_t globbuf = { .gl_offs = 0 };
1130 #ifdef SOLARIS
1131                                 glob_ret = glob(cfinclude->include, GLOB_NOCHECK, NULL, &globbuf);
1132 #else
1133                                 glob_ret = glob(cfinclude->include, GLOB_NOMAGIC|GLOB_BRACE, NULL, &globbuf);
1134 #endif
1135                                 /* On error, we reparse */
1136                                 if (glob_ret == GLOB_NOSPACE || glob_ret  == GLOB_ABORTED)
1137                                         unchanged = 0;
1138                                 else  {
1139                                         /* loop over expanded files */
1140                                         int j;
1141                                         for (j = 0; j < globbuf.gl_pathc; j++) {
1142                                                 ast_copy_string(fn2, globbuf.gl_pathv[j], sizeof(fn2));
1143 #else
1144                                                 ast_copy_string(fn2, cfinclude->include);
1145 #endif
1146                                                 if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
1147                                                         unchanged = 0;
1148                                                         /* One change is enough to short-circuit and reload the whole shebang */
1149                                                         break;
1150                                                 }
1151 #ifdef AST_INCLUDE_GLOB
1152                                         }
1153                                 }
1154 #endif
1155                         }
1156
1157                         if (unchanged) {
1158                                 AST_LIST_UNLOCK(&cfmtime_head);
1159                                 return CONFIG_STATUS_FILEUNCHANGED;
1160                         }
1161                 }
1162                 if (!ast_test_flag(&flags, CONFIG_FLAG_NOCACHE))
1163                         AST_LIST_UNLOCK(&cfmtime_head);
1164
1165                 /* If cfg is NULL, then we just want an answer */
1166                 if (cfg == NULL)
1167                         return NULL;
1168
1169                 if (cfmtime)
1170                         cfmtime->mtime = statbuf.st_mtime;
1171
1172                 ast_verb(2, "Parsing '%s': ", fn);
1173                         fflush(stdout);
1174                 if (!(f = fopen(fn, "r"))) {
1175                         ast_debug(1, "No file to parse: %s\n", fn);
1176                         ast_verb(2, "Not found (%s)\n", strerror(errno));
1177                         continue;
1178                 }
1179                 count++;
1180                 /* If we get to this point, then we're loading regardless */
1181                 ast_clear_flag(&flags, CONFIG_FLAG_FILEUNCHANGED);
1182                 ast_debug(1, "Parsing %s\n", fn);
1183                 ast_verb(2, "Found\n");
1184                 while (!feof(f)) {
1185                         lineno++;
1186                         if (fgets(buf, sizeof(buf), f)) {
1187                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1188                                         CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer);       /* add the current lline buffer to the comment buffer */
1189                                         lline_buffer[0] = 0;        /* erase the lline buffer */
1190                                 }
1191                                 
1192                                 new_buf = buf;
1193                                 if (comment) 
1194                                         process_buf = NULL;
1195                                 else
1196                                         process_buf = buf;
1197                                 
1198                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer[0] && (ast_strlen_zero(buf) || strlen(buf) == strspn(buf," \t\n\r"))) {
1199                                         /* blank line? really? Can we add it to an existing comment and maybe preserve inter- and post- comment spacing? */
1200                                         CB_ADD(&comment_buffer, &comment_buffer_size, "\n");       /* add a newline to the comment buffer */
1201                                         continue; /* go get a new line, then */
1202                                 }
1203                                 
1204                                 while ((comment_p = strchr(new_buf, COMMENT_META))) {
1205                                         if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
1206                                                 /* Escaped semicolons aren't comments. */
1207                                                 new_buf = comment_p + 1;
1208                                         } else if (comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
1209                                                 /* Meta-Comment start detected ";--" */
1210                                                 if (comment < MAX_NESTED_COMMENTS) {
1211                                                         *comment_p = '\0';
1212                                                         new_buf = comment_p + 3;
1213                                                         comment++;
1214                                                         nest[comment-1] = lineno;
1215                                                 } else {
1216                                                         ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
1217                                                 }
1218                                         } else if ((comment_p >= new_buf + 2) &&
1219                                                    (*(comment_p - 1) == COMMENT_TAG) &&
1220                                                    (*(comment_p - 2) == COMMENT_TAG)) {
1221                                                 /* Meta-Comment end detected */
1222                                                 comment--;
1223                                                 new_buf = comment_p + 1;
1224                                                 if (!comment) {
1225                                                         /* Back to non-comment now */
1226                                                         if (process_buf) {
1227                                                                 /* Actually have to move what's left over the top, then continue */
1228                                                                 char *oldptr;
1229                                                                 oldptr = process_buf + strlen(process_buf);
1230                                                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1231                                                                         CB_ADD(&comment_buffer, &comment_buffer_size, ";");
1232                                                                         CB_ADD_LEN(&comment_buffer, &comment_buffer_size, oldptr+1, new_buf-oldptr-1);
1233                                                                 }
1234                                                                 
1235                                                                 memmove(oldptr, new_buf, strlen(new_buf) + 1);
1236                                                                 new_buf = oldptr;
1237                                                         } else
1238                                                                 process_buf = new_buf;
1239                                                 }
1240                                         } else {
1241                                                 if (!comment) {
1242                                                         /* If ; is found, and we are not nested in a comment, 
1243                                                            we immediately stop all comment processing */
1244                                                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS)) {
1245                                                                 LLB_ADD(&lline_buffer, &lline_buffer_size, comment_p);
1246                                                         }
1247                                                         *comment_p = '\0'; 
1248                                                         new_buf = comment_p;
1249                                                 } else
1250                                                         new_buf = comment_p + 1;
1251                                         }
1252                                 }
1253                                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment && !process_buf )
1254                                 {
1255                                         CB_ADD(&comment_buffer, &comment_buffer_size, buf);  /* the whole line is a comment, store it */
1256                                 }
1257                                 
1258                                 if (process_buf) {
1259                                         char *buf = ast_strip(process_buf);
1260                                         if (!ast_strlen_zero(buf)) {
1261                                                 if (process_text_line(cfg, &cat, buf, lineno, fn, flags, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size, suggested_include_file, &last_cat, &last_var)) {
1262                                                         cfg = NULL;
1263                                                         break;
1264                                                 }
1265                                         }
1266                                 }
1267                         }
1268                 }
1269                 /* end of file-- anything in a comment buffer? */
1270                 if (last_cat) {
1271                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer[0] ) {
1272                                 CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer);       /* add the current lline buffer to the comment buffer */
1273                                 lline_buffer[0] = 0;        /* erase the lline buffer */
1274                                 last_cat->trailing = ALLOC_COMMENT(comment_buffer);
1275                         }
1276                 } else if (last_var) {
1277                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && comment_buffer[0] ) {
1278                                 CB_ADD(&comment_buffer, &comment_buffer_size, lline_buffer);       /* add the current lline buffer to the comment buffer */
1279                                 lline_buffer[0] = 0;        /* erase the lline buffer */
1280                                 last_var->trailing = ALLOC_COMMENT(comment_buffer);
1281                         }
1282                 } else {
1283                         if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer && (comment_buffer)[0] ) {
1284                                 ast_debug(1, "Nothing to attach comments to, discarded: %s\n", comment_buffer);
1285                         }
1286                 }
1287                 if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS))
1288                         CB_RESET(&comment_buffer, &lline_buffer);
1289
1290                 fclose(f);              
1291         } while (0);
1292         if (comment) {
1293                 ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment - 1]);
1294         }
1295 #ifdef AST_INCLUDE_GLOB
1296                                         if (cfg == NULL || cfg == CONFIG_STATUS_FILEUNCHANGED)
1297                                                 break;
1298                                 }
1299                                 globfree(&globbuf);
1300                         }
1301                 }
1302 #endif
1303
1304         if (cfg && cfg != CONFIG_STATUS_FILEUNCHANGED && cfg->include_level == 1 && ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && comment_buffer) {
1305                 ast_free(comment_buffer);
1306                 ast_free(lline_buffer);
1307                 comment_buffer = NULL;
1308                 lline_buffer = NULL;
1309                 comment_buffer_size = 0;
1310                 lline_buffer_size = 0;
1311         }
1312         
1313         if (count == 0)
1314                 return NULL;
1315
1316         return cfg;
1317 }
1318
1319
1320 /* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
1321    which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
1322    recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
1323    be shocked and mystified as to why things are not showing up in the files! 
1324
1325    Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
1326    and line number are stored for each include, plus the name of the file included, so that these statements may be
1327    included in the output files on a file_save operation. 
1328
1329    The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
1330    are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
1331    the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
1332    and a header gets added.
1333
1334    vars and category heads are output in the order they are stored in the config file. So, if the software
1335    shuffles these at all, then the placement of #include directives might get a little mixed up, because the
1336    file/lineno data probably won't get changed.
1337
1338 */
1339
1340 static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
1341 {
1342         char date[256]="";
1343         time_t t;
1344         time(&t);
1345         ast_copy_string(date, ctime(&t), sizeof(date));
1346
1347         fprintf(f1, ";!\n");
1348         fprintf(f1, ";! Automatically generated configuration file\n");
1349         if (strcmp(configfile, fn))
1350                 fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
1351         else
1352                 fprintf(f1, ";! Filename: %s\n", configfile);
1353         fprintf(f1, ";! Generator: %s\n", generator);
1354         fprintf(f1, ";! Creation Date: %s", date);
1355         fprintf(f1, ";!\n");
1356 }
1357
1358 static void   inclfile_destroy(void *obj)
1359 {
1360         const struct inclfile *o = obj;
1361         if (o->fname)
1362                 free(o->fname);
1363 }
1364
1365
1366 static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
1367 {
1368         struct inclfile lookup;
1369         
1370         if (!file || file[0] == 0) {
1371                 if (configfile[0] == '/')
1372                         ast_copy_string(fn, configfile, fn_size);
1373                 else
1374                         snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
1375         } else if (file[0] == '/') 
1376                 ast_copy_string(fn, file, fn_size);
1377         else
1378                 snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
1379         lookup.fname = fn;
1380         *fi = ao2_find(fileset, &lookup, OBJ_POINTER);
1381         if (!(*fi)) {
1382                 /* set up a file scratch pad */
1383                 struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
1384                 fx->fname = ast_strdup(fn);
1385                 fx->lineno = 1;
1386                 *fi = fx;
1387                 ao2_link(fileset, fx);
1388                 ao2_ref(fx,1); /* bump the ref count, so it looks like we just got the ref from find */
1389         }
1390 }
1391
1392 static int count_linefeeds(char *str)
1393 {
1394         int count = 0;
1395         while (*str) {
1396                 if (*str =='\n')
1397                         count++;
1398                 str++;
1399         }
1400         return count;
1401 }
1402
1403 static int count_linefeeds_in_comments(struct ast_comment *x)
1404 {
1405         int count = 0;
1406         while (x)
1407         {
1408                 count += count_linefeeds(x->cmt);
1409                 x = x->next;
1410         }
1411         return count;
1412 }
1413
1414 static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
1415 {
1416         int precomment_lines = count_linefeeds_in_comments(precomments);
1417         int i;
1418
1419         /* I don't have to worry about those ;! comments, they are
1420            stored in the precomments, but not printed back out.
1421            I did have to make sure that comments following
1422            the ;! header comments were not also deleted in the process */
1423         for (i=fi->lineno;i<lineno - precomment_lines; i++) {
1424                 fprintf(fp,"\n");
1425         }
1426         fi->lineno = lineno+1; /* Advance the file lineno */
1427 }
1428
1429 int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
1430 {
1431         FILE *f;
1432         char fn[256];
1433         struct ast_variable *var;
1434         struct ast_category *cat;
1435         struct ast_comment *cmt;
1436         struct ast_config_include *incl;
1437         int blanklines = 0;
1438         struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
1439         struct inclfile *fi = 0;
1440
1441         /* reset all the output flags, in case this isn't our first time saving this data */
1442
1443         for (incl=cfg->includes; incl; incl = incl->next)
1444                 incl->output = 0;
1445
1446         /* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
1447            are all truncated to zero bytes and have that nice header*/
1448
1449         for (incl=cfg->includes; incl; incl = incl->next)
1450         {
1451                 if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
1452                         FILE *f1;
1453
1454                         set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
1455                         f1 = fopen(fn,"w");
1456                         if (f1) {
1457                                 gen_header(f1, configfile, fn, generator);
1458                                 fclose(f1); /* this should zero out the file */
1459                         } else {
1460                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1461                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1462                         }
1463                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1464                         fi = 0;
1465                 }
1466         }
1467
1468         set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
1469 #ifdef __CYGWIN__       
1470         if ((f = fopen(fn, "w+"))) {
1471 #else
1472         if ((f = fopen(fn, "w"))) {
1473 #endif      
1474                 ast_verb(2, "Saving '%s': ", fn);
1475                 gen_header(f, configfile, fn, generator);
1476                 cat = cfg->root;
1477                 fclose(f);
1478                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1479                 
1480                 /* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
1481                 /* since each var, cat, and associated comments can come from any file, we have to be 
1482                    mobile, and open each file, print, and close it on an entry-by-entry basis */
1483
1484                 while (cat) {
1485                         set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
1486                         f = fopen(fn, "a");
1487                         if (!f)
1488                         {
1489                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1490                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1491                                 ao2_ref(fileset, -1);
1492                                 return -1;
1493                         }
1494
1495                         /* dump any includes that happen before this category header */
1496                         for (incl=cfg->includes; incl; incl = incl->next) {
1497                                 if (strcmp(incl->include_location_file, cat->file) == 0){
1498                                         if (cat->lineno > incl->include_location_lineno && !incl->output) {
1499                                                 if (incl->exec)
1500                                                         fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1501                                                 else
1502                                                         fprintf(f,"#include \"%s\"\n", incl->included_file);
1503                                                 incl->output = 1;
1504                                         }
1505                                 }
1506                         }
1507                         
1508                         insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
1509                         /* Dump section with any appropriate comment */
1510                         for (cmt = cat->precomments; cmt; cmt=cmt->next) {
1511                                 char *cmtp = cmt->cmt;
1512                                 while (*cmtp == ';' && *(cmtp+1) == '!') {
1513                                         char *cmtp2 = strchr(cmtp+1, '\n');
1514                                         if (cmtp2)
1515                                                 cmtp = cmtp2+1;
1516                                         else cmtp = 0;
1517                                 }
1518                                 if (cmtp)
1519                                         fprintf(f,"%s", cmtp);
1520                         }
1521                         if (!cat->precomments)
1522                                 fprintf(f,"\n");
1523                         fprintf(f, "[%s]", cat->name);
1524                         for (cmt = cat->sameline; cmt; cmt=cmt->next) {
1525                                 fprintf(f,"%s", cmt->cmt);
1526                         }
1527                         if (!cat->sameline)
1528                                 fprintf(f,"\n");
1529                         for (cmt = cat->trailing; cmt; cmt=cmt->next) {
1530                                 if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1531                                         fprintf(f,"%s", cmt->cmt);
1532                         }
1533                         fclose(f);
1534                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1535                         fi = 0;
1536                         
1537                         var = cat->root;
1538                         while (var) {
1539                                 set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
1540                                 f = fopen(fn, "a");
1541                                 if (!f)
1542                                 {
1543                                         ast_debug(1, "Unable to open for writing: %s\n", fn);
1544                                         ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1545                                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1546                                         fi = 0;
1547                                         ao2_ref(fileset, -1);
1548                                         return -1;
1549                                 }
1550                                 
1551                                 /* dump any includes that happen before this category header */
1552                                 for (incl=cfg->includes; incl; incl = incl->next) {
1553                                         if (strcmp(incl->include_location_file, var->file) == 0){
1554                                                 if (var->lineno > incl->include_location_lineno && !incl->output) {
1555                                                         if (incl->exec)
1556                                                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1557                                                         else
1558                                                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1559                                                         incl->output = 1;
1560                                                 }
1561                                         }
1562                                 }
1563                                 
1564                                 insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
1565                                 for (cmt = var->precomments; cmt; cmt=cmt->next) {
1566                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1567                                                 fprintf(f,"%s", cmt->cmt);
1568                                 }
1569                                 if (var->sameline) 
1570                                         fprintf(f, "%s %s %s  %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
1571                                 else    
1572                                         fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
1573                                 for (cmt = var->trailing; cmt; cmt=cmt->next) {
1574                                         if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
1575                                                 fprintf(f,"%s", cmt->cmt);
1576                                 }
1577                                 if (var->blanklines) {
1578                                         blanklines = var->blanklines;
1579                                         while (blanklines--)
1580                                                 fprintf(f, "\n");
1581                                 }
1582                                 
1583                                 fclose(f);
1584                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1585                                 fi = 0;
1586                                 
1587                                 var = var->next;
1588                         }
1589                         cat = cat->next;
1590                 }
1591                 if (!option_debug)
1592                         ast_verb(2, "Saved\n");
1593         } else {
1594                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1595                 ast_verb(2, "Unable to write (%s)", strerror(errno));
1596                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1597                 ao2_ref(fileset, -1);
1598                 return -1;
1599         }
1600
1601         /* Now, for files with trailing #include/#exec statements,
1602            we have to make sure every entry is output */
1603
1604         for (incl=cfg->includes; incl; incl = incl->next) {
1605                 if (!incl->output) {
1606                         /* open the respective file */
1607                         set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
1608                         f = fopen(fn, "a");
1609                         if (!f)
1610                         {
1611                                 ast_debug(1, "Unable to open for writing: %s\n", fn);
1612                                 ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
1613                                 ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1614                                 fi = 0;
1615                                 ao2_ref(fileset, -1);
1616                                 return -1;
1617                         }
1618                         
1619                         /* output the respective include */
1620                         if (incl->exec)
1621                                 fprintf(f,"#exec \"%s\"\n", incl->exec_file);
1622                         else
1623                                 fprintf(f,"#include \"%s\"\n", incl->included_file);
1624                         fclose(f);
1625                         incl->output = 1;
1626                         ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
1627                         fi = 0;
1628                 }
1629         }
1630         ao2_ref(fileset, -1); /* this should destroy the hash container */
1631                                 
1632         return 0;
1633 }
1634
1635 static void clear_config_maps(void) 
1636 {
1637         struct ast_config_map *map;
1638
1639         ast_mutex_lock(&config_lock);
1640
1641         while (config_maps) {
1642                 map = config_maps;
1643                 config_maps = config_maps->next;
1644                 ast_free(map);
1645         }
1646                 
1647         ast_mutex_unlock(&config_lock);
1648 }
1649
1650 static int append_mapping(char *name, char *driver, char *database, char *table)
1651 {
1652         struct ast_config_map *map;
1653         int length;
1654
1655         length = sizeof(*map);
1656         length += strlen(name) + 1;
1657         length += strlen(driver) + 1;
1658         length += strlen(database) + 1;
1659         if (table)
1660                 length += strlen(table) + 1;
1661
1662         if (!(map = ast_calloc(1, length)))
1663                 return -1;
1664
1665         map->name = map->stuff;
1666         strcpy(map->name, name);
1667         map->driver = map->name + strlen(map->name) + 1;
1668         strcpy(map->driver, driver);
1669         map->database = map->driver + strlen(map->driver) + 1;
1670         strcpy(map->database, database);
1671         if (table) {
1672                 map->table = map->database + strlen(map->database) + 1;
1673                 strcpy(map->table, table);
1674         }
1675         map->next = config_maps;
1676
1677         ast_verb(2, "Binding %s to %s/%s/%s\n", map->name, map->driver, map->database, map->table ? map->table : map->name);
1678
1679         config_maps = map;
1680         return 0;
1681 }
1682
1683 int read_config_maps(void) 
1684 {
1685         struct ast_config *config, *configtmp;
1686         struct ast_variable *v;
1687         char *driver, *table, *database, *stringp, *tmp;
1688         struct ast_flags flags = { 0 };
1689
1690         clear_config_maps();
1691
1692         configtmp = ast_config_new();
1693         configtmp->max_include_level = 1;
1694         config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
1695         if (!config) {
1696                 ast_config_destroy(configtmp);
1697                 return 0;
1698         }
1699
1700         for (v = ast_variable_browse(config, "settings"); v; v = v->next) {
1701                 stringp = v->value;
1702                 driver = strsep(&stringp, ",");
1703
1704                 if ((tmp = strchr(stringp, '\"')))
1705                         stringp = tmp;
1706
1707                 /* check if the database text starts with a double quote */
1708                 if (*stringp == '"') {
1709                         stringp++;
1710                         database = strsep(&stringp, "\"");
1711                         strsep(&stringp, ",");
1712                 } else {
1713                         /* apparently this text has no quotes */
1714                         database = strsep(&stringp, ",");
1715                 }
1716
1717                 table = strsep(&stringp, ",");
1718
1719                 if (!strcmp(v->name, extconfig_conf)) {
1720                         ast_log(LOG_WARNING, "Cannot bind '%s'!\n", extconfig_conf);
1721                         continue;
1722                 }
1723
1724                 if (!strcmp(v->name, "asterisk.conf")) {
1725                         ast_log(LOG_WARNING, "Cannot bind 'asterisk.conf'!\n");
1726                         continue;
1727                 }
1728
1729                 if (!strcmp(v->name, "logger.conf")) {
1730                         ast_log(LOG_WARNING, "Cannot bind 'logger.conf'!\n");
1731                         continue;
1732                 }
1733
1734                 if (!driver || !database)
1735                         continue;
1736                 if (!strcasecmp(v->name, "sipfriends")) {
1737                         ast_log(LOG_WARNING, "The 'sipfriends' table is obsolete, update your config to use sipusers and sippeers, though they can point to the same table.\n");
1738                         append_mapping("sipusers", driver, database, table ? table : "sipfriends");
1739                         append_mapping("sippeers", driver, database, table ? table : "sipfriends");
1740                 } else if (!strcasecmp(v->name, "iaxfriends")) {
1741                         ast_log(LOG_WARNING, "The 'iaxfriends' table is obsolete, update your config to use iaxusers and iaxpeers, though they can point to the same table.\n");
1742                         append_mapping("iaxusers", driver, database, table ? table : "iaxfriends");
1743                         append_mapping("iaxpeers", driver, database, table ? table : "iaxfriends");
1744                 } else 
1745                         append_mapping(v->name, driver, database, table);
1746         }
1747                 
1748         ast_config_destroy(config);
1749         return 0;
1750 }
1751
1752 int ast_config_engine_register(struct ast_config_engine *new) 
1753 {
1754         struct ast_config_engine *ptr;
1755
1756         ast_mutex_lock(&config_lock);
1757
1758         if (!config_engine_list) {
1759                 config_engine_list = new;
1760         } else {
1761                 for (ptr = config_engine_list; ptr->next; ptr=ptr->next);
1762                 ptr->next = new;
1763         }
1764
1765         ast_mutex_unlock(&config_lock);
1766         ast_log(LOG_NOTICE,"Registered Config Engine %s\n", new->name);
1767
1768         return 1;
1769 }
1770
1771 int ast_config_engine_deregister(struct ast_config_engine *del) 
1772 {
1773         struct ast_config_engine *ptr, *last=NULL;
1774
1775         ast_mutex_lock(&config_lock);
1776
1777         for (ptr = config_engine_list; ptr; ptr=ptr->next) {
1778                 if (ptr == del) {
1779                         if (last)
1780                                 last->next = ptr->next;
1781                         else
1782                                 config_engine_list = ptr->next;
1783                         break;
1784                 }
1785                 last = ptr;
1786         }
1787
1788         ast_mutex_unlock(&config_lock);
1789
1790         return 0;
1791 }
1792
1793 /*! \brief Find realtime engine for realtime family */
1794 static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz) 
1795 {
1796         struct ast_config_engine *eng, *ret = NULL;
1797         struct ast_config_map *map;
1798
1799         ast_mutex_lock(&config_lock);
1800
1801         for (map = config_maps; map; map = map->next) {
1802                 if (!strcasecmp(family, map->name)) {
1803                         if (database)
1804                                 ast_copy_string(database, map->database, dbsiz);
1805                         if (table)
1806                                 ast_copy_string(table, map->table ? map->table : family, tabsiz);
1807                         break;
1808                 }
1809         }
1810
1811         /* Check if the required driver (engine) exist */
1812         if (map) {
1813                 for (eng = config_engine_list; !ret && eng; eng = eng->next) {
1814                         if (!strcasecmp(eng->name, map->driver))
1815                                 ret = eng;
1816                 }
1817         }
1818
1819         ast_mutex_unlock(&config_lock);
1820         
1821         /* if we found a mapping, but the engine is not available, then issue a warning */
1822         if (map && !ret)
1823                 ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
1824
1825         return ret;
1826 }
1827
1828 static struct ast_config_engine text_file_engine = {
1829         .name = "text",
1830         .load_func = config_text_file_load,
1831 };
1832
1833 struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
1834 {
1835         char db[256];
1836         char table[256];
1837         struct ast_config_engine *loader = &text_file_engine;
1838         struct ast_config *result; 
1839
1840         if (cfg->include_level == cfg->max_include_level) {
1841                 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
1842                 return NULL;
1843         }
1844
1845         cfg->include_level++;
1846
1847         if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
1848                 struct ast_config_engine *eng;
1849
1850                 eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
1851
1852
1853                 if (eng && eng->load_func) {
1854                         loader = eng;
1855                 } else {
1856                         eng = find_engine("global", db, sizeof(db), table, sizeof(table));
1857                         if (eng && eng->load_func)
1858                                 loader = eng;
1859                 }
1860         }
1861
1862         result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
1863
1864         if (result && result != CONFIG_STATUS_FILEUNCHANGED)
1865                 result->include_level--;
1866         else
1867                 cfg->include_level--;
1868
1869         return result;
1870 }
1871
1872 struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
1873 {
1874         struct ast_config *cfg;
1875         struct ast_config *result;
1876
1877         cfg = ast_config_new();
1878         if (!cfg)
1879                 return NULL;
1880
1881         result = ast_config_internal_load(filename, cfg, flags, "");
1882         if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
1883                 ast_config_destroy(cfg);
1884
1885         return result;
1886 }
1887
1888 static struct ast_variable *ast_load_realtime_helper(const char *family, va_list ap)
1889 {
1890         struct ast_config_engine *eng;
1891         char db[256]="";
1892         char table[256]="";
1893         struct ast_variable *res=NULL;
1894
1895         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1896         if (eng && eng->realtime_func) 
1897                 res = eng->realtime_func(db, table, ap);
1898
1899         return res;
1900 }
1901
1902 struct ast_variable *ast_load_realtime_all(const char *family, ...)
1903 {
1904         struct ast_variable *res;
1905         va_list ap;
1906
1907         va_start(ap, family);
1908         res = ast_load_realtime_helper(family, ap);
1909         va_end(ap);
1910
1911         return res;
1912 }
1913
1914 struct ast_variable *ast_load_realtime(const char *family, ...)
1915 {
1916         struct ast_variable *res, *cur, *prev = NULL, *freeme = NULL;
1917         va_list ap;
1918
1919         va_start(ap, family);
1920         res = ast_load_realtime_helper(family, ap);
1921         va_end(ap);
1922
1923         /* Eliminate blank entries */
1924         for (cur = res; cur; cur = cur->next) {
1925                 if (freeme) {
1926                         ast_free(freeme);
1927                         freeme = NULL;
1928                 }
1929
1930                 if (ast_strlen_zero(cur->value)) {
1931                         if (prev)
1932                                 prev->next = cur->next;
1933                         else
1934                                 res = cur->next;
1935                         freeme = cur;
1936                 } else {
1937                         prev = cur;
1938                 }
1939         }
1940         return res;
1941 }
1942
1943 /*! \brief Check if realtime engine is configured for family */
1944 int ast_check_realtime(const char *family)
1945 {
1946         struct ast_config_engine *eng;
1947
1948         eng = find_engine(family, NULL, 0, NULL, 0);
1949         if (eng)
1950                 return 1;
1951         return 0;
1952
1953 }
1954
1955 /*! \brief Check if there's any realtime engines loaded */
1956 int ast_realtime_enabled()
1957 {
1958         return config_maps ? 1 : 0;
1959 }
1960
1961 struct ast_config *ast_load_realtime_multientry(const char *family, ...)
1962 {
1963         struct ast_config_engine *eng;
1964         char db[256]="";
1965         char table[256]="";
1966         struct ast_config *res=NULL;
1967         va_list ap;
1968
1969         va_start(ap, family);
1970         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1971         if (eng && eng->realtime_multi_func) 
1972                 res = eng->realtime_multi_func(db, table, ap);
1973         va_end(ap);
1974
1975         return res;
1976 }
1977
1978 int ast_update_realtime(const char *family, const char *keyfield, const char *lookup, ...)
1979 {
1980         struct ast_config_engine *eng;
1981         int res = -1;
1982         char db[256]="";
1983         char table[256]="";
1984         va_list ap;
1985
1986         va_start(ap, lookup);
1987         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
1988         if (eng && eng->update_func) 
1989                 res = eng->update_func(db, table, keyfield, lookup, ap);
1990         va_end(ap);
1991
1992         return res;
1993 }
1994
1995 int ast_store_realtime(const char *family, ...) {
1996         struct ast_config_engine *eng;
1997         int res = -1;
1998         char db[256]="";
1999         char table[256]="";
2000         va_list ap;
2001
2002         va_start(ap, family);
2003         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2004         if (eng && eng->store_func) 
2005                 res = eng->store_func(db, table, ap);
2006         va_end(ap);
2007
2008         return res;
2009 }
2010
2011 int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup, ...) {
2012         struct ast_config_engine *eng;
2013         int res = -1;
2014         char db[256]="";
2015         char table[256]="";
2016         va_list ap;
2017
2018         va_start(ap, lookup);
2019         eng = find_engine(family, db, sizeof(db), table, sizeof(table));
2020         if (eng && eng->destroy_func) 
2021                 res = eng->destroy_func(db, table, keyfield, lookup, ap);
2022         va_end(ap);
2023
2024         return res;
2025 }
2026
2027 /*! \brief Helper function to parse arguments
2028  * See documentation in config.h
2029  */
2030 int ast_parse_arg(const char *arg, enum ast_parse_flags flags,
2031         void *p_result, ...)
2032 {
2033         va_list ap;
2034         int error = 0;
2035
2036         va_start(ap, p_result);
2037         switch (flags & PARSE_TYPE) {
2038         case PARSE_INT32:
2039             {
2040                 int32_t *result = p_result;
2041                 int32_t x, def = result ? *result : 0,
2042                         high = (int32_t)0x7fffffff,
2043                         low  = (int32_t)0x80000000;
2044                 /* optional argument: first default value, then range */
2045                 if (flags & PARSE_DEFAULT)
2046                         def = va_arg(ap, int32_t);
2047                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2048                         /* range requested, update bounds */
2049                         low = va_arg(ap, int32_t);
2050                         high = va_arg(ap, int32_t);
2051                 }
2052                 x = strtol(arg, NULL, 0);
2053                 error = (x < low) || (x > high);
2054                 if (flags & PARSE_OUT_RANGE)
2055                         error = !error;
2056                 if (result)
2057                         *result  = error ? def : x;
2058                 ast_debug(3,
2059                         "extract int from [%s] in [%d, %d] gives [%d](%d)\n",
2060                         arg, low, high,
2061                         result ? *result : x, error);
2062                 break;
2063             }
2064
2065         case PARSE_UINT32:
2066             {
2067                 uint32_t *result = p_result;
2068                 uint32_t x, def = result ? *result : 0,
2069                         low = 0, high = (uint32_t)~0;
2070                 /* optional argument: first default value, then range */
2071                 if (flags & PARSE_DEFAULT)
2072                         def = va_arg(ap, uint32_t);
2073                 if (flags & (PARSE_IN_RANGE|PARSE_OUT_RANGE)) {
2074                         /* range requested, update bounds */
2075                         low = va_arg(ap, uint32_t);
2076                         high = va_arg(ap, uint32_t);
2077                 }
2078                 x = strtoul(arg, NULL, 0);
2079                 error = (x < low) || (x > high);
2080                 if (flags & PARSE_OUT_RANGE)
2081                         error = !error;
2082                 if (result)
2083                         *result  = error ? def : x;
2084                 ast_debug(3,
2085                         "extract uint from [%s] in [%u, %u] gives [%u](%d)\n",
2086                         arg, low, high,
2087                         result ? *result : x, error);
2088                 break;
2089             }
2090
2091         case PARSE_INADDR:
2092             {
2093                 char *port, *buf;
2094                 struct sockaddr_in _sa_buf;     /* buffer for the result */
2095                 struct sockaddr_in *sa = p_result ?
2096                         (struct sockaddr_in *)p_result : &_sa_buf;
2097                 /* default is either the supplied value or the result itself */
2098                 struct sockaddr_in *def = (flags & PARSE_DEFAULT) ?
2099                         va_arg(ap, struct sockaddr_in *) : sa;
2100                 struct hostent *hp;
2101                 struct ast_hostent ahp;
2102
2103                 bzero(&_sa_buf, sizeof(_sa_buf)); /* clear buffer */
2104                 /* duplicate the string to strip away the :port */
2105                 port = ast_strdupa(arg);
2106                 buf = strsep(&port, ":");
2107                 sa->sin_family = AF_INET;       /* assign family */
2108                 /*
2109                  * honor the ports flag setting, assign default value
2110                  * in case of errors or field unset.
2111                  */
2112                 flags &= PARSE_PORT_MASK; /* the only flags left to process */
2113                 if (port) {
2114                         if (flags == PARSE_PORT_FORBID) {
2115                                 error = 1;      /* port was forbidden */
2116                                 sa->sin_port = def->sin_port;
2117                         } else if (flags == PARSE_PORT_IGNORE)
2118                                 sa->sin_port = def->sin_port;
2119                         else /* accept or require */
2120                                 sa->sin_port = htons(strtol(port, NULL, 0));
2121                 } else {
2122                         sa->sin_port = def->sin_port;
2123                         if (flags == PARSE_PORT_REQUIRE)
2124                                 error = 1;
2125                 }
2126                 /* Now deal with host part, even if we have errors before. */
2127                 hp = ast_gethostbyname(buf, &ahp);
2128                 if (hp) /* resolved successfully */
2129                         memcpy(&sa->sin_addr, hp->h_addr, sizeof(sa->sin_addr));
2130                 else {
2131                         error = 1;
2132                         sa->sin_addr = def->sin_addr;
2133                 }
2134                 ast_debug(3,
2135                         "extract inaddr from [%s] gives [%s:%d](%d)\n",
2136                         arg, ast_inet_ntoa(sa->sin_addr),
2137                         ntohs(sa->sin_port), error);
2138                 break;
2139             }
2140         }
2141         va_end(ap);
2142         return error;
2143 }
2144
2145 static int config_command(int fd, int argc, char **argv) 
2146 {
2147         struct ast_config_engine *eng;
2148         struct ast_config_map *map;
2149         
2150         ast_mutex_lock(&config_lock);
2151
2152         ast_cli(fd, "\n\n");
2153         for (eng = config_engine_list; eng; eng = eng->next) {
2154                 ast_cli(fd, "\nConfig Engine: %s\n", eng->name);
2155                 for (map = config_maps; map; map = map->next)
2156                         if (!strcasecmp(map->driver, eng->name)) {
2157                                 ast_cli(fd, "===> %s (db=%s, table=%s)\n", map->name, map->database,
2158                                         map->table ? map->table : map->name);
2159                         }
2160         }
2161         ast_cli(fd,"\n\n");
2162         
2163         ast_mutex_unlock(&config_lock);
2164
2165         return 0;
2166 }
2167
2168 static char show_config_help[] =
2169         "Usage: core show config mappings\n"
2170         "       Shows the filenames to config engines.\n";
2171
2172 static struct ast_cli_entry cli_config[] = {
2173         { { "core", "show", "config", "mappings", NULL },
2174         config_command, "Display config mappings (file names to config engines)",
2175         show_config_help },
2176 };
2177
2178 int register_config_cli() 
2179 {
2180         ast_cli_register_multiple(cli_config, sizeof(cli_config) / sizeof(struct ast_cli_entry));
2181         return 0;
2182 }