mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-29 02:57:23 +00:00
This fix closes issue #10642 -- it's not perfect, but should retain most blank lines in config files, via read/write cycles.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@82288 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
139
main/config.c
139
main/config.c
@@ -54,6 +54,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
|
||||
#define MAX_NESTED_COMMENTS 128
|
||||
#define COMMENT_START ";--"
|
||||
@@ -162,6 +163,40 @@ static struct ast_comment *ALLOC_COMMENT(const char *buffer)
|
||||
return x;
|
||||
}
|
||||
|
||||
/* I need to keep track of each config file, and all its inclusions,
|
||||
so that we can track blank lines in each */
|
||||
|
||||
struct inclfile
|
||||
{
|
||||
char *fname;
|
||||
int lineno;
|
||||
};
|
||||
|
||||
static int hash_string(const void *obj, const int flags)
|
||||
{
|
||||
char *str = ((struct inclfile*)obj)->fname;
|
||||
int total;
|
||||
|
||||
for (total=0; *str; str++)
|
||||
{
|
||||
unsigned int tmp = total;
|
||||
total <<= 1; /* multiply by 2 */
|
||||
total += tmp; /* multiply by 3 */
|
||||
total <<= 2; /* multiply by 12 */
|
||||
total += tmp; /* multiply by 13 */
|
||||
|
||||
total += ((unsigned int)(*str));
|
||||
}
|
||||
if (total < 0)
|
||||
total = -total;
|
||||
return total;
|
||||
}
|
||||
|
||||
static int hashtab_compare_strings(void *a, void *b, int flags)
|
||||
{
|
||||
const struct inclfile *ae = a, *be = b;
|
||||
return !strcmp(ae->fname, be->fname) ? CMP_MATCH : 0;
|
||||
}
|
||||
|
||||
static struct ast_config_map {
|
||||
struct ast_config_map *next;
|
||||
@@ -1320,8 +1355,18 @@ static void gen_header(FILE *f1, const char *configfile, const char *fn, const c
|
||||
fprintf(f1, ";!\n");
|
||||
}
|
||||
|
||||
static void set_fn(char *fn, int fn_size, const char *file, const char *configfile)
|
||||
static void inclfile_destroy(void *obj)
|
||||
{
|
||||
const struct inclfile *o = obj;
|
||||
if (o->fname)
|
||||
free(o->fname);
|
||||
}
|
||||
|
||||
|
||||
static void set_fn(char *fn, int fn_size, const char *file, const char *configfile, struct ao2_container *fileset, struct inclfile **fi)
|
||||
{
|
||||
struct inclfile lookup;
|
||||
|
||||
if (!file || file[0] == 0) {
|
||||
if (configfile[0] == '/')
|
||||
ast_copy_string(fn, configfile, fn_size);
|
||||
@@ -1331,6 +1376,54 @@ static void set_fn(char *fn, int fn_size, const char *file, const char *configfi
|
||||
ast_copy_string(fn, file, fn_size);
|
||||
else
|
||||
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
|
||||
lookup.fname = fn;
|
||||
*fi = ao2_find(fileset, &lookup, OBJ_POINTER);
|
||||
if (!(*fi)) {
|
||||
/* set up a file scratch pad */
|
||||
struct inclfile *fx = ao2_alloc(sizeof(struct inclfile), inclfile_destroy);
|
||||
fx->fname = ast_strdup(fn);
|
||||
fx->lineno = 1;
|
||||
*fi = fx;
|
||||
ao2_link(fileset, fx);
|
||||
ao2_ref(fx,1); /* bump the ref count, so it looks like we just got the ref from find */
|
||||
}
|
||||
}
|
||||
|
||||
static int count_linefeeds(char *str)
|
||||
{
|
||||
int count = 0;
|
||||
while (*str) {
|
||||
if (*str =='\n')
|
||||
count++;
|
||||
str++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int count_linefeeds_in_comments(struct ast_comment *x)
|
||||
{
|
||||
int count = 0;
|
||||
while (x)
|
||||
{
|
||||
count += count_linefeeds(x->cmt);
|
||||
x = x->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void insert_leading_blank_lines(FILE *fp, struct inclfile *fi, struct ast_comment *precomments, int lineno)
|
||||
{
|
||||
int precomment_lines = count_linefeeds_in_comments(precomments);
|
||||
int i;
|
||||
|
||||
/* I don't have to worry about those ;! comments, they are
|
||||
stored in the precomments, but not printed back out.
|
||||
I did have to make sure that comments following
|
||||
the ;! header comments were not also deleted in the process */
|
||||
for (i=fi->lineno;i<lineno - precomment_lines; i++) {
|
||||
fprintf(fp,"\n");
|
||||
}
|
||||
fi->lineno = lineno+1; /* Advance the file lineno */
|
||||
}
|
||||
|
||||
int config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
|
||||
@@ -1342,6 +1435,8 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
struct ast_comment *cmt;
|
||||
struct ast_config_include *incl;
|
||||
int blanklines = 0;
|
||||
struct ao2_container *fileset = ao2_container_alloc(180000, hash_string, hashtab_compare_strings);
|
||||
struct inclfile *fi = 0;
|
||||
|
||||
/* reset all the output flags, in case this isn't our first time saving this data */
|
||||
|
||||
@@ -1356,7 +1451,7 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
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 */
|
||||
set_fn(fn, sizeof(fn), incl->included_file, configfile, fileset, &fi); /* 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);
|
||||
@@ -1365,10 +1460,12 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
ast_debug(1, "Unable to open for writing: %s\n", fn);
|
||||
ast_verb(2, "Unable to write %s (%s)", fn, strerror(errno));
|
||||
}
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
fi = 0;
|
||||
}
|
||||
}
|
||||
|
||||
set_fn(fn, sizeof(fn), 0, configfile); /* just set fn to absolute ver of configfile */
|
||||
set_fn(fn, sizeof(fn), 0, configfile, fileset, &fi); /* just set fn to absolute ver of configfile */
|
||||
#ifdef __CYGWIN__
|
||||
if ((f = fopen(fn, "w+"))) {
|
||||
#else
|
||||
@@ -1378,18 +1475,20 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
gen_header(f, configfile, fn, generator);
|
||||
cat = cfg->root;
|
||||
fclose(f);
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
|
||||
/* 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) {
|
||||
set_fn(fn, sizeof(fn), cat->file, configfile);
|
||||
set_fn(fn, sizeof(fn), cat->file, configfile, fileset, &fi);
|
||||
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));
|
||||
ao2_ref(fileset, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1406,10 +1505,18 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
}
|
||||
}
|
||||
|
||||
insert_leading_blank_lines(f, fi, cat->precomments, cat->lineno);
|
||||
/* 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);
|
||||
char *cmtp = cmt->cmt;
|
||||
while (*cmtp == ';' && *(cmtp+1) == '!') {
|
||||
char *cmtp2 = strchr(cmtp+1, '\n');
|
||||
if (cmtp2)
|
||||
cmtp = cmtp2+1;
|
||||
else cmtp = 0;
|
||||
}
|
||||
if (cmtp)
|
||||
fprintf(f,"%s", cmtp);
|
||||
}
|
||||
if (!cat->precomments)
|
||||
fprintf(f,"\n");
|
||||
@@ -1424,15 +1531,20 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
fprintf(f,"%s", cmt->cmt);
|
||||
}
|
||||
fclose(f);
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
fi = 0;
|
||||
|
||||
var = cat->root;
|
||||
while (var) {
|
||||
set_fn(fn, sizeof(fn), var->file, configfile);
|
||||
set_fn(fn, sizeof(fn), var->file, configfile, fileset, &fi);
|
||||
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));
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
fi = 0;
|
||||
ao2_ref(fileset, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1449,6 +1561,7 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
}
|
||||
}
|
||||
|
||||
insert_leading_blank_lines(f, fi, var->precomments, var->lineno);
|
||||
for (cmt = var->precomments; cmt; cmt=cmt->next) {
|
||||
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
|
||||
fprintf(f,"%s", cmt->cmt);
|
||||
@@ -1468,6 +1581,8 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
fi = 0;
|
||||
|
||||
var = var->next;
|
||||
}
|
||||
@@ -1478,6 +1593,8 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
} else {
|
||||
ast_debug(1, "Unable to open for writing: %s\n", fn);
|
||||
ast_verb(2, "Unable to write (%s)", strerror(errno));
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
ao2_ref(fileset, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1487,12 +1604,15 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
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);
|
||||
set_fn(fn, sizeof(fn), incl->include_location_file, configfile, fileset, &fi);
|
||||
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));
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
fi = 0;
|
||||
ao2_ref(fileset, -1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1503,8 +1623,11 @@ int config_text_file_save(const char *configfile, const struct ast_config *cfg,
|
||||
fprintf(f,"#include \"%s\"\n", incl->included_file);
|
||||
fclose(f);
|
||||
incl->output = 1;
|
||||
ao2_ref(fi,-1); /* we are giving up this reference to the object ptd to by fi */
|
||||
fi = 0;
|
||||
}
|
||||
}
|
||||
ao2_ref(fileset, -1); /* this should destroy the hash container */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user