From bb2e8a89d2e4caf5345e565f4da5aeb6fc39655b Mon Sep 17 00:00:00 2001 From: "cebka@mailsupport.rambler.ru" Date: Thu, 18 Sep 2008 18:25:00 +0400 Subject: [PATCH] * Add support of variables and variable substitution in config file --- cfg_file.h | 3 +++ cfg_file.l | 34 +++++++++++------------ cfg_file.y | 10 ++++++- cfg_utils.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 19 deletions(-) diff --git a/cfg_file.h b/cfg_file.h index 5fe89cf6d..1a211e680 100644 --- a/cfg_file.h +++ b/cfg_file.h @@ -114,6 +114,7 @@ struct config_file { LIST_HEAD (modulesq, perl_module) perl_modules; LIST_HEAD (cmodulesq, c_module) c_modules; GHashTable* modules_opts; + GHashTable* variables; }; int add_memcached_server (struct config_file *cf, char *str); @@ -125,6 +126,8 @@ char* get_module_opt (struct config_file *cfg, char *module_name, char *opt_name size_t parse_limit (const char *limit); unsigned int parse_seconds (const char *t); char parse_flag (const char *str); +char* substitute_variable (struct config_file *cfg, char *str, u_char recursive); +void post_load_config (struct config_file *cfg); int yylex (void); int yyparse (void); diff --git a/cfg_file.l b/cfg_file.l index 89ca8a171..fd5f3bc7f 100644 --- a/cfg_file.l +++ b/cfg_file.l @@ -13,6 +13,7 @@ #define MAX_INCLUDE_DEPTH 10 YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; +extern struct config_file *cfg; %} @@ -45,7 +46,6 @@ script_message return SCRIPT_MESSAGE; script_url return SCRIPT_URL; script_chain return SCRIPT_CHAIN; - \{ return OBRACE; \} return EBRACE; ; return SEMICOLON; @@ -56,6 +56,7 @@ yes|YES|no|NO|[yY]|[nN] yylval.flag=parse_flag(yytext); return FLAG; [ \t]+ /* ignore whitespace */; \"[^"]+\" yylval.string=strdup(yytext + 1); yylval.string[strlen(yylval.string) - 1] = '\0'; return QUOTEDSTRING; \" return QUOTE; +\$[a-zA-Z_][a-zA-Z0-9_]+ yylval.string=strdup(yytext + 1); return VARIABLE; [0-9]+ yylval.number=strtol(yytext, NULL, 10); return NUMBER; [0-9]+[kKmMgG]? yylval.limit=parse_limit(yytext); return SIZELIMIT; [0-9]+[sS]|[0-9]+[mM][sS] yylval.seconds=parse_seconds(yytext); return SECONDS; @@ -68,38 +69,34 @@ yes|YES|no|NO|[yY]|[nN] yylval.flag=parse_flag(yytext); return FLAG; [a-zA-Z0-9].[a-zA-Z0-9\/.-]+ yylval.string=strdup(yytext); return DOMAIN; [ \t]* /* eat the whitespace */ [^ \t\n]+ { /* got the include file name */ - if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { - yyerror ("yylex: includes nested too deeply" ); + if (include_stack_ptr >= MAX_INCLUDE_DEPTH) { + yyerror ("yylex: includes nested too deeply"); return -1; } include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; - yyin = fopen( yytext, "r" ); + yyin = fopen (yytext, "r"); - if ( ! yyin ) { - yyerror("yylex: cannot open include file"); + if (! yyin) { + yyerror ("yylex: cannot open include file"); return -1; } - yy_switch_to_buffer( - yy_create_buffer( yyin, YY_BUF_SIZE ) ); + yy_switch_to_buffer (yy_create_buffer (yyin, YY_BUF_SIZE)); BEGIN(INITIAL); } <> { - if ( --include_stack_ptr < 0 ) - { - yyterminate(); - } - - else - { - yy_delete_buffer( YY_CURRENT_BUFFER ); - yy_switch_to_buffer( - include_stack[include_stack_ptr] ); + if ( --include_stack_ptr < 0 ) { + post_load_config (cfg); + yyterminate (); + } + else { + yy_delete_buffer (YY_CURRENT_BUFFER); + yy_switch_to_buffer (include_stack[include_stack_ptr]); } } @@ -110,6 +107,7 @@ yes|YES|no|NO|[yY]|[nN] yylval.flag=parse_flag(yytext); return FLAG; \} return EBRACE; \; return SEMICOLON; [a-zA-Z0-9_-] yylval.string=strdup(yytext); return PARAM; +\$[a-zA-Z_][a-zA-Z0-9_]+ yylval.string=strdup(yytext + 1); return VARIABLE; \"[^"]+\" yylval.string=strdup(yytext + 1); yylval.string[strlen(yylval.string) - 1] = '\0'; return QUOTEDSTRING; %% diff --git a/cfg_file.y b/cfg_file.y index 8448933b4..fb31c4186 100644 --- a/cfg_file.y +++ b/cfg_file.y @@ -48,9 +48,10 @@ LIST_HEAD (moduleoptq, module_opt) *cur_module_opt = NULL; %token READ_SERVERS WRITE_SERVER DIRECTORY_SERVERS MAILBOX_QUERY USERS_QUERY LASTLOGIN_QUERY %token MEMCACHED WORKERS REQUIRE MODULE %token FILTER METRIC SCRIPT_HEADER SCRIPT_MIME SCRIPT_MESSAGE SCRIPT_URL SCRIPT_CHAIN SCRIPT_PARAM -%token MODULE_OPT, PARAM +%token MODULE_OPT PARAM VARIABLE %type STRING +%type VARIABLE %type QUOTEDSTRING MODULE_OPT PARAM %type FILENAME %type SOCKCRED @@ -80,6 +81,7 @@ command : | require | filter | module_opt + | variable ; tempdir : @@ -403,6 +405,12 @@ optcmd: } ; +variable: + VARIABLE EQSIGN QUOTEDSTRING { + g_hash_table_insert (cfg->variables, $1, $3); + } + ; + %% /* * vi:ts=4 diff --git a/cfg_utils.c b/cfg_utils.c index 3339e2bd0..a00a075b3 100644 --- a/cfg_utils.c +++ b/cfg_utils.c @@ -149,6 +149,7 @@ init_defaults (struct config_file *cfg) cfg->workers_number = DEFAULT_WORKERS_NUM; cfg->modules_opts = g_hash_table_new (g_str_hash, g_str_equal); + cfg->variables = g_hash_table_new (g_str_hash, g_str_equal); LIST_INIT (&cfg->filters); LIST_INIT (&cfg->perl_modules); @@ -205,6 +206,8 @@ free_config (struct config_file *cfg) g_hash_table_foreach (cfg->modules_opts, clean_hash_bucket, NULL); g_hash_table_remove_all (cfg->modules_opts); g_hash_table_unref (cfg->modules_opts); + g_hash_table_remove_all (cfg->variables); + g_hash_table_unref (cfg->variables); } int @@ -323,6 +326,81 @@ parse_flag (const char *str) return -1; } +/* + * Try to substitute all variables in given string + * Return: newly allocated string with substituted variables (original string may be freed if variables are found) + */ +char * +substitute_variable (struct config_file *cfg, char *str, u_char recursive) +{ + char *var, *new, *v_begin, *v_end; + size_t len; + + while ((v_begin = strstr (str, "${")) != NULL) { + len = strlen (str); + *v_begin = '\0'; + v_begin += 2; + if ((v_end = strstr (v_begin, "}")) == NULL) { + /* Not a variable, skip */ + continue; + } + *v_end = '\0'; + var = g_hash_table_lookup (cfg->variables, v_begin); + if (var == NULL) { + yywarn ("substitute_variable: variable %s is not defined", v_begin); + /* Substitute unknown variables with empty string */ + var = ""; + } + else if (recursive) { + var = substitute_variable (cfg, var, recursive); + } + /* Allocate new string */ + new = g_malloc (len - strlen (v_begin) + strlen (var) + 1); + + snprintf (new, len - strlen (v_begin) + strlen (var) + 1, "%s%s%s", + str, var, v_end + 1); + g_free (str); + str = new; + } + + return str; +} + +static void +substitute_module_variables (gpointer key, gpointer value, gpointer data) +{ + struct config_file *cfg = (struct config_file *)data; + LIST_HEAD (moduleoptq, module_opt) *cur_module_opt = (struct moduleoptq *)value; + struct module_opt *cur, *tmp; + + LIST_FOREACH_SAFE (cur, cur_module_opt, next, tmp) { + if (cur->value) { + cur->value = substitute_variable (cfg, cur->value, 0); + } + } +} + +static void +substitute_all_variables (gpointer key, gpointer value, gpointer data) +{ + struct config_file *cfg = (struct config_file *)data; + char *var; + + var = value; + /* Do recursive substitution */ + var = substitute_variable (cfg, var, 1); +} + +/* + * Substitute all variables in strings + */ +void +post_load_config (struct config_file *cfg) +{ + g_hash_table_foreach (cfg->variables, substitute_all_variables, cfg); + g_hash_table_foreach (cfg->modules_opts, substitute_module_variables, cfg); +} + /* * vi:ts=4 */ -- 2.39.5