00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <stdio.h>
00015 #include <unistd.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #include <errno.h>
00019 #include <time.h>
00020 #include <asterisk/config.h>
00021 #include <asterisk/options.h>
00022 #include <asterisk/logger.h>
00023 #include "asterisk.h"
00024 #include "astconf.h"
00025
00026 #define MAX_INCLUDE_LEVEL 10
00027
00028 struct ast_category {
00029 char name[80];
00030 struct ast_variable *root;
00031 struct ast_category *next;
00032 #ifdef PRESERVE_COMMENTS
00033 struct ast_comment *precomments;
00034 struct ast_comment *sameline;
00035 #endif
00036 };
00037
00038 struct ast_config {
00039
00040
00041 struct ast_category *root;
00042 struct ast_category *prev;
00043 #ifdef PRESERVE_COMMENTS
00044 struct ast_comment *trailingcomments;
00045 #endif
00046 };
00047
00048 #ifdef PRESERVE_COMMENTS
00049 struct ast_comment_struct
00050 {
00051 struct ast_comment *root;
00052 struct ast_comment *prev;
00053 };
00054 #endif
00055
00056 static char *strip(char *buf)
00057 {
00058 char *start;
00059
00060 while(strlen(buf) && (buf[strlen(buf)-1]<33))
00061 buf[strlen(buf)-1] = '\0';
00062 start = buf;
00063
00064 while(*start && (*start < 33))
00065 *start++ = '\0';
00066 return start;
00067 }
00068
00069 #ifdef PRESERVE_COMMENTS
00070 static void free_comments(struct ast_comment *com)
00071 {
00072 struct ast_comment *l;
00073 while (com) {
00074 l = com;
00075 com = com->next;
00076 free(l);
00077 }
00078 }
00079 #endif
00080
00081 void ast_destroy(struct ast_config *ast)
00082 {
00083 struct ast_category *cat, *catn;
00084 struct ast_variable *v, *vn;
00085
00086 if (!ast)
00087 return;
00088
00089 cat = ast->root;
00090 while(cat) {
00091 v = cat->root;
00092 while(v) {
00093 vn = v;
00094 free(v->name);
00095 free(v->value);
00096 #ifdef PRESERVE_COMMENTS
00097 free_comments(v->precomments);
00098 free_comments(v->sameline);
00099 #endif
00100 v = v->next;
00101 free(vn);
00102 }
00103 catn = cat;
00104 #ifdef PRESERVE_COMMENTS
00105 free_comments(cat->precomments);
00106 free_comments(cat->sameline);
00107 #endif
00108 cat = cat->next;
00109 free(catn);
00110 }
00111 #ifdef PRESERVE_COMMENTS
00112 free_comments(ast->trailingcomments);
00113 #endif
00114 free(ast);
00115 }
00116
00117 int ast_true(char *s)
00118 {
00119 if (!s)
00120 return 0;
00121
00122 if (!strcasecmp(s, "yes") ||
00123 !strcasecmp(s, "true") ||
00124 !strcasecmp(s, "y") ||
00125 !strcasecmp(s, "t") ||
00126 !strcasecmp(s, "1"))
00127 return -1;
00128 return 0;
00129 }
00130
00131 int ast_false(char *s)
00132 {
00133 if (!s)
00134 return 0;
00135
00136 if (!strcasecmp(s, "no") ||
00137 !strcasecmp(s, "false") ||
00138 !strcasecmp(s, "n") ||
00139 !strcasecmp(s, "f") ||
00140 !strcasecmp(s, "0"))
00141 return -1;
00142 return 0;
00143 }
00144
00145 struct ast_variable *ast_variable_browse(struct ast_config *config, char *category)
00146 {
00147 struct ast_category *cat;
00148 cat = config->root;
00149 while(cat) {
00150 if (cat->name == category)
00151 return cat->root;
00152 cat = cat->next;
00153 }
00154 cat = config->root;
00155 while(cat) {
00156 if (!strcasecmp(cat->name, category))
00157 return cat->root;
00158 cat = cat->next;
00159 }
00160 return NULL;
00161 }
00162
00163 char *ast_variable_retrieve(struct ast_config *config, char *category, char *value)
00164 {
00165 struct ast_variable *v;
00166 if (category) {
00167 v = ast_variable_browse(config, category);
00168 while (v) {
00169 if (value == v->name)
00170 return v->value;
00171 v=v->next;
00172 }
00173 v = ast_variable_browse(config, category);
00174 while (v) {
00175 if (!strcasecmp(value, v->name))
00176 return v->value;
00177 v=v->next;
00178 }
00179 } else {
00180 struct ast_category *cat;
00181 cat = config->root;
00182 while(cat) {
00183 v = cat->root;
00184 while (v) {
00185 if (!strcasecmp(value, v->name))
00186 return v->value;
00187 v=v->next;
00188 }
00189 cat = cat->next;
00190 }
00191 }
00192 return NULL;
00193 }
00194
00195 #ifdef PRESERVE_COMMENTS
00196 int ast_variable_delete(struct ast_config *cfg, char *category, char *variable, char *value)
00197 {
00198 struct ast_variable *v, *pv, *bv, *bpv;
00199 struct ast_category *cat;
00200 cat = cfg->root;
00201 while(cat) {
00202 if (cat->name == category) {
00203 break;
00204 }
00205 cat = cat->next;
00206 }
00207 if (!cat) {
00208 cat = cfg->root;
00209 while(cat) {
00210 if (!strcasecmp(cat->name, category)) {
00211 break;
00212 }
00213 cat = cat->next;
00214 }
00215 }
00216 if (!cat)
00217 return -1;
00218 v = cat->root;
00219 pv = NULL;
00220 while (v) {
00221 if ((variable == v->name) && (!value || !strcmp(v->value, value)))
00222 break;
00223 pv = v;
00224 v=v->next;
00225 }
00226 if (!v) {
00227
00228 bv = NULL;
00229 bpv = NULL;
00230 v = cat->root;
00231 pv = NULL;
00232 while (v) {
00233 if (!strcasecmp(variable, v->name) && (!value || !strcmp(v->value, value))) {
00234 bv = v;
00235 bpv = pv;
00236 }
00237 pv = v;
00238 v=v->next;
00239 }
00240 v = bv;
00241 }
00242
00243 if (v) {
00244
00245 if (pv)
00246 pv->next = v->next;
00247 else
00248 cat->root = v->next;
00249 v->next = NULL;
00250 free(v->name);
00251 if (v->value)
00252 free(v->value);
00253 free_comments(v->sameline);
00254 free_comments(v->precomments);
00255 return 0;
00256 }
00257 return -1;
00258 }
00259
00260 int ast_category_delete(struct ast_config *cfg, char *category)
00261 {
00262 struct ast_variable *v, *pv;
00263 struct ast_category *cat, *cprev;
00264 cat = cfg->root;
00265 cprev = NULL;
00266 while(cat) {
00267 if (cat->name == category) {
00268 break;
00269 }
00270 cprev = cat;
00271 cat = cat->next;
00272 }
00273 if (!cat) {
00274 cat = cfg->root;
00275 cprev = NULL;
00276 while(cat) {
00277 if (!strcasecmp(cat->name, category)) {
00278 break;
00279 }
00280 cprev = cat;
00281 cat = cat->next;
00282 }
00283 }
00284 if (!cat)
00285 return -1;
00286
00287 if (cprev)
00288 cprev->next = cat->next;
00289 else
00290 cfg->root = cat->next;
00291 v = cat->root;
00292 while (v) {
00293 pv = v;
00294 v=v->next;
00295 if (pv->value)
00296 free(pv->value);
00297 if (pv->name)
00298 free(pv->name);
00299 free_comments(pv->sameline);
00300 free_comments(pv->precomments);
00301 free(pv);
00302 }
00303 free_comments(cat->sameline);
00304 free_comments(cat->precomments);
00305 free(cat);
00306 return 0;
00307 }
00308
00309 struct ast_variable *ast_variable_append_modify(struct ast_config *config, char *category, char *variable, char *value, int newcat, int newvar, int move)
00310 {
00311 struct ast_variable *v, *pv=NULL, *bv, *bpv;
00312 struct ast_category *cat, *pcat;
00313 cat = config->root;
00314 if (!newcat) {
00315 while(cat) {
00316 if (cat->name == category) {
00317 break;
00318 }
00319 cat = cat->next;
00320 }
00321 if (!cat) {
00322 cat = config->root;
00323 while(cat) {
00324 if (!strcasecmp(cat->name, category)) {
00325 break;
00326 }
00327 cat = cat->next;
00328 }
00329 }
00330 }
00331 if (!cat) {
00332 cat = malloc(sizeof(struct ast_category));
00333 if (!cat)
00334 return NULL;
00335 memset(cat, 0, sizeof(struct ast_category));
00336 strncpy(cat->name, category, sizeof(cat->name));
00337 if (config->root) {
00338
00339 pcat = config->root;
00340 while(pcat->next)
00341 pcat = pcat->next;
00342 pcat->next = cat;
00343 } else {
00344
00345 config->root = cat;
00346 }
00347
00348 }
00349 if (!newvar) {
00350 v = cat->root;
00351 pv = NULL;
00352 while (v) {
00353 if (variable == v->name)
00354 break;
00355 pv = v;
00356 v=v->next;
00357 }
00358 if (!v) {
00359
00360 bv = NULL;
00361 bpv = NULL;
00362 v = cat->root;
00363 pv = NULL;
00364 while (v) {
00365 if (!strcasecmp(variable, v->name)) {
00366 bv = v;
00367 bpv = pv;
00368 }
00369 pv = v;
00370 v=v->next;
00371 }
00372 v = bv;
00373 }
00374 } else v = NULL;
00375 if (v && move) {
00376
00377 if (pv)
00378 pv->next = v->next;
00379 else
00380 cat->root = v->next;
00381 v->next = NULL;
00382 }
00383 if (!v) {
00384 v = malloc(sizeof(struct ast_variable));
00385 if (!v)
00386 return NULL;
00387 memset(v, 0, sizeof(struct ast_variable));
00388 v->name = strdup(variable);
00389 move = 1;
00390 }
00391 if (v->value)
00392 free(v->value);
00393 if (value)
00394 v->value = strdup(value);
00395 else
00396 v->value = strdup("");
00397 if (move) {
00398 if (cat->root) {
00399 pv = cat->root;
00400 while (pv->next)
00401 pv = pv->next;
00402 pv->next = v;
00403 } else {
00404 cat->root = v;
00405 }
00406 }
00407 return v;
00408 }
00409 #endif
00410
00411 int ast_category_exist(struct ast_config *config, char *category_name)
00412 {
00413 struct ast_category *category = NULL;
00414
00415 category = config->root;
00416
00417 while(category) {
00418 if (!strcasecmp(category->name,category_name))
00419 return 1;
00420 category = category->next;
00421 }
00422
00423 return 0;
00424 }
00425
00426 #ifdef PRESERVE_COMMENTS
00427 static struct ast_comment *build_comment(char *cmt)
00428 {
00429 struct ast_comment *c;
00430 int len = strlen(cmt) + 1;
00431 c = malloc(sizeof(struct ast_comment) + len);
00432 if (c) {
00433
00434 memset(c, 0, sizeof(struct ast_comment));
00435
00436 strcpy(c->cmt, cmt);
00437 }
00438 return c;
00439 }
00440 #endif
00441
00442 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
00443 #ifdef PRESERVE_COMMENTS
00444 , struct ast_comment_struct *acs
00445 #endif
00446 );
00447
00448 static int cfg_process(struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, char *buf, int lineno, char *configfile, int includelevel
00449 #ifdef PRESERVE_COMMENTS
00450 ,struct ast_comment_struct *acs
00451 #endif
00452 )
00453 {
00454 char *c;
00455 char *cur;
00456 struct ast_variable *v;
00457 #ifdef PRESERVE_COMMENTS
00458 struct ast_comment *com = NULL;
00459 #endif
00460 int object;
00461
00462 c = strchr(buf, ';');
00463 if (c) {
00464 *c = '\0';
00465 #ifdef PRESERVE_COMMENTS
00466 c++;
00467 if (*c != '!')
00468 com = build_comment(c);
00469 #endif
00470 }
00471 cur = strip(buf);
00472 if (strlen(cur)) {
00473
00474 if (cur[0] == '[') {
00475
00476 c = strchr(cur, ']');
00477 if (c) {
00478 *c = 0;
00479 *_tmpc = malloc(sizeof(struct ast_category));
00480 if (!*_tmpc) {
00481 ast_destroy(tmp);
00482 ast_log(LOG_WARNING,
00483 "Out of memory, line %d\n", lineno);
00484 return -1;
00485 }
00486 memset(*_tmpc, 0, sizeof(struct ast_category));
00487 strncpy((*_tmpc)->name, cur+1, sizeof((*_tmpc)->name) - 1);
00488 (*_tmpc)->root = NULL;
00489 #ifdef PRESERVE_COMMENTS
00490 (*_tmpc)->precomments = acs->root;
00491 (*_tmpc)->sameline = com;
00492 #endif
00493 if (!tmp->prev)
00494 tmp->root = *_tmpc;
00495 else
00496 tmp->prev->next = *_tmpc;
00497
00498 tmp->prev = *_tmpc;
00499 #ifdef PRESERVE_COMMENTS
00500 acs->root = NULL;
00501 acs->prev = NULL;
00502 #endif
00503 *_last = NULL;
00504 } else {
00505 ast_log(LOG_WARNING,
00506 "parse error: no closing ']', line %d of %s\n", lineno, configfile);
00507 }
00508 } else if (cur[0] == '#') {
00509
00510 cur++;
00511 c = cur;
00512 while(*c && (*c > 32)) c++;
00513 if (*c) {
00514 *c = '\0';
00515 c++;
00516
00517 while(*c && (*c < 33)) c++;
00518 if (!*c)
00519 c = NULL;
00520 } else
00521 c = NULL;
00522 if (!strcasecmp(cur, "include")) {
00523
00524 if (c) {
00525 while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
00526
00527 cur = c;
00528 while(strlen(cur)) {
00529 c = cur + strlen(cur) - 1;
00530 if ((*c == '>') || (*c == '<') || (*c == '\"'))
00531 *c = '\0';
00532 else
00533 break;
00534 }
00535 if (includelevel < MAX_INCLUDE_LEVEL) {
00536 __ast_load(cur, tmp, _tmpc, _last, includelevel + 1
00537 #ifdef PRESERVE_COMMENTS
00538 ,acs
00539 #endif
00540 );
00541 } else
00542 ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", includelevel);
00543 } else
00544 ast_log(LOG_WARNING, "Directive '#include' needs an argument (filename) at line %d of %s\n", lineno, configfile);
00545
00546 } else
00547 ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
00548 } else {
00549
00550 if (!*_tmpc) {
00551 ast_log(LOG_WARNING,
00552 "parse error: No category context for line %d of %s\n", lineno, configfile);
00553 ast_destroy(tmp);
00554 return -1;
00555 }
00556 c = strchr(cur, '=');
00557 if (c) {
00558 *c = 0;
00559 c++;
00560
00561 if (*c== '>') {
00562 object = 1;
00563 c++;
00564 } else
00565 object = 0;
00566 v = malloc(sizeof(struct ast_variable));
00567 if (v) {
00568 memset(v, 0, sizeof(struct ast_variable));
00569 v->next = NULL;
00570 v->name = strdup(strip(cur));
00571 v->value = strdup(strip(c));
00572 v->lineno = lineno;
00573 v->object = object;
00574
00575 #ifdef PRESERVE_COMMENTS
00576 v->precomments = acs->root;
00577 v->sameline = com;
00578 acs->prev = NULL;
00579 acs->root = NULL;
00580 #endif
00581 v->blanklines = 0;
00582 if (*_last)
00583 (*_last)->next = v;
00584 else
00585 (*_tmpc)->root = v;
00586 *_last = v;
00587 } else {
00588 ast_destroy(tmp);
00589 ast_log(LOG_WARNING, "Out of memory, line %d\n", lineno);
00590 return -1;
00591 }
00592 } else {
00593 ast_log(LOG_WARNING, "No '=' (equal sign) in line %d of %s\n", lineno, configfile);
00594 }
00595
00596 }
00597 } else {
00598
00599 #ifdef PRESERVE_COMMENTS
00600 if (com) {
00601 if (acs->prev)
00602 acs->prev->next = com;
00603 else
00604 acs->root = com;
00605 acs->prev = com;
00606 } else {
00607 if (*_last)
00608 (*_last)->blanklines++;
00609
00610 }
00611 #endif
00612 }
00613 return 0;
00614 }
00615
00616 #ifdef PRESERVE_COMMENTS
00617 static void dump_comments(FILE *f, struct ast_comment *comment)
00618 {
00619 while (comment) {
00620 fprintf(f, ";%s", comment->cmt);
00621 comment = comment->next;
00622 }
00623 }
00624 #endif
00625
00626 int ast_save(char *configfile, struct ast_config *cfg, char *generator)
00627 {
00628 FILE *f;
00629 char fn[256];
00630 char date[256];
00631 time_t t;
00632 struct ast_variable *var;
00633 struct ast_category *cat;
00634 int blanklines = 0;
00635 if (configfile[0] == '/') {
00636 strncpy(fn, configfile, sizeof(fn)-1);
00637 } else {
00638 snprintf(fn, sizeof(fn), "%s/%s", AST_CONFIG_DIR, configfile);
00639 }
00640 time(&t);
00641 strncpy(date, ctime(&t), sizeof(date));
00642 if ((f = fopen(fn, "w"))) {
00643 if ((option_verbose > 1) && !option_debug)
00644 ast_verbose( VERBOSE_PREFIX_2 "Saving '%s': ", fn);
00645 fprintf(f, ";!\n");
00646 fprintf(f, ";! Automatically generated configuration file\n");
00647 fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
00648 fprintf(f, ";! Generator: %s\n", generator);
00649 fprintf(f, ";! Creation Date: %s", date);
00650 fprintf(f, ";!\n");
00651 cat = cfg->root;
00652 while(cat) {
00653 #ifdef PRESERVE_COMMENTS
00654
00655 dump_comments(f, cat->precomments);
00656 #endif
00657
00658 #ifdef PRESERVE_COMMENTS
00659 if (cat->sameline)
00660 fprintf(f, "[%s] ; %s\n", cat->name, cat->sameline->cmt);
00661 else
00662 #endif
00663 fprintf(f, "[%s]\n", cat->name);
00664 var = cat->root;
00665 while(var) {
00666 #ifdef PRESERVE_COMMENTS
00667 dump_comments(f, var->precomments);
00668 #endif
00669 if (var->sameline)
00670 fprintf(f, "%s %s %s ; %s\n", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
00671 else
00672 fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
00673 if (var->blanklines) {
00674 blanklines = var->blanklines;
00675 while (blanklines) {
00676 fprintf(f, "\n");
00677 blanklines--;
00678 }
00679 }
00680
00681 var = var->next;
00682 }
00683 #if 0
00684
00685 fprintf(f, "\n");
00686 #endif
00687 cat = cat->next;
00688 }
00689 #ifdef PRESERVE_COMMENTS
00690 dump_comments(f, cfg->trailingcomments);
00691 #endif
00692 } else {
00693 if (option_debug)
00694 printf("Unable to open for writing: %s\n", fn);
00695 else if (option_verbose > 1)
00696 printf( "Unable to write (%s)", strerror(errno));
00697 return -1;
00698 }
00699 fclose(f);
00700 return 0;
00701 }
00702
00703 static struct ast_config *__ast_load(char *configfile, struct ast_config *tmp, struct ast_category **_tmpc, struct ast_variable **_last, int includelevel
00704 #ifdef PRESERVE_COMMENTS
00705 , struct ast_comment_struct *acs
00706 #endif
00707 )
00708 {
00709 char fn[256];
00710 char buf[512];
00711 FILE *f;
00712 int lineno=0;
00713 int master=0;
00714
00715 if (configfile[0] == '/') {
00716 strncpy(fn, configfile, sizeof(fn)-1);
00717 } else {
00718 snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, configfile);
00719 }
00720 if ((option_verbose > 1) && !option_debug) {
00721 ast_verbose( VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
00722 fflush(stdout);
00723 }
00724 if ((f = fopen(fn, "r"))) {
00725 if (option_debug)
00726 ast_log(LOG_DEBUG, "Parsing %s\n", fn);
00727 else if (option_verbose > 1)
00728 ast_verbose( "Found\n");
00729 if (!tmp) {
00730 tmp = malloc(sizeof(struct ast_config));
00731 if (tmp)
00732 memset(tmp, 0, sizeof(struct ast_config));
00733
00734 master = 1;
00735 }
00736 if (!tmp) {
00737 ast_log(LOG_WARNING, "Out of memory\n");
00738 fclose(f);
00739 return NULL;
00740 }
00741 while(!feof(f)) {
00742 fgets(buf, sizeof(buf), f);
00743 lineno++;
00744 if (!feof(f)) {
00745 if (cfg_process(tmp, _tmpc, _last, buf, lineno, configfile, includelevel
00746 #ifdef PRESERVE_COMMENTS
00747 , acs
00748 #endif
00749 )) {
00750 fclose(f);
00751 return NULL;
00752 }
00753 }
00754 }
00755 fclose(f);
00756 } else {
00757 if (option_debug)
00758 ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
00759 else if (option_verbose > 1)
00760 ast_verbose( "Not found (%s)\n", strerror(errno));
00761 }
00762 #ifdef PRESERVE_COMMENTS
00763 if (master) {
00764
00765 tmp->trailingcomments = acs->root;
00766 acs->root = NULL;
00767 acs->prev = NULL;
00768 }
00769 #endif
00770 return tmp;
00771 }
00772
00773 struct ast_config *ast_load(char *configfile)
00774 {
00775 struct ast_category *tmpc=NULL;
00776 struct ast_variable *last = NULL;
00777 #ifdef PRESERVE_COMMENTS
00778 struct ast_comment_struct acs = { NULL, NULL };
00779 #endif
00780 return __ast_load(configfile, NULL, &tmpc, &last, 0
00781 #ifdef PRESERVE_COMMENTS
00782 ,&acs
00783 #endif
00784 );
00785 }
00786
00787 char *ast_category_browse(struct ast_config *config, char *prev)
00788 {
00789 struct ast_category *cat;
00790 if (!prev) {
00791 if (config->root)
00792 return config->root->name;
00793 else
00794 return NULL;
00795 }
00796 cat = config->root;
00797 while(cat) {
00798 if (cat->name == prev) {
00799 if (cat->next)
00800 return cat->next->name;
00801 else
00802 return NULL;
00803 }
00804 cat = cat->next;
00805 }
00806 cat = config->root;
00807 while(cat) {
00808 if (!strcasecmp(cat->name, prev)) {
00809 if (cat->next)
00810 return cat->next->name;
00811 else
00812 return NULL;
00813 }
00814 cat = cat->next;
00815 }
00816 return NULL;
00817 }