浏览代码

Merge branch 'master' of ssh://[email protected]/sip-router

* 'master' of ssh://[email protected]/sip-router:
  makefiles: fix auto lib re-building
  script parsing: string switch support
  script engine: string switch optimization and fixup
  script engine: string switch execution
  rvalues: fix rval_new( empty string )
  core: sint2str check for space before adding the sign
Jan Janak 16 年之前
父节点
当前提交
665489993d
共有 8 个文件被更改,包括 572 次插入28 次删除
  1. 17 5
      Makefile.rules
  2. 67 0
      action.c
  3. 112 12
      cfg.y
  4. 2 2
      route_struct.h
  5. 6 2
      rvalue.c
  6. 337 4
      switch.c
  7. 30 2
      switch.h
  8. 1 1
      ut.h

+ 17 - 5
Makefile.rules

@@ -27,12 +27,20 @@
 # (if not rebuild everything)
 ifeq (,$(filter $(nodep_targets),$(MAKECMDGOALS)))
 -include makecfg.lst
-ifneq ($(strip $(DEFS)), $(strip $(CFG_DEFS)))
+# if trying  to build a lib automatically and the lib is already compiled,
+# don't rebuild it if the only differences in DEFS or INCLUDES are covered
+# by LIB_NOREBUILD_DEFS/LIB_NOREBUILD_INCLUDES
+LIB_NOREBUILD_DEFS+= -DMOD_NAME% -D%_MOD_INTERFACE -DMOD_INTERFACE_% -DSR_%
+
+# don't rebuild if the differences are covered by NOREBUILD_DEFS or 
+# NOREBUILD_INCLUDES
+ifneq ($(filter-out $(NOREBUILD_DEFS),$(strip $(DEFS))), $(strip $(CFG_DEFS)))
 #$(warning different defs: <$(strip $(DEFS))> != )
 #$(warning               : <$(strip $(CFG_DEFS))>)
 $(shell rm -f makecfg.lst)
 endif
-ifneq ($(strip $(INCLUDES)), $(strip $(CFG_INCLUDES)))
+ifneq ($(filter-out $(NOREBUILD_INCLUDES), $(strip $(INCLUDES))),\
+		$(strip $(CFG_INCLUDES)))
 $(shell rm -f makecfg.lst)
 endif
 endif
@@ -95,7 +103,9 @@ ALL_LIBS+=$(foreach l, $(SER_LIBS), -L$(dir $l) -l$(notdir $l))
 $(NAME): librpath.lst $(SER_LIBS_DEPS)
 
 $(SER_LIBS_DEPS): FORCE
-	$(MAKE) -wC $(dir $@)  compile_for_install=$(lib_compile_for_install)
+	@$(MAKE) -wC $(dir $@)  compile_for_install=$(lib_compile_for_install) \
+		NOREBUILD_DEFS="$(NOREBUILD_DEFS) $(LIB_NOREBUILD_DEFS)" \
+		NOREBUILD_INCLUDES="$(NOREBUILD_INCLUDES) $(LIB_NOREBUILD_INCLUDES)"
 
 .PHONY: FORCE
 FORCE:
@@ -134,8 +144,10 @@ librpath.lst: $(ALLDEP)
 	@echo LIB_RPATH_LST:=$(SER_RPATH_LST) >librpath.lst
 
 makecfg.lst:
