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