Browse Source

Merge commit 'origin/andrei/script_vars'

* commit 'origin/andrei/script_vars': (30 commits)
  script parsing: helper function updated & moved
  script engine: different operators for int and str +
  script engine: keep file pos. info when optimizing
  script engine: optimize op($v, ct) where ct=0 or 1
  script engine: optimize $v - ct
  script engine: pvar & avp fixes
  script: fix pvar parsing
  script parsing: better expression error reporting
  script parsing: expression type checking on startup
  script engine: rve_check_type() extended
  pvars: silent version for pv_parse_spec
  script parsing: better error reporting
  script parsing: fix line/column counting in strings
  script parsing: support for breaking strings
  script engine: more rvalue optimizations
  script engine: better debugging info
  core: new command line option (debugging)
  script engine: rvalue fixes & cleanup
  script engine: expression optimizations
  script parsing: fixed wrong operators in expr.
  ...
Andrei Pelinescu-Onciul 16 years ago
parent
commit
2cd96a3563
17 changed files with 4843 additions and 697 deletions
  1. 17 1
      NEWS
  2. 11 116
      action.c
  3. 221 42
      cfg.lex
  4. 518 292
      cfg.y
  5. 367 0
      lvalue.c
  6. 67 0
      lvalue.h
  7. 15 5
      main.c
  8. 53 34
      pvapi.c
  9. 2 1
      pvar.h
  10. 541 198
      route.c
  11. 5 1
      route.h
  12. 83 5
      route_struct.c
  13. 16 2
      route_struct.h
  14. 2628 0
      rvalue.c
  15. 212 0
      rvalue.h
  16. 43 0
      sr_compat.c
  17. 44 0
      sr_compat.h

+ 17 - 1
NEWS

@@ -1,8 +1,24 @@
-Release notes for SIP Express Router (ser)
+Release notes for SIP Router (sr)
 ***********************************************
 
 $Id$
 
+sip-router changes
+
+core:
+  - support for dual module interfaces: ser and kamailio
+config script changes:
+  - script mode can be switched between ser compatible, kamailio compatible
+    and max compatibility (compatible with both as much as possible), using
+      #!SER
+      #!KAMAILIO
+      #!OPENSER
+      #!ALL
+      #!MAXCOMPAT
+    where #!KAMAILIO is equivalent with #!OPENSER and #!ALL with #!MAXCOMPAT
+  - support for kamailio style pvars
+
+
 
 
 2.1.0 changes

+ 11 - 116
action.c

@@ -47,6 +47,7 @@
  *  2007-06-14  run_actions & do_action need a ctx or handle now, no more 
  *               static vars (andrei)
  *  2008-11-18  support for variable parameter module functions (andrei)
+ *  2008-12-03  use lvalues/rvalues for assignments (andrei)
  */
 
 
@@ -63,6 +64,7 @@
 #include "parser/msg_parser.h"
 #include "parser/parse_uri.h"
 #include "ut.h"
+#include "lvalue.h"
 #include "sr_module.h"
 #include "mem/mem.h"
 #include "globals.h"
@@ -108,8 +110,6 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 	struct sip_uri uri, next_hop;
 	struct sip_uri *u;
 	unsigned short port;
-	unsigned short flags;
-	int_str name, value;
 	str* dst_host;
 
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
@@ -870,121 +870,16 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 			ret=1; /* continue processing */
 			break;
 
-	        case ADD_T:
-	        case ASSIGN_T:
-
-			/* If the left attr was specified without indexing brackets delete
-			 * existing AVPs before adding new ones
-			 */
-			if ((a->val[0].u.attr->type & AVP_INDEX_ALL) != AVP_INDEX_ALL) delete_avp(a->val[0].u.attr->type, a->val[0].u.attr->name);
-
-			if (a->val[1].type == STRING_ST) {
-				value.s = a->val[1].u.str;
-				flags = a->val[0].u.attr->type | AVP_VAL_STR;
-				name = a->val[0].u.attr->name;
-				ret = 1;
-			} else if (a->val[1].type == NUMBER_ST) {
-				value.n = a->val[1].u.number;
-				flags = a->val[0].u.attr->type;
-				name = a->val[0].u.attr->name;
+	 case ADD_T:
+	case ASSIGN_T:
+			v=lval_assign(h, msg, (struct lvalue*)a->val[0].u.data,
+								  (struct rval_expr*)a->val[1].u.data);
+			if (likely(v>=0)) 
 				ret = 1;
-			} else if (a->val[1].type == ACTION_ST) {
-				flags = a->val[0].u.attr->type;
-				name = a->val[0].u.attr->name;
-				if (a->val[1].u.data) {
-					value.n = run_actions(h, (struct action*)a->val[1].u.data,
-											msg);
-				} else {
-					value.n = -1;
-				}
-				ret = value.n;
-			} else if(a->val[1].type == EXPR_ST && a->val[1].u.data) {
-				v = eval_expr(h, (struct expr*)a->val[1].u.data, msg);
-				if (v < 0) {
-					if (v == EXPR_DROP){ /* hack to quit on DROP*/
-						ret = 0;
-						break;
-					} else {
-						LOG(L_WARN,"WARNING: do_action: error in expression\n");
-						v = 0; /* error is treated as false (Miklos) */
-					}
-				}
-
-				flags = a->val[0].u.attr->type;
-				name = a->val[0].u.attr->name;
-				value.n = v;
-			} else if (a->val[1].type == AVP_ST) {
-				struct search_state st;
-				avp_t* avp;
-				avp_t* avp_mark;
-
-				avp_mark = NULL;
-				if ((a->val[1].u.attr->type & AVP_INDEX_ALL) == AVP_INDEX_ALL) {
-					avp = search_first_avp(a->val[1].u.attr->type, a->val[1].u.attr->name, &value, &st);
-					while(avp) {
-						     /* We take only the type of value and name from the source avp
-						      * and reset class and track flags
-						      */
-						flags = (a->val[0].u.attr->type & ~AVP_INDEX_ALL) | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
-
-						if (add_avp_before(avp_mark, flags, a->val[0].u.attr->name, value) < 0) {
-							LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
-							ret=E_UNSPEC;
-							break;
-						}
-
-						/* move the mark, so the next found AVP will come before the one currently added
-						 * so they will have the same order as in the source list
-						 */
-						if (avp_mark) {
-							avp_mark=avp_mark->next;
-						} else {
-							avp_mark=search_first_avp(flags, a->val[0].u.attr->name, NULL, NULL);
-						}
-
-						avp = search_next_avp(&st, &value);
-					}
-					ret = 1;
-					break;
-				} else {
-					avp = search_avp_by_index(a->val[1].u.attr->type, a->val[1].u.attr->name, &value, a->val[1].u.attr->index);
-					if (avp) {
-						flags = a->val[0].u.attr->type | (avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL));
-						name = a->val[0].u.attr->name;
-						ret = 1;
-					} else {
-						ret = E_UNSPEC;
-						break;
-					}
-				}
-			} else if (a->val[1].type == SELECT_ST) {
-				int r;
-				r = run_select(&value.s, a->val[1].u.select, msg);
-				if (r < 0) {
-					ret=E_UNSPEC;
-					break;
-				} else if (r > 0) {
-					value.s.s = "";
-					value.s.len = 0;
-				}
-
-				flags = a->val[0].u.attr->type | AVP_VAL_STR;
-				name = a->val[0].u.attr->name;
-				ret = 1;
-			} else {
-				LOG(L_CRIT, "BUG: do_action: Bad right side of avp assignment\n");
-				ret=E_BUG;
-				break;
-			}
-
-			/* If the action is assign then remove the old avp value
-			 * before adding new ones */
-/*			if ((unsigned char)a->type == ASSIGN_T) delete_avp(flags, name); */
-			if (add_avp(flags & ~AVP_INDEX_ALL, name, value) < 0) {
-				LOG(L_CRIT, "ERROR: Failed to assign value to attribute\n");
-				ret=E_UNSPEC;
-				break;
-			}
+			else if (unlikely (v == EXPR_DROP)) /* hack to quit on DROP*/
+				ret=0;
+			else
+				ret=v;
 			break;
 
 		default:

+ 221 - 42
cfg.lex

@@ -75,6 +75,8 @@
  *  2007-11-28  added TCP_OPT_{FD_CACHE, DEFER_ACCEPT, DELAYED_ACK, SYNCNT,
  *              LINGER2, KEEPALIVE, KEEPIDLE, KEEPINTVL, KEEPCNT} (andrei)
  *  2008-01-24  added CFG_DESCRIPTION used by cfg_var (Miklos)
+ *  2008-11-28  added support for kamailio pvars and avp/pvar guessing (andrei)
+ *  2008-12-11  added support for "string1" "string2" (andrei)
 */
 
 
@@ -88,14 +90,19 @@
 	#include "usr_avp.h"
 	#include "select.h"
 	#include "cfg.tab.h"
+	#include "sr_compat.h"
 
 	/* states */
 	#define INITIAL_S		0
 	#define COMMENT_S		1
 	#define COMMENT_LN_S	        2
 	#define STRING_S		3
-	#define ATTR_S                  4
-        #define SELECT_S                5
+	#define ATTR_S                  4  /* avp/attr */
+	#define SELECT_S                5
+	#define AVP_PVAR_S              6  /* avp or pvar */
+	#define PVAR_P_S                7  /* pvar: $(...)  or $foo(...)*/
+	#define PVARID_S                8  /* $foo.bar...*/
+	#define STR_BETWEEN_S		9
 
 	#define STR_BUF_ALLOC_UNIT	128
 	struct str_buf{
@@ -106,21 +113,33 @@
 
 
 	static int comment_nest=0;
+	static int p_nest=0;
 	static int state=0, old_state=0, old_initial=0;
 	static struct str_buf s_buf;
 	int line=1;
 	int column=1;
 	int startcolumn=1;
+	int startline=1;
+	static int ign_lines=0;
+	static int ign_columns=0;
 
 	static char* addchar(struct str_buf *, char);
 	static char* addstr(struct str_buf *, char*, int);
 	static void count();
+	static void count_more();
+	static void count_ignore();
 
 
 %}
 
 /* start conditions */
-%x STRING1 STRING2 COMMENT COMMENT_LN ATTR SELECT
+%x STRING1 STRING2 STR_BETWEEN COMMENT COMMENT_LN ATTR SELECT AVP_PVAR PVAR_P 
+%x PVARID
+
+/* config script types : #!SER  or #!KAMAILIO or #!MAX_COMPAT */
+SER_CFG			SER
+KAMAILIO_CFG	KAMAILIO|OPENSER
+MAXCOMPAT_CFG	MAXCOMPAT|ALL
 
 /* action keywords */
 FORWARD	forward
@@ -219,7 +238,8 @@ PLUS	"+"
 MINUS	"-"
 
 /* Attribute specification */
-ATTR_MARK   "$"|"%"
+ATTR_MARK   "%"
+VAR_MARK    "$"
 SELECT_MARK  "@"
 ATTR_FROM         "f"
 ATTR_TO           "t"
@@ -231,6 +251,9 @@ ATTR_FROMDOMAIN   "fd"
 ATTR_TODOMAIN     "td"
 ATTR_GLOBAL       "g"
 
+/* avp prefix */
+AVP_PREF	(([ft][rud]?)|g)\.
+
 /* config vars. */
 DEBUG	debug
 FORK	fork
@@ -384,7 +407,7 @@ ID			{LETTER}{ALPHANUM}*
 HEX			[0-9a-fA-F]
 HEXNUMBER	0x{HEX}+
 OCTNUMBER	0[0-7]+
-DECNUMBER       0|-?([1-9]{DIGIT}*)
+DECNUMBER       0|([1-9]{DIGIT}*)
 BINNUMBER       [0-1]+b
 HEX4		{HEX}{1,4}
 IPV6ADDR	({HEX4}":"){7}{HEX4}|({HEX4}":"){1,7}(":"{HEX4}){1,7}|":"(":"{HEX4}){1,7}|({HEX4}":"){1,7}":"|"::"
@@ -729,7 +752,8 @@ EAT_ABLE	[\ \t\b\r]
 <SELECT>{BINNUMBER}     { count(); yylval.intval=(int)strtol(yytext, 0, 2); return NUMBER; }
 
 
-<INITIAL>{ATTR_MARK}    { count(); state = ATTR_S; BEGIN(ATTR); return ATTR_MARK; }
+<INITIAL>{ATTR_MARK}    { count(); state = ATTR_S; BEGIN(ATTR);
+							return ATTR_MARK; }
 <ATTR>{ATTR_FROM}       { count(); return ATTR_FROM; }
 <ATTR>{ATTR_TO}         { count(); return ATTR_TO; }
 <ATTR>{ATTR_FROMURI}    { count(); return ATTR_FROMURI; }
@@ -742,15 +766,88 @@ EAT_ABLE	[\ \t\b\r]
 <ATTR>{DOT}             { count(); return DOT; }
 <ATTR>{LBRACK}          { count(); return LBRACK; }
 <ATTR>{RBRACK}          { count(); return RBRACK; }
-<ATTR>{STAR}		{ count(); return STAR; }
-<ATTR>{DECNUMBER}	{ count(); yylval.intval=atoi(yytext);return NUMBER; }
-<ATTR>{ID}		{ count(); addstr(&s_buf, yytext, yyleng);
-                           yylval.strval=s_buf.s;
-			   memset(&s_buf, 0, sizeof(s_buf));
-                           state = INITIAL_S;
-                           BEGIN(INITIAL);
-			   return ID;
-                        }
+<ATTR>{STAR}			{ count(); return STAR; }
+<ATTR>{DECNUMBER}		{ count(); yylval.intval=atoi(yytext);return NUMBER; }
+<ATTR>{ID}				{ count(); addstr(&s_buf, yytext, yyleng);
+							yylval.strval=s_buf.s;
+							memset(&s_buf, 0, sizeof(s_buf));
+							state = INITIAL_S;
+							BEGIN(INITIAL);
+							return ID;
+						}
+
+<INITIAL>{VAR_MARK}{LPAREN}	{
+								switch(sr_cfg_compat){
+									case SR_COMPAT_SER:
+										state=ATTR_S; BEGIN(ATTR);
+										yyless(1);
+										count();
+										return ATTR_MARK;
+										break;
+									case SR_COMPAT_KAMAILIO:
+									case SR_COMPAT_MAX:
+									default:
+										state = PVAR_P_S; BEGIN(PVAR_P);
+										p_nest=1; yymore();
+										break;
+								}
+							}
+	/* eat everything between 2 () and return PVAR token and a string
+	   containing everything (including $ and ()) */
+<PVAR_P>{RPAREN}			{	p_nest--;
+								if (p_nest==0){
+									count();
+									addstr(&s_buf, yytext, yyleng);
+									yylval.strval=s_buf.s;
+									memset(&s_buf, 0, sizeof(s_buf));
+									state=INITIAL_S;
+									BEGIN(INITIAL);
+									return PVAR;
+								}
+								yymore();
+							}
+<PVAR_P>{LPAREN}			{ p_nest++; yymore(); }
+<PVAR_P>.					{ yymore(); }
+
+<PVARID>{ID}|'.'			{yymore(); }
+<PVARID>{LPAREN}			{	state = PVAR_P_S; BEGIN(PVAR_P);
+								p_nest=1; yymore(); }
+<PVARID>.					{ yyless(0); state=INITIAL_S; BEGIN(INITIAL);
+								return PVAR;
+							}
+
+
+<INITIAL>{VAR_MARK}			{
+								switch(sr_cfg_compat){
+									case SR_COMPAT_SER:
+										count();
+										state=ATTR_S; BEGIN(ATTR);
+										return ATTR_MARK;
+										break;
+									case SR_COMPAT_KAMAILIO:
+										state=PVARID_S; BEGIN(PVARID);
+										yymore();
+										break;
+									case SR_COMPAT_MAX:
+									default: 
+										state=AVP_PVAR_S; BEGIN(AVP_PVAR);
+										yymore();
+										break;
+								}
+							}
+	/* avp prefix detected -> go to avp mode */
+<AVP_PVAR>{AVP_PREF}		|
+<AVP_PVAR>{ID}{LBRACK}		{ state = ATTR_S; BEGIN(ATTR); yyless(1); count();
+							  return ATTR_MARK; }
+<AVP_PVAR>{ID}{LPAREN}		{ state = PVAR_P_S; p_nest=1; BEGIN(PVAR_P);
+								yymore(); }
+<AVP_PVAR>{ID}				{	count(); addstr(&s_buf, yytext, yyleng);
+								yylval.strval=s_buf.s;
+								memset(&s_buf, 0, sizeof(s_buf));
+								state = INITIAL_S;
+								BEGIN(INITIAL);
+								return AVP_OR_PVAR;
+							}
 
 <INITIAL>{IPV6ADDR}		{ count(); yylval.strval=yytext; return IPV6ADDR; }
 <INITIAL>{DECNUMBER}		{ count(); yylval.intval=atoi(yytext);return NUMBER; }
@@ -781,7 +878,7 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{COMMA}		{ count(); return COMMA; }
 <INITIAL>{SEMICOLON}	{ count(); return SEMICOLON; }
 <INITIAL>{COLON}	{ count(); return COLON; }
-<INITIAL>{STAR}	{ count(); return STAR; }
+<INITIAL>{STAR}		{ count(); return STAR; }
 <INITIAL>{RPAREN}	{ count(); return RPAREN; }
 <INITIAL>{LPAREN}	{ count(); return LPAREN; }
 <INITIAL>{LBRACE}	{ count(); return LBRACE; }
@@ -794,18 +891,19 @@ EAT_ABLE	[\ \t\b\r]
 <INITIAL>{CR}		{ count();/* return CR;*/ }
 
 