-	@echo CFG_DEFS:=$(subst ',\', $(subst ",\", $(strip $(DEFS)))) >>$@
-	@echo CFG_INCLUDES:=$(subst ',\', $(subst ",\", $(strip $(INCLUDES)))) >>$@
+	@echo CFG_DEFS:=$(subst ',\', $(subst ",\", \
+		$(filter-out $(NOREBUILD_DEFS), $(strip $(DEFS))))) >>$@
+	@echo CFG_INCLUDES:=$(subst ',\', $(subst ",\", \
+		$(filter-out $(NOREBUILD_INCLUDES), $(strip $(INCLUDES))))) >>$@
 .PHONY: all
 all: $(NAME) modules
 

+ 67 - 0
action.c

@@ -117,6 +117,11 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 	struct switch_cond_table* sct;
 	struct switch_jmp_table*  sjt;
 	struct rval_expr* rve;
+	struct match_cond_table* mct;
+	struct rvalue* rv;
+	struct rvalue* rv1;
+	struct rval_cache c1;
+	str s;
 
 
 	/* reset the value of error to E_UNSPEC so avoid unknowledgable
@@ -908,6 +913,68 @@ sw_jt_def:
 											   returns passthrough */
 			}
 			break;
+		case MATCH_COND_T:
+			mct=(struct match_cond_table*)a->val[1].u.data;
+			rval_cache_init(&c1);
+			rv=0;
+			rv1=0;
+			ret=rval_expr_eval_rvint(h, msg, &rv, &v, 
+									(struct rval_expr*)a->val[0].u.data, &c1);
+									
+			if (unlikely( ret<0)){
+				/* handle error in expression => use default */
+				ret=-1;
+				goto match_cond_def;
+			}
+			if (h->run_flags & EXIT_R_F){
+				ret=0;
+				break;
+			}
+			h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break
+													    in expr */
+			if (likely(rv)){
+				rv1=rval_convert(h, msg, RV_STR, rv, &c1);
+				if (unlikely(rv1==0)){
+					ret=-1;
+					goto match_cond_def;
+				}
+				s=rv1->v.s;
+			}else{
+				/* int result in v */
+				rval_cache_clean(&c1);
+				s.s=sint2str(v, &s.len);
+			}
+			ret=1; /* default is continue */
+			for(i=0; i<mct->n; i++)
+				if (( mct->match[i].type==MATCH_STR &&
+						mct->match[i].l.s.len==s.len &&
+						memcmp(mct->match[i].l.s.s, s.s, s.len) == 0 ) ||
+					 ( mct->match[i].type==MATCH_RE &&
+					  regexec(mct->match[i].l.regex, s.s, 0, 0, 0) == 0)
+					){
+					if (likely(mct->jump[i])){
+						ret=run_actions(h, mct->jump[i], msg);
+						h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+													   returns passthrough */
+					}
+					goto match_cleanup;
+				}
+match_cond_def:
+			if (mct->def){
+				ret=run_actions(h, mct->def, msg);
+				h->run_flags &= ~BREAK_R_F; /* catch breaks, but let
+											   returns passthrough */
+			}
+match_cleanup:
+			if (rv1){
+				rval_destroy(rv1);
+				rval_destroy(rv);
+				rval_cache_clean(&c1);
+			}else if (rv){
+				rval_destroy(rv);
+				rval_cache_clean(&c1);
+			}
+			break;
 		case WHILE_T:
 			i=0;
 			flags=0;

+ 112 - 12
cfg.y

@@ -216,7 +216,11 @@ static struct socket_id* mk_listen_id2(struct name_lst*, int, int);
 static void free_name_lst(struct name_lst* lst);
 static void free_socket_id_lst(struct socket_id* i);
 
-static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a);
+static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re, 
+									struct action* a, int* err);
+static int case_check_type(struct case_stms* stms);
+static int case_check_default(struct case_stms* stms);
+
 
 %}
 
@@ -1805,10 +1809,12 @@ ct_rval: rval_expr {
 			$$=0;
 			if (!rve_is_constant($1)){
 				yyerror("constant expected");
-			}else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
+			/*
+			} else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){
 				yyerror("invalid expression (bad type)");
 			}else if (i_tmp!=RV_INT){
 				yyerror("invalid expression type, int expected\n");
+			*/
 			}else
 				$$=$1;
 		}
