Main Page | Alphabetical List | Data Structures | File List | Data Fields | Globals

config.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Configuration File Parser
00005  * 
00006  * Copyright (C) 1999, Mark Spencer
00007  *
00008  * Mark Spencer <markster@linux-support.net>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU General Public License
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    /* Maybe this structure isn't necessary but we'll keep it
00040       for now */
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    /* Strip off trailing whitespace, returns, etc */
00060    while(strlen(buf) && (buf[strlen(buf)-1]<33))
00061       buf[strlen(buf)-1] = '\0';
00062    start = buf;
00063    /* Strip off leading whitespace, returns, etc */
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    /* Determine if this is a true value */
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    /* Determine if this is a false value */
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       /* Get the last one that looks like it */
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       /* Unlink from original position */
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    /* Unlink it */
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          /* Put us at the end */
00339          pcat = config->root;
00340          while(pcat->next)
00341             pcat = pcat->next;
00342          pcat->next = cat;
00343       } else {
00344          /* We're the first one */
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          /* Get the last one that looks like it */
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       /* Unlink from original position */
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       /* Memset the header */
00434       memset(c, 0, sizeof(struct ast_comment));
00435       /* Copy the rest */
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    /* Strip off lines using ; as comment */
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       /* Actually parse the entry */
00474       if (cur[0] == '[') {
00475          /* A category header */
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          /* A directive */
00510          cur++;
00511          c = cur;
00512          while(*c && (*c > 32)) c++;
00513          if (*c) {
00514             *c = '\0';
00515             c++;
00516             /* Find real argument */
00517             while(*c  && (*c < 33)) c++;
00518             if (!*c)
00519                c = NULL;
00520          } else 
00521             c = NULL;
00522          if (!strcasecmp(cur, "include")) {
00523             /* A #include */
00524             if (c) {
00525                while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
00526                /* Get rid of leading mess */
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             /* Strip off leading and trailing "'s and <>'s */
00546          } else 
00547             ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
00548       } else {
00549          /* Just a line (variable = value) */
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             /* Ignore > in => */
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                /* Put and reset comments */
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       /* store any comments if there are any */
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          /* Dump any precomments */
00655          dump_comments(f, cat->precomments);
00656 #endif
00657          /* Dump section with any appropriate comment */
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          /* Put an empty line */
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       /* Keep trailing comments */
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 }

Generated on Sun Apr 18 23:33:50 2004 for Asterisk by doxygen 1.3.6-20040222