-<INITIAL,SELECT>{QUOTES} { count(); old_initial = YY_START; old_state = state; state=STRING_S; BEGIN(STRING1); }
-<INITIAL>{TICK} { count(); old_initial = YY_START; old_state = state; state=STRING_S; BEGIN(STRING2); }
+<INITIAL,SELECT>{QUOTES} { count(); old_initial = YY_START; 
+							old_state = state; state=STRING_S;
+							BEGIN(STRING1); }
+<INITIAL>{TICK} { count(); old_initial = YY_START; old_state = state;
+					state=STRING_S; BEGIN(STRING2); }
 
 
-<STRING1>{QUOTES} { count(); state=old_state; BEGIN(old_initial);
+<STRING1>{QUOTES} { count_more(); 
 						yytext[yyleng-1]=0; yyleng--;
 						addstr(&s_buf, yytext, yyleng);
-						yylval.strval=s_buf.s;
-						memset(&s_buf, 0, sizeof(s_buf));
-						return STRING;
+						BEGIN(STR_BETWEEN);
 					}
-<STRING2>{TICK}  { count(); state=old_state; BEGIN(old_initial);
+<STRING2>{TICK}  { count_more(); state=old_state; BEGIN(old_initial);
 						yytext[yyleng-1]=0; yyleng--;
 						addstr(&s_buf, yytext, yyleng);
 						yylval.strval=s_buf.s;
@@ -814,21 +912,33 @@ EAT_ABLE	[\ \t\b\r]
 					}
 <STRING2>.|{EAT_ABLE}|{CR}	{ yymore(); }
 
-<STRING1>\\n		{ count(); addchar(&s_buf, '\n'); }
-<STRING1>\\r		{ count(); addchar(&s_buf, '\r'); }
-<STRING1>\\a		{ count(); addchar(&s_buf, '\a'); }
-<STRING1>\\t		{ count(); addchar(&s_buf, '\t'); }
-<STRING1>\\{QUOTES}	{ count(); addchar(&s_buf, '"');  }
-<STRING1>\\\\		{ count(); addchar(&s_buf, '\\'); }
-<STRING1>\\x{HEX}{1,2}	{ count(); addchar(&s_buf,
+<STRING1>\\n		{ count_more(); addchar(&s_buf, '\n'); }
+<STRING1>\\r		{ count_more(); addchar(&s_buf, '\r'); }
+<STRING1>\\a		{ count_more(); addchar(&s_buf, '\a'); }
+<STRING1>\\t		{ count_more(); addchar(&s_buf, '\t'); }
+<STRING1>\\{QUOTES}	{ count_more(); addchar(&s_buf, '"');  }
+<STRING1>\\\\		{ count_more(); addchar(&s_buf, '\\'); }
+<STRING1>\\x{HEX}{1,2}	{ count_more(); addchar(&s_buf,
 											(char)strtol(yytext+2, 0, 16)); }
  /* don't allow \[0-7]{1}, it will eat the backreferences from
     subst_uri if allowed (although everybody should use '' in subt_uri) */
-<STRING1>\\[0-7]{2,3}	{ count(); addchar(&s_buf,
+<STRING1>\\[0-7]{2,3}	{ count_more(); addchar(&s_buf,
 											(char)strtol(yytext+1, 0, 8));  }
-<STRING1>\\{CR}		{ count(); } /* eat escaped CRs */
-<STRING1>.|{EAT_ABLE}|{CR}	{ addchar(&s_buf, *yytext); }
-
+<STRING1>\\{CR}		{ count_more(); } /* eat escaped CRs */
+<STRING1>.|{EAT_ABLE}|{CR}	{ count_more(); addchar(&s_buf, *yytext); }
+
+<STR_BETWEEN>{EAT_ABLE}|{CR}	{ count_ignore(); }
+<STR_BETWEEN>{QUOTES}			{ count_more(); state=STRING_S;
+								  BEGIN(STRING1);}
+<STR_BETWEEN>.					{	
+									yyless(0); /* reparse it */
+									/* ignore the whitespace now that is
+									  counted, return saved string value */
+									state=old_state; BEGIN(old_initial);
+									yylval.strval=s_buf.s;
+									memset(&s_buf, 0, sizeof(s_buf));
+									return STRING;
+								}
 
 <INITIAL,COMMENT>{COM_START}	{ count(); comment_nest++; state=COMMENT_S;
 										BEGIN(COMMENT); }
@@ -840,6 +950,12 @@ EAT_ABLE	[\ \t\b\r]
 								}
 <COMMENT>.|{EAT_ABLE}|{CR}				{ count(); };
 
+<INITIAL>{COM_LINE}!{SER_CFG}{CR}		{ count();
+											sr_cfg_compat=SR_COMPAT_SER;}
+<INITIAL>{COM_LINE}!{KAMAILIO_CFG}{CR}	{ count(); 
+											sr_cfg_compat=SR_COMPAT_KAMAILIO;}
+<INITIAL>{COM_LINE}!{MAXCOMPAT_CFG}{CR}	{ count(); 
+												sr_cfg_compat=SR_COMPAT_MAX;}
 <INITIAL>{COM_LINE}.*{CR}	{ count(); }
 
 <INITIAL>{ID}			{ count(); addstr(&s_buf, yytext, yyleng);
@@ -852,6 +968,12 @@ EAT_ABLE	[\ \t\b\r]
 
 <<EOF>>							{
 									switch(state){
+										case STR_BETWEEN_S:
+											state=old_state;
+											BEGIN(old_initial);
+											yylval.strval=s_buf.s;
+											memset(&s_buf, 0, sizeof(s_buf));
+											return STRING;
 										case STRING_S:
 											LOG(L_CRIT, "ERROR: cfg. parser: unexpected EOF in"
 														" unclosed string\n");
@@ -869,6 +991,23 @@ EAT_ABLE	[\ \t\b\r]
 											LOG(L_CRIT, "ERROR: unexpected EOF:"
 														"comment line open\n");
 											break;
+										case  ATTR_S:
+											LOG(L_CRIT, "ERROR: unexpected EOF"
+													" while parsing"
+													" avp name\n");
+											break;
+										case PVARID_S:
+											p_nest=0;
+										case PVAR_P_S: 
+											LOG(L_CRIT, "ERROR: unexpected EOF"
+													" while parsing pvar name"
+													" (%d paranthesis open)\n",
+													p_nest);
+											break;
+										case AVP_PVAR_S:
+											LOG(L_CRIT, "ERROR: unexpected EOF"
+													" while parsing"
+													" avp or pvar name\n");
 									}
 									return 0;
 								}
@@ -917,26 +1056,66 @@ error:
 
 
 
-static void count()
+/** helper function for count_*(). */
+static void count_lc(int* l, int* c)
 {
 	int i;
-
-	startcolumn=column;
 	for (i=0; i<yyleng;i++){
 		if (yytext[i]=='\n'){
-			line++;
-			column=startcolumn=1;
+			(*l)++;
+			(*c)=1;
 		}else if (yytext[i]=='\t'){
-			column++;
-			/*column+=8 -(column%8);*/
+			(*c)++;
+			/*(*c)+=8 -((*c)%8);*/
 		}else{
-			column++;
+			(*c)++;
 		}
 	}
 }
 
 
 
+/* helper function */
+static void count_restore_ignored()
+{
+	if (ign_lines) /* ignored line(s) => column has changed */
+		column=ign_columns;
+	else
+		column+=ign_columns;
+	line+=ign_lines;
+	ign_lines=ign_columns=0;
+}
+
+
+
+/** count/record position for stuff added to the current token. */
+static void count_more()
+{
+	count_restore_ignored();
+	count_lc(&line, &column);
+}
+
+
+
+/** count/record position for a new token. */
+static void count()
+{
+	count_restore_ignored();
+	startline=line;
+	startcolumn=column;
+	count_more();
+}
+
+
+
+/** record discarded stuff (not contained in the token) so that
+    the next token position can be adjusted properly*/
+static void count_ignore()
+{
+	count_lc(&ign_lines, &ign_columns);
+}
+
+
 /* replacement yywrap, removes libfl dependency */
 int yywrap()
 {

+ 518 - 292
cfg.y

@@ -89,12 +89,17 @@
  *              LINGER2, KEEPALIVE, KEEPIDLE, KEEPINTVL, KEEPCNT} (andrei)
  * 2008-01-24  added cfg_var definition (Miklos)
  * 2008-11-18  support for variable parameter module functions (andrei)
+ * 2007-12-03  support for generalised lvalues and rvalues:
+ *               lval=rval_expr, where lval=avp|pvar  (andrei)
+ * 2007-12-06  expression are now evaluated in terms of rvalues;
+ *             NUMBER is now always positive; cleanup (andrei)
 */
 
 %{
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdarg.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -118,6 +123,10 @@
 #include "tcp_init.h"
 #include "tcp_options.h"
 #include "sctp_options.h"
+#include "pvar.h"
+#include "lvalue.h"
+#include "rvalue.h"
+#include "sr_compat.h"
 
 #include "config.h"
 #include "cfg_core.h"
@@ -172,7 +181,7 @@
 
 
 extern int yylex();
-static void yyerror(char* s);
+static void yyerror(char* s, ...);
 static char* tmp;
 static int i_tmp;
 static unsigned u_tmp;
@@ -185,9 +194,17 @@ static struct ip_addr* ip_tmp;
 static struct avp_spec* s_attr;
 static select_t sel;
 static select_t* sel_ptr;
+static pv_spec_t* pv_spec;
 static struct action *mod_func_action;
+static struct lvalue* lval_tmp;
+static struct rvalue* rval_tmp;
 
 static void warn(char* s);
+static void get_cpos(struct cfg_pos* pos);
+static struct rval_expr* mk_rve_rval(enum rval_type, void* v);
+static struct rval_expr* mk_rve1(enum rval_expr_op op, struct rval_expr* rve1);
+static struct rval_expr* mk_rve2(enum rval_expr_op op, struct rval_expr* rve1,
+									struct rval_expr* rve2);
 static struct socket_id* mk_listen_id(char*, int, int);
 static struct name_lst* mk_name_lst(char* name, int flags);
 static struct socket_id* mk_listen_id2(struct name_lst*, int, int);
@@ -207,6 +224,10 @@ static void free_socket_id_lst(struct socket_id* i);
 	struct socket_id* sockid;
 	struct name_lst* name_l;
 	struct avp_spec* attr;
+	struct _pv_spec* pvar;
+	struct lvalue* lval;
+	struct rvalue* rval;
+	struct rval_expr* rv_expr;
 	select_t* select;
 }
 
@@ -426,32 +447,33 @@ static void free_socket_id_lst(struct socket_id* i);
 %token ATTR_GLOBAL
 %token ADDEQ
 
+
 %token STUN_REFRESH_INTERVAL
 %token STUN_ALLOW_STUN
 %token STUN_ALLOW_FP
 
 
-/* operators */
-%nonassoc EQUAL
-%nonassoc EQUAL_T
-%nonassoc GT
-%nonassoc LT
-%nonassoc GTE
-%nonassoc LTE
-%nonassoc DIFF
-%nonassoc MATCH
+/* operators, C like precedence */
+%right EQUAL
 %left LOG_OR
 %left LOG_AND
 %left BIN_OR
 %left BIN_AND
+%left EQUAL_T DIFF MATCH
+%left GT LT GTE LTE
 %left PLUS MINUS
+%left STAR SLASH
 %right NOT
+%left DOT
 
 /* values */
 %token <intval> NUMBER
 %token <strval> ID
 %token <strval> STRING
 %token <strval> IPV6ADDR
+%token <strval> PVAR
+/* not clear yet if this is an avp or pvar */
+%token <strval> AVP_OR_PVAR
 
 /* other */
 %token COMMA
@@ -462,16 +484,15 @@ static void free_socket_id_lst(struct socket_id* i);
 %token RBRACE
 %token LBRACK
 %token RBRACK
-%token SLASH
-%token DOT
 %token CR
 %token COLON
-%token STAR
 
 
 /*non-terminals */
-%type <expr> exp exp_elem /*, condition*/
-%type <action> action actions cmd fcmd if_cmd stm exp_stm assign_action
+%type <expr> exp exp_elem
+%type <intval> intno eint_op eint_op_onsend
+%type <intval> eip_op eip_op_onsend
+%type <action> action actions cmd fcmd if_cmd stm /*exp_stm*/ assign_action
 %type <ipaddr> ipv4 ipv6 ipv6addr ip
 %type <ipnet> ipnet
 %type <strval> host
@@ -482,22 +503,27 @@ static void free_socket_id_lst(struct socket_id* i);
 %type <sockid>  phostport
 %type <sockid>  listen_phostport
 %type <intval> proto port
-%type <intval> equalop strop intop binop
-%type <strval> host_sep
+%type <intval> equalop strop cmpop rve_cmpop rve_equalop
 %type <intval> uri_type
 %type <attr> attr_id
 %type <attr> attr_id_num_idx
 %type <attr> attr_id_no_idx
 %type <attr> attr_id_ass
-%type <attr> attr_id_val
+/*%type <attr> attr_id_val*/
 %type <attr> attr_id_any
 %type <attr> attr_id_any_str
+%type <pvar> pvar
+%type <lval> lval
+%type <rv_expr> rval rval_expr 
+%type <lval> avp_pvar
 /* %type <intval> class_id */
 %type <intval> assign_op
 %type <select> select_id
 %type <strval>	flag_name;
 %type <strval>	route_name;
 %type <intval> avpflag_oper
+%type <intval> rve_un_op
+/* %type <intval> rve_op */
 
 /*%type <route_el> rules;
   %type <route_el> rule;
@@ -609,6 +635,10 @@ id_lst:
 	| listen_phostport id_lst	{ $$=$1; $$->next=$2; }
 	;
 
+intno: NUMBER
+	|  MINUS %prec NOT NUMBER { $$=-$2; }
+	;
+
 flags_decl:		FLAGS_DECL	flag_list
 			|	FLAGS_DECL error { yyerror("flag list expected\n"); }
 ;
@@ -644,7 +674,7 @@ avpflag_spec:
 	}
 	;
 assign_stm:
-	DEBUG_V EQUAL NUMBER { default_core_cfg.debug=$3; }
+	DEBUG_V EQUAL intno { default_core_cfg.debug=$3; }
 	| DEBUG_V EQUAL error  { yyerror("number  expected"); }
 	| FORK  EQUAL NUMBER { dont_fork= ! $3; }
 	| FORK  EQUAL error  { yyerror("boolean value expected"); }
@@ -667,13 +697,13 @@ assign_stm:
 	| DNS_TRY_NAPTR error { yyerror("boolean value expected"); }
 	| DNS_SRV_LB EQUAL NUMBER   { IF_DNS_FAILOVER(default_core_cfg.dns_srv_lb=$3); }
 	| DNS_SRV_LB error { yyerror("boolean value expected"); }
-	| DNS_UDP_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_udp_pref=$3);}
+	| DNS_UDP_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_udp_pref=$3);}
 	| DNS_UDP_PREF error { yyerror("number expected"); }
-	| DNS_TCP_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_tcp_pref=$3);}
+	| DNS_TCP_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_tcp_pref=$3);}
 	| DNS_TCP_PREF error { yyerror("number expected"); }
-	| DNS_TLS_PREF EQUAL NUMBER { IF_NAPTR(default_core_cfg.dns_tls_pref=$3);}
+	| DNS_TLS_PREF EQUAL intno { IF_NAPTR(default_core_cfg.dns_tls_pref=$3);}
 	| DNS_TLS_PREF error { yyerror("number expected"); }
-	| DNS_SCTP_PREF EQUAL NUMBER { 
+	| DNS_SCTP_PREF EQUAL intno { 
 								IF_NAPTR(default_core_cfg.dns_sctp_pref=$3); }
 	| DNS_SCTP_PREF error { yyerror("number expected"); }
 	| DNS_RETR_TIME EQUAL NUMBER   { default_core_cfg.dns_retr_time=$3; }
@@ -733,9 +763,9 @@ assign_stm:
 	| PHONE2TEL EQUAL error { yyerror("boolean value expected"); }
 	| SYN_BRANCH EQUAL NUMBER { syn_branch=$3; }
 	| SYN_BRANCH EQUAL error { yyerror("boolean value expected"); }
-	| MEMLOG EQUAL NUMBER { memlog=$3; }
+	| MEMLOG EQUAL intno { memlog=$3; }
 	| MEMLOG EQUAL error { yyerror("int value expected"); }
-	| MEMDBG EQUAL NUMBER { memdbg=$3; }
+	| MEMDBG EQUAL intno { memdbg=$3; }
 	| MEMDBG EQUAL error { yyerror("int value expected"); }
 	| SIP_WARNING EQUAL NUMBER { sip_warning=$3; }
 	| SIP_WARNING EQUAL error { yyerror("boolean value expected"); }
@@ -777,7 +807,7 @@ assign_stm:
 		#endif
 	}
 	| TCP_CHILDREN EQUAL error { yyerror("number expected"); }
-	| TCP_CONNECT_TIMEOUT EQUAL NUMBER {
+	| TCP_CONNECT_TIMEOUT EQUAL intno {
 		#ifdef USE_TCP
 			tcp_connect_timeout=$3;
 		#else
@@ -785,7 +815,7 @@ assign_stm:
 		#endif
 	}
 	| TCP_CONNECT_TIMEOUT EQUAL error { yyerror("number expected"); }
-	| TCP_SEND_TIMEOUT EQUAL NUMBER {
+	| TCP_SEND_TIMEOUT EQUAL intno {
 		#ifdef USE_TCP
 			tcp_send_timeout=$3;
 		#else
@@ -793,7 +823,7 @@ assign_stm:
 		#endif
 	}
 	| TCP_SEND_TIMEOUT EQUAL error { yyerror("number expected"); }
-	| TCP_CON_LIFETIME EQUAL NUMBER {
+	| TCP_CON_LIFETIME EQUAL intno {
 		#ifdef USE_TCP
 			tcp_con_lifetime=$3;
 		#else
@@ -1462,281 +1492,183 @@ send_route_stm: ROUTE_SEND LBRACE actions RBRACE {
 	}
 	| ROUTE_SEND error { yyerror("invalid onsend_route statement"); }
 	;
-/*
-rules:
-	rules rule { push($2, &$1); $$=$1; }
-	| rule {$$=$1; }
-	| rules error { $$=0; yyerror("invalid rule"); }
-	;
-rule:
-	condition actions CR {
-		$$=0;
-		if (add_rule($1, $2, &$$)<0) {
-			yyerror("error calling add_rule");
-			YYABORT;
+
+exp:	rval_expr
+		{
+			if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
+				yyerror("invalid expression");
+				$$=0;
+			}else if (i_tmp!=RV_INT && i_tmp!=RV_NONE){
+				yyerror("invalid expression type, int expected\n");
+				$$=0;
+			}else
+				$$=mk_elem(NO_OP, RVEXP_O, $1, 0, 0);
 		}
-	}
-	| CR	{ $$=0;}
-	| condition error { $$=0; yyerror("bad actions in rule"); }
-	;
-condition:
-	exp {$$=$1;}
-*/
-exp:	exp LOG_AND exp		{ $$=mk_exp(LOGAND_OP, $1, $3); }
-	| exp LOG_OR exp	{ $$=mk_exp(LOGOR_OP, $1, $3);  }
-	| NOT exp 		{ $$=mk_exp(NOT_OP, $2, 0);  }
-	| LPAREN exp RPAREN	{ $$=$2; }
-	| exp_elem		{ $$=$1; }
 	;
+
+/* exp elem operators */
 equalop:
 	EQUAL_T {$$=EQUAL_OP; }
 	| DIFF	{$$=DIFF_OP; }
 	;
-intop:	equalop	{$$=$1; }
-	| GT	{$$=GT_OP; }
+cmpop:
+	  GT	{$$=GT_OP; }
 	| LT	{$$=LT_OP; }
 	| GTE	{$$=GTE_OP; }
 	| LTE	{$$=LTE_OP; }
 	;
-binop :
-	BIN_OR { $$= BINOR_OP; }
-	| BIN_AND { $$ = BINAND_OP; }
-	;
 strop:
 	equalop	{$$=$1; }
 	| MATCH	{$$=MATCH_OP; }
 	;
+
+
+/* rve expr. operators */
+rve_equalop:
+	EQUAL_T {$$=RVE_EQ_OP; }
+	| DIFF	{$$=RVE_DIFF_OP; }
+	;
+rve_cmpop:
+	  GT	{$$=RVE_GT_OP; }
+	| LT	{$$=RVE_LT_OP; }
+	| GTE	{$$=RVE_GTE_OP; }
+	| LTE	{$$=RVE_LTE_OP; }
+	;
+
+
+
+/* boolean expression uri operands */
 uri_type:
 	URI		{$$=URI_O;}
 	| FROM_URI	{$$=FROM_URI_O;}
 	| TO_URI	{$$=TO_URI_O;}
 	;
 
+
+/* boolean expression integer operands, available only in the
+  onsend route */
+eint_op_onsend:
+			SNDPORT		{ $$=SNDPORT_O; }
+		|	TOPORT		{ $$=TOPORT_O; }
+		|	SNDPROTO	{ $$=SNDPROTO_O; }
+		|	SNDAF		{ $$=SNDAF_O; }
+		;
+
+/* boolean expression integer operands */
+eint_op:	SRCPORT		{ $$=SRCPORT_O; }
+		|	DSTPORT		{ $$=DSTPORT_O; }
+		|	PROTO		{ $$=PROTO_O; }
+		|	AF			{ $$=AF_O; }
+		|	MSGLEN		{ $$=MSGLEN_O; }
+		|	RETCODE		{ $$=RETCODE_O; }
+		| eint_op_onsend
+	;
+
+/* boolean expression ip/ipnet operands */
+eip_op_onsend:
+			SNDIP		{ onsend_check("snd_ip"); $$=SNDIP_O; }
+		|	TOIP		{ onsend_check("to_ip");  $$=TOIP_O; }
+		;
+
+eip_op:		SRCIP		{ $$=SRCIP_O; }
+		|	DSTIP		{ $$=DSTIP_O; }
+		| eip_op_onsend
+		;
+
+
+
 exp_elem:
-	METHOD strop STRING	{$$= mk_elem($2, METHOD_O, 0, STRING_ST, $3);}
-	| METHOD strop attr_id_val  {$$ = mk_elem($2, METHOD_O, 0, AVP_ST, $3); }
-	| METHOD strop select_id {$$ = mk_elem($2, METHOD_O, 0, SELECT_ST, $3); }
-	| METHOD strop  ID	{$$ = mk_elem($2, METHOD_O, 0, STRING_ST,$3); }
+	METHOD strop %prec EQUAL_T rval_expr
+		{$$= mk_elem($2, METHOD_O, 0, RVE_ST, $3);}
+	| METHOD strop %prec EQUAL_T ID
+		{$$ = mk_elem($2, METHOD_O, 0, STRING_ST,$3); }
 	| METHOD strop error { $$=0; yyerror("string expected"); }
-	| METHOD error	{ $$=0; yyerror("invalid operator,== , !=, or =~ expected"); }
-	| uri_type strop STRING	{$$ = mk_elem($2, $1, 0, STRING_ST, $3); }
-	| uri_type strop host 	{$$ = mk_elem($2, $1, 0, STRING_ST, $3); }
-	| uri_type strop attr_id_val {$$ = mk_elem($2, $1, 0, AVP_ST, $3); }
-	| uri_type strop select_id {$$ = mk_elem($2, $1, 0, SELECT_ST, $3); }
-	| uri_type equalop MYSELF {$$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
-	| uri_type strop error { $$=0; yyerror("string or MYSELF expected"); }
-	| uri_type error	{ $$=0; yyerror("invalid operator, == , != or =~ expected"); }
-
-	| SRCPORT intop NUMBER { $$=mk_elem($2, SRCPORT_O, 0, NUMBER_ST, (void*)$3 ); }
-	| SRCPORT intop attr_id_val { $$=mk_elem($2, SRCPORT_O, 0, AVP_ST, (void*)$3 ); }
-	| SRCPORT intop error { $$=0; yyerror("number expected"); }
-	| SRCPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-	| DSTPORT intop NUMBER	{ $$=mk_elem($2, DSTPORT_O, 0, NUMBER_ST, (void*)$3 ); }
-	| DSTPORT intop attr_id_val	{ $$=mk_elem($2, DSTPORT_O, 0, AVP_ST, (void*)$3 ); }
-	| DSTPORT intop error { $$=0; yyerror("number expected"); }
-	| DSTPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-	| SNDPORT intop NUMBER {
-		onsend_check("snd_port");
-		$$=mk_elem($2, SNDPORT_O, 0, NUMBER_ST, (void*)$3 );
-	}
-	| SNDPORT intop attr_id_val {
-		onsend_check("snd_port");
-		$$=mk_elem($2, SNDPORT_O, 0, AVP_ST, (void*)$3 );
-	}
-	| SNDPORT intop error { $$=0; yyerror("number expected"); }
-	| SNDPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-	| TOPORT intop NUMBER {
-		onsend_check("to_port");
-		$$=mk_elem($2, TOPORT_O, 0, NUMBER_ST, (void*)$3 );
-	}
-	| TOPORT intop attr_id_val {
-		onsend_check("to_port");
-		$$=mk_elem($2, TOPORT_O, 0, AVP_ST, (void*)$3 );
-	}
-	| TOPORT intop error { $$=0; yyerror("number expected"); }
-	| TOPORT error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
-
-	| PROTO intop proto	{ $$=mk_elem($2, PROTO_O, 0, NUMBER_ST, (void*)$3 ); }
-	| PROTO intop attr_id_val	{ $$=mk_elem($2, PROTO_O, 0, AVP_ST, (void*)$3 ); }
-	| PROTO intop error { $$=0; yyerror("protocol expected (udp, tcp or tls)"); }
-
-	| PROTO error { $$=0; yyerror("equal/!= operator expected"); }
-
-	| SNDPROTO intop proto	{
-		onsend_check("snd_proto");
-		$$=mk_elem($2, SNDPROTO_O, 0, NUMBER_ST, (void*)$3 );
-	}
-	| SNDPROTO intop attr_id_val {
-		onsend_check("snd_proto");
-		$$=mk_elem($2, SNDPROTO_O, 0, AVP_ST, (void*)$3 );
-	}
-	| SNDPROTO intop error { $$=0; yyerror("protocol expected (udp, tcp or tls)"); }
-	| SNDPROTO error { $$=0; yyerror("equal/!= operator expected"); }
-
-	| AF intop NUMBER	{ $$=mk_elem($2, AF_O, 0, NUMBER_ST,(void *) $3 ); }
-	| AF intop attr_id_val	{ $$=mk_elem($2, AF_O, 0, AVP_ST,(void *) $3 ); }
-	| AF intop error { $$=0; yyerror("number expected"); }
-	| AF error { $$=0; yyerror("equal/!= operator expected"); }
-
-	| SNDAF intop NUMBER {
-		onsend_check("snd_af");
-		$$=mk_elem($2, SNDAF_O, 0, NUMBER_ST, (void *) $3 ); }
-	| SNDAF intop attr_id_val {
-		onsend_check("snd_af");
-		$$=mk_elem($2, SNDAF_O, 0, AVP_ST, (void *) $3 );
-	}
-	| SNDAF intop error { $$=0; yyerror("number expected"); }
-	| SNDAF error { $$=0; yyerror("equal/!= operator expected"); }
-
-	| MSGLEN intop NUMBER		{ $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) $3 ); }
-	| MSGLEN intop attr_id_val	{ $$=mk_elem($2, MSGLEN_O, 0, AVP_ST, (void *) $3 ); }
-	| MSGLEN intop MAX_LEN		{ $$=mk_elem($2, MSGLEN_O, 0, NUMBER_ST, (void *) BUF_SIZE); }
-	| MSGLEN intop error { $$=0; yyerror("number expected"); }
-	| MSGLEN error { $$=0; yyerror("equal/!= operator expected"); }
-
-	| RETCODE intop NUMBER	{ $$=mk_elem($2, RETCODE_O, 0, NUMBER_ST, (void *) $3 ); }
-	| RETCODE intop attr_id_val	{ $$=mk_elem($2, RETCODE_O, 0, AVP_ST, (void *) $3 ); }
-	| RETCODE intop error { $$=0; yyerror("number expected"); }
-	| RETCODE error { $$=0; yyerror("equal/!= operator expected"); }
-
-	| SRCIP equalop ipnet	{ $$=mk_elem($2, SRCIP_O, 0, NET_ST, $3); }
-	| SRCIP strop STRING {
-		s_tmp.s=$3;
-		s_tmp.len=strlen($3);
-		ip_tmp=str2ip(&s_tmp);
-	#ifdef USE_IPV6
-		if (ip_tmp==0)
-			ip_tmp=str2ip6(&s_tmp);
-	#endif
-		if (ip_tmp) {
-			$$=mk_elem($2, SRCIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-		} else {
-			$$=mk_elem($2, SRCIP_O, 0, STRING_ST, $3);
-		}
-	}
-	| SRCIP strop host	{ $$=mk_elem($2, SRCIP_O, 0, STRING_ST, $3); }
-	| SRCIP equalop MYSELF  { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0);
-							}
-	| SRCIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-	| SRCIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected");}
-	| DSTIP equalop ipnet	{ $$=mk_elem(	$2, DSTIP_O, 0, NET_ST, (void*)$3); }
-	| DSTIP strop STRING	{
-		s_tmp.s=$3;
-		s_tmp.len=strlen($3);
-		ip_tmp=str2ip(&s_tmp);
-	#ifdef USE_IPV6
-		if (ip_tmp==0)
-			ip_tmp=str2ip6(&s_tmp);
-	#endif /* USE_IPV6 */
-		if (ip_tmp) {
-			$$=mk_elem($2, DSTIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-		} else {
-			$$=mk_elem($2, DSTIP_O, 0, STRING_ST, $3);
-		}
-	}
-	| DSTIP strop host	{ $$=mk_elem(	$2, DSTIP_O, 0, STRING_ST, $3); }
-	| DSTIP equalop MYSELF  { $$=mk_elem(	$2, DSTIP_O, 0, MYSELF_ST, 0); }
-	| DSTIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-	| DSTIP error { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
-	| SNDIP equalop ipnet {
-		onsend_check("snd_ip");
-		$$=mk_elem($2, SNDIP_O, 0, NET_ST, $3);
-	}
-	| SNDIP strop STRING	{
-		onsend_check("snd_ip");
-		s_tmp.s=$3;
-		s_tmp.len=strlen($3);
-		ip_tmp=str2ip(&s_tmp);
-	#ifdef USE_IPV6
-		if (ip_tmp==0)
-			ip_tmp=str2ip6(&s_tmp);
-	#endif /* USE_IPV6 */
-		if (ip_tmp) {
-			$$=mk_elem($2, SNDIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-		} else {
-			$$=mk_elem($2, SNDIP_O, 0, STRING_ST, $3);
-		}
-	}
-	| SNDIP strop host	{
-		onsend_check("snd_ip");
-		$$=mk_elem($2, SNDIP_O, 0, STRING_ST, $3);
-	}
-	| SNDIP equalop attr_id_val	{
-		onsend_check("snd_ip");
-	    $$=mk_elem($2, SNDIP_O, 0, AVP_ST, (void*)$3 ); 
-	}
-	| SNDIP equalop MYSELF  {
-		onsend_check("snd_ip");
-		$$=mk_elem($2, SNDIP_O, 0, MYSELF_ST, 0);
-	}
-	| SNDIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-	| SNDIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
-	| TOIP equalop ipnet	{
-		onsend_check("to_ip");
-		$$=mk_elem($2, TOIP_O, 0, NET_ST, $3);
-	}
-	| TOIP strop STRING	{
-		onsend_check("to_ip");
-		s_tmp.s=$3;
-		s_tmp.len=strlen($3);
-		ip_tmp=str2ip(&s_tmp);
-	#ifdef USE_IPV6
-		if (ip_tmp==0)
-			ip_tmp=str2ip6(&s_tmp);
-	#endif /* USE_IPV6 */
-		if (ip_tmp) {
-			$$=mk_elem($2, TOIP_O, 0, NET_ST, mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
-		} else {
-			$$=mk_elem($2, TOIP_O, 0, STRING_ST, $3);
+	| METHOD error	
+		{ $$=0; yyerror("invalid operator,== , !=, or =~ expected"); }
+	| uri_type strop %prec EQUAL_T rval_expr
+		{$$ = mk_elem($2, $1, 0, RVE_ST, $3); }
+	| uri_type strop %prec EQUAL_T MYSELF
+		{$$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
+	| uri_type strop %prec EQUAL_T error
+		{ $$=0; yyerror("string or MYSELF expected"); }
+	| uri_type error
+		{ $$=0; yyerror("invalid operator, == , != or =~ expected"); }
+	| eint_op cmpop %prec GT rval_expr { $$=mk_elem($2, $1, 0, RVE_ST, $3 ); }
+	| eint_op equalop %prec EQUAL_T rval_expr 
+		{ $$=mk_elem($2, $1, 0, RVE_ST, $3 ); }
+	| eint_op cmpop error   { $$=0; yyerror("number expected"); }
+	| eint_op equalop error { $$=0; yyerror("number expected"); }
+	| eint_op error { $$=0; yyerror("==, !=, <,>, >= or <=  expected"); }
+	| eip_op strop %prec EQUAL_T ipnet { $$=mk_elem($2, $1, 0, NET_ST, $3); }
+	| eip_op strop %prec EQUAL_T rval_expr {
+			s_tmp.s=0;
+			$$=0;
+			if (rve_is_constant($3)){
+				i_tmp=rve_guess_type($3);
+				if (i_tmp==RV_INT)
+					yyerror("string expected");
+				else if (i_tmp==RV_STR){
+					if (((rval_tmp=rval_expr_eval(0, 0, $3))==0) ||
+								(rval_get_str(0, 0, &s_tmp, rval_tmp, 0)<0)){
+						rval_destroy(rval_tmp);
+						yyerror("bad rvalue expression");
+					}else{
+						rval_destroy(rval_tmp);
+					}
+				}else{
+					yyerror("BUG: unexpected dynamic type");
+				}
+			}else{
+					warn("non constant rvalue in ip comparison");
+			}
+			if (s_tmp.s){
+				ip_tmp=str2ip(&s_tmp);
+			#ifdef USE_IPV6
+				if (ip_tmp==0)
+					ip_tmp=str2ip6(&s_tmp);
+			#endif
+				pkg_free(s_tmp.s);
+				if (ip_tmp) {
+					$$=mk_elem($2, $1, 0, NET_ST, 
+								mk_net_bitlen(ip_tmp, ip_tmp->len*8) );
+				} else {
+					$$=mk_elem($2, $1, 0, RVE_ST, $3);
+				}
+			}else{
+				$$=mk_elem($2, $1, 0, RVE_ST, $3);
+			}
 		}
-	}
-	| TOIP strop host	{
-		onsend_check("to_ip");
-		$$=mk_elem($2, TOIP_O, 0, STRING_ST, $3);
-	}
-	| TOIP equalop attr_id_val	{
-		onsend_check("to_ip");
-	    $$=mk_elem($2, TOIP_O, 0, AVP_ST, (void*)$3 ); 
-	}
-	| TOIP equalop MYSELF  {
-		onsend_check("to_ip");
-		$$=mk_elem($2, TOIP_O, 0, MYSELF_ST, 0);
-	}
-	| TOIP strop error { $$=0; yyerror( "ip address or hostname expected" ); }
-	| TOIP error  { $$=0; yyerror("invalid operator, ==, != or =~ expected"); }
-
-	| MYSELF equalop uri_type	{ $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
-	| MYSELF equalop SRCIP  { $$=mk_elem($2, SRCIP_O, 0, MYSELF_ST, 0); }
-	| MYSELF equalop DSTIP  { $$=mk_elem($2, DSTIP_O, 0, MYSELF_ST, 0); }
-	| MYSELF equalop SNDIP  {
-		onsend_check("snd_ip");
-		$$=mk_elem($2, SNDIP_O, 0, MYSELF_ST, 0);
-	}
-	| MYSELF equalop TOIP  {
-		onsend_check("to_ip");
-		$$=mk_elem($2, TOIP_O, 0, MYSELF_ST, 0);
-	}
-	| MYSELF equalop error { $$=0; yyerror(" URI, SRCIP or DSTIP expected"); }
+	| eip_op strop %prec EQUAL_T host
+		{ $$=mk_elem($2, $1, 0, STRING_ST, $3); }
+	| eip_op strop %prec EQUAL_T MYSELF
+		{ $$=mk_elem($2, $1, 0, MYSELF_ST, 0); }
+	| eip_op strop %prec EQUAL_T error
+		{ $$=0; yyerror( "ip address or hostname expected" ); }
+	| eip_op error
+		{ $$=0; yyerror("invalid operator, ==, != or =~ expected");}
+	
+	| MYSELF equalop %prec EQUAL_T uri_type
+		{ $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
+	| MYSELF equalop %prec EQUAL_T eip_op
+		{ $$=mk_elem($2, $3, 0, MYSELF_ST, 0); }
+	| MYSELF equalop %prec EQUAL_T error
+		{ $$=0; yyerror(" URI, SRCIP or DSTIP expected"); }
 	| MYSELF error	{ $$=0; yyerror ("invalid operator, == or != expected"); }
-	| exp_stm	{ $$=mk_elem( NO_OP, ACTION_O, 0, ACTIONS_ST, $1);  }
-	| NUMBER	{ $$=mk_elem( NO_OP, NUMBER_O, 0, NUMBER_ST, (void*)$1 ); }
-
-	| attr_id_any				{$$=mk_elem( NO_OP, AVP_O, (void*)$1, 0, 0); }
-	| attr_id_val strop STRING	{$$=mk_elem( $2, AVP_O, (void*)$1, STRING_ST, $3); }
-	| attr_id_val strop select_id	{$$=mk_elem( $2, AVP_O, (void*)$1, SELECT_ST, $3); }
-	| attr_id_val intop NUMBER	{$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
-	| attr_id_val binop NUMBER	{$$=mk_elem( $2, AVP_O, (void*)$1, NUMBER_ST, (void*)$3); }
-	| attr_id_val strop attr_id_val {$$=mk_elem( $2, AVP_O, (void*)$1, AVP_ST, (void*)$3); }
-	| attr_id_val intop attr_id_val {$$=mk_elem( $2, AVP_O, (void*)$1, AVP_ST, (void*)$3); }
-
-	| select_id                 { $$=mk_elem( NO_OP, SELECT_O, $1, 0, 0); }
-	| select_id strop STRING    { $$=mk_elem( $2, SELECT_O, $1, STRING_ST, $3); }
-	| select_id strop attr_id_val   { $$=mk_elem( $2, SELECT_O, $1, AVP_ST, (void*)$3); }
-	| select_id strop select_id { $$=mk_elem( $2, SELECT_O, $1, SELECT_ST, $3); }
+	;
+/*
+exp_elem2:
+	rval_expr cmpop %prec GT rval_expr
+		{ $$=mk_elem( $2, RVE_ST, $1, RVE_ST, $3);}
+	|
+	rval_expr equalop %prec EQUAL_T rval_expr
+		{ $$=mk_elem( $2, RVE_ST, $1, RVE_ST, $3);}
+	| rval_expr LOG_AND rval_expr
+		{ $$=mk_exp_rve(LOGAND_OP, $1, $3);}
+	| rval_expr LOG_OR rval_expr
+		{ $$=mk_exp_rve(LOGOR_OP, $1, $3);}
 ;
+*/
+
 ipnet:
 	ip SLASH ip	{ $$=mk_net($1, $3); }
 	| ip SLASH NUMBER {
@@ -1753,20 +1685,29 @@ ipnet:
 	| ip	{ $$=mk_net_bitlen($1, $1->len*8); }
 	| ip SLASH error { $$=0; yyerror("netmask (eg:255.0.0.0 or 8) expected"); }
 	;
-host_sep:
-	DOT {$$=".";}
-	| MINUS {$$="-"; }
-	;
 
 host:
 	ID { $$=$1; }
-	| host host_sep ID {
+	| host DOT ID {
 		$$=(char*)pkg_malloc(strlen($1)+1+strlen($3)+1);
 		if ($$==0) {
 			LOG(L_CRIT, "ERROR: cfg. parser: memory allocation failure while parsing host\n");
 		} else {
 			memcpy($$, $1, strlen($1));
-			$$[strlen($1)]=*$2;
+			$$[strlen($1)]='.';
+			memcpy($$+strlen($1)+1, $3, strlen($3));
+			$$[strlen($1)+1+strlen($3)]=0;
+		}
+		pkg_free($1);
+		pkg_free($3);
+	}
+	| host MINUS ID {
+		$$=(char*)pkg_malloc(strlen($1)+1+strlen($3)+1);
+		if ($$==0) {
+			LOG(L_CRIT, "ERROR: cfg. parser: memory allocation failure while parsing host\n");
+		} else {
+			memcpy($$, $1, strlen($1));
+			$$[strlen($1)]='-';
 			memcpy($$+strlen($1)+1, $3, strlen($3));
 			$$[strlen($1)+1+strlen($3)]=0;
 		}
@@ -1801,12 +1742,14 @@ fcmd:
 		}
 	}
 	;
+/*
 exp_stm:
 	fcmd	{ $$=$1; }
 	| if_cmd	{ $$=$1; }
 	| assign_action { $$ = $1; }
 	| LBRACE actions RBRACE	{ $$=$2; }
 	;
+*/
 stm:
 	action	{ $$=$1; }
 	| LBRACE actions RBRACE	{ $$=$2; }
@@ -1912,7 +1855,7 @@ attr_id:
 	attr_mark attr_spec { $$ = s_attr; }
 	;
 attr_id_num_idx:
-	attr_mark attr_spec LBRACK NUMBER RBRACK {
+	attr_mark attr_spec LBRACK intno RBRACK {
 		s_attr->type|= (AVP_NAME_STR | ($4<0?AVP_INDEX_BACKWARD:AVP_INDEX_FORWARD));
 		s_attr->index = ($4<0?-$4:$4);
 		$$ = s_attr;
@@ -1928,10 +1871,12 @@ attr_id_ass:
 	attr_id
 	| attr_id_no_idx
 	;
+/*
 attr_id_val:
 	attr_id
 	| attr_id_num_idx
 	;
+*/
 attr_id_any:
 	attr_id
 	| attr_id_no_idx
@@ -1954,7 +1899,7 @@ attr_id_any_str:
 		s.len = strlen(s.s);
 		if (parse_avp_name(&s, &type, &avp_spec->name, &idx)) {
 			yyerror("error when parsing AVP");
-		        pkg_free(avp_spec);
+			pkg_free(avp_spec);
 			YYABORT;
 		}
 		avp_spec->type = type;
@@ -1962,6 +1907,49 @@ attr_id_any_str:
 		$$ = avp_spec;
 	}
 	;
+
+pvar:	PVAR {
+			pv_spec=pkg_malloc(sizeof(*pv_spec));
+			if (!pv_spec) {
+				yyerror("Not enough memory");
+				YYABORT;
+			}
+			memset(pv_spec, 0, sizeof(*pv_spec));
+			s_tmp.s=$1; s_tmp.len=strlen($1);
+			if (pv_parse_spec(&s_tmp, pv_spec)==0){
+				yyerror("unknown script pseudo variable");
+				pkg_free(pv_spec);
+				pv_spec=0;
+				YYABORT;
+			}
+			$$=pv_spec;
+		}
+	;
+
+avp_pvar:	AVP_OR_PVAR {
+				lval_tmp=pkg_malloc(sizeof(*lval_tmp));
+				if (!lval_tmp) {
+					yyerror("Not enough memory");
+					YYABORT;
+				}
+				memset(lval_tmp, 0, sizeof(*lval_tmp));
+				s_tmp.s=$1; s_tmp.len=strlen(s_tmp.s);
+				if (pv_parse_spec2(&s_tmp, &lval_tmp->lv.pvs, 1)==0){
+					/* not a pvar, try avps */
+					lval_tmp->lv.avps.type|= AVP_NAME_STR;
+					lval_tmp->lv.avps.name.s.s = s_tmp.s+1;
+					lval_tmp->lv.avps.name.s.len = s_tmp.len-1;
+					lval_tmp->type=LV_AVP;
+				}else{
+					lval_tmp->type=LV_PVAR;
+				}
+				$$ = lval_tmp;
+				DBG("parsed ambigous avp/pvar \"%.*s\" to %d\n",
+							s_tmp.len, s_tmp.s, lval_tmp->type);
+			}
+	;
+
+
 /*
 assign_op:
 	ADDEQ { $$ = ADD_T; }
@@ -1971,6 +1959,127 @@ assign_op:
 assign_op:
 	EQUAL { $$ = ASSIGN_T; }
 	;
+
+
+lval: attr_id_ass {
+					lval_tmp=pkg_malloc(sizeof(*lval_tmp));
+					if (!lval_tmp) {
+						yyerror("Not enough memory");
+						YYABORT;
+					}
+					lval_tmp->type=LV_AVP; lval_tmp->lv.avps=*$1;
+					pkg_free($1); /* free the avp spec we just copied */
+					$$=lval_tmp;
+				}
+	| pvar        {
+					if (!pv_is_w($1))
+						yyerror("read only pvar in assignment left side");
+					if ($1->trans!=0)
+						yyerror("pvar with transformations in assignment"
+								" left side");
+					lval_tmp=pkg_malloc(sizeof(*lval_tmp));
+					if (!lval_tmp) {
+						yyerror("Not enough memory");
+						YYABORT;
+					}
+					lval_tmp->type=LV_PVAR; lval_tmp->lv.pvs=*($1);
+					pkg_free($1); /* free the pvar spec we just copied */
+					$$=lval_tmp;
+				}
+	| avp_pvar    {
+					if (($1)->type==LV_PVAR){
+						if (!pv_is_w(&($1)->lv.pvs))
+							yyerror("read only pvar in assignment left side");
+						if ($1->lv.pvs.trans!=0)
+							yyerror("pvar with transformations in assignment"
+									" left side");
+					}
+					$$=$1;
+				}
+	;
+
+rval: intno			{$$=mk_rve_rval(RV_INT, (void*)$1); }
+	| STRING			{	s_tmp.s=$1; s_tmp.len=strlen($1);
+							$$=mk_rve_rval(RV_STR, &s_tmp); }
+	| attr_id_any		{$$=mk_rve_rval(RV_AVP, $1); pkg_free($1); }
+	| pvar				{$$=mk_rve_rval(RV_PVAR, $1); pkg_free($1); }
+	| avp_pvar			{
+							switch($1->type){
+								case LV_AVP:
+									$$=mk_rve_rval(RV_AVP, &$1->lv.avps);
+									break;
+								case LV_PVAR:
+									$$=mk_rve_rval(RV_PVAR, &$1->lv.pvs);
+									break;
+								default:
+									yyerror("BUG: invalid lvalue type ");
+									YYABORT;
+							}
+							pkg_free($1); /* not needed anymore */
+						}
+	| select_id			{$$=mk_rve_rval(RV_SEL, $1); pkg_free($1); }
+	| fcmd				{$$=mk_rve_rval(RV_ACTION_ST, $1); }
+	| exp_elem { $$=mk_rve_rval(RV_BEXPR, $1); }
+	| LBRACE actions RBRACE	{$$=mk_rve_rval(RV_ACTION_ST, $2); }
+	| LBRACE error RBRACE	{ yyerror("bad command block"); }
+	| LPAREN assign_action RPAREN	{$$=mk_rve_rval(RV_ACTION_ST, $2); }
+	| LPAREN error RPAREN	{ yyerror("bad expression"); }
+	;
+
+
+rve_un_op: NOT	{ $$=RVE_LNOT_OP; }
+		|  MINUS %prec NOT	{ $$=RVE_UMINUS_OP; } 
+		/* TODO: RVE_BOOL_OP, RVE_NOT_OP? */
+	;
+
+/*
+rve_op:		PLUS		{ $$=RVE_PLUS_OP; }
+		|	MINUS		{ $$=RVE_MINUS_OP; }
+		|	STAR		{ $$=RVE_MUL_OP; }
+		|	SLASH		{ $$=RVE_DIV_OP; }
+	;
+*/
+
+rval_expr: rval						{ $$=$1;
+											if ($$==0){
+												yyerror("out of memory\n");
+												YYABORT;
+											}
+									}
+		| rve_un_op %prec NOT rval_expr	{$$=mk_rve1($1, $2); }
+		| rval_expr PLUS rval_expr		{$$=mk_rve2(RVE_PLUS_OP, $1, $3); }
+		| rval_expr MINUS rval_expr		{$$=mk_rve2(RVE_MINUS_OP, $1, $3); }
+		| rval_expr STAR rval_expr		{$$=mk_rve2(RVE_MUL_OP, $1, $3); }
+		| rval_expr SLASH rval_expr		{$$=mk_rve2(RVE_DIV_OP, $1, $3); }
+		| rval_expr BIN_OR rval_expr	{$$=mk_rve2(RVE_BOR_OP, $1,  $3); }
+		| rval_expr BIN_AND rval_expr	{$$=mk_rve2(RVE_BAND_OP, $1,  $3);}
+		| rval_expr rve_cmpop %prec GT rval_expr { $$=mk_rve2( $2, $1, $3);}
+		| rval_expr rve_equalop %prec EQUAL_T rval_expr
+			{ $$=mk_rve2( $2, $1, $3);}
+		| rval_expr LOG_AND rval_expr	{ $$=mk_rve2(RVE_LAND_OP, $1, $3);}
+		| rval_expr LOG_OR rval_expr	{ $$=mk_rve2(RVE_LOR_OP, $1, $3);}
+		| LPAREN rval_expr RPAREN		{ $$=$2;}
+		| rve_un_op %prec NOT error		{ yyerror("bad expression"); }
+		| rval_expr PLUS error			{ yyerror("bad expression"); }
+		| rval_expr MINUS error			{ yyerror("bad expression"); }
+		| rval_expr STAR error			{ yyerror("bad expression"); }
+		| rval_expr SLASH error			{ yyerror("bad expression"); }
+		| rval_expr BIN_OR error		{ yyerror("bad expression"); }
+		| rval_expr BIN_AND error		{ yyerror("bad expression"); }
+		| rval_expr rve_cmpop %prec GT error
+			{ yyerror("bad expression"); }
+		| rval_expr rve_equalop %prec EQUAL_T error
+			{ yyerror("bad expression"); }
+		| rval_expr LOG_AND error		{ yyerror("bad expression"); }
+		| rval_expr LOG_OR error		{ yyerror("bad expression"); }
+		;
+
+assign_action: lval assign_op  rval_expr	{ $$=mk_action($2, 2, LVAL_ST, $1, 
+														 	  RVE_ST, $3);
+										}
+	;
+
+/*
 assign_action:
 	attr_id_ass assign_op STRING  { $$=mk_action($2, 2, AVP_ST, $1, STRING_ST, $3); }
 	| attr_id_ass assign_op NUMBER  { $$=mk_action($2, 2, AVP_ST, $1, NUMBER_ST, (void*)$3); }
@@ -1979,6 +2088,8 @@ assign_action:
 	| attr_id_ass assign_op select_id { $$=mk_action($2, 2, AVP_ST, (void*)$1, SELECT_ST, (void*)$3); }
 	| attr_id_ass assign_op LPAREN exp RPAREN { $$ = mk_action($2, 2, AVP_ST, $1, EXPR_ST, $4); }
 	;
+*/
+
 avpflag_oper:
 	SETAVPFLAG { $$ = 1; }
 	| RESETAVPFLAG { $$ = 0; }
@@ -2404,12 +2515,13 @@ cmd:
 		}
 		$$ = mod_func_action;
 	}
+	| ID error					{ yyerror("'('')' expected (function call)");}
 	;
 func_params:
 	/* empty */
 	| func_params COMMA func_param { }
 	| func_param {}
-	| func_params error { yyerror("call params error\n"); YYABORT; }
+	| func_params error { yyerror("call params error\n"); }
 	;
 func_param:
         NUMBER {
@@ -2438,21 +2550,135 @@ func_param:
 extern int line;
 extern int column;
 extern int startcolumn;
+extern int startline;
+
+
+static void get_cpos(struct cfg_pos* pos)
+{
+	pos->s_line=startline;
+	pos->e_line=line;
+	pos->s_col=startcolumn;
+	pos->e_col=column-1;
+}
+
+
 static void warn(char* s)
 {
-	LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n", line, startcolumn,
-			column, s);
+	if (line!=startline)
+		LOG(L_WARN, "cfg. warning: (%d,%d-%d,%d): %s\n",
+					startline, startcolumn, line, column-1, s);
+	else if (startcolumn!=(column-1))
+		LOG(L_WARN, "cfg. warning: (%d,%d-%d): %s\n", startline, startcolumn,
+					column-1, s);
+	else
+		LOG(L_WARN, "cfg. warning: (%d,%d): %s\n", startline, startcolumn, s);
 	cfg_warnings++;
 }
 
-static void yyerror(char* s)
+static void yyerror_at(struct cfg_pos* p, char* format, ...)
 {
-	LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d): %s\n", line, startcolumn,
-			column, s);
+	va_list ap;
+	char s[256];
+	
+	va_start(ap, format);
+	vsnprintf(s, sizeof(s), format, ap);
+	va_end(ap);
+	if (p->e_line!=p->s_line)
+		LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d,%d): %s\n", 
+					p->s_line, p->s_col, p->e_line, p->e_col, s);
+	else if (p->s_col!=p->e_col)
+		LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d-%d): %s\n", 
+					p->s_line, p->s_col, p->e_col, s);
+	else
+		LOG(L_CRIT, "*** PARSE ERROR *** (%d,%d): %s\n", 
+					p->s_line, p->s_col, s);
 	cfg_errors++;
 }
 
 
+static void yyerror(char* format, ...)
+{
+	va_list ap;
+	char s[256];
+	struct cfg_pos pos;
+	
+	get_cpos(&pos);
+	va_start(ap, format);
+	vsnprintf(s, sizeof(s), format, ap);
+	va_end(ap);
+	yyerror_at(&pos, s);
+}
+
+
+
+/** mk_rval_expr_v wrapper.
+ *  checks mk_rval_expr_v return value and sets the cfg. pos
+ *  (line and column numbers)
+ *  @return rval_expr* on success, 0 on error (@see mk_rval_expr_v)
+ */
+static struct rval_expr* mk_rve_rval(enum rval_type type, void* v)
+{
+	struct rval_expr* ret;
+	struct cfg_pos pos;
+
+	get_cpos(&pos);
+	ret=mk_rval_expr_v(type, v, &pos);
+	if (ret==0){
+		yyerror("internal error: failed to create rval expr");
+		/* YYABORT; */
+	}
+	return ret;
+}
+
+
+/** mk_rval_expr1 wrapper.
+ *  checks mk_rval_expr1 return value (!=0 and type checking)
+ *  @return rval_expr* on success, 0 on error (@see mk_rval_expr1)
+ */
+static struct rval_expr* mk_rve1(enum rval_expr_op op, struct rval_expr* rve1)
+{
+	struct rval_expr* ret;
+	struct rval_expr* bad_rve;
+	enum rval_type type, bad_t, exp_t;
+	
+	if (rve1==0)
+		return 0;
+	ret=mk_rval_expr1(op, rve1, &rve1->fpos);
+	if (ret && (rve_check_type(&type, ret, &bad_rve, &bad_t, &exp_t)!=1)){
+		yyerror_at(&rve1->fpos, "bad expression: type mismatch"
+					" (%s instead of %s)", rval_type_name(bad_t),
+					rval_type_name(exp_t));
+	}
+	return ret;
+}
+
+
+/** mk_rval_expr2 wrapper.
+ *  checks mk_rval_expr2 return value (!=0 and type checking)
+ *  @return rval_expr* on success, 0 on error (@see mk_rval_expr2)
+ */
+static struct rval_expr* mk_rve2(enum rval_expr_op op, struct rval_expr* rve1,
+									struct rval_expr* rve2)
+{
+	struct rval_expr* ret;
+	struct rval_expr* bad_rve;
+	enum rval_type type, bad_t, exp_t;
+	struct cfg_pos pos;
+	
+	if ((rve1==0) || (rve2==0))
+		return 0;
+	cfg_pos_join(&pos, &rve1->fpos, &rve2->fpos);
+	ret=mk_rval_expr2(op, rve1, rve2, &pos);
+	if (ret && (rve_check_type(&type, ret, &bad_rve, &bad_t, &exp_t)!=1)){
+		yyerror_at(&pos, "bad expression: type mismatch:"
+						" %s instead of %s at (%d,%d)",
+						rval_type_name(bad_t), rval_type_name(exp_t),
+						bad_rve->fpos.s_line, bad_rve->fpos.s_col);
+	}
+	return ret;
+}
+
+
 static struct name_lst* mk_name_lst(char* host, int flags)
 {
 	struct name_lst* l;

+ 367 - 0
lvalue.c

@@ -0,0 +1,367 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file 
+ * @brief lvalues (assignment)
+ */
+/* 
+ * History:
+ * --------
+ *  2008-11-30  initial version (andrei)
+ */
+
+#include "lvalue.h"
+#include "dprint.h"
+#include "route.h"
+
+
+
+/** eval rve and assign the result to an avp
+ * lv->lv.avp=eval(rve)
+ *
+ * based on do_action() ASSIGN_T
+ *
+ * @param h  - script context
+ * @param msg - sip msg
+ * @param lv - lvalue
+ * @param rve - rvalue expression
+ * @return >= 0 on success (expr. bool value), -1 on error
+ */
+inline static int lval_avp_assign(struct run_act_ctx* h, struct sip_msg* msg,
+									struct lvalue* lv, struct rvalue* rv)
+{
+	avp_spec_t* avp;
+	avp_t* r_avp;
+	avp_t* avp_mark;
+	pv_value_t pval;
+	int_str value;
+	unsigned short flags;
+	struct search_state st;
+	int ret;
+	int v;
+	int destroy_pval;
+	
+	destroy_pval=0;
+	avp=&lv->lv.avps;
+	ret=0;
+	/* If the left attr was specified without indexing brackets delete
+	 * existing AVPs before adding new ones */
+	if ((avp->type & AVP_INDEX_ALL) != AVP_INDEX_ALL)
+		delete_avp(avp->type, avp->name);
+	switch(rv->type){
+		case RV_NONE:
+			BUG("non-intialized rval / rval expr \n");
+			goto error;
+		case RV_INT:
+			value.n=rv->v.l;
+			flags=avp->type & ~AVP_VAL_STR;
+			ret=!(!value.n);
+			break;
+		case RV_STR:
+			value.s=rv->v.s;
+			flags=avp->type | AVP_VAL_STR;
+			ret=(value.s.len>0);
+			break;
+		case RV_ACTION_ST:
+			flags=avp->type & ~AVP_VAL_STR;
+			if (rv->v.action)
+				value.n=run_actions(h, rv->v.action, msg);
+			else
+				value.n=-1;
+			ret=value.n;
+			break;
+		case RV_BEXPR: /* logic/boolean expr. */
+			value.n=eval_expr(h, rv->v.bexpr, msg);
+			if (unlikely(value.n<0)){
+				if (value.n==EXPR_DROP) /* hack to quit on drop */
+					goto drop;
+				WARN("error in expression\n");
+				value.n=0; /* expr. is treated as false */
+			}
+			flags=avp->type & ~AVP_VAL_STR;
+			ret=value.n;
+			break;
+		case RV_SEL:
+			v=run_select(&value.s, &rv->v.sel, msg);
+			if (unlikely(v!=0)){
+				if (v<0){
+					ret=-1;
+					goto error;
+				}else { /* v>0 */
+					value.s.s="";
+					value.s.len=0;
+				}
+			}
+			flags=avp->type|AVP_VAL_STR;
+			ret=(value.s.len>0);
+			break;
+		case RV_AVP:
+			avp_mark=0;
+			if (unlikely((rv->v.avps.type & AVP_INDEX_ALL) == AVP_INDEX_ALL)){
+				r_avp = search_first_avp(rv->v.avps.type, rv->v.avps.name,
+											&value, &st);
+				while(r_avp){
+					/* We take only the val type  from the source avp
+					 * and reset the class, track flags and name type  */
+					flags=(avp->type & ~(AVP_INDEX_ALL|AVP_VAL_STR)) | 
+							(r_avp->flags & ~(AVP_CLASS_ALL|AVP_TRACK_ALL|
+												AVP_NAME_STR|AVP_NAME_RE));
+					if (add_avp_before(avp_mark, flags, avp->name, value)<0){
+						ERR("failed to assign avp\n");
+						ret=-1;
+						goto error;
+					}
+					/* move the mark, so the next found AVP will come before
+					   the one currently added so they will have the same 
+					   order as in the source list */
+					if (avp_mark) avp_mark=avp_mark->next;
+					else
+						avp_mark=search_first_avp(flags, avp->name, 0, 0);
+					r_avp=search_next_avp(&st, &value);
+				}
+				ret=1;
+				goto end;
+			}else{
+				r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
+											&value, rv->v.avps.index);
+				if (likely(r_avp)){
+					/* take only the val type from the source avp
+					 * and reset the class, track flags and name type  */
+					flags=(avp->type & ~AVP_VAL_STR) | (r_avp->flags & 
+								~(AVP_CLASS_ALL|AVP_TRACK_ALL|AVP_NAME_STR|
+									AVP_NAME_RE));
+					ret=1;
+				}else{
+					ret=-1;
+					goto error;
+				}
+			}
+			break;
+		case RV_PVAR:
+			memset(&pval, 0, sizeof(pval));
+			if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
+				destroy_pval=1;
+				if (pval.flags & PV_TYPE_INT){
+					value.n=pval.ri;
+					ret=value.n;
+					flags=avp->type & ~AVP_VAL_STR;
+				}else if (pval.flags & PV_VAL_STR){
+					value.s=pval.rs;
+					ret=(value.s.len>0);
+					flags=avp->type | AVP_VAL_STR;
+				}else if (pval.flags==PV_VAL_NONE ||
+							(pval.flags & (PV_VAL_NULL|PV_VAL_EMPTY))){
+					value.s.s="";
+					value.s.len=0;
+					ret=0;
+					flags=avp->type | AVP_VAL_STR;
+				}
+			}else{
+				/* non existing pvar */
+				value.s.s="";
+				value.s.len=0;
+				ret=0;
+				flags=avp->type | AVP_VAL_STR;
+			}
+			break;
+	}
+	if (add_avp(flags & ~AVP_INDEX_ALL, avp->name, value) < 0) {
+		ERR("failed to assign value to avp\n");
+		goto error;
+	}
+end:
+	if (destroy_pval) pv_value_destroy(&pval);
+	return ret;
+error:
+	if (destroy_pval) pv_value_destroy(&pval);
+	return -1;
+drop:
+	if (destroy_pval) pv_value_destroy(&pval);
+	return EXPR_DROP;
+}
+
+
+
+/** eval rve and assign the result to a pvar
+ * lv->lv.pvar=eval(rve)
+ *
+ * based on do_action() ASSIGN_T
+ *
+ * @param h  - script context
+ * @param msg - sip msg
+ * @param lv - lvalue
+ * @param rve - rvalue expression
+ * @return >= 0 on success (expr. bool value), -1 on error
+ */
+inline static int lval_pvar_assign(struct run_act_ctx* h, struct sip_msg* msg,
+									struct lvalue* lv, struct rvalue* rv)
+{
+	pv_spec_t* pvar;
+	pv_value_t pval;
+	avp_t* r_avp;
+	int_str avp_val;
+	int ret;
+	int v;
+	int destroy_pval;
+	
+	destroy_pval=0;
+	pvar=&lv->lv.pvs;
+	if (unlikely(!pv_is_w(pvar))){
+		ERR("read only pvar\n");
+		goto error;
+	}
+	memset(&pval, 0, sizeof(pval));
+	ret=0;
+	switch(rv->type){
+		case RV_NONE:
+			BUG("non-intialized rval / rval expr \n");
+			goto error;
+		case RV_INT:
+			pval.flags=PV_TYPE_INT|PV_VAL_INT;
+			pval.ri=rv->v.l;
+			ret=!(!pval.ri);
+			break;
+		case RV_STR:
+			pval.flags=PV_VAL_STR;
+			pval.rs=rv->v.s;
+			ret=(pval.rs.len>0);
+			break;
+		case RV_ACTION_ST:
+			pval.flags=PV_TYPE_INT|PV_VAL_INT;
+			if (rv->v.action)
+				pval.ri=run_actions(h, rv->v.action, msg);
+			else
+				pval.ri=0;
+			ret=pval.ri;
+			break;
+		case RV_BEXPR: /* logic/boolean expr. */
+			pval.flags=PV_TYPE_INT|PV_VAL_INT;
+			pval.ri=eval_expr(h, rv->v.bexpr, msg);
+			if (unlikely(pval.ri<0)){
+				if (pval.ri==EXPR_DROP) /* hack to quit on drop */
+					goto drop;
+				WARN("error in expression\n");
+				pval.ri=0; /* expr. is treated as false */
+			}
+			ret=pval.ri;
+			break;
+		case RV_SEL:
+			pval.flags=PV_VAL_STR;
+			v=run_select(&pval.rs, &rv->v.sel, msg);
+			if (unlikely(v!=0)){
+				if (v<0){
+					ret=-1;
+					goto error;
+				}else { /* v>0 */
+					pval.rs.s="";
+					pval.rs.len=0;
+				}
+			}
+			ret=(pval.rs.len)>0;
+			break;
+		case RV_AVP:
+				r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
+											&avp_val, rv->v.avps.index);
+				if (likely(r_avp)){
+					if (r_avp->flags & AVP_VAL_STR){
+						pval.flags=PV_VAL_STR;
+						pval.rs=avp_val.s;
+						ret=(pval.rs.len>0);
+					}else{
+						pval.flags=PV_TYPE_INT|PV_VAL_INT;
+						pval.ri=avp_val.n;
+						ret=!(!pval.ri);
+					}
+				}else{
+					ret=-1;
+					goto error;
+				}
+			break;
+		case RV_PVAR:
+			if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
+				destroy_pval=1;
+				if (pval.flags & PV_TYPE_INT){
+					ret=!(!pval.ri);
+				}else if (pval.flags & PV_VAL_STR){
+					ret=(pval.rs.len>0);
+				}else{
+					ERR("no value in pvar assignment rval\n");
+					ret=-1;
+					goto error;
+				}
+			}else{
+				ERR("non existing right pvar\n");
+				ret=-1;
+				goto error;
+			}
+			break;
+	}
+	if (unlikely(pvar->setf(msg, &pvar->pvp, EQ_T, &pval)<0)){
+		ERR("setting pvar failed\n");
+		goto error;
+	}
+	if (destroy_pval) pv_value_destroy(&pval);
+	return ret;
+error:
+	if (destroy_pval) pv_value_destroy(&pval);
+	return -1;
+drop:
+	if (destroy_pval) pv_value_destroy(&pval);
+	return EXPR_DROP;
+}
+
+
+
+/** eval rve and assign the result to lv
+ * lv=eval(rve)
+ *
+ * @param h  - script context
+ * @param msg - sip msg
+ * @param lv - lvalue
+ * @param rve - rvalue expression
+ * @return >= 0 on success (expr. bool value), -1 on error
+ */
+int lval_assign(struct run_act_ctx* h, struct sip_msg* msg, 
+				struct lvalue* lv, struct rval_expr* rve)
+{
+	struct rvalue* rv;
+	int ret;
+	
+	ret=0;
+	rv=rval_expr_eval(h, msg, rve);
+	if (unlikely(rv==0)){
+		ERR("rval expression evaluation failed\n");
+		goto error;
+	}
+	switch(lv->type){
+		case LV_NONE:
+			BUG("uninitialized/invalid lvalue (%d)\n", lv->type);
+			goto error;
+		case LV_AVP:
+			ret=lval_avp_assign(h, msg, lv, rv);
+			break;
+		case LV_PVAR:
+			ret=lval_pvar_assign(h, msg, lv, rv);
+			break;
+	}
+	rval_destroy(rv);
+	return ret;
+error:
+	if (rv) rval_destroy(rv);
+	return -1;
+}

+ 67 - 0
lvalue.h

@@ -0,0 +1,67 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file 
+ * @brief lvalues (assignment)
+ */
+/* 
+ * History:
+ * --------
+ *  2008-11-30  initial version (andrei)
+ */
+
+#ifndef __lvalue_h_
+#define __lvalue_h_
+
+#include "rvalue.h"
+#include "usr_avp.h"
+#include "pvar.h"
+#include "parser/msg_parser.h"
+#include "action.h"
+
+union lval_u{
+	pv_spec_t pvs;
+	avp_spec_t avps;
+};
+
+enum lval_type{
+	LV_NONE, LV_AVP, LV_PVAR
+};
+
+struct lvalue{
+	enum lval_type type;
+	union lval_u lv;
+};
+
+/* lval operators */
+#define EQ_T 254 /* k compatibility */
+
+
+
+/** eval rve and assign the result to lv
+ * lv=eval(rve)
+ *
+ * @param h  - script context
+ * @param msg - sip msg
+ * @param lv - lvalue
+ * @param rve - rvalue expression
+ * @return >= 0 on success (expr. bool value), -1 on error
+ */
+int lval_assign(struct run_act_ctx* h, struct sip_msg* msg, 
+				struct lvalue* lv, struct rval_expr* rve);
+#endif /* __lvalue_h_*/

+ 15 - 5
main.c

@@ -222,7 +222,7 @@ Options:\n\
 #endif
 #ifdef USE_SCTP
 "    -S           disable sctp\n\
-    -O            Number of sctp child processes (default: equal to `-n')\n"
+    -Q            Number of sctp child processes (default: equal to `-n')\n"
 #endif /* USE_SCTP */
 "    -V           Version number\n\
     -h           This help message\n\
@@ -234,7 +234,8 @@ Options:\n\
     -u uid       Change uid \n\
     -g gid       Change gid \n\
     -P file      Create a pid file\n\
-    -G file      Create a pgid file\n"
+    -G file      Create a pgid file\n\
+    -O nr        Script optimization level (debugging option)\n"
 #ifdef STATS
 "    -s file     File to which statistics is dumped (disabled otherwise)\n"
 #endif
@@ -1502,7 +1503,7 @@ int main(int argc, char** argv)
 		"DBG_MSG_QA enabled, ser may exit abruptly\n");
 #endif
 
-	options=  ":f:cm:dVhEb:l:L:n:vrRDTN:W:w:t:u:g:P:G:SO:"
+	options=  ":f:cm:dVhEb:l:L:n:vrRDTN:W:w:t:u:g:P:G:SQ:O:"
 #ifdef STATS
 		"s:"
 #endif
@@ -1572,6 +1573,14 @@ int main(int argc, char** argv)
 			case 'E':
 					log_stderr=1;
 					break;
+			case 'O':
+					scr_opt_lev=strtol(optarg, &tmp, 10);
+					if (tmp &&(*tmp)){
+						fprintf(stderr, "bad optimization level: -O %s\n",
+										optarg);
+						goto error;
+					};
+					break;
 			case 'b':
 			case 'l':
 			case 'n':
@@ -1589,7 +1598,7 @@ int main(int argc, char** argv)
 			case 'P':
 			case 'G':
 			case 'S':
-			case 'O':
+			case 'Q':
 			case 's':
 					break;
 			case '?':
@@ -1676,6 +1685,7 @@ try_again:
 			case 'd':
 			case 'V':
 			case 'h':
+			case 'O':
 					break;
 			case 'E':
 					log_stderr=1;	// use in both getopt switches
@@ -1762,7 +1772,7 @@ try_again:
 					fprintf(stderr,"WARNING: sctp support not compiled in\n");
 				#endif
 					break;
-			case 'O':
+			case 'Q':
 				#ifdef USE_SCTP
 					sctp_children_no=strtol(optarg, &tmp, 10);
 					if ((tmp==0) ||(*tmp)){

+ 53 - 34
pvapi.c

@@ -352,7 +352,7 @@ pv_export_t* pv_lookup_spec_name(str *pvname, pv_spec_p e)
 	return NULL;
 }
 
-char* pv_parse_spec(str *in, pv_spec_p e)
+char* pv_parse_spec2(str *in, pv_spec_p e, int silent)
 {
 	char *p;
 	str s;
@@ -364,7 +364,7 @@ char* pv_parse_spec(str *in, pv_spec_p e)
 
 	if(in==NULL || in->s==NULL || e==NULL || *in->s!=PV_MARKER)
 	{
-		LM_ERR("bad parameters\n");
+		if (!silent) LM_ERR("bad parameters\n");
 		return NULL;
 	}
 	
@@ -411,8 +411,9 @@ char* pv_parse_spec(str *in, pv_spec_p e)
 			p++;
 			pvstate = 4;
 		} else {
-			LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s,
-					pvstate);
+			if (!silent)
+				LM_ERR("invalid char '%c' in [%.*s] (%d)\n",
+							*p, in->len, in->s, pvstate);
 			goto error;
 		}
 	} else { 
@@ -433,21 +434,24 @@ char* pv_parse_spec(str *in, pv_spec_p e)
 done_inm:
 	if((pte = pv_lookup_spec_name(&pvname, e))==NULL)
 	{
-		LM_ERR("error searching pvar \"%.*s\"\n", pvname.len, pvname.s);
+		if (!silent) 
+			LM_ERR("error searching pvar \"%.*s\"\n", pvname.len, pvname.s);
 		goto error;
 	}
 	if(pte->parse_name!=NULL && pvstate!=2 && pvstate!=5)
 	{
-		LM_ERR("pvar \"%.*s\" expects an inner name\n",
-				pvname.len, pvname.s);
+		if (!silent) 
+			LM_ERR("pvar \"%.*s\" expects an inner name\n",
+						pvname.len, pvname.s);
 		goto error;
 	}
 	if(pvstate==2 || pvstate==5)
 	{
 		if(pte->parse_name==NULL)
 		{
-			LM_ERR("pvar \"%.*s\" does not get name param\n",
-					pvname.len, pvname.s);
+			if (!silent)
+				LM_ERR("pvar \"%.*s\" does not get name param\n",
+						pvname.len, pvname.s);
 			goto error;
 		}
 		s.s = p;
@@ -470,15 +474,17 @@ done_inm:
 
 		if(p==s.s)
 		{
-			LM_ERR("pvar \"%.*s\" does not get empty name param\n",
-					pvname.len, pvname.s);
+			if (!silent)
+				LM_ERR("pvar \"%.*s\" does not get empty name param\n",
+						pvname.len, pvname.s);
 			goto error;
 		}
 		s.len = p - s.s;
 		if(pte->parse_name(e, &s)!=0)
 		{
-			LM_ERR("pvar \"%.*s\" has an invalid name param [%.*s]\n",
-					pvname.len, pvname.s, s.len, s.s);
+			if (!silent)
+				LM_ERR("pvar \"%.*s\" has an invalid name param [%.*s]\n",
+						pvname.len, pvname.s, s.len, s.s);
 			goto error;
 		}
 		if(pvstate==2)
@@ -494,8 +500,9 @@ done_inm:
 				p++;
 				pvstate = 4;
 			} else {
-				LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s,
-					pvstate);
+				if (!silent)
+					LM_ERR("invalid char '%c' in [%.*s] (%d)\n",
+								*p, in->len, in->s, pvstate);
 				goto error;
 			}
 		} else {
@@ -504,8 +511,9 @@ done_inm:
 				p++;
 				goto done_all;
 			} else {
-				LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s,
-					pvstate);
+				if (!silent)
+					LM_ERR("invalid char '%c' in [%.*s] (%d)\n",
+								*p, in->len, in->s, pvstate);
 				goto error;
 			}
 		}
@@ -515,8 +523,9 @@ done_vnm:
 	{
 		if(pte->parse_index==NULL)
 		{
-			LM_ERR("pvar \"%.*s\" does not get index param\n",
-					pvname.len, pvname.s);
+			if (!silent)
+				LM_ERR("pvar \"%.*s\" does not get index param\n",
+						pvname.len, pvname.s);
 			goto error;
 		}
 		s.s = p;
@@ -538,15 +547,17 @@ done_vnm:
 
 		if(p==s.s)
 		{
-			LM_ERR("pvar \"%.*s\" does not get empty index param\n",
-					pvname.len, pvname.s);
+			if (!silent)
+				LM_ERR("pvar \"%.*s\" does not get empty index param\n",
+						pvname.len, pvname.s);
 			goto error;
 		}
 		s.len = p - s.s;
 		if(pte->parse_index(e, &s)!=0)
 		{
-			LM_ERR("pvar \"%.*s\" has an invalid index param [%.*s]\n",
-					pvname.len, pvname.s, s.len, s.s);
+			if (!silent)
+				LM_ERR("pvar \"%.*s\" has an invalid index param [%.*s]\n",
+						pvname.len, pvname.s, s.len, s.s);
 			goto error;
 		}
 		p++;
@@ -557,8 +568,9 @@ done_vnm:
 			p++;
 			pvstate = 4;
 		} else {
-			LM_ERR("invalid char '%c' in [%.*s] (%d)\n", *p, in->len, in->s,
-					pvstate);
+			if (!silent)
+				LM_ERR("invalid char '%c' in [%.*s] (%d)\n",
+							*p, in->len, in->s, pvstate);
 			goto error;
 		}
 	}
@@ -594,8 +606,9 @@ done_idx:
 
 		if(p==s.s)
 		{
-			LM_ERR("pvar \"%.*s\" does not get empty index param\n",
-					pvname.len, pvname.s);
+			if (!silent)
+				LM_ERR("pvar \"%.*s\" does not get empty index param\n",
+						pvname.len, pvname.s);
 			goto error;
 		}
 		s.len = p - s.s + 1;
@@ -603,12 +616,14 @@ done_idx:
 		p = tr_lookup(&s, &tr);
 		if(p==NULL)
 		{
-			LM_ERR("bad tr in pvar name \"%.*s\"\n", pvname.len, pvname.s);
+			if (!silent)
+				LM_ERR("bad tr in pvar name \"%.*s\"\n", pvname.len, pvname.s);
 			goto error;
 		}
 		if(*p!=PV_RNBRACKET)
 		{
-			LM_ERR("bad pvar name \"%.*s\" (%c)!\n", in->len, in->s, *p);
+			if (!silent)
+				LM_ERR("bad pvar name \"%.*s\" (%c)!\n", in->len, in->s, *p);
 			goto error;
 		}
 		e->trans = (void*)tr;
@@ -621,11 +636,15 @@ done_all:
 	return p;
 
 error:
-	if(p!=NULL)
-		LM_ERR("wrong char [%c/%d] in [%.*s] at [%d (%d)]\n", *p, (int)*p,
-			in->len, in->s, (int)(p-in->s), pvstate);
-	else
-		LM_ERR("invalid parsing in [%.*s] at (%d)\n", in->len, in->s, pvstate);
+	if(p!=NULL){
+		if (!silent)
+			LM_ERR("wrong char [%c/%d] in [%.*s] at [%d (%d)]\n", *p, (int)*p,
+					in->len, in->s, (int)(p-in->s), pvstate);
+	}else{
+		if (!silent)
+			LM_ERR("invalid parsing in [%.*s] at (%d)\n",
+						in->len, in->s, pvstate);
+	}
 	return NULL;
 
 } /* end: pv_parse_spec */

+ 2 - 1
pvar.h

@@ -158,7 +158,8 @@ typedef struct _pv_elem
 	struct _pv_elem *next;
 } pv_elem_t, *pv_elem_p;
 
-char* pv_parse_spec(str *in, pv_spec_p sp);
+char* pv_parse_spec2(str *in, pv_spec_p sp, int silent);
+#define pv_parse_spec(in, sp) pv_parse_spec2((in), (sp), 0)
 int pv_get_spec_value(struct sip_msg* msg, pv_spec_p sp, pv_value_t *value);
 int pv_print_spec(struct sip_msg* msg, pv_spec_p sp, char *buf, int *len);
 int pv_printf(struct sip_msg* msg, pv_elem_p list, char *buf, int *len);

File diff suppressed because it is too large
+ 541 - 198
route.c


+ 5 - 1
route.h

@@ -59,6 +59,8 @@ extern struct route_list failure_rt;
 extern struct route_list branch_rt;
 extern struct route_list onsend_rt;
 
+/* script optimization level */
+extern int scr_opt_lev;
 
 int init_routes();
 void destroy_routes();
@@ -73,7 +75,9 @@ int fix_rls();
 int eval_expr(struct run_act_ctx* h, struct expr* e, struct sip_msg* msg);
 
 
-
+/* fixup functions*/
+int fix_actions(struct action* a);
+int fix_expr(struct expr* exp);
 
 
 

+ 83 - 5
route_struct.c

@@ -52,6 +52,30 @@
 #include "ut.h" /* ZSW() */
 
 
+
+/** joins to cfg file positions into a new one. */
+void cfg_pos_join(struct cfg_pos* res,
+							struct cfg_pos* pos1, struct cfg_pos* pos2)
+{
+	struct cfg_pos ret;
+	ret=*pos1;
+	if ((ret.s_line == 0) || (ret.s_line > pos2->s_line)){
+		ret.s_line=pos2->s_line;
+		ret.s_col=pos2->s_col;
+	}else if ((ret.s_line == pos2->s_line) && (ret.s_col > pos2->s_col)){
+		ret.s_col=pos2->s_col;
+	}
+	if ((ret.e_line == 0) || (ret.e_line < pos2->e_line)){
+		ret.e_line=pos2->e_line;
+		ret.e_col=pos2->e_col;
+	}else if ((ret.e_line == pos2->e_line) && (ret.e_col < pos2->e_col)){
+		ret.e_col=pos2->e_col;
+	}
+	*res=ret;
+}
+
+
+
 struct expr* mk_exp(int op, struct expr* left, struct expr* right)
 {
 	struct expr * e;
@@ -68,6 +92,27 @@ error:
 }
 
 
+struct expr* mk_exp_rve(int op, void* left, void* right)
+{
+	struct expr * e;
+	e=(struct expr*)pkg_malloc(sizeof (struct expr));
+	if (e==0) goto error;
+	e->type=EXP_T;
+	e->op=op;
+	e->l.param=mk_elem(RVEXP_O, RVE_ST, left, 0, 0);
+	e->r.param=mk_elem(RVEXP_O, RVE_ST, right, 0, 0);
+	if (e->l.param==0 || e->r.param==0){
+		if (e->l.param) pkg_free(e->l.param);
+		if (e->r.param) pkg_free(e->r.param);
+		pkg_free(e);
+		goto error;
+	}
+	return e;
+error:
+	LOG(L_CRIT, "ERROR: mk_exp_rve: memory allocation failure\n");
+	return 0;
+}
+
 struct expr* mk_elem(int op, int ltype, void* lparam, int rtype, void* rparam)
 {
 	struct expr * e;
@@ -160,15 +205,48 @@ void print_expr(struct expr* exp)
 			case DSTPORT_O:
 				DBG("dstport");
 				break;
-			case NUMBER_O:
+			case PROTO_O:
+				DBG("proto");
+				break;
+			case AF_O:
+				DBG("af");
+				break;
+			case MSGLEN_O:
+				DBG("msglen");
 				break;
 			case ACTION_O:
 				break;
-		        case AVP_ST:
-				DBG("attr");
+			case NUMBER_O:
 				break;
-		        case SELECT_ST:
-			        DBG("select");
+			case AVP_O:
+				DBG("avp");
+				break;
+			case SNDIP_O:
+				DBG("sndip");
+				break;
+			case SNDPORT_O:
+				DBG("sndport");
+				break;
+			case TOIP_O:
+				DBG("toip");
+				break;
+			case TOPORT_O:
+				DBG("toport");
+				break;
+			case SNDPROTO_O:
+				DBG("sndproto");
+				break;
+			case SNDAF_O:
+				DBG("sndaf");
+				break;
+			case RETCODE_O:
+				DBG("retcode");
+				break;
+			case SELECT_O:
+				DBG("select");
+				break;
+			case RVEXP_O:
+				DBG("rval");
 				break;
 
 			default:

+ 16 - 2
route_struct.h

@@ -65,7 +65,7 @@ enum { EQUAL_OP=10, MATCH_OP, GT_OP, LT_OP, GTE_OP, LTE_OP, DIFF_OP, NO_OP };
 enum { METHOD_O=1, URI_O, FROM_URI_O, TO_URI_O, SRCIP_O, SRCPORT_O,
 	   DSTIP_O, DSTPORT_O, PROTO_O, AF_O, MSGLEN_O, DEFAULT_O, ACTION_O,
 	   NUMBER_O, AVP_O, SNDIP_O, SNDPORT_O, TOIP_O, TOPORT_O, SNDPROTO_O,
-	   SNDAF_O, RETCODE_O, SELECT_O};
+	   SNDAF_O, RETCODE_O, SELECT_O, PVAR_O, RVEXP_O};
 
 enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
 		SET_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T,
@@ -93,13 +93,24 @@ enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T,
 };
 enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
 		EXPR_ST, ACTIONS_ST, MODEXP_ST, MODFIXUP_ST, URIHOST_ST, URIPORT_ST,
-		MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, AVP_ST, SELECT_ST,
+		MYSELF_ST, STR_ST, SOCKID_ST, SOCKETINFO_ST, ACTION_ST, AVP_ST,
+		SELECT_ST, PVAR_ST,
+		LVAL_ST,  RVE_ST,
 		RETCODE_ST};
 
 /* run flags */
 #define EXIT_R_F   1
 #define RETURN_R_F 2
 
+
+struct cfg_pos{
+	int s_line;
+	int e_line;
+	unsigned short s_col;
+	unsigned short e_col;
+};
+
+
 /* Expression operand */
 union exp_op {
 	void* param;
@@ -156,5 +167,8 @@ void print_action(struct action* a);
 void print_actions(struct action* a);
 void print_expr(struct expr* exp);
 
+/** joins to cfg file positions into a new one. */
+void cfg_pos_join(struct cfg_pos* res,
+							struct cfg_pos* pos1, struct cfg_pos* pos2);
 #endif
 

+ 2628 - 0
rvalue.c

@@ -0,0 +1,2628 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file 
+ * @brief rvalue expressions
+ */
+/* 
+ * History:
+ * --------
+ *  2008-12-01  initial version (andrei)
+ */
+
+#include "rvalue.h"
+
+/* minimum size alloc'ed for STR RVs (to accomodate
+ * strops without reallocs) */
+#define RV_STR_EXTRA 80
+
+#define rv_ref(rv) ((rv)->refcnt++)
+
+/** unref rv and returns true if 0 */
+#define rv_unref(rv) ((--(rv)->refcnt)==0)
+
+
+inline static void rval_force_clean(struct rvalue* rv)
+{
+	if (rv->flags & RV_CNT_ALLOCED_F){
+		switch(rv->type){
+			case RV_STR:
+				pkg_free(rv->v.s.s);
+				rv->v.s.s=0;
+				rv->v.s.len=0;
+				break;
+			default:
+				BUG("RV_CNT_ALLOCED_F not supported for type %d\n", rv->type);
+		}
+		rv->flags&=~RV_CNT_ALLOCED_F;
+	}
+}
+
+
+
+/** frees a rval returned by rval_new(), rval_convert() or rval_expr_eval().
+ *   Note: ir will be freed only when refcnt reaches 0
+ */
+void rval_destroy(struct rvalue* rv)
+{
+	if (rv && rv_unref(rv)){
+		rval_force_clean(rv);
+		if (rv->flags & RV_RV_ALLOCED_F){
+			pkg_free(rv);
+		}
+	}
+}
+
+
+
+void rval_clean(struct rvalue* rv)
+{
+	if (rv_unref(rv))
+		rval_force_clean(rv);
+}
+
+
+
+void rve_destroy(struct rval_expr* rve)
+{
+	if (rve){
+		if (rve->op==RVE_RVAL_OP){
+			if (rve->left.rval.refcnt){
+				if (rve->left.rval.refcnt==1)
+					rval_destroy(&rve->left.rval);
+				else
+					BUG("rval expr rval with invalid refcnt: %d\n", 
+							rve->left.rval.refcnt);
+			}
+			if (rve->right.rval.refcnt){
+				if (rve->right.rval.refcnt==1)
+					rval_destroy(&rve->right.rval);
+				else
+					BUG("rval expr rval with invalid refcnt: %d\n", 
+							rve->right.rval.refcnt);
+			}
+		}else{
+			if (rve->left.rve)
+				rve_destroy(rve->left.rve);
+			if (rve->right.rve)
+				rve_destroy(rve->right.rve);
+		}
+		pkg_free(rve);
+	}
+}
+
+
+
+void rval_cache_clean(struct rval_cache* rvc)
+{
+	if (rvc->cache_type==RV_CACHE_PVAR){
+		pv_value_destroy(&rvc->c.pval);
+	}
+	rvc->cache_type=RV_CACHE_EMPTY;
+	rvc->val_type=RV_NONE;
+}
+
+
+#define rv_chg_in_place(rv)  ((rv)->refcnt==1) 
+
+
+
+/** init a rvalue structure.
+ * Note: not needed if the structure is allocate with one of the 
+ * rval_new* functions
+ */
+void rval_init(struct rvalue* rv, enum rval_type t, union rval_val* v, 
+				int flags)
+{
+	rv->flags=flags;
+	rv->refcnt=1;
+	rv->type=t;
+	if (v){
+		rv->v=*v;
+	}else{
+		memset (&rv->v, 0, sizeof(rv->v));
+	}
+}
+
+
+
+/** create a new pk_malloc'ed empty rvalue.
+  *
+  * @param extra_size - extra space to allocate
+  *                    (e.g.: so that future string operation can reuse
+  *                     the space)
+  * @return new rv or 0 on error
+  */
+struct rvalue* rval_new_empty(int extra_size)
+{
+	struct rvalue* rv;
+	int size; /* extra size at the end */
+	
+	size=ROUND_LONG(sizeof(*rv)-sizeof(rv->buf)+extra_size); /* round up */
+	rv=pkg_malloc(size);
+	if (likely(rv)){
+		rv->bsize=size-sizeof(*rv)-sizeof(rv->buf); /* remaining size->buffer*/
+		rv->flags=RV_RV_ALLOCED_F;
+		rv->refcnt=1;
+		rv->type=RV_NONE;
+	}
+	return rv;
+}
+
+
+
+/** create a new pk_malloc'ed rv from a str.
+  *
+  * @param s - pointer to str, must be non-null
+  * @param extra_size - extra space to allocate
+  *                    (so that future string operation can reuse
+  *                     the space)
+  * @return new rv or 0 on error
+  */
+struct rvalue* rval_new_str(str* s, int extra_size)
+{
+	struct rvalue* rv;
+	
+	rv=rval_new_empty(extra_size+s->len+1/* 0 term */);
+	if (likely(rv)){
+		rv->type=RV_STR;
+		rv->v.s.s=&rv->buf[0];
+		rv->v.s.len=s->len;
+		memcpy(rv->v.s.s, s->s, s->len);
+		rv->v.s.s[s->len]=0;
+	}
+	return rv;
+}
+
+
+
+/** get string name for a type.
+  *
+  * @return - null terminated name of the type
+  */
+char* rval_type_name(enum rval_type type)
+{
+	switch(type){
+		case RV_NONE:
+			return "none";
+		case RV_INT:
+			return "int";
+		case RV_STR:
+			return "str";
+		case RV_BEXPR:
+			return "bexpr_t";
+		case RV_ACTION_ST:
+			return "action_t";
+		case RV_PVAR:
+			return "pvar";
+		case RV_AVP:
+			return "avp";
+			break;
+		case RV_SEL:
+			return "select";
+	}
+	return "error_unkown_type";
+}
+
+
+
+/** create a new pk_malloc'ed rvalue from a rval_val union.
+  *
+  * @param s - pointer to str, must be non-null
+  * @param extra_size - extra space to allocate
+  *                    (so that future string operation can reuse
+  *                     the space)
+  * @return new rv or 0 on error
+  */
+struct rvalue* rval_new(enum rval_type t, union rval_val* v, int extra_size)
+{
+	struct rvalue* rv;
+	
+	if (t==RV_STR && v && v->s.len)
+		return rval_new_str(&v->s, extra_size);
+	rv=rval_new_empty(extra_size);
+	if (likely(rv)){
+		rv->type=t;
+		if (likely(v)){
+			rv->v=*v;
+		}else
+			memset (&rv->v, 0, sizeof(rv->v));
+	}
+	return rv;
+}
+
+
+
+/** get rvalue basic type (RV_INT or RV_STR).
+  *
+  * Given a rvalue it tries to determinte its basic type.
+  * Fills val_cache if non-null and empty (can be used in other rval*
+  * function calls, to avoid re-resolving avps or pvars). It must be
+  * rval_cache_clean()'en when no longer needed.
+  *
+  * @param rv - target rvalue
+  * @param val_cache - value cache, might be filled if non-null, 
+  *                    it _must_ be rval_cache_clean()'en when done.
+  * @return - basic type or RV_NONE on error
+  */
+inline static enum rval_type rval_get_btype(struct run_act_ctx* h,
+											struct sip_msg* msg,
+											struct rvalue* rv,
+											struct rval_cache* val_cache)
+{
+	avp_t* r_avp;
+	int_str tmp_avp_val;
+	int_str* avpv;
+	pv_value_t tmp_pval;
+	pv_value_t* pv;
+	enum rval_type tmp;
+	enum rval_type* ptype;
+	
+	switch(rv->type){
+		case RV_INT:
+		case RV_STR:
+			return rv->type;
+		case RV_BEXPR:
+		case RV_ACTION_ST:
+			return RV_INT;
+		case RV_PVAR:
+			if (likely(val_cache && val_cache->cache_type==RV_CACHE_EMPTY)){
+				pv=&val_cache->c.pval;
+			}else{
+				val_cache=0;
+				pv=&tmp_pval;
+			}
+			memset(pv, 0, sizeof(tmp_pval));
+			if (likely(pv_get_spec_value(msg, &rv->v.pvs, pv)==0)){
+				if (pv->flags & PV_VAL_STR){
+					if (unlikely(val_cache==0)) pv_value_destroy(pv);
+					else{
+						val_cache->cache_type=RV_CACHE_PVAR;
+						val_cache->val_type=RV_STR;
+					}
+					return RV_STR;
+				}else if (pv->flags & PV_TYPE_INT){
+					if (unlikely(val_cache==0)) pv_value_destroy(pv);
+					else{
+						val_cache->cache_type=RV_CACHE_PVAR;
+						val_cache->val_type=RV_INT;
+					}
+					return RV_INT;
+				}else{
+					pv_value_destroy(pv);
+					goto error;
+				}
+			}else{
+				goto error;
+			}
+			break;
+		case RV_AVP:
+			if (likely(val_cache && val_cache==RV_CACHE_EMPTY)){
+				ptype=&val_cache->val_type;
+				avpv=&val_cache->c.avp_val;
+				val_cache->cache_type=RV_CACHE_AVP;
+			}else{
+				ptype=&tmp;
+				avpv=&tmp_avp_val;
+			}
+			r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
+											avpv, rv->v.avps.index);
+			if (likely(r_avp)){
+				if (r_avp->flags & AVP_VAL_STR){
+					*ptype=RV_STR;
+					return RV_STR;
+				}else{
+					*ptype=RV_INT;
+					return RV_INT;
+				}
+			}else{
+				*ptype=RV_NONE;
+				if (val_cache) val_cache->cache_type=RV_CACHE_EMPTY;
+				goto error;
+			}
+			break;
+		case RV_SEL:
+			return RV_STR;
+		default:
+			BUG("rv type %d not handled\n", rv->type);
+	}
+error:
+	return RV_NONE;
+}
+
+
+
+/** guess the type of an expression.
+  * @return RV_INT, RV_STR or RV_NONE (when type could not be found,
+  * e.g. avp or pvar)
+  */
+enum rval_type rve_guess_type( struct rval_expr* rve)
+{
+	switch(rve->op){
+		case RVE_RVAL_OP:
+			switch(rve->left.rval.type){
+				case RV_STR:
+				case RV_SEL:
+					return RV_STR;
+				case RV_INT:
+				case RV_BEXPR:
+				case RV_ACTION_ST:
+					return RV_INT;
+				case RV_PVAR:
+				case RV_AVP:
+				case RV_NONE:
+					return RV_NONE;
+			}
+			break;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+		case RVE_MINUS_OP:
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_IPLUS_OP:
+			return RV_INT;
+		case RVE_PLUS_OP:
+			/* '+' evaluates to the type of the left operand */
+			return rve_guess_type(rve->left.rve);
+		case RVE_CONCAT_OP:
+			return RV_STR;
+		case RVE_NONE_OP:
+			break;
+	}
+	return RV_NONE;
+}
+
+
+
+/** returns true if expression is constant.
+  * @return 0 or 1 on
+  *  non constant type
+  */
+int rve_is_constant(struct rval_expr* rve)
+{
+	switch(rve->op){
+		case RVE_RVAL_OP:
+			switch(rve->left.rval.type){
+				case RV_STR:
+					return 1;
+				case RV_INT:
+					return 1;
+				case RV_SEL:
+				case RV_BEXPR:
+				case RV_ACTION_ST:
+				case RV_PVAR:
+				case RV_AVP:
+				case RV_NONE:
+					return 0;
+			}
+			break;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			return rve_is_constant(rve->left.rve);
+		case RVE_MINUS_OP:
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_PLUS_OP:
+		case RVE_IPLUS_OP:
+		case RVE_CONCAT_OP:
+			return rve_is_constant(rve->left.rve) &&
+					rve_is_constant(rve->right.rve);
+		case RVE_NONE_OP:
+			break;
+	}
+	return 0;
+}
+
+
+
+/** returns true if operator is unary (takes only 1 arg).
+  * @return 0 or 1 on
+  */
+static int rve_op_unary(enum rval_expr_op op)
+{
+	switch(op){
+		case RVE_RVAL_OP: /* not realy an operator */
+			return -1;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			return 1;
+		case RVE_MINUS_OP:
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_PLUS_OP:
+		case RVE_IPLUS_OP:
+		case RVE_CONCAT_OP:
+			return 0;
+		case RVE_NONE_OP:
+			return -1;
+			break;
+	}
+	return 0;
+}
+
+
+
+/** returns 1 if expression is valid (type-wise).
+  * @param type - filled with the type of the expression (RV_INT, RV_STR or
+  *                RV_NONE if it's dynamic)
+  * @param rve  - checked expression
+  * @param bad_rve - set on failure to the subexpression for which the 
+  *                    type check failed
+  * @param bad_type - set on failure to the type of the bad subexpression
+  * @param exp_type - set on failure to the expected type for the bad
+  *                   subexpression
+  * @return 0 or 1  and sets *type to the resulting type
+  * (RV_INT, RV_STR or RV_NONE if it can be found only at runtime)
+  */
+int rve_check_type(enum rval_type* type, struct rval_expr* rve,
+					struct rval_expr** bad_rve, 
+					enum rval_type* bad_t,
+					enum rval_type* exp_t)
+{
+	enum rval_type type1, type2;
+	
+	switch(rve->op){
+		case RVE_RVAL_OP:
+			*type=rve_guess_type(rve);
+			return 1;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			*type=RV_INT;
+			if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
+				if (type1==RV_STR){
+					if (bad_rve) *bad_rve=rve->left.rve;
+					if (bad_t) *bad_t=type1;
+					if (exp_t) *exp_t=RV_INT;
+					return 0;
+				}
+				return 1;
+			}
+			return 0;
+			break;
+		case RVE_MINUS_OP:
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_IPLUS_OP:
+			*type=RV_INT;
+			if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
+				if (type1==RV_STR){
+					if (bad_rve) *bad_rve=rve->left.rve;
+					if (bad_t) *bad_t=type1;
+					if (exp_t) *exp_t=RV_INT;
+					return 0;
+				}
+				if (rve_check_type(&type2, rve->right.rve, bad_rve,
+									bad_t, exp_t)){
+					if (type2==RV_STR){
+						if (bad_rve) *bad_rve=rve->right.rve;
+						if (bad_t) *bad_t=type2;
+						if (exp_t) *exp_t=RV_INT;
+						return 0;
+					}
+					return 1;
+				}
+			}
+			return 0;
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+			*type=RV_INT;
+			if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
+				if (rve_check_type(&type2, rve->right.rve, bad_rve, bad_t,
+										exp_t)){
+					if ((type2!=type1) && (type1!=RV_NONE) &&
+							(type2!=RV_NONE) && 
+							!(type1==RV_STR && type2==RV_INT)){
+						if (bad_rve) *bad_rve=rve->right.rve;
+						if (bad_t) *bad_t=type2;
+						if (exp_t) *exp_t=type1;
+						return 0;
+					}
+					return 1;
+				}
+			}
+			return 0;
+		case RVE_PLUS_OP:
+			*type=RV_NONE;
+			if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
+				if (rve_check_type(&type2, rve->right.rve, bad_rve, bad_t,
+									exp_t)){
+					if ((type2!=type1) && (type1!=RV_NONE) &&
+							(type2!=RV_NONE) && 
+							!(type1==RV_STR && type2==RV_INT)){
+						if (bad_rve) *bad_rve=rve->right.rve;
+						if (bad_t) *bad_t=type2;
+						if (exp_t) *exp_t=type1;
+						return 0;
+					}
+					*type=type1;
+					return 1;
+				}
+			}
+		case RVE_CONCAT_OP:
+			*type=RV_STR;
+			if (rve_check_type(&type1, rve->left.rve, bad_rve, bad_t, exp_t)){
+				if (rve_check_type(&type2, rve->right.rve, bad_rve, bad_t,
+									exp_t)){
+					if ((type2!=type1) && (type1!=RV_NONE) &&
+							(type2!=RV_NONE) && 
+							!(type1==RV_STR && type2==RV_INT)){
+						if (bad_rve) *bad_rve=rve->right.rve;
+						if (bad_t) *bad_t=type2;
+						if (exp_t) *exp_t=type1;
+						return 0;
+					}
+					if (type1==RV_INT){
+						if (bad_rve) *bad_rve=rve->left.rve;
+						if (bad_t) *bad_t=type1;
+						if (exp_t) *exp_t=RV_STR;
+						return 0;
+					}
+					return 1;
+				}
+			}
+		case RVE_NONE_OP:
+			break;
+	}
+	return 0;
+}
+
+
+
+/** get the integer value of an rvalue.
+  * *i=(int)rv
+  * @return 0 on success, \<0 on error and EXPR_DROP on drop
+ */
+int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg,
+								int* i, struct rvalue* rv,
+								struct rval_cache* cache)
+{
+	avp_t* r_avp;
+	int_str avp_val;
+	pv_value_t pval;
+	
+	switch(rv->type){
+		case RV_INT:
+			*i=rv->v.l;
+			break;
+		case RV_STR:
+			goto rv_str;
+		case RV_BEXPR:
+			*i=eval_expr(h, rv->v.bexpr, msg);
+			if (*i==EXPR_DROP){
+				*i=0; /* false */
+				return EXPR_DROP;
+			}
+			break;
+		case RV_ACTION_ST:
+			if (rv->v.action)
+				*i=run_actions(h, rv->v.action, msg);
+			else 
+				*i=0;
+			break;
+		case RV_SEL:
+			goto rv_str;
+		case RV_AVP:
+			if (unlikely(cache && cache->cache_type==RV_CACHE_AVP)){
+				if (likely(cache->val_type==RV_INT)){
+					*i=cache->c.avp_val.n;
+				}else if (cache->val_type==RV_STR)
+					goto rv_str;
+				else goto error;
+			}else{
+				r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
+											&avp_val, rv->v.avps.index);
+				if (likely(r_avp)){
+					if (unlikely(r_avp->flags & AVP_VAL_STR)){
+						goto rv_str;
+					}else{
+						*i=avp_val.n;
+					}
+				}else{
+					goto error;
+				}
+			}
+			break;
+		case RV_PVAR:
+			if (unlikely(cache && cache->cache_type==RV_CACHE_PVAR)){
+				if (likely((cache->val_type==RV_INT) || 
+								(cache->c.pval.flags & PV_VAL_INT))){
+					*i=cache->c.pval.ri;
+				}else if (cache->val_type==RV_STR)
+					goto rv_str;
+				else goto error;
+			}else{
+				memset(&pval, 0, sizeof(pval));
+				if (likely(pv_get_spec_value(msg, &rv->v.pvs, &pval)==0)){
+					if (likely(pval.flags & PV_VAL_INT)){
+						*i=pval.ri;
+						pv_value_destroy(&pval);
+					}else if (likely(pval.flags & PV_VAL_STR)){
+						pv_value_destroy(&pval);
+						goto rv_str;
+					}else{
+						pv_value_destroy(&pval);
+						goto error;
+					}
+				}else{
+					goto error;
+				}
+			}
+			break;
+		default:
+			BUG("rv type %d not handled\n", rv->type);
+			goto error;
+	}
+	return 0;
+rv_str:
+	/* rv is of string type => error */
+	/* ERR("string in int expression\n"); */
+error:
+	return -1;
+}
+
+
+
+/** get the string value of an rv in a tmp variable
+  * *s=(str)rv
+  * The result points either to a temporary string or inside
+  * new_cache. new_cache must be non zero, initialized previously,
+  * and it _must_ be rval_cache_clean(...)'ed when done.
+  * WARNING: it's not intended for general use, it might return a pointer
+  * to a static buffer (int2str) so use the result a.s.a.p, make a copy.
+  * or use rval_get_str() instead.
+  * @param h - script context handle
+  * @param msg - sip msg
+  * @param tmpv - str return value (pointer to a str struct that will be
+  *               be filled.
+  * @param rv   - rvalue to be converted
+  * @param cache - cached rv value (read-only)
+  * @param tmp_cache - used for temporary storage (so that tmpv will not
+  *                 point to possible freed data), it must be non-null,
+  *                 initialized and cleaned afterwards.
+  * @return 0 on success, <0 on error and EXPR_DROP on drop
+ */
+int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
+								str* tmpv, struct rvalue* rv,
+								struct rval_cache* cache,
+								struct rval_cache* tmp_cache)
+{
+	avp_t* r_avp;
+	pv_value_t pval;
+	int i;
+	
+	switch(rv->type){
+		case RV_INT:
+			tmpv->s=int2str(rv->v.l, &tmpv->len);
+			break;
+		case RV_STR:
+			*tmpv=rv->v.s;
+			break;
+		case RV_ACTION_ST:
+			if (rv->v.action)
+				i=run_actions(h, rv->v.action, msg);
+			else 
+				i=0;
+			tmpv->s=int2str(i, &tmpv->len);
+			break;
+		case RV_BEXPR:
+			i=eval_expr(h, rv->v.bexpr, msg);
+			if (i==EXPR_DROP){
+				i=0; /* false */
+				tmpv->s=int2str(i, &tmpv->len);
+				return EXPR_DROP;
+			}
+			tmpv->s=int2str(i, &tmpv->len);
+			break;
+		case RV_SEL:
+			i=run_select(tmpv, &rv->v.sel, msg);
+			if (unlikely(i!=0)){
+				if (i<0){
+					goto error;
+				}else { /* i>0 */
+					tmpv->s="";
+					tmpv->len=0;
+				}
+			}
+			break;
+		case RV_AVP:
+			if (likely(cache && cache->cache_type==RV_CACHE_AVP)){
+				if (likely(cache->val_type==RV_STR)){
+					*tmpv=cache->c.avp_val.s;
+				}else if (cache->val_type==RV_INT){
+					i=cache->c.avp_val.n;
+					tmpv->s=int2str(i, &tmpv->len);
+				}else goto error;
+			}else{
+				r_avp = search_avp_by_index(rv->v.avps.type, rv->v.avps.name,
+											&tmp_cache->c.avp_val,
+											rv->v.avps.index);
+				if (likely(r_avp)){
+					if (likely(r_avp->flags & AVP_VAL_STR)){
+						tmp_cache->cache_type=RV_CACHE_AVP;
+						tmp_cache->val_type=RV_STR;
+						*tmpv=tmp_cache->c.avp_val.s;
+					}else{
+						i=tmp_cache->c.avp_val.n;
+						tmpv->s=int2str(i, &tmpv->len);
+					}
+				}else{
+					goto error;
+				}
+			}
+			break;
+		case RV_PVAR:
+			if (likely(cache && cache->cache_type==RV_CACHE_PVAR)){
+				if (likely(cache->val_type==RV_STR)){
+					*tmpv=cache->c.pval.rs;
+				}else if (cache->val_type==RV_INT){
+					i=cache->c.pval.ri;
+					tmpv->s=int2str(i, &tmpv->len);
+				}else goto error;
+			}else{
+				memset(&pval, 0, sizeof(pval));
+				if (likely(pv_get_spec_value(msg, &rv->v.pvs,
+												&tmp_cache->c.pval)==0)){
+					if (likely(pval.flags & PV_VAL_STR)){
+						/*  the value is not destroyed, but saved instead
+							in tmp_cache so that it can be destroyed later
+							when no longer needed */
+						tmp_cache->cache_type=RV_CACHE_PVAR;
+						tmp_cache->val_type=RV_STR;
+						*tmpv=tmp_cache->c.pval.rs;
+					}else if (likely(pval.flags & PV_VAL_INT)){
+						i=pval.ri;
+						pv_value_destroy(&tmp_cache->c.pval);
+						tmpv->s=int2str(i, &tmpv->len);
+					}else{
+						pv_value_destroy(&tmp_cache->c.pval);
+						goto error;
+					}
+				}else{
+					goto error;
+				}
+			}
+			break;
+		default:
+			BUG("rv type %d not handled\n", rv->type);
+			goto error;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+
+/** get the string value of an rv.
+  * *s=(str)rv
+  * The result is pkg malloc'ed (so it should be pkg_free()'ed when finished.
+  * @return 0 on success, <0 on error and EXPR_DROP on drop
+ */
+int rval_get_str(struct run_act_ctx* h, struct sip_msg* msg,
+								str* s, struct rvalue* rv,
+								struct rval_cache* cache)
+{
+	str tmp;
+	struct rval_cache tmp_cache;
+	
+	rval_cache_init(&tmp_cache);
+	if (unlikely(rval_get_tmp_str(h, msg, &tmp, rv, cache, &tmp_cache)<0))
+		goto error;
+	s->s=pkg_malloc(tmp.len+1/* 0 term */);
+	if (unlikely(s->s==0)){
+		ERR("memory allocation error\n");
+		goto error;
+	}
+	s->len=tmp.len;
+	memcpy(s->s, tmp.s, tmp.len);
+	s->s[tmp.len]=0; /* 0 term */
+	rval_cache_clean(&tmp_cache);
+	return 0;
+error:
+	rval_cache_clean(&tmp_cache);
+	return -1;
+}
+
+
+
+/** convert a rvalue to another rvalue, of a specific type.
+ *
+ * The result is read-only in most cases (can be a reference
+ * to another rvalue, can be checked by using rv_chg_in_place()) and
+ * _must_ be rval_destroy()'ed.
+ *
+ * @param type - type to convert to
+ * @param v - rvalue to convert
+ * @param c - rval_cache (cached v value if known/filled by another
+ *            function), can be 0 (unknown/not needed)
+ * @return pointer to a rvalue (reference to an existing one or a new
+ *   one, @see rv_chg_in_place() and the above comment) or 0 on error.
+ */
+struct rvalue* rval_convert(struct run_act_ctx* h, struct sip_msg* msg,
+							enum rval_type type, struct rvalue* v,
+							struct rval_cache* c)
+{
+	int i;
+	struct rval_cache tmp_cache;
+	str tmp;
+	struct rvalue* ret;
+	union rval_val val;
+	
+	if (v->type==type){
+		rv_ref(v);
+		return v;
+	}
+	switch(type){
+		case RV_INT:
+			if (unlikely(rval_get_int(h, msg, &i, v, c) < 0))
+				return 0;
+			val.l=i;
+			return rval_new(RV_INT, &val, 0);
+		case RV_STR:
+			rval_cache_init(&tmp_cache);
+			if (unlikely(rval_get_tmp_str(h, msg, &tmp, v, c, &tmp_cache) < 0))
+			{
+				rval_cache_clean(&tmp_cache);
+				return 0;
+			}
+			ret=rval_new_str(&tmp, RV_STR_EXTRA);
+			rval_cache_clean(&tmp_cache);
+			return ret;
+		case RV_NONE:
+		default:
+			BUG("unsupported conversion to type %d\n", type);
+			return 0;
+	}
+	return 0;
+}
+
+
+
+/** integer operation: *res= op v.
+  * @return 0 on succes, \<0 on error
+  */
+inline static int int_intop1(int* res, enum rval_expr_op op, int v)
+{
+	switch(op){
+		case RVE_UMINUS_OP:
+			*res=-v;
+			break;
+		case RVE_BOOL_OP:
+			*res=!!v;
+			break;
+		case RVE_LNOT_OP:
+			*res=!v;
+			break;
+		default:
+			BUG("rv unsupported intop1 %d\n", op);
+			return -1;
+	}
+	return 0;
+}
+
+
+
+/** integer operation: *res= op v.
+  * @return 0 on succes, \<0 on error
+  */
+inline static int int_intop2(int* res, enum rval_expr_op op, int v1, int v2)
+{
+	switch(op){
+		case RVE_PLUS_OP:
+		case RVE_IPLUS_OP:
+			*res=v1+v2;
+			break;
+		case RVE_MINUS_OP:
+			*res=v1-v2;
+			break;
+		case RVE_MUL_OP:
+			*res=v1*v2;
+			break;
+		case RVE_DIV_OP:
+			if (unlikely(v2==0)){
+				ERR("rv div by 0\n");
+				return -1;
+			}
+			*res=v1/v2;
+			break;
+		case RVE_BOR_OP:
+			*res=v1|v2;
+			break;
+		case RVE_BAND_OP:
+			*res=v1&v2;
+			break;
+		case RVE_LAND_OP:
+			*res=v1 && v2;
+			break;
+		case RVE_LOR_OP:
+			*res=v1 || v2;
+			break;
+		case RVE_GT_OP:
+			*res=v1 > v2;
+			break;
+		case RVE_GTE_OP:
+			*res=v1 >= v2;
+			break;
+		case RVE_LT_OP:
+			*res=v1 < v2;
+			break;
+		case RVE_LTE_OP:
+			*res=v1 <= v2;
+			break;
+		case RVE_EQ_OP:
+			*res=v1 == v2;
+			break;
+		case RVE_DIFF_OP:
+			*res=v1 != v2;
+			break;
+		case RVE_CONCAT_OP:
+			*res=0;
+			/* invalid operand for int */
+			return -1;
+		default:
+			BUG("rv unsupported intop %d\n", op);
+			return -1;
+	}
+	return 0;
+}
+
+
+
+inline static int bool_strop2( enum rval_expr_op op, int* res,
+								str* s1, str* s2)
+{
+	if (s1->len!=s2->len)
+		*res= op==RVE_DIFF_OP;
+	else if (memcmp(s1->s, s2->s, s1->len)==0)
+		*res= op==RVE_EQ_OP;
+	else
+		*res= op==RVE_DIFF_OP;
+	return 0;
+}
+
+
+
+/** integer operation: ret= op v (returns a rvalue).
+ * @return rvalue on success, 0 on error
+ */
+inline static struct rvalue* rval_intop1(struct run_act_ctx* h,
+											struct sip_msg* msg,
+											enum rval_expr_op op,
+											struct rvalue* v)
+{
+	struct rvalue* rv2;
+	struct rvalue* ret;
+	int i;
+	
+	i=0;
+	rv2=rval_convert(h, msg, RV_INT, v, 0);
+	if (unlikely(rv2==0)){
+		ERR("rval int conversion failed\n");
+		goto error;
+	}
+	if (unlikely(int_intop1(&i, op, rv2->v.l)<0))
+		goto error;
+	if (rv_chg_in_place(rv2)){
+		ret=rv2;
+		rv_ref(ret);
+	}else if (rv_chg_in_place(v)){
+		ret=v;
+		rv_ref(ret);
+	}else{
+		ret=rval_new(RV_INT, &rv2->v, 0);
+		if (unlikely(ret==0)){
+			ERR("eval out of memory\n");
+			goto error;
+		}
+	}
+	rval_destroy(rv2);
+	ret->v.l=i;
+	return ret;
+error:
+	rval_destroy(rv2);
+	return 0;
+}
+
+
+
+/** integer operation: ret= l op r (returns a rvalue).
+ * @return rvalue on success, 0 on error
+ */
+inline static struct rvalue* rval_intop2(struct run_act_ctx* h,
+											struct sip_msg* msg,
+											enum rval_expr_op op,
+											struct rvalue* l,
+											struct rvalue* r)
+{
+	struct rvalue* rv1;
+	struct rvalue* rv2;
+	struct rvalue* ret;
+	int i;
+
+	rv2=rv1=0;
+	ret=0;
+	if ((rv1=rval_convert(h, msg, RV_INT, l, 0))==0)
+		goto error;
+	if ((rv2=rval_convert(h, msg, RV_INT, r, 0))==0)
+		goto error;
+	if (unlikely(int_intop2(&i, op, rv1->v.l, rv2->v.l)<0))
+		goto error;
+	if (rv_chg_in_place(rv1)){
+		/* try reusing rv1 */
+		ret=rv1;
+		rv_ref(ret);
+	}else if (rv_chg_in_place(rv2)){
+		/* try reusing rv2 */
+		ret=rv2;
+		rv_ref(ret);
+	}else if ((l->type==RV_INT) && (rv_chg_in_place(l))){
+		ret=l;
+		rv_ref(ret);
+	} else if ((r->type==RV_INT) && (rv_chg_in_place(r))){
+		ret=r;
+		rv_ref(ret);
+	}else{
+		ret=rval_new(RV_INT, &rv1->v, 0);
+		if (unlikely(ret==0)){
+			ERR("rv eval out of memory\n");
+			goto error;
+		}
+	}
+	rval_destroy(rv1); 
+	rval_destroy(rv2); 
+	ret->v.l=i;
+	return ret;
+error:
+	rval_destroy(rv1); 
+	rval_destroy(rv2); 
+	return 0;
+}
+
+
+
+/** string add operation: ret= l . r (returns a rvalue).
+ * Can use cached rvalues (c1 & c2).
+ * @return rvalue on success, 0 on error
+ */
+inline static struct rvalue* rval_str_add2(struct run_act_ctx* h,
+											struct sip_msg* msg,
+											struct rvalue* l,
+											struct rval_cache* c1,
+											struct rvalue* r,
+											struct rval_cache* c2
+											)
+{
+	struct rvalue* rv1;
+	struct rvalue* rv2;
+	struct rvalue* ret;
+	str* s1;
+	str* s2;
+	str tmp;
+	short flags;
+	int len;
+	
+	rv2=rv1=0;
+	ret=0;
+	flags=0;
+	s1=0;
+	s2=0;
+	if ((rv1=rval_convert(h, msg, RV_STR, l, c1))==0)
+		goto error;
+	if ((rv2=rval_convert(h, msg, RV_STR, r, c2))==0)
+		goto error;
+	
+	len=rv1->v.s.len + rv2->v.s.len + 1 /* 0 */;
+	
+	if (rv_chg_in_place(rv1) && (rv1->bsize>=len)){
+		/* try reusing rv1 */
+		ret=rv1;
+		rv_ref(ret);
+		s2=&rv2->v.s;
+		if (ret->v.s.s == &ret->buf[0]) s1=0;
+		else{
+			tmp=ret->v.s;
+			flags=ret->flags;
+			ret->flags &= ~RV_CNT_ALLOCED_F;
+			ret->v.s.s=&ret->buf[0];
+			ret->v.s.len=0;
+			s1=&tmp;
+		}
+	}else if (rv_chg_in_place(rv2) && (rv2->bsize>=len)){
+		/* try reusing rv2 */
+		ret=rv2;
+		rv_ref(ret);
+		s1=&rv1->v.s;
+		if (ret->v.s.s == &ret->buf[0]) 
+			s2=&ret->v.s;
+		else{
+			tmp=ret->v.s;
+			flags=ret->flags;
+			ret->flags &= ~RV_CNT_ALLOCED_F;
+			ret->v.s.s=&ret->buf[0];
+			ret->v.s.len=0;
+			s2=&tmp;
+		}
+	}else if ((l->type==RV_STR) && (rv_chg_in_place(l)) && (l->bsize>=len)){
+		ret=l;
+		rv_ref(ret);
+		s2=&rv2->v.s;
+		if (ret->v.s.s == &ret->buf[0]) s1=0;
+		else{
+			tmp=ret->v.s;
+			flags=ret->flags;
+			ret->flags &= ~RV_CNT_ALLOCED_F;
+			ret->v.s.s=&ret->buf[0];
+			ret->v.s.len=0;
+			s1=&tmp;
+		}
+	} else if ((r->type==RV_STR) && (rv_chg_in_place(r) && (r->bsize>=len))){
+		ret=r;
+		rv_ref(ret);
+		s1=&rv1->v.s;
+		if (ret->v.s.s == &ret->buf[0]) 
+			s2=&ret->v.s;
+		else{
+			tmp=ret->v.s;
+			flags=ret->flags;
+			ret->flags &= ~RV_CNT_ALLOCED_F;
+			ret->v.s.s=&ret->buf[0];
+			ret->v.s.len=0;
+			s2=&tmp;
+		}
+	}else{
+		ret=rval_new(RV_STR, &rv1->v, len + RV_STR_EXTRA);
+		if (unlikely(ret==0)){
+			ERR("rv eval out of memory\n");
+			goto error;
+		}
+		s1=0;
+		s2=&rv2->v.s;
+	}
+	/* do the actual copy */
+	memmove(ret->buf+rv1->v.s.len, s2->s, s2->len);
+	if (s1){
+		memcpy(ret->buf, s1->s, s1->len);
+	}
+	ret->v.s.len=rv1->v.s.len+s2->len;
+	ret->v.s.s[ret->v.s.len]=0;
+	/* cleanup if needed */
+	if (flags & RV_CNT_ALLOCED_F)
+		pkg_free(tmp.s);
+	rval_destroy(rv1); 
+	rval_destroy(rv2); 
+	return ret;
+error:
+	rval_destroy(rv1); 
+	rval_destroy(rv2); 
+	return 0;
+}
+
+
+
+/** bool operation on rval evaluated as strings.
+ * Can use cached rvalues (c1 & c2).
+ * @return 0 success, -1 on error
+ */
+inline static int rval_str_lop2(struct run_act_ctx* h,
+						 struct sip_msg* msg,
+						 int* res,
+						 enum rval_expr_op op,
+						 struct rvalue* l,
+						 struct rval_cache* c1,
+						 struct rvalue* r,
+						 struct rval_cache* c2)
+{
+	struct rvalue* rv1;
+	struct rvalue* rv2;
+	int ret;
+	
+	rv2=rv1=0;
+	ret=0;
+	if ((rv1=rval_convert(h, msg, RV_STR, l, c1))==0)
+		goto error;
+	if ((rv2=rval_convert(h, msg, RV_STR, r, c2))==0)
+		goto error;
+	ret=bool_strop2(op, res, &rv1->v.s, &rv2->v.s);
+	rval_destroy(rv1); 
+	rval_destroy(rv2); 
+	return ret;
+error:
+	rval_destroy(rv1); 
+	rval_destroy(rv2); 
+	return 0;
+}
+
+
+
+/** evals an integer expr  to an int.
+ * 
+ *  *res=(int)eval(rve)
+ *  @return 0 on success, \<0 on error
+ */
+int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
+						int* res, struct rval_expr* rve)
+{
+	int i1, i2, ret;
+	struct rval_cache c1;
+	struct rvalue* rv1;
+	struct rvalue* rv2;
+	
+	ret=-1;
+	switch(rve->op){
+		case RVE_RVAL_OP:
+			ret=rval_get_int(h, msg, res,  &rve->left.rval, 0);
+			break;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			if (unlikely(
+					(ret=rval_expr_eval_int(h, msg, &i1, rve->left.rve)) <0) )
+				break;
+			ret=int_intop1(res, rve->op, i1);
+			break;
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_MINUS_OP:
+		case RVE_PLUS_OP:
+		case RVE_IPLUS_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+			if (unlikely(
+					(ret=rval_expr_eval_int(h, msg, &i1, rve->left.rve)) <0) )
+				break;
+			if (unlikely(
+					(ret=rval_expr_eval_int(h, msg, &i2, rve->right.rve)) <0) )
+				break;
+			ret=int_intop2(res, rve->op, i1, i2);
+			break;
+		case RVE_LAND_OP:
+			if (unlikely(
+					(ret=rval_expr_eval_int(h, msg, &i1, rve->left.rve)) <0) )
+				break;
+			if (i1==0){
+				*res=0;
+			}else{
+				if (unlikely( (ret=rval_expr_eval_int(h, msg, &i2,
+										rve->right.rve)) <0) )
+					break;
+				*res=i1 && i2;
+			}
+			ret=0;
+			break;
+		case RVE_LOR_OP:
+			if (unlikely(
+					(ret=rval_expr_eval_int(h, msg, &i1, rve->left.rve)) <0) )
+				break;
+			if (i1){
+				*res=1;
+			}else{
+				if (unlikely( (ret=rval_expr_eval_int(h, msg, &i2,
+										rve->right.rve)) <0) )
+					break;
+				*res=i1 || i2;
+			}
+			ret=0;
+			break;
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+			/* if left is string, eval left & right as string and
+			   use string diff, else eval as int */
+			rval_cache_init(&c1);
+			if (unlikely( (ret=rval_expr_eval_rvint(h, msg, &rv1, &i1,
+													rve->left.rve, &c1))<0)){
+				rval_cache_clean(&c1);
+				break;
+			}
+			if (likely(rv1==0)){
+				/* int */
+				rval_cache_clean(&c1);
+				if (unlikely( (ret=rval_expr_eval_int(h, msg, &i2,
+														rve->right.rve)) <0) )
+					break;
+				ret=int_intop2(res, rve->op, i1, i2);
+			}else{
+				if (unlikely((rv2=rval_expr_eval(h, msg,
+													rve->right.rve))==0)){
+					rval_destroy(rv1);
+					rval_cache_clean(&c1);
+					ret=-1;
+					break;
+				}
+				ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1, rv2, 0);
+				rval_cache_clean(&c1);
+				rval_destroy(rv1);
+				rval_destroy(rv2);
+			}
+			break;
+#if 0
+		case RVE_MATCH_OP:
+				if (unlikely((rv1=rval_expr_eval(h, msg, rve->left.rve))==0)){
+					ret=-1;
+					break;
+				}
+				if (unlikely((rv2=rval_expr_eval(h, msg,
+													rve->right.rve))==0)){
+					rval_destroy(rv1);
+					ret=-1;
+					break;
+				}
+				ret=rval_str_lop2(res, rve->op, rv1, 0, rv2, 0);
+				rval_destroy(rv1);
+				rval_destroy(rv2);
+			break;
+#endif
+		case RVE_CONCAT_OP:
+			*res=0;
+			ret=-1;
+			break;
+		case RVE_NONE_OP:
+		/*default:*/
+			BUG("invalid rval int expression operation %d\n", rve->op);
+			ret=-1;
+	};
+	return ret;
+}
+
+
+
+/** evals a rval expr. into an int or another rv(str).
+ * WARNING: rv result (rv_res) must be rval_destroy()'ed if non-null
+ * (it might be a reference to another rval). The result can be 
+ * modified only if rv_chg_in_place() returns true.
+ * @result  0 on success, -1 on error,  sets *res_rv or *res_i.
+ */
+int rval_expr_eval_rvint(			   struct run_act_ctx* h,
+									   struct sip_msg* msg,
+									   struct rvalue** res_rv,
+									   int* res_i,
+									   struct rval_expr* rve,
+									   struct rval_cache* cache
+									   )
+{
+	struct rvalue* rv1;
+	struct rvalue* rv2;
+	struct rval_cache c1; /* local cache */
+	int ret;
+	int r, i, j;
+	enum rval_type type;
+	
+	rv1=0;
+	rv2=0;
+	switch(rve->op){
+		case RVE_RVAL_OP:
+			rv1=&rve->left.rval;
+			rv_ref(rv1);
+			type=rval_get_btype(h, msg, rv1, cache);
+			if (type==RV_INT){
+					r=rval_get_int(h, msg, res_i, rv1, cache);
+					if (unlikely(r<0)){
+						ERR("rval expression evaluation failed\n");
+						goto error;
+					}
+					*res_rv=0;
+					ret=0;
+			}else{
+				/* RV_STR, RV_PVAR, RV_AVP a.s.o => return rv1 and the 
+				   cached resolved value in cache*/
+					*res_rv=rv1;
+					rv_ref(rv1);
+					ret=0;
+			}
+			break;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+		case RVE_MINUS_OP:
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_IPLUS_OP:
+			/* operator forces integer type */
+			ret=rval_expr_eval_int(h, msg, res_i, rve);
+			*res_rv=0;
+			break;
+		case RVE_PLUS_OP:
+			rval_cache_init(&c1);
+			r=rval_expr_eval_rvint(h, msg, &rv1, &i, rve->left.rve, &c1);
+			if (unlikely(r<0)){
+				ERR("rval expression evaluation failed\n");
+				rval_cache_clean(&c1);
+				goto error;
+			}
+			if (rv1==0){
+				if (unlikely((r=rval_expr_eval_int(h, msg, &j,
+														rve->right.rve))<0)){
+						ERR("rval expression evaluation failed\n");
+						rval_cache_clean(&c1);
+						goto error;
+				}
+				int_intop2(res_i, rve->op, i, j);
+				*res_rv=0;
+			}else{
+				rv2=rval_expr_eval(h, msg, rve->right.rve);
+				if (unlikely(rv2==0)){
+					ERR("rval expression evaluation failed\n");
+					rval_cache_clean(&c1);
+					goto error;
+				}
+				*res_rv=rval_str_add2(h, msg, rv1, &c1, rv2, 0);
+				ret=-(*res_rv==0);
+			}
+			rval_cache_clean(&c1);
+			break;
+		case RVE_CONCAT_OP:
+			*res_rv=rval_expr_eval(h, msg, rve);
+			ret=-(*res_rv==0);
+			break;
+		case RVE_NONE_OP:
+		/*default:*/
+			BUG("invalid rval expression operation %d\n", rve->op);
+			goto error;
+	};
+	rval_destroy(rv1);
+	rval_destroy(rv2);
+	return ret;
+error:
+	rval_destroy(rv1);
+	rval_destroy(rv2);
+	return -1;
+}
+
+
+
+/** evals a rval expr..
+ * WARNING: result must be rval_destroy()'ed if non-null (it might be
+ * a reference to another rval). The result can be modified only
+ * if rv_chg_in_place() returns true.
+ * @result rvalue on success, 0 on error
+ */
+struct rvalue* rval_expr_eval(struct run_act_ctx* h, struct sip_msg* msg,
+								struct rval_expr* rve)
+{
+	struct rvalue* rv1;
+	struct rvalue* rv2;
+	struct rvalue* ret;
+	struct rval_cache c1;
+	union rval_val v;
+	int r, i, j;
+	enum rval_type type;
+	
+	rv1=0;
+	rv2=0;
+	switch(rve->op){
+		case RVE_RVAL_OP:
+			rv_ref(&rve->left.rval);
+			return &rve->left.rval;
+			break;
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+		case RVE_MINUS_OP:
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_IPLUS_OP:
+			/* operator forces integer type */
+			r=rval_expr_eval_int(h, msg, &i, rve);
+			if (likely(r==0)){
+				v.l=i;
+				ret=rval_new(RV_INT, &v, 0);
+				if (unlikely(ret==0)){
+					ERR("rv eval int expression: out of memory\n");
+					goto error;
+				}
+				return ret;
+			}else{
+				ERR("rval expression evaluation failed\n");
+				goto error;
+			}
+			break;
+		case RVE_PLUS_OP:
+			rv1=rval_expr_eval(h, msg, rve->left.rve);
+			if (unlikely(rv1==0)){
+				ERR("rval expression evaluation failed\n");
+				goto error;
+			}
+			rval_cache_init(&c1);
+			type=rval_get_btype(h, msg, rv1, &c1);
+			switch(type){
+				case RV_INT:
+					if (unlikely((r=rval_get_int(h, msg, &i, rv1, &c1))<0)){
+						rval_cache_clean(&c1);
+						ERR("rval expression evaluation failed\n");
+						goto error;
+					}
+					if (unlikely((r=rval_expr_eval_int(h, msg, &j,
+														rve->right.rve))<0)){
+						rval_cache_clean(&c1);
+						ERR("rval expression evaluation failed\n");
+						goto error;
+					}
+					int_intop2(&r, rve->op, i, j);
+					if (rv_chg_in_place(rv1)){
+						rv1->v.l=r;
+						ret=rv1;
+						rv_ref(ret);
+					}else{
+						v.l=r;
+						ret=rval_new(RV_INT, &v, 0);
+						if (unlikely(ret==0)){
+							rval_cache_clean(&c1);
+							ERR("rv eval int expression: out of memory\n");
+							goto error;
+						}
+					}
+					break;
+				case RV_STR:
+					rv2=rval_expr_eval(h, msg, rve->right.rve);
+					if (unlikely(rv2==0)){
+						ERR("rval expression evaluation failed\n");
+						rval_cache_clean(&c1);
+						goto error;
+					}
+					ret=rval_str_add2(h, msg, rv1, &c1, rv2, 0);
+					break;
+				default:
+					BUG("rv unsupported basic type %d\n", type);
+				case RV_NONE:
+					rval_cache_clean(&c1);
+					goto error;
+			}
+			rval_cache_clean(&c1);
+			break;
+		case RVE_CONCAT_OP:
+			rv1=rval_expr_eval(h, msg, rve->left.rve);
+			if (unlikely(rv1==0)){
+				ERR("rval expression evaluation failed\n");
+				goto error;
+			}
+			rv2=rval_expr_eval(h, msg, rve->right.rve);
+			if (unlikely(rv2==0)){
+				ERR("rval expression evaluation failed\n");
+				goto error;
+			}
+			ret=rval_str_add2(h, msg, rv1, 0, rv2, 0);
+			break;
+		case RVE_NONE_OP:
+		/*default:*/
+			BUG("invalid rval expression operation %d\n", rve->op);
+			goto error;
+	};
+	rval_destroy(rv1);
+	rval_destroy(rv2);
+	return ret;
+error:
+	rval_destroy(rv1);
+	rval_destroy(rv2);
+	return 0;
+}
+
+
+
+/** evals a rval expr and always returns a new rval.
+ * like rval_expr_eval, but always returns a new rvalue (never a reference
+ * to an exisiting one).
+ * WARNING: result must be rval_destroy()'ed if non-null (it might be
+ * a reference to another rval). The result can be modified only
+ * if rv_chg_in_place() returns true.
+ * @result rvalue on success, 0 on error
+ */
+struct rvalue* rval_expr_eval_new(struct run_act_ctx* h, struct sip_msg* msg,
+								struct rval_expr* rve)
+{
+	struct rvalue* ret;
+	struct rvalue* rv;
+	
+	ret=rval_expr_eval(h, msg, rve);
+	if (ret && !rv_chg_in_place(ret)){
+		rv=ret;
+		/* create a new rv */
+		ret=rval_new(rv->type, &rv->v, 0);
+		rval_destroy(rv);
+	}
+	return ret;
+}
+
+
+
+/** create a RVE_RVAL_OP rval_expr, containing a single rval of the given type.
+ *
+ * @param rv_type - rval type
+ * @param val     - rval value
+ * @param pos     - config position
+ * @return new pkg_malloc'ed rval_expr or 0 on error.
+ */
+struct rval_expr* mk_rval_expr_v(enum rval_type rv_type, void* val, 
+									struct cfg_pos* pos)
+{
+	struct rval_expr* rve;
+	union rval_val v;
+	str* s;
+	int flags;
+	
+	rve=pkg_malloc(sizeof(*rve));
+	if (rve==0) 
+		return 0;
+	memset(rve, sizeof(*rve), 0);
+	flags=0;
+	switch(rv_type){
+		case RV_INT:
+			v.l=(long)val;
+			break;
+		case RV_STR:
+			s=(str*)val;
+			v.s.s=pkg_malloc(s->len+1 /*0*/);
+			if (v.s.s==0){
+				ERR("memory allocation failure\n");
+				return 0;
+			}
+			v.s.len=s->len;
+			memcpy(v.s.s, s->s, s->len);
+			v.s.s[s->len]=0;
+			flags=RV_CNT_ALLOCED_F;
+			break;
+		case RV_AVP:
+			v.avps=*(avp_spec_t*)val;
+			break;
+		case RV_PVAR:
+			v.pvs=*(pv_spec_t*)val;
+			break;
+		case RV_SEL:
+			v.sel=*(select_t*)val;
+			break;
+		case RV_BEXPR:
+			v.bexpr=(struct expr*)val;
+			break;
+		case RV_ACTION_ST:
+			v.action=(struct action*)val;
+			break;
+		default:
+			BUG("unsupported rv type %d\n", rv_type);
+			pkg_free(rve);
+			return 0;
+	}
+	rval_init(&rve->left.rval, rv_type, &v, flags);
+	rve->op=RVE_RVAL_OP;
+	if (pos) rve->fpos=*pos;
+	return rve;
+}
+
+
+
+/** create a unary op. rval_expr..
+ * ret= op rve1
+ * @param op   - rval expr. unary operator
+ * @param rve1 - rval expr. on which the operator will act.
+ * @return new pkg_malloc'ed rval_expr or 0 on error.
+ */
+struct rval_expr* mk_rval_expr1(enum rval_expr_op op, struct rval_expr* rve1,
+								struct cfg_pos* pos)
+{
+	struct rval_expr* ret;
+	
+	switch(op){
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			break;
+		default:
+			BUG("unsupported unary operator %d\n", op);
+			return 0;
+	}
+	ret=pkg_malloc(sizeof(*ret));
+	if (ret==0) 
+		return 0;
+	memset(ret, sizeof(*ret), 0);
+	ret->op=op;
+	ret->left.rve=rve1;
+	if (pos) ret->fpos=*pos;
+	return ret;
+}
+
+
+
+/** create a rval_expr. from 2 other rval exprs, using op.
+ * ret = rve1 op rve2
+ * @param op   - rval expr. operator
+ * @param rve1 - rval expr. on which the operator will act.
+ * @param rve2 - rval expr. on which the operator will act.
+ * @return new pkg_malloc'ed rval_expr or 0 on error.
+ */
+struct rval_expr* mk_rval_expr2(enum rval_expr_op op, struct rval_expr* rve1,
+													  struct rval_expr* rve2,
+													  struct cfg_pos* pos)
+{
+	struct rval_expr* ret;
+	
+	switch(op){
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_MINUS_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_PLUS_OP:
+		case RVE_IPLUS_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_CONCAT_OP:
+			break;
+		default:
+			BUG("unsupported operator %d\n", op);
+			return 0;
+	}
+	ret=pkg_malloc(sizeof(*ret));
+	if (ret==0) 
+		return 0;
+	memset(ret, sizeof(*ret), 0);
+	ret->op=op;
+	ret->left.rve=rve1;
+	ret->right.rve=rve2;
+	if (pos) ret->fpos=*pos;
+	return ret;
+}
+
+
+
+/** returns true if the operator is associative. */
+static int rve_op_is_assoc(enum rval_expr_op op)
+{
+	switch(op){
+		case RVE_NONE_OP:
+		case RVE_RVAL_OP:
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			/* one operand expression => cannot be assoc. */
+			return 0;
+		case RVE_DIV_OP:
+		case RVE_MINUS_OP:
+			return 0;
+		case RVE_PLUS_OP:
+		case RVE_IPLUS_OP:
+		case RVE_CONCAT_OP:
+		case RVE_MUL_OP:
+		case RVE_BAND_OP:
+		case RVE_BOR_OP:
+			return 1;
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+			return 1;
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+			return 0;
+	}
+	return 0;
+}
+
+
+
+/** returns true if the operator is commutative. */
+static int rve_op_is_commutative(enum rval_expr_op op, enum rval_type type)
+{
+	switch(op){
+		case RVE_NONE_OP:
+		case RVE_RVAL_OP:
+		case RVE_UMINUS_OP:
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			/* one operand expression => cannot be commut. */
+			return 0;
+		case RVE_DIV_OP:
+		case RVE_MINUS_OP:
+			return 0;
+		case RVE_PLUS_OP:
+			return type==RV_INT; /* commutative only for INT*/
+		case RVE_IPLUS_OP:
+		case RVE_MUL_OP:
+		case RVE_BAND_OP:
+		case RVE_BOR_OP:
+			return 1;
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+			return 1;
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_CONCAT_OP:
+			return 0;
+	}
+	return 0;
+}
+
+
+#if 0
+/** returns true if the rval expr can be optimized to an int.
+ *  (if left & right are leafs (RVE_RVAL_OP) and both of them are
+ *   ints return true, else false)
+ *  @return 0 or 1
+ */
+static int rve_can_optimize_int(struct rval_expr* rve)
+{
+	if (scr_opt_lev<1)
+		return 0;
+	if (rve->op == RVE_RVAL_OP)
+		return 0;
+	if (rve->left.rve->op != RVE_RVAL_OP)
+		return 0;
+	if (rve->left.rve->left.rval.type!=RV_INT)
+		return 0;
+	if (rve->right.rve){
+		if  (rve->right.rve->op != RVE_RVAL_OP)
+			return 0;
+		if (rve->right.rve->left.rval.type!=RV_INT)
+			return 0;
+	}
+	DBG("rve_can_optimize_int: left %d, right %d\n", 
+			rve->left.rve->op, rve->right.rve?rve->right.rve->op:0);
+	return 1;
+}
+
+
+
+/** returns true if the rval expr can be optimized to a str.
+ *  (if left & right are leafs (RVE_RVAL_OP) and both of them are
+ *   str or left is str and right is int return true, else false)
+ *  @return 0 or 1
+ */
+static int rve_can_optimize_str(struct rval_expr* rve)
+{
+	if (scr_opt_lev<1)
+		return 0;
+	if (rve->op == RVE_RVAL_OP)
+		return 0;
+	DBG("rve_can_optimize_str: left %d, right %d\n", 
+			rve->left.rve->op, rve->right.rve?rve->right.rve->op:0);
+	if (rve->left.rve->op != RVE_RVAL_OP)
+		return 0;
+	if (rve->left.rve->left.rval.type!=RV_STR)
+		return 0;
+	if (rve->right.rve){
+		if  (rve->right.rve->op != RVE_RVAL_OP)
+			return 0;
+		if ((rve->right.rve->left.rval.type!=RV_STR) && 
+				(rve->right.rve->left.rval.type!=RV_INT))
+			return 0;
+	}
+	return 1;
+}
+#endif
+
+
+
+static int fix_rval(struct rvalue* rv)
+{
+	DBG("RV fixing type %d\n", rv->type);
+	switch(rv->type){
+		case RV_INT:
+			/*nothing to do*/
+			DBG("RV is int: %d\n", (int)rv->v.l);
+			return 0;
+		case RV_STR:
+			/*nothing to do*/
+			DBG("RV is str: \"%s\"\n", rv->v.s.s);
+			return 0;
+		case RV_BEXPR:
+			return fix_expr(rv->v.bexpr);
+		case RV_ACTION_ST:
+			return fix_actions(rv->v.action);
+		case RV_SEL:
+			if (resolve_select(&rv->v.sel)<0){
+				BUG("Unable to resolve select\n");
+				print_select(&rv->v.sel);
+			}
+			return 0;
+		case RV_AVP:
+			/* nothing to do, resolved at runtime */
+			return 0;
+		case RV_PVAR:
+			/* nothing to do, resolved at parsing time */
+			return 0;
+		case RV_NONE:
+			BUG("uninitialized rvalue\n");
+			return -1;
+	}
+	BUG("unknown rvalue type %d\n", rv->type);
+	return -1;
+}
+
+
+
+static int rve_replace_with_ct_rv(struct rval_expr* rve, struct rvalue* rv)
+{
+	enum rval_type type;
+	int flags;
+	int i;
+	union rval_val v;
+	
+	type=rv->type;
+	flags=0;
+	if (rv->type==RV_INT){
+		if (rval_get_int(0, 0, &i, rv, 0)!=0){
+			BUG("unexpected int evaluation failure\n");
+			return -1;
+		}
+		v.l=i;
+	}else if(rv->type==RV_STR){
+		if (rval_get_str(0, 0, &v.s, rv, 0)<0){
+			BUG("unexpected str evaluation failure\n");
+			return -1;
+		}
+		flags=RV_CNT_ALLOCED_F;
+	}else{
+		BUG("unknown constant expression type %d\n", rv->type);
+		return -1;
+	}
+	if (rve->op!=RVE_RVAL_OP){
+		rve_destroy(rve->left.rve);
+		if (rve_op_unary(rve->op)==0)
+			rve_destroy(rve->right.rve);
+	}else
+		rval_destroy(&rve->left.rval);
+	rval_init(&rve->left.rval, type, &v, flags);
+	rval_init(&rve->right.rval, RV_NONE, 0, 0);
+	rve->op=RVE_RVAL_OP;
+	return 0;
+}
+
+
+
+/** optimize op($v, 0) or op($v, 1).
+ * Note: internal use only from rve_optimize
+ * It should be called after ct optimization, for non-contant
+ *  expressions (the left or right side is not constant).
+ * @return 1 on success (rve was changed), 0 on failure and -1 on error
+ */
+static int rve_opt_01(struct rval_expr* rve, enum rval_type rve_type)
+{
+	struct rvalue* rv;
+	struct rval_expr* ct_rve;
+	struct rval_expr* v_rve;
+	int i;
+	int ret;
+	enum rval_expr_op op;
+	struct cfg_pos pos;
+	int right; /* debugging msg */
+	
+	rv=0;
+	ret=0;
+	right=0;
+	
+	if (rve_is_constant(rve->right.rve)){
+		ct_rve=rve->right.rve;
+		v_rve=rve->left.rve;
+		right=1;
+	}else if (rve_is_constant(rve->left.rve)){
+		ct_rve=rve->left.rve;
+		v_rve=rve->right.rve;
+		right=0;
+	}else
+		return 0; /* op($v, $w) */
+	
+	/* rval_expr_eval_new() instead of rval_expr_eval() to avoid
+	   referencing a ct_rve->left.rval if ct_rve is a rval, which
+	   would prevent rve_destroy(ct_rve) from working */
+	if ((rv=rval_expr_eval_new(0, 0, ct_rve))==0){
+		ERR("optimization failure, bad expression\n");
+		goto error;
+	}
+	op=rve->op;
+	if (rv->type==RV_INT){
+		i=rv->v.l;
+		switch(op){
+			case RVE_MUL_OP:
+				if (i==0){
+					/* $v *  0 -> 0
+					 *  0 * $v -> 0 */
+					if (rve_replace_with_ct_rv(rve, rv)<0)
+						goto error;
+					ret=1;
+				}else if (i==1){
+					/* $v *  1 -> $v
+					 *  1 * $v -> $v */
+					rve_destroy(ct_rve);
+					pos=rve->fpos;
+					*rve=*v_rve; /* replace current expr. with $v */
+					rve->fpos=pos;
+					pkg_free(v_rve);/* rve_destroy(v_rve) would free
+									   everything*/
+					ret=1;
+				}
+				break;
+			case RVE_DIV_OP:
+				if (i==0){
+					if (ct_rve==rve->left.rve){
+						/* 0 / $v -> 0 */
+						if (rve_replace_with_ct_rv(rve, rv)<0)
+							goto error;
+						ret=1;
+					}else{
+						/* $v / 0 */
+						ERR("RVE divide by 0 at %d,%d\n",
+								ct_rve->fpos.s_line, ct_rve->fpos.s_col);
+					}
+				}else if (i==1){
+					if (ct_rve==rve->right.rve){
+						/* $v / 1 -> $v */
+						rve_destroy(ct_rve);
+						pos=rve->fpos;
+						*rve=*v_rve; /* replace current expr. with $v */
+						rve->fpos=pos;
+						pkg_free(v_rve);/* rve_destroy(v_rve) would free
+										   everything*/
+						ret=1;
+					}
+				}
+				break;
+			case RVE_MINUS_OP:
+				if (i==0){
+					if (ct_rve==rve->right.rve){
+						/* $v - 0 -> $v */
+						rve_destroy(ct_rve);
+						pos=rve->fpos;
+						*rve=*v_rve; /* replace current expr. with $v */
+						rve->fpos=pos;
+						pkg_free(v_rve);/* rve_destroy(v_rve) would free
+										   everything*/
+						ret=1;
+					}
+					/* ? 0 - $v -> -($v)  ? */
+				}
+				break;
+			case RVE_BAND_OP:
+				if (i==0){
+					/* $v &  0 -> 0
+					 *  0 & $v -> 0 */
+					if (rve_replace_with_ct_rv(rve, rv)<0)
+						goto error;
+					ret=1;
+				}
+				/* no 0xffffff optimization for now (haven't decide on
+				   the number of bits ) */
+				break;
+			case RVE_BOR_OP:
+				if (i==0){
+					/* $v |  0 -> $v
+					 *  0 | $v -> $v */
+					rve_destroy(ct_rve);
+					pos=rve->fpos;
+					*rve=*v_rve; /* replace current expr. with $v */
+					rve->fpos=pos;
+					pkg_free(v_rve);/* rve_destroy(v_rve) would free
+									   everything*/
+					ret=1;
+				}
+				break;
+			case RVE_LAND_OP:
+				if (i==0){
+					/* $v &&  0 -> 0
+					 *  0 && $v -> 0 */
+					if (rve_replace_with_ct_rv(rve, rv)<0)
+						goto error;
+					ret=1;
+				}else if (i==1){
+					/* $v &&  1 -> $v
+					 *  1 && $v -> $v */
+					rve_destroy(ct_rve);
+					pos=rve->fpos;
+					*rve=*v_rve; /* replace current expr. with $v */
+					rve->fpos=pos;
+					pkg_free(v_rve);/* rve_destroy(v_rve) would free
+									   everything*/
+					ret=1;
+				}
+				break;
+			case RVE_LOR_OP:
+				if (i==1){
+					/* $v ||  1 -> 1
+					 *  1 || $v -> 1 */
+					if (rve_replace_with_ct_rv(rve, rv)<0)
+						goto error;
+					ret=1;
+				}else if (i==0){
+					/* $v ||  0 -> $v
+					 *  0 && $v -> $v */
+					rve_destroy(ct_rve);
+					pos=rve->fpos;
+					*rve=*v_rve; /* replace current expr. with $v */
+					rve->fpos=pos;
+					pkg_free(v_rve);/* rve_destroy(v_rve) would free
+									   everything*/
+					ret=1;
+				}
+				break;
+			case RVE_PLUS_OP:
+			case RVE_IPLUS_OP:
+				/* we must make sure that this is an int PLUS
+				   (because "foo"+0 is valid => "foo0") */
+				if ((i==0) && ((op==RVE_IPLUS_OP)||(rve_type==RV_INT))){
+					/* $v +  0 -> $v
+					 *  0 + $v -> $v */
+					rve_destroy(ct_rve);
+					pos=rve->fpos;
+					*rve=*v_rve; /* replace current expr. with $v */
+					rve->fpos=pos;
+					pkg_free(v_rve);/* rve_destroy(v_rve) would free
+									   everything*/
+					ret=1;
+				}
+				break;
+			default:
+				/* do nothing */
+				break;
+		}
+		/* debugging messages */
+		if (ret==1){
+			if (right){
+				if ((rve->op==RVE_RVAL_OP) && (rve->left.rval.type==RV_INT))
+					DBG("FIXUP RVE: (%d,%d-%d,%d) optimized"
+							" op%d($v, %d) -> %d\n", 
+							rve->fpos.s_line, rve->fpos.s_col,
+							rve->fpos.e_line, rve->fpos.e_col,
+							op, i, (int)rve->left.rval.v.l);
+				else
+					DBG("FIXUP RVE: (%d,%d-%d,%d) optimized"
+							" op%d($v, %d) -> $v\n",
+							rve->fpos.s_line, rve->fpos.s_col,
+							rve->fpos.e_line, rve->fpos.e_col,
+							op, i);
+			}else{
+				if ((rve->op==RVE_RVAL_OP) && (rve->left.rval.type==RV_INT))
+					DBG("FIXUP RVE: (%d,%d-%d,%d) optimized"
+							" op%d(%d, $v) -> %d\n", 
+							rve->fpos.s_line, rve->fpos.s_col,
+							rve->fpos.e_line, rve->fpos.e_col,
+							op, i, (int)rve->left.rval.v.l);
+				else
+					DBG("FIXUP RVE: (%d,%d-%d,%d) optimized"
+							" op%d(%d, $v) -> $v\n",
+							rve->fpos.s_line, rve->fpos.s_col,
+							rve->fpos.e_line, rve->fpos.e_col,
+							op, i);
+			}
+		}
+	}else if (rv->type==RV_STR){
+		switch(op){
+			case RVE_CONCAT_OP:
+				if (rv->v.s.len==0){
+					/* $v . "" -> $v 
+					   "" . $v -> $v */
+					rve_destroy(ct_rve);
+					pos=rve->fpos;
+					*rve=*v_rve; /* replace current expr. with $v */
+					rve->fpos=pos;
+					pkg_free(v_rve);/* rve_destroy(v_rve) would free
+									   everything*/
+					ret=1;
+				}
+				break;
+			default:
+				break;
+		}
+	/* no optimization for generic RVE_PLUS_OP for now, only for RVE_CONCAT_OP
+	   (We could optimize $v + "" or ""+$v, but this ""+$v is a way
+	    to force convert $v to str , it might mess up type checking
+	    (e.g. errors w/o optimization and no errors with) and it brings
+	    a very small benefit anyway (it's unlikely we'll see a lot of
+	    "")
+	*/
+	}
+	if (rv) rval_destroy(rv);
+	return ret;
+error:
+	if (rv) rval_destroy(rv);
+	return -1;
+}
+
+
+
+/** tries to optimize a rval_expr. */
+static int rve_optimize(struct rval_expr* rve)
+{
+	int ret;
+	struct rvalue* rv;
+	struct rvalue* trv; /* used only for DBG() */
+	enum rval_expr_op op;
+	int flags;
+	struct rval_expr tmp_rve;
+	enum rval_type type, l_type;
+	struct rval_expr* bad_rve;
+	enum rval_type bad_type, exp_type;
+	
+	ret=0;
+	flags=0;
+	rv=0;
+	if (scr_opt_lev<1)
+		return 0;
+	if (rve->op == RVE_RVAL_OP) /* if rval, nothing to do */
+		return 0;
+	if (rve_is_constant(rve)){
+		if ((rv=rval_expr_eval(0, 0, rve))==0){
+			ERR("optimization failure, bad expression\n");
+			goto error;
+		}
+		op=rve->op;
+		if (rve_replace_with_ct_rv(rve, rv)<0)
+			goto error;
+		rval_destroy(rv);
+		rv=0;
+		trv=&rve->left.rval;
+		if (trv->type==RV_INT)
+			DBG("FIXUP RVE: optimized constant int rve (old op %d) to %d\n",
+					op, (int)trv->v.l);
+		else if (trv->type==RV_STR)
+			DBG("FIXUP RVE: optimized constant str rve (old op %d) to"
+					" \"%.*s\"\n", op, trv->v.s.len, trv->v.s.s);
+		ret=1;
+	}else{
+		/* expression is not constant */
+		/* if unary => nothing to do */
+		if (rve_op_unary(rve->op))
+			return rve_optimize(rve->left.rve);
+		rve_optimize(rve->left.rve);
+		rve_optimize(rve->right.rve);
+		if (!rve_check_type(&type, rve, &bad_rve, &bad_type, &exp_type)){
+			ERR("optimization failure, type mismatch in expression (%d,%d), "
+					"type %s, but expected %s\n",
+					bad_rve->fpos.s_line, bad_rve->fpos.s_col,
+					rval_type_name(bad_type), rval_type_name(exp_type));
+			return 0;
+		}
+		/* $v - a => $v + (-a)  (easier to optimize)*/
+		if ((rve->op==RVE_MINUS_OP) && (rve_is_constant(rve->right.rve))){
+			if ((rv=rval_expr_eval(0, 0, rve->right.rve))==0){
+				ERR("optimization failure, bad expression\n");
+				goto error;
+			}
+			if (rv->type==RV_INT){
+				rv->v.l=-rv->v.l;
+				if (rve_replace_with_ct_rv(rve->right.rve, rv)<0)
+					goto error;
+				rve->op=RVE_IPLUS_OP;
+				DBG("FIXUP RVE: optimized $v - a into $v + (%d)\n",
+								(int)rve->right.rve->left.rval.v.l);
+			}
+			rval_destroy(rv);
+			rv=0;
+		}
+		
+		/* e1 PLUS_OP e2 -> change op if we know e1 basic type */
+		if (rve->op==RVE_PLUS_OP){
+			l_type=rve_guess_type(rve->left.rve);
+			if (l_type==RV_INT){
+				rve->op=RVE_IPLUS_OP;
+				DBG("FIXUP RVE (%d,%d-%d,%d): changed + into interger plus\n",
+						rve->fpos.s_line, rve->fpos.s_col,
+						rve->fpos.e_line, rve->fpos.e_col);
+			}else if (l_type==RV_STR){
+				rve->op=RVE_CONCAT_OP;
+				DBG("FIXUP RVE (%d,%d-%d,%d): changed + into string concat\n",
+						rve->fpos.s_line, rve->fpos.s_col,
+						rve->fpos.e_line, rve->fpos.e_col);
+			}
+		}
+		
+		/* $v * 0 => 0; $v * 1 => $v (for *, /, &, |, &&, ||, +, -) */
+		if (rve_opt_01(rve, type)==1){
+			/* success, rve was changed => return now
+			  (since this is recursively invoked the "new" rve
+			   is already optimized) */
+			ret=1;
+			goto end;
+		}
+		
+		/* op(op($v, a), b) => op($v, op(a,b)) */
+		if (rve_is_constant(rve->right.rve)){
+			/* op1(op2(...), b) */
+			if ((rve->op==rve->left.rve->op) && rve_op_is_assoc(rve->op)){
+				/* op(op(...), b) */
+				if (rve_is_constant(rve->left.rve->right.rve)){
+					/* op(op($v, a), b) => op($v, op(a, b)) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve->right.rve;
+					tmp_rve.right.rve=rve->right.rve;
+					/* hack for RVE_PLUS_OP which can work on string, ints
+					   or a combination of them */
+					if ((rve->op==RVE_PLUS_OP) &&
+						(rve_guess_type(tmp_rve.left.rve)!=RV_STR)){
+						DBG("RVE optimization failed: cannot optimize"
+								" +(+($v, a), b) when typeof(a)==INT\n");
+						return 0;
+					}
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op($v, rv) */
+					if (rve_replace_with_ct_rv(rve->right.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					rve_destroy(tmp_rve.left.rve);
+					rve->left.rve=rve->left.rve->left.rve;
+					trv=&rve->right.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(op($v, a), b)"
+								" with op($v, %d); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(op($v, a), b)"
+								" with op($v, \"%.*s\"); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}else if (rve_is_constant(rve->left.rve->left.rve) &&
+							rve_op_is_commutative(rve->op, type)){
+					/* op(op(a, $v), b) => op(op(a, b), $v) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve->left.rve;
+					tmp_rve.right.rve=rve->right.rve;
+					/* no need for the RVE_PLUS_OP hack, all the bad
+					   cases are caught by rve_op_is_commutative()
+					   (in this case type will be typeof(a)) => ok only if
+					   typeof(a) is int) */
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op(rv, $v) */
+					rve_destroy(rve->right.rve);
+					rve->right.rve=rve->left.rve->right.rve;
+					rve->left.rve->right.rve=0;
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					trv=&rve->left.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(op(a, $v), b)"
+								" with op(%d, $v); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(op(a, $v), b)"
+								" with op(\"%.*s\", $v); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}
+				/* op(op($v, $w),b) => can't optimize */
+			}
+			/* op1(op2(...), b) and op1!=op2 or op is non assoc.
+			   => can't optimize */
+		}else if (rve_is_constant(rve->left.rve)){
+			/* op1(a, op2(...)) */
+			if ((rve->op==rve->right.rve->op) && rve_op_is_assoc(rve->op)){
+				/* op(a, op(...)) */
+				if (rve_is_constant(rve->right.rve->right.rve) &&
+						rve_op_is_commutative(rve->op, type)){
+					/* op(a, op($v, b)) => op(op(a, b), $v) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve;
+					tmp_rve.right.rve=rve->right.rve->right.rve;
+					/* no need for the RVE_PLUS_OP hack, all the bad
+					   cases are caught by rve_op_is_commutative()
+					   (in this case type will be typeof(a)) => ok only if
+					   typeof(a) is int) */
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op(rv, $v) */
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					rve_destroy(tmp_rve.right.rve);
+					rve->right.rve=rve->right.rve->left.rve;
+					trv=&rve->left.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(a, op($v, b))"
+								" with op(%d, $v); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(a, op($v, b))"
+								" with op(\"%.*s\", $v); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}else if (rve_is_constant(rve->right.rve->left.rve)){
+					/* op(a, op(b, $v)) => op(op(a, b), $v) */
+					/* rv= op(a, b) */
+					tmp_rve.op=rve->op;
+					tmp_rve.left.rve=rve->left.rve;
+					tmp_rve.right.rve=rve->right.rve->left.rve;
+					/* hack for RVE_PLUS_OP which can work on string, ints
+					   or a combination of them */
+					if ((rve->op==RVE_PLUS_OP) &&
+						(rve_guess_type(tmp_rve.left.rve) != 
+						 	rve_guess_type(tmp_rve.right.rve))){
+						DBG("RVE optimization failed: cannot optimize"
+								" +(a, +(b, $v)) when typeof(a)!=typeof(b)\n");
+						return 0;
+					}
+					if ((rv=rval_expr_eval(0, 0, &tmp_rve))==0){
+						ERR("optimization failure, bad expression\n");
+						goto error;
+					}
+					/* op(rv, $v) */
+					if (rve_replace_with_ct_rv(rve->left.rve, rv)<0)
+						goto error;
+					rval_destroy(rv);
+					rv=0;
+					rve_destroy(tmp_rve.right.rve);
+					rve->right.rve=rve->right.rve->right.rve;
+					trv=&rve->left.rve->left.rval;
+					if (trv->type==RV_INT)
+						DBG("FIXUP RVE: optimized int rve: op(a, op(b, $v))"
+								" with op(%d, $v); op=%d\n",
+								(int)trv->v.l, rve->op);
+					else if (trv->type==RV_STR)
+						DBG("FIXUP RVE: optimized str rve op(a, op(b, $v))"
+								" with op(\"%.*s\", $v); op=%d\n",
+								trv->v.s.len, trv->v.s.s, rve->op);
+					ret=1;
+				}
+				/* op(a, op($v, $w)) => can't optimize */
+			}
+			/* op1(a, op2(...)) and op1!=op2 or op is non assoc.
+			   => can't optimize */
+		}
+		/* op(op($v,a), op($w,b)) => no optimizations for now (TODO) */
+	}
+end:
+	return ret;
+error:
+	if (rv) rval_destroy(rv);
+	return -1;
+}
+
+
+
+/** fix a rval_expr.
+ * fixes action, bexprs, resolves selects, pvars and
+ * optimizes simple sub expressions (e.g. 1+2).
+ * It might modify *p.
+ *
+ * @param p - double pointer to a rval_expr (might be changed to a new one)
+ * @return 0 on success, <0 on error (modifies also *p)
+ */
+int fix_rval_expr(void** p)
+{
+	struct rval_expr** prve;
+	struct rval_expr* rve;
+	int ret;
+	
+	prve=(struct rval_expr**)p;
+	rve=*prve;
+	
+	switch(rve->op){
+		case RVE_NONE_OP:
+			BUG("empty rval expr\n");
+			break;
+		case RVE_RVAL_OP:
+			return fix_rval(&rve->left.rval);
+		case RVE_UMINUS_OP: /* unary operators */
+		case RVE_BOOL_OP:
+		case RVE_LNOT_OP:
+			ret=fix_rval_expr((void**)&rve->left.rve);
+			if (ret<0) return ret;
+			break;
+		case RVE_MUL_OP:
+		case RVE_DIV_OP:
+		case RVE_MINUS_OP:
+		case RVE_BOR_OP:
+		case RVE_BAND_OP:
+		case RVE_LAND_OP:
+		case RVE_LOR_OP:
+		case RVE_GT_OP:
+		case RVE_GTE_OP:
+		case RVE_LT_OP:
+		case RVE_LTE_OP:
+		case RVE_PLUS_OP:
+		case RVE_IPLUS_OP:
+		case RVE_EQ_OP:
+		case RVE_DIFF_OP:
+		case RVE_CONCAT_OP:
+			ret=fix_rval_expr((void**)&rve->left.rve);
+			if (ret<0) return ret;
+			ret=fix_rval_expr((void**)&rve->right.rve);
+			if (ret<0) return ret;
+			break;
+		default:
+			BUG("unsupported op type %d\n", rve->op);
+	}
+	/* try to optimize */
+	rve_optimize(rve);
+	return 0;
+}

+ 212 - 0
rvalue.h

@@ -0,0 +1,212 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file 
+ * @brief rvalue expressions
+ */
+/* 
+ * History:
+ * --------
+ *  2008-11-30  initial version (andrei)
+ */
+
+#ifndef _rvalue_h_
+#define _rvalue_h_
+
+#include "str.h"
+#include "usr_avp.h"
+#include "select.h"
+#include "pvar.h"
+#include "route.h"
+#include "parser/msg_parser.h"
+#include "action.h"
+
+enum rval_type{
+	RV_NONE, RV_INT, RV_STR, /* basic types */
+	RV_BEXPR, RV_ACTION_ST,  /* special values */
+	RV_SEL, RV_AVP, RV_PVAR
+};
+
+enum rval_expr_op{
+	RVE_NONE_OP,  /* uninit / empty */
+	RVE_RVAL_OP,  /* special op, means that the expr. is in fact a rval */
+	RVE_UMINUS_OP, /* one member expression, returns -(val) */
+	RVE_BOOL_OP,  /* one member evaluate as bool. : (val!=0)*/
+	RVE_LNOT_OP,  /* one member evaluate as bool. : (!val)*/
+	RVE_MUL_OP,   /* 2 members, returns left * right */
+	RVE_DIV_OP,   /* 2 members, returns left / right */
+	RVE_MINUS_OP, /* 2 members, returns left - right */
+	RVE_BAND_OP,  /* 2 members, returns left | right */
+	RVE_BOR_OP,   /* 2 members, returns left & right */
+	RVE_LAND_OP,  /* 2 members, returns left && right */
+	RVE_LOR_OP,   /* 2 members, returns left || right */
+	RVE_GT_OP,    /*  2 members, returns left > right */
+	RVE_GTE_OP,   /*  2 members, returns left >= right */
+	RVE_LT_OP,    /*  2 members, returns left  < right */
+	RVE_LTE_OP,   /*  2 members, returns left <= right */
+	RVE_IPLUS_OP, /* 2 members, integer +, returns int(a)+int(b) */
+	/* common int & str */
+	RVE_PLUS_OP,  /* generic plus (int or str) returns left + right */
+	RVE_EQ_OP,    /*  2 members, returns left == right  (int)*/
+	RVE_DIFF_OP,  /*  2 members, returns left != right  (int)*/
+	RVE_CONCAT_OP,/* string concatenation, returns left . right */
+	/* str only */
+};
+
+
+union rval_val{
+	void* p;
+	long  l;
+	str s;
+	avp_spec_t avps;
+	select_t sel;
+	pv_spec_t pvs;
+	struct action* action;
+	struct expr* bexpr;
+};
+
+
+struct rvalue{
+	enum rval_type type;
+	int refcnt; /**< refcnt, on 0 the structure is destroyed */
+	union rval_val v;
+	int bsize; /**< extra data size */
+	short flags;
+	char buf[1]; /**< extra data, like string contents can be stored here */
+};
+
+
+/* rvalue flags */
+#define RV_CNT_ALLOCED_F  1  /* free contents  (pkg mem allocated) */
+#define RV_RV_ALLOCED_F   2  /* free rv itself (pkg_free(rv)) */
+#define RV_ALL_ALLOCED_F  (RV_CNT_ALLOCED|RV_RV_ALLOCED)
+
+
+struct rval_expr{
+	enum rval_expr_op op;
+	union{
+		struct rval_expr* rve;
+		struct rvalue rval;
+	}left;
+	union{
+		struct rval_expr* rve;
+		struct rvalue rval;
+	}right;
+	struct cfg_pos fpos;
+};
+
+
+enum rval_cache_type{
+	RV_CACHE_EMPTY,
+	RV_CACHE_PVAR,
+	RV_CACHE_AVP
+};
+
+/** value cache for a rvalue struct.
+  * Used to optimize functions that would need to 
+  * get the value repeatedly (e.g. rval_get_btype() and then rval_get_int())
+  */
+struct rval_cache{
+	enum rval_cache_type cache_type;
+	enum rval_type val_type;
+	union{
+		int_str avp_val; /**< avp value */
+		pv_value_t pval; /**< pvar value */
+	}c;
+};
+
+
+
+/** allocates a new rval (should be freed by rval_destroy()). */
+struct rvalue* rval_new_empty(int extra_size);
+struct rvalue* rval_new_str(str* s, int extra_size);
+struct rvalue* rval_new(enum rval_type t, union rval_val* v, int extra_size);
+
+/** inits a rvalue structure- */
+void rval_init(struct rvalue* rv, enum rval_type t, union rval_val* v,
+					int flags);
+/** frees a rval_new(), rval_convert() or rval_expr_eval() returned rval. */
+void rval_destroy(struct rvalue* rv);
+
+/** frees a rval contents */
+void rval_clean(struct rvalue* rv);
+
+/** init a rval_cache struct */
+#define rval_cache_init(rvc) \
+	do{ (rvc)->cache_type=RV_CACHE_EMPTY; (rvc)->val_type=RV_NONE; }while(0)
+
+/** destroy a rval_cache struct contents */
+void rval_cache_clean(struct rval_cache* rvc);
+
+
+/** convert a rvalue to another type.  */
+struct rvalue* rval_convert(struct run_act_ctx* h, struct sip_msg* msg, 
+							enum rval_type type, struct rvalue* v,
+							struct rval_cache* c);
+
+/** get the integer value of an rvalue. */
+int rval_get_int(struct run_act_ctx* h, struct sip_msg* msg, int* i, 
+				struct rvalue* rv, struct rval_cache* cache);
+/** get the string value of an rv. */
+int rval_get_str(struct run_act_ctx* h, struct sip_msg* msg,
+								str* s, struct rvalue* rv,
+								struct rval_cache* cache);
+/** get the string value of an rv in a tmp variable */
+int rval_get_tmp_str(struct run_act_ctx* h, struct sip_msg* msg,
+								str* tmpv, struct rvalue* rv,
+								struct rval_cache* cache,
+								struct rval_cache* tmp_cache);
+
+/** evals an integer expr  to an int. */
+int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
+						int* res, struct rval_expr* rve);
+/** evals a rval expr.. */
+struct rvalue* rval_expr_eval(struct run_act_ctx* h, struct sip_msg* msg,
+								struct rval_expr* rve);
+/** evals an integer expr  to an int or rvalue. */
+int rval_expr_eval_rvint( struct run_act_ctx* h, struct sip_msg* msg,
+						 struct rvalue** rv_res, int* i_res,
+						 struct rval_expr* rve, struct rval_cache* cache);
+
+
+/** guess the type of an expression.  */
+enum rval_type rve_guess_type(struct rval_expr* rve);
+/** returns true if expression is constant. */
+int rve_is_constant(struct rval_expr* rve);
+/** returns 1 if expression is valid (type-wise).*/
+int rve_check_type(enum rval_type* type, struct rval_expr* rve,
+					struct rval_expr** bad_rve, enum rval_type* bad_type,
+					enum rval_type* exp_type);
+/** returns a string name for type (debugging).*/
+char* rval_type_name(enum rval_type type);
+
+/** create a RVE_RVAL_OP rval_expr, containing a single rval of the given type
+  */
+struct rval_expr* mk_rval_expr_v(enum rval_type rv_type, void* val,
+									struct cfg_pos* pos);
+/** create a unary op. rval_expr.. */
+struct rval_expr* mk_rval_expr1(enum rval_expr_op op, struct rval_expr* rve1,
+									struct cfg_pos* pos);
+/** create a rval_expr. from 2 other rval exprs, using op. */
+struct rval_expr* mk_rval_expr2(enum rval_expr_op op, struct rval_expr* rve1,
+													  struct rval_expr* rve2,
+													  struct cfg_pos* pos);
+
+/** fix a rval_expr. */
+int fix_rval_expr(void** p);
+#endif /* _rvalue_h */

+ 43 - 0
sr_compat.c

@@ -0,0 +1,43 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file 
+ * @brief  ser/kamailio/openser compatibility macros & vars.
+ */
+/* 
+ * History:
+ * --------
+ *  2008-11-29  initial version (andrei)
+ */
+
+
+#include "sr_compat.h"
+
+#ifdef SR_SER
+#define SR_DEFAULT_COMPAT SR_COMPAT_SER
+#elif defined SR_KAMAILIO || defined SR_OPENSER
+#define SR_DEFAULT_COMPAT SR_COMPAT_KAMAILIO
+#elif defined SR_ALL || defined SR_MAX_COMPAT
+#define SR_DEFAULT_COMPAT SR_COMPAT_MAX
+#else
+/* default */
+#define SR_DEFAULT_COMPAT SR_COMPAT_MAX
+#endif
+
+int sr_compat=SR_DEFAULT_COMPAT;
+int sr_cfg_compat=SR_DEFAULT_COMPAT;

+ 44 - 0
sr_compat.h

@@ -0,0 +1,44 @@
+/* 
+ * $Id$
+ * 
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/**
+ * @file 
+ * @brief  ser/kamailio/openser compatibility macros & vars.
+ */
+/* 
+ * History:
+ * --------
+ *  2008-11-29  initial version (andrei)
+ */
+
+
+#ifndef _sr_compat_h
+#define _sr_compat_h
+
+/** max compat mode: support as many features as possible from all xSERs */
+#define SR_COMPAT_MAX 0
+/** maximum compatibility mode with ser */
+#define SR_COMPAT_SER 1
+/** maximum compatibility mode with kamailio/openser */
+#define SR_COMPAT_KAMAILIO 2
+#define SR_COMPAT_OPENSER 2
+
+
+extern int sr_compat;
+extern int sr_cfg_compat;
+
+#endif /* _sr_compat_h */

Some files were not shown because too many files changed in this diff