@@ -1817,28 +1823,38 @@ single_case:
 	CASE ct_rval COLON actions {
 		$$=0;
 		if ($2==0) yyerror ("bad case label");
-		else if (($$=mk_case_stm($2, $4))==0){
-				yyerror("internal error: memory allocation failure");
+		else if ((($$=mk_case_stm($2, 0, $4, &i_tmp))==0) && (i_tmp==-10)){
+				YYABORT;
+		}
+	}
+| CASE SLASH ct_rval COLON actions {
+		$$=0;
+		if ($3==0) yyerror ("bad case label");
+		else if ((($$=mk_case_stm($3, 1, $5, &i_tmp))==0) && (i_tmp==-10)){
 				YYABORT;
 		}
 	}
 	| CASE ct_rval COLON {
 		$$=0;
 		if ($2==0) yyerror ("bad case label");
-		else if (($$=mk_case_stm($2, 0))==0){
-				yyerror("internal error: memory allocation failure");
+		else if ((($$=mk_case_stm($2, 0, 0, &i_tmp))==0) && (i_tmp==-10)){
+				YYABORT;
+		}
+	}
+	| CASE SLASH ct_rval COLON {
+		$$=0;
+		if ($3==0) yyerror ("bad case label");
+		else if ((($$=mk_case_stm($3, 1, 0, &i_tmp))==0) && (i_tmp==-10)){
 				YYABORT;
 		}
 	}
 	| DEFAULT COLON actions {
-		if (($$=mk_case_stm(0, $3))==0){
-				yyerror("internal error: memory allocation failure");
+		if ((($$=mk_case_stm(0, 0, $3, &i_tmp))==0) && (i_tmp=-10)){
 				YYABORT;
 		}
 	}
 	| DEFAULT COLON {
-		if (($$=mk_case_stm(0, 0))==0){
-				yyerror("internal error: memory allocation failure");
+		if ((($$=mk_case_stm(0, 0, 0, &i_tmp))==0) && (i_tmp==-10)){
 				YYABORT;
 		}
 	}
@@ -1866,6 +1882,12 @@ switch_cmd:
 		$$=0;
 		if ($2==0) yyerror("bad expression in switch(...)");
 		else if ($4==0) yyerror ("bad switch body");
+		else if (case_check_default($4)!=0)
+			yyerror_at(&$2->fpos, "bad switch(): too many "
+							"\"default:\" labels\n");
+		else if (case_check_type($4)!=0)
+			yyerror_at(&$2->fpos, "bad switch(): mixed integer and"
+							" string/RE cases not allowed\n");
 		else{
 			$$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, $4);
 			if ($$==0) {
@@ -2926,15 +2948,52 @@ static void free_socket_id_lst(struct socket_id* lst)
 }
 
 
-static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a)
+/** create a temporary case statmenet structure.
+ *  *err will be filled in case of error (return == 0):
+ *   -1 - non constant expression
+ *   -2 - expression error (bad type)
+ *   -10 - memory allocation error
+ */
+static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re,
+											struct action* a, int* err)
 {
 	struct case_stms* s;
+	struct rval_expr* bad_rve;
+	enum rval_type type, bad_t, exp_t;
+	enum match_str_type t;
+	
+	t=MATCH_UNKNOWN;
+	if (ct){
+		/* if ct!=0 => case, else if ct==0 is a default */
+		if (!rve_is_constant(ct)){
+			yyerror_at(&ct->fpos, "non constant expression in case");
+			*err=-1;
+			return 0;
+		}
+		if (rve_check_type(&type, ct, &bad_rve, &bad_t, &exp_t)!=1){
+			yyerror_at(&ct->fpos, "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);
+			*err=-2;
+			return 0;
+		}
+		if (is_re)
+			t=MATCH_RE;
+		else if (type==RV_STR)
+			t=MATCH_STR;
+		else
+			t=MATCH_INT;
+	}
+
 	s=pkg_malloc(sizeof(*s));
 	if (s==0) {
-		LOG(L_CRIT,"ERROR: cfg. parser: out of memory.\n");
+		yyerror("internal error: memory allocation failure");
+		*err=-10;
 	} else {
 		memset(s, 0, sizeof(*s));
 		s->ct_rve=ct;
+		s->type=t;
 		s->actions=a;
 		s->next=0;
 		s->append=0;
@@ -2942,6 +3001,47 @@ static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a)
 	return s;
 }
 
+
+/*
+ * @return 0 on success, -1 on error.
+ */
+static int case_check_type(struct case_stms* stms)
+{
+	struct case_stms* c;
+	struct case_stms* s;
+	
+	for(c=stms; c ; c=c->next){
+		if (!c->ct_rve) continue;
+		for (s=c->next; s; s=s->next){
+			if (!s->ct_rve) continue;
+			if ((s->type!=c->type) &&
+				!(	(c->type==MATCH_STR || c->type==MATCH_RE) &&
+					(s->type==MATCH_STR || s->type==MATCH_RE) ) ){
+					yyerror_at(&s->ct_rve->fpos, "type mismatch in case");
+					return -1;
+			}
+		}
+	}
+	return 0;
+}
+
+
+/*
+ * @return 0 on success, -1 on error.
+ */
+static int case_check_default(struct case_stms* stms)
+{
+	struct case_stms* c;
+	int default_no;
+	
+	default_no=0;
+	for(c=stms; c ; c=c->next)
+		if (c->ct_rve==0) default_no++;
+	return (default_no<=1)?0:-1;
+}
+
+
+
 /*
 int main(int argc, char ** argv)
 {

+ 2 - 2
route_struct.h

@@ -72,7 +72,7 @@ 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,
 		SET_PORT_T, SET_URI_T, SET_HOSTPORTTRANS_T,
 		IF_T, SWITCH_T /* only until fixup*/,
-		BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, WHILE_T,
+		BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, MATCH_COND_T, WHILE_T,
 		MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, MODULEX_T,
 		SETFLAG_T, RESETFLAG_T, ISFLAGSET_T ,
 		AVPFLAG_OPER_T,
@@ -101,7 +101,7 @@ enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST,
 		SELECT_ST, PVAR_ST,
 		LVAL_ST,  RVE_ST,
 		RETCODE_ST, CASE_ST,
-		BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST
+		BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST, MATCH_CONDTABLE_ST
 };
 
 /* run flags */

+ 6 - 2
rvalue.c

@@ -233,13 +233,17 @@ 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)
+	if (t==RV_STR && v && v->s.s)
 		return rval_new_str(&v->s, extra_size);
 	rv=rval_new_empty(extra_size);
 	if (likely(rv)){
 		rv->type=t;
-		if (likely(v)){
+		if (likely(v && t!=RV_STR)){
 			rv->v=*v;
+		}else if (t==RV_STR){
+			rv->v.s.s=&rv->buf[0];
+			rv->v.s.len=0;
+			if (likely(extra_size)) rv->v.s.s[0]=0;
 		}else
 			memset (&rv->v, 0, sizeof(rv->v));
 	}

+ 337 - 4
switch.c

@@ -22,6 +22,7 @@
  * History:
  * --------
  *  2009-02-02  initial version (andrei)
+ *  2009-02-19  string and RE switch support added (andrei)
 */
 
 #include "switch.h"
@@ -84,6 +85,32 @@ static struct switch_jmp_table* mk_switch_jmp_table(int jmp_size, int rest)
 
 
 
+/** create a match cond table structure (pkg_malloc'ed).
+ * @return 0 on error, pointer on success
+ */
+static struct match_cond_table* mk_match_cond_table(int n)
+{
+	struct match_cond_table* mct;
+	
+	/* allocate everything in a single block, better for cache locality */
+	mct=pkg_malloc(ROUND_POINTER(sizeof(*mct))+
+					ROUND_POINTER(n*sizeof(mct->match[0]))+
+					n*sizeof(mct->jump[0]));
+	if (mct==0) return 0;
+	mct->n=n;
+	mct->match=(struct match_str*)((char*)mct+ROUND_POINTER(sizeof(*mct)));
+	mct->jump=(struct action**)
+				((char*)mct->match+ROUND_POINTER(n*sizeof(mct->match[0])));
+	mct->def=0;
+	return mct;
+}
+
+
+
+static int fix_match(struct action* t);
+
+
+
 void destroy_case_stms(struct case_stms *lst)
 {
 	struct case_stms* l;
@@ -138,6 +165,11 @@ int fix_switch(struct action* t)
 	def_jmp_bm=0;
 	labels_no=0;
 	default_found=0;
+	/* check if string switch (first case is string or RE) */
+	for (c=(struct case_stms*)t->val[1].u.data; c && c->is_default; c=c->next);
+	if (c && (c->type==MATCH_STR || c->type==MATCH_RE))
+		return fix_match(t);
+	
 	sw_rve=(struct rval_expr*)t->val[0].u.data;
 	/*  handle null actions: optimize away if no
 	   sideffects */
@@ -159,12 +191,17 @@ int fix_switch(struct action* t)
 	n=0;
 	for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
 		if (c->ct_rve){
+			if (c->type!=MATCH_INT){
+				LOG(L_ERR, "ERROR: fix_switch: wrong case type %d (int"
+							"expected)\n", c->type);
+				return E_UNSPEC;
+			}
 			if (!rve_is_constant(c->ct_rve)){
 				LOG(L_ERR, "ERROR: fix_switch: non constant "
 						"expression in case\n");
 				return E_BUG;
 			}
-			if (rval_expr_eval_int(0, 0,  &c->int_label, c->ct_rve)
+			if (rval_expr_eval_int(0, 0,  &c->label.match_int, c->ct_rve)
 					<0){
 				LOG(L_ERR, "ERROR: fix_switch: case expression"
 						" (%d,%d) has non-interger type\n",
@@ -183,7 +220,7 @@ int fix_switch(struct action* t)
 				return E_UNSPEC;
 			}
 			default_found=1;
-			c->int_label=-1;
+			c->label.match_int=-1;
 			c->is_default=1;
 			def_a=c->actions;
 		}
@@ -241,14 +278,14 @@ int fix_switch(struct action* t)
 			def_jmp_bm=tail;
 		} else {
 			for (j=0; j<n; j++){
-				if (cond[j]==c->int_label){
+				if (cond[j]==c->label.match_int){
 					LOG(L_ERR, "ERROR: fix_switch: duplicate case (%d,%d)\n",
 							c->ct_rve->fpos.s_line, c->ct_rve->fpos.s_col);
 					ret=E_UNSPEC;
 					goto error;
 				}
 			}
-			cond[n]=c->int_label;
+			cond[n]=c->label.match_int;
 			jmp_bm[n]=tail;
 			n++;
 		}
@@ -371,4 +408,300 @@ error:
 	return ret;
 }
 
+
+
+/** fixup function for MATCH_T actions.
+ * can produce 3 different action types:
+ *  - BLOCK_T (actions) - actions grouped in a block, break ends the block
+ *    execution.
+ *  - EVAL_T (cond)  - null switch block, but the condition has to be
+ *                       evaluated due to possible side-effects.
+ *  - MATCH_COND_T(cond, jumps) - condition table
+ */
+static int fix_match(struct action* t)
+{
+	struct case_stms* c;
+	int n, i, j, ret;
+	struct action* a;
+	struct action* block;
+	struct action* def_a;
+	struct action* action_lst;
+	struct action** tail;
+	struct match_cond_table* mct;
+	int labels_no;
+	struct action** def_jmp_bm;
+	struct match_str* match;
+	struct action*** jmp_bm;
+	int default_found;
+	struct rval_expr* m_rve;
+	struct rvalue* rv;
+	regex_t* regex;
+	str s;
+	
+	ret=E_BUG;
+	match=0;
+	jmp_bm=0;
+	def_jmp_bm=0;
+	labels_no=0;
+	default_found=0;
+	rv=0;
+	s.s=0;
+	s.len=0;
+	m_rve=(struct rval_expr*)t->val[0].u.data;
+	/*  handle null actions: optimize away if no
+	   sideffects */
+	if (t->val[1].u.data==0){
+		if (!rve_has_side_effects(m_rve)){
+			t->type=BLOCK_T;
+			rve_destroy(m_rve);
+			t->val[0].type=BLOCK_ST;
+			t->val[0].u.data=0;
+			DBG("MATCH: null switch optimized away\n");
+		}else{
+			t->type=EVAL_T;
+			t->val[0].type=RVE_ST;
+			DBG("MATCH: null switch turned to EVAL_T\n");
+		}
+		return 0;
+	}
+	def_a=0;
+	n=0;
+	for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
+		if (c->ct_rve){
+			if (c->type!=MATCH_STR && c->type!=MATCH_RE){
+				LOG(L_ERR, "ERROR: fix_match: wrong case type %d (string"
+							"or RE expected)\n", c->type);
+				return E_UNSPEC;
+			}
+			if (!rve_is_constant(c->ct_rve)){
+				LOG(L_ERR, "ERROR: fix_match: non constant "
+						"expression in case\n");
+				ret=E_BUG;
+				goto error;
+			}
+			if ((rv=rval_expr_eval(0, 0, c->ct_rve)) == 0 ){
+				LOG(L_ERR, "ERROR: fix_match: bad case expression"
+						" (%d,%d)\n",
+						c->ct_rve->fpos.s_line,
+						c->ct_rve->fpos.s_col);
+				ret=E_BUG;
+				goto error;
+			}
+			if (rval_get_str(0, 0, &s, rv, 0)<0){
+				LOG(L_ERR, "ERROR: fix_match (%d,%d): out of memory?\n",
+						c->ct_rve->fpos.s_line,
+						c->ct_rve->fpos.s_col);
+				ret=E_BUG;
+				goto error;
+			}
+			if (c->type==MATCH_RE){
+				if ((regex=pkg_malloc(sizeof(regex_t))) == 0){
+					LOG(L_ERR, "ERROR: fix_match: out of memory\n");
+					ret=E_OUT_OF_MEM;
+					goto error;
+				}
+				if (regcomp(regex, s.s, 
+							REG_EXTENDED | REG_NOSUB | c->re_flags) !=0){
+					pkg_free(regex);
+					regex=0;
+					LOG(L_ERR, "ERROR: fix_match (%d, %d): bad regular"
+								" expression %.*s\n",
+							c->ct_rve->fpos.s_line,
+							c->ct_rve->fpos.s_col,
+							s.len, ZSW(s.s));
+					ret=E_UNSPEC;
+					goto error;
+				}
+				c->label.match_re=regex;
+				regex=0;
+			}else if (c->type==MATCH_STR){
+				c->label.match_str=s;
+				s.s=0;
+				s.len=0;
+			}else{
+				LOG(L_CRIT, "BUG: fix_match (%d,%d): wrong case type %d\n",
+						c->ct_rve->fpos.s_line, c->ct_rve->fpos.s_col,
+						c->type);
+				ret=E_BUG;
+				goto error;
+			}
+			c->is_default=0;
+			n++; /* count only non-default cases */
+			/* cleanup */
+			rval_destroy(rv);
+			rv=0;
+			if (s.s){
+				pkg_free(s.s);
+				s.s=0;
+				s.len=0;
+			}
+		}else{
+			if (default_found){
+				LOG(L_ERR, "ERROR: fix_match: more then one \"default\""
+						" label found (%d, %d)\n",
+						c->ct_rve->fpos.s_line,
+						c->ct_rve->fpos.s_col);
+				ret=E_UNSPEC;
+				goto error;
+			}
+			default_found=1;
+			c->is_default=1;
+			def_a=c->actions;
+		}
+		if ( c->actions && ((ret=fix_actions(c->actions))<0))
+			goto error;
+	}
+	DBG("MATCH: %d cases, %d default\n", n, default_found);
+	/*: handle n==0 (no case only a default:) */
+	if (n==0){
+		if (default_found){
+			if (!rve_has_side_effects(m_rve)){
+				t->type=BLOCK_T;
+				rve_destroy(m_rve);
+				destroy_case_stms(t->val[1].u.data);
+				t->val[0].type=BLOCK_ST;
+				t->val[0].u.data=def_a;
+				t->val[1].type=0;
+				t->val[1].u.data=0;
+				DBG("MATCH: default only switch optimized away (BLOCK_T)\n");
+				return 0;
+			}
+			DBG("MATCH: default only switch with side-effect...\n");
+		}else{
+			LOG(L_CRIT, "BUG: fix_match: empty switch not expected at this"
+					" point\n");
+			ret=E_BUG;
+			goto error;
+		}
+	}
+	labels_no=n;
+	match=pkg_malloc(sizeof(match[0])*n);
+	jmp_bm=pkg_malloc(sizeof(jmp_bm[0])*n);
+	if (match==0 || jmp_bm==0){
+		LOG(L_ERR, "ERROR: fix_match: memory allocation failure\n");
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	
+	/* fill condition table and jump point bookmarks and "flatten" the action 
+	   lists (transform them into a single list for the entire switch, rather
+	    then one block per case ) */
+	n=0;
+	action_lst=0;
+	tail=&action_lst;
+	for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){
+		a=c->actions;
+		if (a){
+			for (; a->next; a=a->next);
+			if (action_lst==0)
+				action_lst=c->actions;
+			else
+				*tail=c->actions;
+		}
+		if (c->is_default){
+			def_jmp_bm=tail;
+		} else{
+			match[n].type=c->type;
+			if (match[n].type == MATCH_STR){
+				for (j=0; j<n; j++){
+					if ( match[j].type == c->type &&
+						 match[j].l.s.len ==  c->label.match_str.len &&
+						 memcmp(match[j].l.s.s, c->label.match_str.s,
+							 		match[j].l.s.len) == 0 ){
+						LOG(L_ERR, "ERROR: fix_match: duplicate case"
+								" (%d,%d)\n", c->ct_rve->fpos.s_line,
+								c->ct_rve->fpos.s_col);
+						ret=E_UNSPEC;
+						goto error;
+					}
+				}
+				match[n].flags=0;
+				match[n].l.s=c->label.match_str;
+				c->label.match_str.s=0; /* prevent s being freed */
+				c->label.match_str.len=0;
+			} else {
+				match[n].flags=c->re_flags | REG_EXTENDED | REG_NOSUB;
+				match[n].l.regex=c->label.match_re;
+				c->label.match_re=0;
+			}
+			jmp_bm[n]=tail;
+			n++;
+		}
+		if (c->actions)
+			tail=&a->next;
+	}
+	/* handle constant rve w/ no side-effects: replace the whole case 
+	   with the case rve block */
+	if ( (scr_opt_lev>=2) &&
+			!rve_has_side_effects(m_rve) && rve_is_constant(m_rve)){
+		if ((rv=rval_expr_eval(0, 0, m_rve)) == 0){
+			LOG(L_ERR, "ERROR: fix_match: bad expression (%d,%d)\n", 
+					m_rve->fpos.s_line, m_rve->fpos.s_col);
+			ret=E_UNSPEC;
+			goto error;
+		}
+		if (rval_get_str(0, 0, &s, rv, 0) < 0 ){
+				LOG(L_ERR, "ERROR: fix_match (%d,%d): bad string expression\n",
+						m_rve->fpos.s_line,
+						m_rve->fpos.s_col);
+			ret=E_UNSPEC;
+			goto error;
+		}
+		/* start with the "default:" value in case nothing is found */
+		block=def_jmp_bm?*def_jmp_bm:0;
+		for (i=0; i<n; i++){
+			if (((match[i].type == MATCH_STR) && (match[i].l.s.len == s.len) &&
+					(memcmp(match[i].l.s.s, s.s, s.len) == 0)) ||
+				((match[i].type == MATCH_RE) && 
+					regexec(match[i].l.regex, s.s, 0, 0, 0) == 0) ) {
+				block=*jmp_bm[i];
+				break;
+			}
+		}
+		DBG("MATCH: constant switch(\"%.*s\") with %d cases optimized away"
+				" to case no. %d\n", s.len, ZSW(s.s), n, i);
+		/* cleanup */
+		rval_destroy(rv);
+		rv=0;
+		pkg_free(s.s);
+		s.s=0;
+		s.len=0;
+		ret=0;
+		/* replace with BLOCK_ST */
+		rve_destroy(m_rve);
+		destroy_case_stms(t->val[1].u.data);
+		t->type=BLOCK_T;
+		t->val[0].type=BLOCK_ST;
+		t->val[0].u.data=block;
+		t->val[1].type=0;
+		t->val[1].u.data=0;
+		goto end;
+	}
+	mct=mk_match_cond_table(n);
+	if (mct==0){
+		LOG(L_ERR, "ERROR: fix_match: memory allocation error\n");
+		ret=E_OUT_OF_MEM;
+		goto error;
+	}
+	mct->n=n;
+	for (i=0; i<n; i++){
+		mct->match[i]=match[i];
+		mct->jump[i]=*jmp_bm[i];
+	}
+	mct->def=def_jmp_bm?*def_jmp_bm:0;
+	t->type=MATCH_COND_T;
+	t->val[1].type=MATCH_CONDTABLE_ST;
+	t->val[1].u.data=mct;
+	DBG("MATCH: optimized to match condtable (%d) default: %s\n ",
+				mct->n, mct->def?"yes":"no");
+		ret=0;
+end:
+error:
+	if (match) pkg_free(match);
+	if (jmp_bm) pkg_free(jmp_bm);
+	/* cleanup rv & s*/
+	if (rv) rval_destroy(rv);
+	if (s.s) pkg_free(s.s);
+	return ret;
+}
 /* vi: set ts=4 sw=4 tw=79:ai:cindent: */

+ 30 - 2
switch.h

@@ -27,15 +27,24 @@
 #ifndef __switch_h
 #define __switch_h
 
+#include <regex.h>
+
 #include "route_struct.h"
 
+
 struct case_stms{
 	struct rval_expr* ct_rve;
 	struct action* actions;
 	struct case_stms* next;
 	struct case_stms** append;
-	int int_label;
+	int type;     /**< type: MATCH_UNKOWN, MATCH_INT, MATCH_STR, MATCH_RE */
+	int re_flags; /**< used only for REs */
 	int is_default;
+	union {
+		int match_int;
+		str match_str;
+		regex_t* match_re;
+	} label;  /**< fixed case argument */
 };
 
 
@@ -43,7 +52,7 @@ struct switch_cond_table{
 	int n;                  /**< size */
 	int* cond;              /**< int labels array */
 	struct action** jump;   /**< jump points array */
-	struct action* def; /**< default jump  */
+	struct action* def;     /**< default jump  */
 };
 
 
@@ -54,6 +63,25 @@ struct switch_jmp_table{
 	struct switch_cond_table rest; /**< normal cond. table for the rest */
 };
 
+
+enum match_str_type { MATCH_UNKNOWN, MATCH_INT, MATCH_STR, MATCH_RE };
+
+struct match_str{
+	enum match_str_type type;/**< string or RE */
+	int flags;               /**< flags for re */
+	union{
+		str s;              /* string */
+		regex_t* regex;     /**< compiled regex */
+	}l;
+};
+
+struct match_cond_table{
+	int n;                   /**< size */
+	struct match_str* match; /**< match array */
+	struct action** jump;    /**< jump points array */
+	struct action* def;      /**< default jmp */
+};
+
 int fix_switch(struct action* t);
 
 #endif /*__switch_h*/

+ 1 - 1
ut.h

@@ -312,7 +312,7 @@ static inline char* sint2str(long l, int* len)
 		l = -l;
 	}
 	p = int2str((unsigned long)l, len);
-	if(sign) {
+	if(sign && *len<(INT2STR_MAX_LEN-1)) {
 		*(--p) = '-';
 		if (len) (*len)++;
 	}