dect
/
asterisk
Archived
13
0
Fork 0

This code was in team/murf/bug8684-trunk; it should fix bug 8684 in trunk. I didn't add it to 1.4 yet, because it's not entirely clear to me if this is a bug fix or an enhancement. A lot of files were affected by small changes like ast_variable_new getting an added arg, for the file name the var was defined in; ast_category_new gets added args of filename and lineno; ast_category and ast_variable structures now record file and lineno for each entry; a list of all #include and #execs in a config file (or any of its inclusions are now kept in the ast_config struct; at save time, each entry is put back into its proper file of origin, in order. #include and #exec directives are folded in properly. Headers indicating that the file was generated, are generated also for each included file. Some changes to main/manager.c to take care of file renaming, via the UpdateConfig command. Multiple inclusions of the same file are handled by exploding these into multiple include files, uniquely named. There's probably more, but I can't remember it right now.

git-svn-id: http://svn.digium.com/svn/asterisk/trunk@81361 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
murf 2007-08-29 20:55:40 +00:00
parent 2149521341
commit 88e10708c5
18 changed files with 807 additions and 234 deletions

View File

@ -386,7 +386,7 @@ static struct ast_config *realtime_directory(char *context)
/* Does the context exist within the config file? If not, make one */
cat = ast_category_get(cfg, context);
if (!cat) {
cat = ast_category_new(context);
cat = ast_category_new(context, "", 99999);
if (!cat) {
ast_log(LOG_WARNING, "Out of memory\n");
ast_config_destroy(cfg);
@ -402,7 +402,7 @@ static struct ast_config *realtime_directory(char *context)
snprintf(tmp, sizeof(tmp), "no-password,%s,hidefromdir=%s",
fullname ? fullname : "",
hidefromdir ? hidefromdir : "no");
var = ast_variable_new(mailbox, tmp);
var = ast_variable_new(mailbox, tmp, "");
if (var)
ast_variable_append(cat, var);
else

View File

@ -2141,7 +2141,7 @@ static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
if (varname && (varval = strchr(varname, '='))) {
*varval = '\0';
varval++;
if ((tmpvar = ast_variable_new(varname, varval))) {
if ((tmpvar = ast_variable_new(varname, varval, ""))) {
tmpvar->next = vmu->chanvars;
vmu->chanvars = tmpvar;
}

View File

@ -130,7 +130,7 @@ static int parkandannounce_exec(struct ast_channel *chan, void *data)
snprintf(buf, sizeof(buf), "%d", lot);
oh.parent_channel = chan;
oh.vars = ast_variable_new("_PARKEDAT", buf);
oh.vars = ast_variable_new("_PARKEDAT", buf, "");
dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, args.dial, 30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
if (dchan) {

View File

@ -960,7 +960,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
if (!strcasecmp(category, vmu->mailbox)) {
if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
ast_debug(3, "looks like we need to make vmsecret!\n");
var = ast_variable_new("vmsecret", newpassword);
var = ast_variable_new("vmsecret", newpassword, "");
}
new = alloca(strlen(newpassword)+1);
sprintf(new, "%s", newpassword);

View File

@ -5225,7 +5225,7 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
/* We found our match (use the first) */
/* copy vars */
for (v = user->vars ; v ; v = v->next) {
if((tmpvar = ast_variable_new(v->name, v->value))) {
if((tmpvar = ast_variable_new(v->name, v->value, v->file))) {
tmpvar->next = iaxs[callno]->vars;
iaxs[callno]->vars = tmpvar;
}
@ -9741,7 +9741,7 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
if (varname && (varval = strchr(varname,'='))) {
*varval = '\0';
varval++;
if((tmpvar = ast_variable_new(varname, varval))) {
if((tmpvar = ast_variable_new(varname, varval, ""))) {
tmpvar->next = user->vars;
user->vars = tmpvar;
}

View File

@ -10028,7 +10028,7 @@ static struct ast_variable *copy_vars(struct ast_variable *src)
struct ast_variable *res = NULL, *tmp, *v = NULL;
for (v = src ; v ; v = v->next) {
if ((tmp = ast_variable_new(v->name, v->value))) {
if ((tmp = ast_variable_new(v->name, v->value, v->file))) {
tmp->next = res;
res = tmp;
}
@ -17137,7 +17137,7 @@ static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
if ((varval = strchr(varname,'='))) {
*varval++ = '\0';
if ((tmpvar = ast_variable_new(varname, varval))) {
if ((tmpvar = ast_variable_new(varname, varval, ""))) {
tmpvar->next = list;
list = tmpvar;
}

View File

@ -1463,7 +1463,7 @@ static struct ast_variable *add_var(const char *buf, struct ast_variable *list)
if ((varval = strchr(varname,'='))) {
*varval++ = '\0';
if ((tmpvar = ast_variable_new(varname, varval))) {
if ((tmpvar = ast_variable_new(varname, varval, ""))) {
tmpvar->next = list;
list = tmpvar;
}

View File

@ -917,7 +917,7 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
int len = strlen(var2->value) + strlen(tmp2) + 1;
char *tmp3 = alloca(len);
snprintf(tmp3, len, "%s%s", var2->value, tmp2);
var = ast_variable_new(tmp, tmp3);
var = ast_variable_new(tmp, tmp3, var2->file);
var->next = var2->next;
if (prev)
prev->next = var;
@ -928,7 +928,7 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
}
}
if (!var2) {
var = ast_variable_new(tmp, tmp2);
var = ast_variable_new(tmp, tmp2, "");
var->next = ies->vars;
ies->vars = var;
}

View File

@ -52,6 +52,7 @@ enum {
struct ast_variable {
char *name;
char *value;
char *file;
int lineno;
int object; /*!< 0 for variable, 1 for object */
int blanklines; /*!< Number of blanklines following entry */
@ -61,7 +62,7 @@ struct ast_variable {
char stuff[0];
};
typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, struct ast_flags flags);
typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, struct ast_flags flags, const char *suggested_include_file);
typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
@ -242,23 +243,25 @@ struct ast_category *ast_config_get_current_category(const struct ast_config *cf
void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat);
const char *ast_config_option(struct ast_config *cfg, const char *cat, const char *var);
struct ast_category *ast_category_new(const char *name);
struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
void ast_category_append(struct ast_config *config, struct ast_category *cat);
int ast_category_delete(struct ast_config *cfg, const char *category);
void ast_category_destroy(struct ast_category *cat);
struct ast_variable *ast_category_detach_variables(struct ast_category *cat);
void ast_category_rename(struct ast_category *cat, const char *name);
struct ast_variable *ast_variable_new(const char *name, const char *value);
struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename);
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);
struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file);
void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file);
void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
int ast_variable_delete(struct ast_category *category, const char *variable, const char *match);
int ast_variable_update(struct ast_category *category, const char *variable,
const char *value, const char *match, unsigned int object);
const char *value, const char *match, unsigned int object);
int config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator);
struct ast_config *ast_config_internal_load(const char *configfile, struct ast_config *cfg, struct ast_flags flags);
struct ast_config *ast_config_internal_load(const char *configfile, struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl_file);
/*! \brief Support code to parse config file arguments
*
* The function ast_parse_arg() provides a generic interface to parse

View File

@ -177,10 +177,10 @@ struct ast_variable *ast_channeltype_list(void)
struct ast_variable *var=NULL, *prev = NULL;
AST_LIST_TRAVERSE(&backends, cl, list) {
if (prev) {
if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description)))
if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description, "")))
prev = prev->next;
} else {
var = ast_variable_new(cl->tech->type, cl->tech->description);
var = ast_variable_new(cl->tech->type, cl->tech->description, "");
prev = var;
}
}

View File

@ -180,7 +180,9 @@ static struct ast_config_engine *config_engine_list;
struct ast_category {
char name[80];
int ignored; /*!< do not let user of the config see this category */
int include_level;
int include_level;
char *file; /*!< the file name from whence this declaration was read */
int lineno;
struct ast_comment *precomments;
struct ast_comment *sameline;
struct ast_variable *root;
@ -192,26 +194,141 @@ struct ast_config {
struct ast_category *root;
struct ast_category *last;
struct ast_category *current;
struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
int include_level;
int max_include_level;
struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
};
struct ast_variable *ast_variable_new(const char *name, const char *value)
struct ast_config_include {
char *include_location_file; /*!< file name in which the include occurs */
int include_location_lineno; /*!< lineno where include occurred */
int exec; /*!< set to non-zero if itsa #exec statement */
char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
char *included_file; /*!< file name included */
int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
we explode the instances and will include those-- so all entries will be unique */
int output; /*!< a flag to indicate if the inclusion has been output */
struct ast_config_include *next; /*!< ptr to next inclusion in the list */
};
struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
{
struct ast_variable *variable;
int name_len = strlen(name) + 1;
if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + sizeof(*variable)))) {
if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
variable->name = variable->stuff;
variable->value = variable->stuff + name_len;
variable->file = variable->stuff + name_len + strlen(value) + 1;
strcpy(variable->name,name);
strcpy(variable->value,value);
strcpy(variable->file,filename);
}
return variable;
}
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)
{
/* a file should be included ONCE. Otherwise, if one of the instances is changed,
then all be changed. -- how do we know to include it? -- Handling modified
instances is possible, I'd have
to create a new master for each instance. */
struct ast_config_include *inc;
inc = ast_include_find(conf, included_file);
if (inc)
{
inc->inclusion_count++;
snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
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);
} else
*real_included_file_name = 0;
inc = ast_calloc(1,sizeof(struct ast_config_include));
inc->include_location_file = ast_strdup(from_file);
inc->include_location_lineno = from_lineno;
if (!ast_strlen_zero(real_included_file_name))
inc->included_file = ast_strdup(real_included_file_name);
else
inc->included_file = ast_strdup(included_file);
inc->exec = is_exec;
if (is_exec)
inc->exec_file = ast_strdup(exec_file);
/* attach this new struct to the conf struct */
inc->next = conf->includes;
conf->includes = inc;
return inc;
}
void ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
{
struct ast_config_include *incl;
struct ast_category *cat;
struct ast_variable *v;
int from_len = strlen(from_file);
int to_len = strlen(to_file);
if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
return;
/* the manager code allows you to read in one config file, then
write it back out under a different name. But, the new arrangement
ties output lines to the file name. So, before you try to write
the config file to disk, better riffle thru the data and make sure
the file names are changed.
*/
/* file names are on categories, includes (of course), and on variables. So,
traverse all this and swap names */
for (incl = conf->includes; incl; incl=incl->next) {
if (strcmp(incl->include_location_file,from_file) == 0) {
if (from_len >= to_len)
strcpy(incl->include_location_file, to_file);
else {
free(incl->include_location_file);
incl->include_location_file = strdup(to_file);
}
}
}
for (cat = conf->root; cat; cat = cat->next) {
if (strcmp(cat->file,from_file) == 0) {
if (from_len >= to_len)
strcpy(cat->file, to_file);
else {
free(cat->file);
cat->file = strdup(to_file);
}
}
for (v = cat->root; v; v = v->next) {
if (strcmp(v->file,from_file) == 0) {
if (from_len >= to_len)
strcpy(v->file, to_file);
else {
free(v->file);
v->file = strdup(to_file);
}
}
}
}
}
struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
{
struct ast_config_include *x;
for (x=conf->includes;x;x=x->next)
{
if (strcmp(x->included_file,included_file) == 0)
return x;
}
return 0;
}
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
{
if (!variable)
@ -281,7 +398,7 @@ const char *ast_variable_retrieve(const struct ast_config *config, const char *c
static struct ast_variable *variable_clone(const struct ast_variable *old)
{
struct ast_variable *new = ast_variable_new(old->name, old->value);
struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
if (new) {
new->lineno = old->lineno;
@ -310,12 +427,14 @@ static void move_variables(struct ast_category *old, struct ast_category *new)
#endif
}
struct ast_category *ast_category_new(const char *name)
struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
{
struct ast_category *category;
if ((category = ast_calloc(1, sizeof(*category))))
ast_copy_string(category->name, name, sizeof(category->name));
category->file = strdup(in_file);
category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
return category;
}
@ -361,9 +480,28 @@ void ast_category_append(struct ast_config *config, struct ast_category *categor
void ast_category_destroy(struct ast_category *cat)
{
ast_variables_destroy(cat->root);
if (cat->file)
free(cat->file);
ast_free(cat);
}
static void ast_includes_destroy(struct ast_config_include *incls)
{
struct ast_config_include *incl,*inclnext;
for (incl=incls; incl; incl = inclnext) {
inclnext = incl->next;
if (incl->include_location_file)
free(incl->include_location_file);
if (incl->exec_file)
free(incl->exec_file);
if (incl->included_file)
free(incl->included_file);
free(incl);
}
}
static struct ast_category *next_available_category(struct ast_category *cat)
{
for (; cat && cat->ignored; cat = cat->next);
@ -494,13 +632,10 @@ int ast_variable_delete(struct ast_category *category, const char *variable, con
}
int ast_variable_update(struct ast_category *category, const char *variable,
const char *value, const char *match, unsigned int object)
const char *value, const char *match, unsigned int object)
{
struct ast_variable *cur, *prev=NULL, *newer;
if (!(newer = ast_variable_new(variable, value)))
return -1;
newer->object = object;
for (cur = category->root; cur; prev = cur, cur = cur->next) {
@ -508,6 +643,9 @@ int ast_variable_update(struct ast_category *category, const char *variable,
(!ast_strlen_zero(match) && strcasecmp(cur->value, match)))
continue;
if (!(newer = ast_variable_new(variable, value, cur->file)))
return -1;
newer->next = cur->next;
newer->object = cur->object || object;
if (prev)
@ -584,6 +722,8 @@ void ast_config_destroy(struct ast_config *cfg)
if (!cfg)
return;
ast_includes_destroy(cfg->includes);
cat = cfg->root;
while (cat) {
ast_variables_destroy(cat->root);
@ -657,7 +797,7 @@ static void config_cache_attribute(const char *configfile, enum config_cache_att
}
static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, struct ast_flags flags,
char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size)
char **comment_buffer, int *comment_buffer_size, char **lline_buffer, int *lline_buffer_size, const char *suggested_include_file)
{
char *c;
char *cur = buf;
@ -681,9 +821,11 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
if (*c++ != '(')
c = NULL;
catname = cur;
if (!(*cat = newcat = ast_category_new(catname))) {
if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
return -1;
}
(*cat)->lineno = lineno;
/* add comments */
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && *comment_buffer && (*comment_buffer)[0] ) {
newcat->precomments = ALLOC_COMMENT(*comment_buffer);
@ -755,10 +897,15 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
}
if (do_include || do_exec) {
if (c) {
char *cur2;
char real_inclusion_name[256];
struct ast_config_include *inclu;
/* Strip off leading and trailing "'s and <>'s */
while ((*c == '<') || (*c == '>') || (*c == '\"')) c++;
/* Get rid of leading mess */
cur = c;
cur2 = cur;
while (!ast_strlen_zero(cur)) {
c = cur + strlen(cur) - 1;
if ((*c == '>') || (*c == '<') || (*c == '\"'))
@ -781,7 +928,10 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
exec_file[0] = '\0';
}
/* A #include */
do_include = ast_config_internal_load(cur, cfg, flags) ? 1 : 0;
/* record this inclusion */
inclu = ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
do_include = ast_config_internal_load(cur, cfg, flags, real_inclusion_name) ? 1 : 0;
if (!ast_strlen_zero(exec_file))
unlink(exec_file);
if (!do_include)
@ -814,7 +964,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
c++;
} else
object = 0;
if ((v = ast_variable_new(ast_strip(cur), ast_strip(c)))) {
if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), *suggested_include_file ? suggested_include_file : configfile))) {
v->lineno = lineno;
v->object = object;
/* Put and reset comments */
@ -840,7 +990,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
return 0;
}
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)
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)
{
char fn[256];
char buf[8192];
@ -951,7 +1101,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
#else
ast_copy_string(fn2, cfinclude->include);
#endif
if (config_text_file_load(NULL, NULL, fn2, NULL, flags) == NULL) {
if (config_text_file_load(NULL, NULL, fn2, NULL, flags, "") == NULL) { /* that last field needs to be looked at in this case... TODO */
unchanged = 0;
/* One change is enough to short-circuit and reload the whole shebang */
break;
@ -1058,7 +1208,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
if (process_buf) {
char *buf = ast_strip(process_buf);
if (!ast_strlen_zero(buf)) {
if (process_text_line(cfg, &cat, buf, lineno, fn, flags, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size)) {
if (process_text_line(cfg, &cat, buf, lineno, fn, flags, &comment_buffer, &comment_buffer_size, &lline_buffer, &lline_buffer_size, suggested_include_file)) {
cfg = NULL;
break;
}
@ -1095,60 +1245,171 @@ static struct ast_config *config_text_file_load(const char *database, const char
return cfg;
}
/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
be shocked and mystified as to why things are not showing up in the files!
Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
and line number are stored for each include, plus the name of the file included, so that these statements may be
included in the output files on a file_save operation.
The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
and a header gets added.
vars and category heads are output in the order they are stored in the config file. So, if the software
shuffles these at all, then the placement of #include directives might get a little mixed up, because the
file/lineno data probably won't get changed.
*/
static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
{
char date[256]="";
time_t t;
time(&t);
ast_copy_string(date, ctime(&t), sizeof(date));
fprintf(f1, ";!\n");
fprintf(f1, ";! Automatically generated configuration file\n");
if (strcmp(configfile, fn))
fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
else
fprintf(f1, ";! Filename: %s\n", configfile);
fprintf(f1, ";! Generator: %s\n", generator);
fprintf(f1, ";! Creation Date: %s", date);
fprintf(f1, ";!\n");
}
static void set_fn(char *fn, int fn_size, const char *file, const char *configfile)
{
if (!file || file[0] == 0) {
if (configfile[0] == '/')
ast_copy_string(fn, configfile, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
} else if (file[0] == '/')
ast_copy_string(fn, file, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
}
int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
{
FILE *f;
char fn[256];
char date[256]="";
time_t t;
struct ast_variable *var;
struct ast_category *cat;
struct ast_comment *cmt;
struct ast_config_include *incl;
int blanklines = 0;
if (configfile[0] == '/') {
ast_copy_string(fn, configfile, sizeof(fn));
} else {
snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
/* reset all the output flags, in case this isn't our first time saving this data */
for (incl=cfg->includes; incl; incl = incl->next)
incl->output = 0;
/* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
are all truncated to zero bytes and have that nice header*/
for (incl=cfg->includes; incl; incl = incl->next)
{
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*/
FILE *f1;
set_fn(fn, sizeof(fn), incl->included_file, configfile); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
f1 = fopen(fn,"w");
if (f1) {
gen_header(f1, configfile, fn, generator);
fclose(f1); /* this should zero out the file */
} else {
ast_debug(1, "Unable to open for writing: %s\n", fn);
ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
}
}
}
time(&t);
ast_copy_string(date, ctime(&t), sizeof(date));
set_fn(fn, sizeof(fn), 0, configfile); /* just set fn to absolute ver of configfile */
#ifdef __CYGWIN__
if ((f = fopen(fn, "w+"))) {
#else
if ((f = fopen(fn, "w"))) {
#endif
ast_verb(2, "Saving '%s': ", fn);
fprintf(f, ";!\n");
fprintf(f, ";! Automatically generated configuration file\n");
if (strcmp(configfile, fn))
fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
else
fprintf(f, ";! Filename: %s\n", configfile);
fprintf(f, ";! Generator: %s\n", generator);
fprintf(f, ";! Creation Date: %s", date);
fprintf(f, ";!\n");
gen_header(f, configfile, fn, generator);
cat = cfg->root;
fclose(f);
/* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
/* since each var, cat, and associated comments can come from any file, we have to be
mobile, and open each file, print, and close it on an entry-by-entry basis */
while (cat) {
/* Dump section with any appropriate comment */
for (cmt = cat->precomments; cmt; cmt=cmt->next)
set_fn(fn, sizeof(fn), cat->file, configfile);
f = fopen(fn, "a");
if (!f)
{
ast_debug(1, "Unable to open for writing: %s\n", fn);
ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, cat->file) == 0){
if (cat->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
incl->output = 1;
}
}
}
/* Dump section with any appropriate comment */
for (cmt = cat->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
if (!cat->precomments)
fprintf(f,"\n");
fprintf(f, "[%s]", cat->name);
for (cmt = cat->sameline; cmt; cmt=cmt->next)
{
for (cmt = cat->sameline; cmt; cmt=cmt->next) {
fprintf(f,"%s", cmt->cmt);
}
if (!cat->sameline)
fprintf(f,"\n");
fclose(f);
var = cat->root;
while (var) {
for (cmt = var->precomments; cmt; cmt=cmt->next)
set_fn(fn, sizeof(fn), var->file, configfile);
f = fopen(fn, "a");
if (!f)
{
ast_debug(1, "Unable to open for writing: %s\n", fn);
ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, var->file) == 0){
if (var->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
incl->output = 1;
}
}
}
for (cmt = var->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
@ -1161,13 +1422,11 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
while (blanklines--)
fprintf(f, "\n");
}
fclose(f);
var = var->next;
}
#if 0
/* Put an empty line */
fprintf(f, "\n");
#endif
cat = cat->next;
}
if (!option_debug)
@ -1177,7 +1436,32 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
ast_verb(2, "Unable to write (%s)", strerror(errno));
return -1;
}
fclose(f);
/* Now, for files with trailing #include/#exec statements,
we have to make sure every entry is output */
for (incl=cfg->includes; incl; incl = incl->next) {
if (!incl->output) {
/* open the respective file */
set_fn(fn, sizeof(fn), incl->include_location_file, configfile);
f = fopen(fn, "a");
if (!f)
{
ast_debug(1, "Unable to open for writing: %s\n", fn);
ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
/* output the respective include */
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
fclose(f);
incl->output = 1;
}
}
return 0;
}
@ -1240,7 +1524,7 @@ int read_config_maps(void)
configtmp = ast_config_new();
configtmp->max_include_level = 1;
config = ast_config_internal_load(extconfig_conf, configtmp, flags);
config = ast_config_internal_load(extconfig_conf, configtmp, flags, "");
if (!config) {
ast_config_destroy(configtmp);
return 0;
@ -1379,7 +1663,7 @@ static struct ast_config_engine text_file_engine = {
.load_func = config_text_file_load,
};
struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags)
struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file)
{
char db[256];
char table[256];
@ -1408,7 +1692,7 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con
}
}
result = loader->load_func(db, table, filename, cfg, flags);
result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file);
if (result && result != CONFIG_STATUS_FILEUNCHANGED)
result->include_level--;
@ -1427,7 +1711,7 @@ struct ast_config *ast_config_load(const char *filename, struct ast_flags flags)
if (!cfg)
return NULL;
result = ast_config_internal_load(filename, cfg, flags);
result = ast_config_internal_load(filename, cfg, flags, "");
if (!result || result == CONFIG_STATUS_FILEUNCHANGED)
ast_config_destroy(cfg);

View File

@ -567,7 +567,7 @@ static struct ast_str *handle_uri(struct server_instance *ser, char *uri, int *s
else
val = "";
ast_uri_decode(var);
if ((v = ast_variable_new(var, val))) {
if ((v = ast_variable_new(var, val, ""))) {
if (vars)
prev->next = v;
else
@ -778,7 +778,7 @@ static void *httpd_helper_thread(void *data)
value = ast_skip_blanks(value);
if (ast_strlen_zero(value))
continue;
var = ast_variable_new(name, value);
var = ast_variable_new(name, value, "");
if (!var)
continue;
var->next = headers;
@ -818,7 +818,7 @@ static void *httpd_helper_thread(void *data)
vval++;
if ( (l = strlen(vval)) )
vval[l - 1] = '\0'; /* trim trailing quote */
var = ast_variable_new(vname, vval);
var = ast_variable_new(vname, vval, "");
if (var) {
if (prev)
prev->next = var;

View File

@ -791,7 +791,7 @@ struct ast_variable *astman_get_variables(const struct message *m)
strsep(&val, "=");
if (!val || ast_strlen_zero(var))
continue;
cur = ast_variable_new(var, val);
cur = ast_variable_new(var, val, "");
cur->next = head;
head = cur;
}
@ -1224,7 +1224,7 @@ static int action_getconfigjson(struct mansession *s, const struct message *m)
}
/* helper function for action_updateconfig */
static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg)
static void handle_updates(struct mansession *s, const struct message *m, struct ast_config *cfg, const char *dfn)
{
int x;
char hdr[40];
@ -1253,7 +1253,7 @@ static void handle_updates(struct mansession *s, const struct message *m, struct
match = astman_get_header(m, hdr);
if (!strcasecmp(action, "newcat")) {
if (!ast_strlen_zero(cat)) {
category = ast_category_new(cat);
category = ast_category_new(cat, dfn, 99999);
if (category) {
ast_category_append(cfg, category);
}
@ -1276,7 +1276,7 @@ static void handle_updates(struct mansession *s, const struct message *m, struct
} else if (!strcasecmp(action, "append")) {
if (!ast_strlen_zero(cat) && !ast_strlen_zero(var) &&
(category = ast_category_get(cfg, cat)) &&
(v = ast_variable_new(var, value))){
(v = ast_variable_new(var, value, dfn))){
if (object || (match && !strcasecmp(match, "object")))
v->object = 1;
ast_variable_append(category, v);
@ -1315,7 +1315,8 @@ static int action_updateconfig(struct mansession *s, const struct message *m)
astman_send_error(s, m, "Config file not found");
return 0;
}
handle_updates(s, m, cfg);
handle_updates(s, m, cfg, dfn);
ast_include_rename(cfg, sfn, dfn); /* change the include references from dfn to sfn, so things match up */
res = config_text_file_save(dfn, cfg, "Manager");
ast_config_destroy(cfg);
if (res) {

View File

@ -219,7 +219,7 @@ static int apply_outgoing(struct outgoing *o, char *fn, FILE *f)
c2 = c;
strsep(&c2, "=");
if (c2) {
var = ast_variable_new(c, c2);
var = ast_variable_new(c, c2, fn);
if (var) {
var->next = o->vars;
o->vars = var;

View File

@ -219,11 +219,11 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl
chunk = strsep(&stringp, ";");
if (!ast_strlen_zero(ast_strip(chunk))) {
if (prev) {
prev->next = ast_variable_new(coltitle, chunk);
prev->next = ast_variable_new(coltitle, chunk, "");
if (prev->next)
prev = prev->next;
} else
prev = var = ast_variable_new(coltitle, chunk);
prev = var = ast_variable_new(coltitle, chunk, "");
}
}
}
@ -334,7 +334,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
continue;
}
cat = ast_category_new("");
cat = ast_category_new("","",99999);
if (!cat) {
ast_log(LOG_WARNING, "Out of memory!\n");
continue;
@ -366,7 +366,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char *
if (!ast_strlen_zero(ast_strip(chunk))) {
if (initfield && !strcmp(initfield, coltitle))
ast_category_rename(cat, chunk);
var = ast_variable_new(coltitle, chunk);
var = ast_variable_new(coltitle, chunk, "");
ast_variable_append(cat, var);
}
}
@ -625,7 +625,7 @@ static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data)
return sth;
}
static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags)
static struct ast_config *config_odbc(const char *database, const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags, const char *sugg_incl)
{
struct ast_variable *new_v;
struct ast_category *cur_cat;
@ -682,7 +682,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
if (!strcmp (q.var_name, "#include")) {
if (!ast_config_internal_load(q.var_val, cfg, loader_flags)) {
if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "")) {
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
return NULL;
@ -690,7 +690,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
continue;
}
if (strcmp(last, q.category) || last_cat_metric != q.cat_metric) {
cur_cat = ast_category_new(q.category);
cur_cat = ast_category_new(q.category, "", 99999);
if (!cur_cat) {
ast_log(LOG_WARNING, "Out of memory!\n");
break;
@ -700,7 +700,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c
ast_category_append(cfg, cur_cat);
}
new_v = ast_variable_new(q.var_name, q.var_val);
new_v = ast_variable_new(q.var_name, q.var_val, "");
ast_variable_append(cur_cat, new_v);
}

View File

@ -175,12 +175,12 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab
chunk = strsep(&stringp, ";");
if (!ast_strlen_zero(ast_strip(chunk))) {
if (prev) {
prev->next = ast_variable_new(fieldnames[i], chunk);
prev->next = ast_variable_new(fieldnames[i], chunk, "");
if (prev->next) {
prev = prev->next;
}
} else {
prev = var = ast_variable_new(fieldnames[i], chunk);
prev = var = ast_variable_new(fieldnames[i], chunk, "");
}
}
}
@ -313,7 +313,7 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
for (rowIndex = 0; rowIndex < num_rows; rowIndex++) {
var = NULL;
if (!(cat = ast_category_new("")))
if (!(cat = ast_category_new("","",99999)))
continue;
for (i = 0; i < numFields; i++) {
stringp = PQgetvalue(result, rowIndex, i);
@ -323,7 +323,7 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char
if (initfield && !strcmp(initfield, fieldnames[i])) {
ast_category_rename(cat, chunk);
}
var = ast_variable_new(fieldnames[i], chunk);
var = ast_variable_new(fieldnames[i], chunk, "");
ast_variable_append(cat, var);
}
}
@ -615,8 +615,8 @@ static int destroy_pgsql(const char *database, const char *table, const char *ke
static struct ast_config *config_pgsql(const char *database, const char *table,
const char *file, struct ast_config *cfg,
struct ast_flags flags)
const char *file, struct ast_config *cfg,
struct ast_flags flags, const char *suggested_incl)
{
PGresult *result = NULL;
long num_rows;
@ -681,7 +681,7 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
char *field_var_val = PQgetvalue(result, rowIndex, 2);
char *field_cat_metric = PQgetvalue(result, rowIndex, 3);
if (!strcmp(field_var_name, "#include")) {
if (!ast_config_internal_load(field_var_val, cfg, flags)) {
if (!ast_config_internal_load(field_var_val, cfg, flags, "")) {
PQclear(result);
ast_mutex_unlock(&pgsql_lock);
return NULL;
@ -690,14 +690,14 @@ static struct ast_config *config_pgsql(const char *database, const char *table,
}
if (strcmp(last, field_category) || last_cat_metric != atoi(field_cat_metric)) {
cur_cat = ast_category_new(field_category);
cur_cat = ast_category_new(field_category, "", 99999);
if (!cur_cat)
break;
strcpy(last, field_category);
last_cat_metric = atoi(field_cat_metric);
ast_category_append(cfg, cur_cat);
}
new_v = ast_variable_new(field_var_name, field_var_val);
new_v = ast_variable_new(field_var_name, field_var_val, "");
ast_variable_append(cur_cat, new_v);
}
} else {

View File

@ -305,9 +305,8 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames);
* \retval NULL if an error occurred
* \see add_cfg_entry()
*/
static struct ast_config * config_handler(const char *database,
const char *table, const char *file,
struct ast_config *cfg, struct ast_flags flags);
static struct ast_config * config_handler(const char *database, const char *table, const char *file,
struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl);
/*!
* \brief Helper function to parse a va_list object into 2 dynamic arrays of
@ -736,7 +735,7 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
args = arg;
if (!args->cat_name || strcmp(args->cat_name, argv[RES_SQLITE_CONFIG_CATEGORY])) {
args->cat = ast_category_new(argv[RES_SQLITE_CONFIG_CATEGORY]);
args->cat = ast_category_new(argv[RES_SQLITE_CONFIG_CATEGORY], "", 99999);
if (!args->cat) {
ast_log(LOG_WARNING, "Unable to allocate category\n");
@ -755,7 +754,7 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
}
var = ast_variable_new(argv[RES_SQLITE_CONFIG_VAR_NAME],
argv[RES_SQLITE_CONFIG_VAR_VAL]);
argv[RES_SQLITE_CONFIG_VAR_VAL], "");
if (!var) {
ast_log(LOG_WARNING, "Unable to allocate variable");
@ -767,8 +766,8 @@ static int add_cfg_entry(void *arg, int argc, char **argv, char **columnNames)
return 0;
}
static struct ast_config *config_handler(const char *database,
const char *table, const char *file, struct ast_config *cfg, struct ast_flags flags)
static struct ast_config *config_handler(const char *database, const char *table, const char *file,
struct ast_config *cfg, struct ast_flags flags, const char *suggested_incl)
{
struct cfg_entry_args args;
char *errormsg;
@ -856,7 +855,7 @@ static int add_rt_cfg_entry(void *arg, int argc, char **argv, char **columnNames
if (!argv[i])
continue;
if (!(var = ast_variable_new(columnNames[i], argv[i])))
if (!(var = ast_variable_new(columnNames[i], argv[i], "")))
return 1;
if (!args->var)
@ -990,7 +989,7 @@ static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv, char **colum
return 1;
}
if (!(cat = ast_category_new(cat_name))) {
if (!(cat = ast_category_new(cat_name, "", 99999))) {
ast_log(LOG_WARNING, "Unable to allocate category\n");
return 1;
}
@ -1001,7 +1000,7 @@ static int add_rt_multi_cfg_entry(void *arg, int argc, char **argv, char **colum
if (!argv[i] || !strcmp(args->initfield, columnNames[i]))
continue;
if (!(var = ast_variable_new(columnNames[i], argv[i]))) {
if (!(var = ast_variable_new(columnNames[i], argv[i], ""))) {
ast_log(LOG_WARNING, "Unable to allocate variable\n");
return 1;
}

View File

@ -620,6 +620,8 @@ struct ast_category {
char name[80];
int ignored; /*!< do not let user of the config see this category */
int include_level;
char *file; /*!< the file name from whence this declaration was read */
int lineno;
struct ast_comment *precomments;
struct ast_comment *sameline;
struct ast_variable *root;
@ -634,9 +636,22 @@ struct ast_config {
struct ast_category *last_browse; /*!< used to cache the last category supplied via category_browse */
int include_level;
int max_include_level;
struct ast_config_include *includes; /*!< a list of inclusions, which should describe the entire tree */
};
typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, int withcomments);
struct ast_config_include {
char *include_location_file; /*!< file name in which the include occurs */
int include_location_lineno; /*!< lineno where include occurred */
int exec; /*!< set to non-zero if itsa #exec statement */
char *exec_file; /*!< if it's an exec, you'll have both the /var/tmp to read, and the original script */
char *included_file; /*!< file name included */
int inclusion_count; /*!< if the file is included more than once, a running count thereof -- but, worry not,
we explode the instances and will include those-- so all entries will be unique */
int output; /*!< a flag to indicate if the inclusion has been output */
struct ast_config_include *next; /*!< ptr to next inclusion in the list */
};
typedef struct ast_config *config_load_func(const char *database, const char *table, const char *configfile, struct ast_config *config, int withcomments, const char *suggested_include_file);
typedef struct ast_variable *realtime_var_get(const char *database, const char *table, va_list ap);
typedef struct ast_config *realtime_multi_get(const char *database, const char *table, va_list ap);
typedef int realtime_update(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap);
@ -653,11 +668,89 @@ struct ast_config_engine {
static struct ast_config_engine *config_engine_list;
/* taken from strings.h */
static force_inline int ast_strlen_zero(const char *s)
{
return (!s || (*s == '\0'));
}
#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b))
AST_INLINE_API(
void ast_copy_string(char *dst, const char *src, size_t size),
{
while (*src && size) {
*dst++ = *src++;
size--;
}
if (__builtin_expect(!size, 0))
dst--;
*dst = '\0';
}
)
AST_INLINE_API(
char *ast_skip_blanks(const char *str),
{
while (*str && *str < 33)
str++;
return (char *)str;
}
)
/*!
\brief Trims trailing whitespace characters from a string.
\param ast_trim_blanks function being used
\param str the input string
\return a pointer to the modified string
*/
AST_INLINE_API(
char *ast_trim_blanks(char *str),
{
char *work = str;
if (work) {
work += strlen(work) - 1;
/* It's tempting to only want to erase after we exit this loop,
but since ast_trim_blanks *could* receive a constant string
(which we presumably wouldn't have to touch), we shouldn't
actually set anything unless we must, and it's easier just
to set each position to \0 than to keep track of a variable
for it */
while ((work >= str) && *work < 33)
*(work--) = '\0';
}
return str;
}
)
/*!
\brief Strip leading/trailing whitespace from a string.
\param s The string to be stripped (will be modified).
\return The stripped string.
This functions strips all leading and trailing whitespace
characters from the input string, and returns a pointer to
the resulting string. The string is modified in place.
*/
AST_INLINE_API(
char *ast_strip(char *s),
{
s = ast_skip_blanks(s);
if (s)
ast_trim_blanks(s);
return s;
}
)
/* from config.h */
struct ast_variable {
char *name;
char *value;
char *file;
int lineno;
int object; /*!< 0 for variable, 1 for object */
int blanklines; /*!< Number of blanklines following entry */
@ -668,31 +761,137 @@ struct ast_variable {
};
static const char *ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable);
static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments);
static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file);
struct ast_config *localized_config_load_with_comments(const char *filename);
static char *ast_category_browse(struct ast_config *config, const char *prev);
static struct ast_variable *ast_variable_browse(const struct ast_config *config, const char *category);
static void ast_variables_destroy(struct ast_variable *v);
static void ast_config_destroy(struct ast_config *cfg);
static 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);
static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file);
void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file);
static struct ast_variable *ast_variable_new(const char *name, const char *value);
static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename);
static struct ast_variable *ast_variable_new(const char *name, const char *value)
static struct ast_variable *ast_variable_new(const char *name, const char *value, const char *filename)
{
struct ast_variable *variable;
int name_len = strlen(name) + 1;
if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + sizeof(*variable)))) {
if ((variable = ast_calloc(1, name_len + strlen(value) + 1 + strlen(filename) + 1 + sizeof(*variable)))) {
variable->name = variable->stuff;
variable->value = variable->stuff + name_len;
variable->file = variable->value + strlen(value) + 1;
strcpy(variable->name,name);
strcpy(variable->value,value);
strcpy(variable->file,filename);
}
return variable;
}
static 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)
{
/* a file should be included ONCE. Otherwise, if one of the instances is changed,
then all be changed. -- how do we know to include it? -- Handling modified
instances is possible, I'd have
to create a new master for each instance. */
struct ast_config_include *inc;
inc = ast_include_find(conf, included_file);
if (inc)
{
inc->inclusion_count++;
snprintf(real_included_file_name, real_included_file_name_size, "%s~~%d", included_file, inc->inclusion_count);
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);
} else
*real_included_file_name = 0;
inc = ast_calloc(1,sizeof(struct ast_config_include));
inc->include_location_file = ast_strdup(from_file);
inc->include_location_lineno = from_lineno;
if (!ast_strlen_zero(real_included_file_name))
inc->included_file = ast_strdup(real_included_file_name);
else
inc->included_file = ast_strdup(included_file);
inc->exec = is_exec;
if (is_exec)
inc->exec_file = ast_strdup(exec_file);
/* attach this new struct to the conf struct */
inc->next = conf->includes;
conf->includes = inc;
return inc;
}
void localized_ast_include_rename(struct ast_config *conf, const char *from_file, const char *to_file)
{
struct ast_config_include *incl;
struct ast_category *cat;
struct ast_variable *v;
int from_len = strlen(from_file);
int to_len = strlen(to_file);
if (strcmp(from_file, to_file) == 0) /* no use wasting time if the name is the same */
return;
/* the manager code allows you to read in one config file, then
write it back out under a different name. But, the new arrangement
ties output lines to the file name. So, before you try to write
the config file to disk, better riffle thru the data and make sure
the file names are changed.
*/
/* file names are on categories, includes (of course), and on variables. So,
traverse all this and swap names */
for (incl = conf->includes; incl; incl=incl->next) {
if (strcmp(incl->include_location_file,from_file) == 0) {
if (from_len >= to_len)
strcpy(incl->include_location_file, to_file);
else {
free(incl->include_location_file);
incl->include_location_file = strdup(to_file);
}
}
}
for (cat = conf->root; cat; cat = cat->next) {
if (strcmp(cat->file,from_file) == 0) {
if (from_len >= to_len)
strcpy(cat->file, to_file);
else {
free(cat->file);
cat->file = strdup(to_file);
}
}
for (v = cat->root; v; v = v->next) {
if (strcmp(v->file,from_file) == 0) {
if (from_len >= to_len)
strcpy(v->file, to_file);
else {
free(v->file);
v->file = strdup(to_file);
}
}
}
}
}
static struct ast_config_include *ast_include_find(struct ast_config *conf, const char *included_file)
{
struct ast_config_include *x;
for (x=conf->includes;x;x=x->next)
{
if (strcmp(x->included_file,included_file) == 0)
return x;
}
return 0;
}
static void ast_variable_append(struct ast_category *category, struct ast_variable *variable);
static void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
@ -768,7 +967,7 @@ static const char *ast_variable_retrieve(const struct ast_config *config, const
static struct ast_variable *variable_clone(const struct ast_variable *old)
{
struct ast_variable *new = ast_variable_new(old->name, old->value);
struct ast_variable *new = ast_variable_new(old->name, old->value, old->file);
if (new) {
new->lineno = old->lineno;
@ -791,6 +990,22 @@ static void ast_variables_destroy(struct ast_variable *v)
}
}
static void ast_includes_destroy(struct ast_config_include *incls)
{
struct ast_config_include *incl,*inclnext;
for (incl=incls; incl; incl = inclnext) {
inclnext = incl->next;
if (incl->include_location_file)
free(incl->include_location_file);
if (incl->exec_file)
free(incl->exec_file);
if (incl->included_file)
free(incl->included_file);
free(incl);
}
}
static void ast_config_destroy(struct ast_config *cfg)
{
struct ast_category *cat, *catn;
@ -798,6 +1013,8 @@ static void ast_config_destroy(struct ast_config *cfg)
if (!cfg)
return;
ast_includes_destroy(cfg->includes);
cat = cfg->root;
while (cat) {
ast_variables_destroy(cat->root);
@ -2431,83 +2648,6 @@ static int clearglobalvars_config = 0;
static void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
/* taken from strings.h */
static force_inline int ast_strlen_zero(const char *s)
{
return (!s || (*s == '\0'));
}
#define S_OR(a, b) (!ast_strlen_zero(a) ? (a) : (b))
AST_INLINE_API(
void ast_copy_string(char *dst, const char *src, size_t size),
{
while (*src && size) {
*dst++ = *src++;
size--;
}
if (__builtin_expect(!size, 0))
dst--;
*dst = '\0';
}
)
AST_INLINE_API(
char *ast_skip_blanks(const char *str),
{
while (*str && *str < 33)
str++;
return (char *)str;
}
)
/*!
\brief Trims trailing whitespace characters from a string.
\param ast_trim_blanks function being used
\param str the input string
\return a pointer to the modified string
*/
AST_INLINE_API(
char *ast_trim_blanks(char *str),
{
char *work = str;
if (work) {
work += strlen(work) - 1;
/* It's tempting to only want to erase after we exit this loop,
but since ast_trim_blanks *could* receive a constant string
(which we presumably wouldn't have to touch), we shouldn't
actually set anything unless we must, and it's easier just
to set each position to \0 than to keep track of a variable
for it */
while ((work >= str) && *work < 33)
*(work--) = '\0';
}
return str;
}
)
/*!
\brief Strip leading/trailing whitespace from a string.
\param s The string to be stripped (will be modified).
\return The stripped string.
This functions strips all leading and trailing whitespace
characters from the input string, and returns a pointer to
the resulting string. The string is modified in place.
*/
AST_INLINE_API(
char *ast_strip(char *s),
{
s = ast_skip_blanks(s);
if (s)
ast_trim_blanks(s);
return s;
}
)
/* stolen from callerid.c */
/*! \brief Clean up phone string
@ -3160,12 +3300,12 @@ static struct ast_config_engine *find_engine(const char *family, char *database,
ret = eng;
}
}
/* if we found a mapping, but the engine is not available, then issue a warning */
if (map && !ret)
ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
return ret;
}
@ -3176,15 +3316,17 @@ struct ast_category *ast_config_get_current_category(const struct ast_config *cf
return cfg->current;
}
static struct ast_category *ast_category_new(const char *name);
static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
static struct ast_category *ast_category_new(const char *name)
static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
{
struct ast_category *category;
if ((category = ast_calloc(1, sizeof(*category))))
ast_copy_string(category->name, name, sizeof(category->name));
return category;
category->file = strdup(in_file);
category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
return category;
}
struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name);
@ -3236,6 +3378,9 @@ static void ast_category_destroy(struct ast_category *cat);
static void ast_category_destroy(struct ast_category *cat)
{
ast_variables_destroy(cat->root);
if (cat->file)
free(cat->file);
free(cat);
}
@ -3245,9 +3390,9 @@ static struct ast_config_engine text_file_engine = {
};
static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments);
static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file);
static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments)
static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file)
{
char db[256];
char table[256];
@ -3279,7 +3424,7 @@ static struct ast_config *ast_config_internal_load(const char *filename, struct
}
}
result = loader->load_func(db, table, filename, cfg, withcomments);
result = loader->load_func(db, table, filename, cfg, withcomments, suggested_incl_file);
/* silence is golden
ast_log(LOG_WARNING, "finished internal loading file %s level=%d\n", filename, cfg->include_level);
*/
@ -3291,7 +3436,7 @@ static struct ast_config *ast_config_internal_load(const char *filename, struct
}
static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments)
static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments, const char *suggested_include_file)
{
char *c;
char *cur = buf;
@ -3315,9 +3460,11 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
if (*c++ != '(')
c = NULL;
catname = cur;
if (!(*cat = newcat = ast_category_new(catname))) {
if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
return -1;
}
(*cat)->lineno = lineno;
/* add comments */
if (withcomments && comment_buffer && comment_buffer[0] ) {
newcat->precomments = ALLOC_COMMENT(comment_buffer);
@ -3390,10 +3537,15 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
}
if (do_include || do_exec) {
if (c) {
char *cur2;
char real_inclusion_name[256];
struct ast_config_include *inclu;
/* Strip off leading and trailing "'s and <>'s */
while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
/* Get rid of leading mess */
cur = c;
cur2 = cur;
while (!ast_strlen_zero(cur)) {
c = cur + strlen(cur) - 1;
if ((*c == '>') || (*c == '<') || (*c == '\"'))
@ -3413,7 +3565,10 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
/* A #include */
/* ast_log(LOG_WARNING, "Reading in included file %s withcomments=%d\n", cur, withcomments); */
do_include = ast_config_internal_load(cur, cfg, withcomments) ? 1 : 0;
/* record this inclusion */
inclu = ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
do_include = ast_config_internal_load(cur, cfg, withcomments, real_inclusion_name) ? 1 : 0;
if(!ast_strlen_zero(exec_file))
unlink(exec_file);
if(!do_include)
@ -3447,7 +3602,7 @@ static int process_text_line(struct ast_config *cfg, struct ast_category **cat,
c++;
} else
object = 0;
if ((v = ast_variable_new(ast_strip(cur), ast_strip(c)))) {
if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), configfile))) {
v->lineno = lineno;
v->object = object;
/* Put and reset comments */
@ -3489,7 +3644,7 @@ void localized_use_conf_dir(void)
}
static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments)
static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file)
{
char fn[256];
char buf[8192];
@ -3635,7 +3790,7 @@ static struct ast_config *config_text_file_load(const char *database, const char
if (process_buf) {
char *buf = ast_strip(process_buf);
if (!ast_strlen_zero(buf)) {
if (process_text_line(cfg, &cat, buf, lineno, filename, withcomments)) {
if (process_text_line(cfg, &cat, buf, lineno, filename, withcomments, suggested_include_file)) {
cfg = NULL;
break;
}
@ -3695,7 +3850,7 @@ struct ast_config *localized_config_load(const char *filename)
if (!cfg)
return NULL;
result = ast_config_internal_load(filename, cfg, 0);
result = ast_config_internal_load(filename, cfg, 0, "");
if (!result)
ast_config_destroy(cfg);
@ -3713,7 +3868,7 @@ struct ast_config *localized_config_load_with_comments(const char *filename)
if (!cfg)
return NULL;
result = ast_config_internal_load(filename, cfg, 1);
result = ast_config_internal_load(filename, cfg, 1, "");
if (!result)
ast_config_destroy(cfg);
@ -3769,26 +3924,94 @@ void ast_config_set_current_category(struct ast_config *cfg, const struct ast_ca
cfg->current = (struct ast_category *) cat;
}
/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
be shocked and mystified as to why things are not showing up in the files!
Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
and line number are stored for each include, plus the name of the file included, so that these statements may be
included in the output files on a file_save operation.
The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
and a header gets added.
vars and category heads are output in the order they are stored in the config file. So, if the software
shuffles these at all, then the placement of #include directives might get a little mixed up, because the
file/lineno data probably won't get changed.
*/
static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
{
char date[256]="";
time_t t;
time(&t);
ast_copy_string(date, ctime(&t), sizeof(date));
fprintf(f1, ";!\n");
fprintf(f1, ";! Automatically generated configuration file\n");
if (strcmp(configfile, fn))
fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
else
fprintf(f1, ";! Filename: %s\n", configfile);
fprintf(f1, ";! Generator: %s\n", generator);
fprintf(f1, ";! Creation Date: %s", date);
fprintf(f1, ";!\n");
}
static void set_fn(char *fn, int fn_size, const char *file, const char *configfile)
{
if (!file || file[0] == 0) {
if (configfile[0] == '/')
ast_copy_string(fn, configfile, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
} else if (file[0] == '/')
ast_copy_string(fn, file, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
}
int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator);
int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
{
FILE *f;
char fn[256];
char date[256]="";
time_t t;
struct ast_variable *var;
struct ast_category *cat;
struct ast_comment *cmt;
struct ast_config_include *incl;
int blanklines = 0;
if (configfile[0] == '/') {
ast_copy_string(fn, configfile, sizeof(fn));
} else {
snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
/* reset all the output flags, in case this isn't our first time saving this data */
for (incl=cfg->includes; incl; incl = incl->next)
incl->output = 0;
/* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
are all truncated to zero bytes and have that nice header*/
for (incl=cfg->includes; incl; incl = incl->next)
{
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*/
FILE *f1;
set_fn(fn, sizeof(fn), incl->included_file, configfile); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
f1 = fopen(fn,"w");
if (f1) {
gen_header(f1, configfile, fn, generator);
fclose(f1); /* this should zero out the file */
} else {
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
}
}
}
time(&t);
ast_copy_string(date, ctime(&t), sizeof(date));
set_fn(fn, sizeof(fn), 0, configfile); /* just set fn to absolute ver of configfile */
#ifdef __CYGWIN__
if ((f = fopen(fn, "w+"))) {
#else
@ -3796,36 +4019,76 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
#endif
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn);
fprintf(f, ";!\n");
fprintf(f, ";! Automatically generated configuration file\n");
if (strcmp(configfile, fn))
fprintf(f, ";! Filename: %s (%s)\n", configfile, fn);
else
fprintf(f, ";! Filename: %s\n", configfile);
fprintf(f, ";! Generator: %s\n", generator);
fprintf(f, ";! Creation Date: %s", date);
fprintf(f, ";!\n");
gen_header(f, configfile, fn, generator);
cat = cfg->root;
fclose(f);
/* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
/* since each var, cat, and associated comments can come from any file, we have to be
mobile, and open each file, print, and close it on an entry-by-entry basis */
while(cat) {
/* Dump section with any appropriate comment */
for (cmt = cat->precomments; cmt; cmt=cmt->next)
set_fn(fn, sizeof(fn), cat->file, configfile);
f = fopen(fn, "a");
if (!f)
{
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, cat->file) == 0){
if (cat->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
incl->output = 1;
}
}
}
/* Dump section with any appropriate comment */
for (cmt = cat->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
if (!cat->precomments)
fprintf(f,"\n");
fprintf(f, "[%s]", cat->name);
for(cmt = cat->sameline; cmt; cmt=cmt->next)
{
for(cmt = cat->sameline; cmt; cmt=cmt->next) {
fprintf(f,"%s", cmt->cmt);
}
if (!cat->sameline)
fprintf(f,"\n");
fclose(f);
var = cat->root;
while(var) {
for (cmt = var->precomments; cmt; cmt=cmt->next)
set_fn(fn, sizeof(fn), var->file, configfile);
f = fopen(fn, "a");
if (!f)
{
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, var->file) == 0){
if (var->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
incl->output = 1;
}
}
}
for (cmt = var->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
@ -3838,13 +4101,12 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
while (blanklines--)
fprintf(f, "\n");
}
fclose(f);
var = var->next;
}
#if 0
/* Put an empty line */
fprintf(f, "\n");
#endif
cat = cat->next;
}
if ((option_verbose > 1) && !option_debug)
@ -3856,7 +4118,31 @@ int localized_config_text_file_save(const char *configfile, const struct ast_con
ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno));
return -1;
}
fclose(f);
/* Now, for files with trailing #include/#exec statements,
we have to make sure every entry is output */
for (incl=cfg->includes; incl; incl = incl->next) {
if (!incl->output) {
/* open the respective file */
set_fn(fn, sizeof(fn), incl->include_location_file, configfile);
f = fopen(fn, "a");
if (!f)
{
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
/* output the respective include */
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
fclose(f);
incl->output = 1;
}
}
return 0;
}