@@ -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); |
@@ -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; | |||
<incl>[ \t]* /* eat the whitespace */ | |||
<incl>[^ \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); | |||
} | |||
<<EOF>> { | |||
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; | |||
<module>\} return EBRACE; | |||
<module>\; return SEMICOLON; | |||
<module>[a-zA-Z0-9_-] yylval.string=strdup(yytext); return PARAM; | |||
<module>\$[a-zA-Z_][a-zA-Z0-9_]+ yylval.string=strdup(yytext + 1); return VARIABLE; | |||
<module>\"[^"]+\" yylval.string=strdup(yytext + 1); yylval.string[strlen(yylval.string) - 1] = '\0'; return QUOTEDSTRING; | |||
%% |
@@ -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> STRING | |||
%type <string> VARIABLE | |||
%type <string> QUOTEDSTRING MODULE_OPT PARAM | |||
%type <string> FILENAME | |||
%type <string> 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 |
@@ -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 | |||
*/ |