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);
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);
#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
+extern struct config_file *cfg;
%}
script_url return SCRIPT_URL;
script_chain return SCRIPT_CHAIN;
-
\{ return OBRACE;
\} return EBRACE;
; return SEMICOLON;
[ \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;
[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]);
}
}
<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;
%%
%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
| require
| filter
| module_opt
+ | variable
;
tempdir :
}
;
+variable:
+ VARIABLE EQSIGN QUOTEDSTRING {
+ g_hash_table_insert (cfg->variables, $1, $3);
+ }
+ ;
+
%%
/*
* vi:ts=4
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);
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
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
*/