mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 03:20:57 +00:00
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: https://origsvn.digium.com/svn/asterisk/trunk@81361 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
542
utils/extconf.c
542
utils/extconf.c
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user