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