* commit 'origin/andrei/switch':
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
... | ... |
@@ -117,6 +117,11 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg) |
117 | 117 |
struct switch_cond_table* sct; |
118 | 118 |
struct switch_jmp_table* sjt; |
119 | 119 |
struct rval_expr* rve; |
120 |
+ struct match_cond_table* mct; |
|
121 |
+ struct rvalue* rv; |
|
122 |
+ struct rvalue* rv1; |
|
123 |
+ struct rval_cache c1; |
|
124 |
+ str s; |
|
120 | 125 |
|
121 | 126 |
|
122 | 127 |
/* reset the value of error to E_UNSPEC so avoid unknowledgable |
... | ... |
@@ -908,6 +913,68 @@ sw_jt_def: |
908 | 913 |
returns passthrough */ |
909 | 914 |
} |
910 | 915 |
break; |
916 |
+ case MATCH_COND_T: |
|
917 |
+ mct=(struct match_cond_table*)a->val[1].u.data; |
|
918 |
+ rval_cache_init(&c1); |
|
919 |
+ rv=0; |
|
920 |
+ rv1=0; |
|
921 |
+ ret=rval_expr_eval_rvint(h, msg, &rv, &v, |
|
922 |
+ (struct rval_expr*)a->val[0].u.data, &c1); |
|
923 |
+ |
|
924 |
+ if (unlikely( ret<0)){ |
|
925 |
+ /* handle error in expression => use default */ |
|
926 |
+ ret=-1; |
|
927 |
+ goto match_cond_def; |
|
928 |
+ } |
|
929 |
+ if (h->run_flags & EXIT_R_F){ |
|
930 |
+ ret=0; |
|
931 |
+ break; |
|
932 |
+ } |
|
933 |
+ h->run_flags &= ~(RETURN_R_F|BREAK_R_F); /* catch return & break |
|
934 |
+ in expr */ |
|
935 |
+ if (likely(rv)){ |
|
936 |
+ rv1=rval_convert(h, msg, RV_STR, rv, &c1); |
|
937 |
+ if (unlikely(rv1==0)){ |
|
938 |
+ ret=-1; |
|
939 |
+ goto match_cond_def; |
|
940 |
+ } |
|
941 |
+ s=rv1->v.s; |
|
942 |
+ }else{ |
|
943 |
+ /* int result in v */ |
|
944 |
+ rval_cache_clean(&c1); |
|
945 |
+ s.s=sint2str(v, &s.len); |
|
946 |
+ } |
|
947 |
+ ret=1; /* default is continue */ |
|
948 |
+ for(i=0; i<mct->n; i++) |
|
949 |
+ if (( mct->match[i].type==MATCH_STR && |
|
950 |
+ mct->match[i].l.s.len==s.len && |
|
951 |
+ memcmp(mct->match[i].l.s.s, s.s, s.len) == 0 ) || |
|
952 |
+ ( mct->match[i].type==MATCH_RE && |
|
953 |
+ regexec(mct->match[i].l.regex, s.s, 0, 0, 0) == 0) |
|
954 |
+ ){ |
|
955 |
+ if (likely(mct->jump[i])){ |
|
956 |
+ ret=run_actions(h, mct->jump[i], msg); |
|
957 |
+ h->run_flags &= ~BREAK_R_F; /* catch breaks, but let |
|
958 |
+ returns passthrough */ |
|
959 |
+ } |
|
960 |
+ goto match_cleanup; |
|
961 |
+ } |
|
962 |
+match_cond_def: |
|
963 |
+ if (mct->def){ |
|
964 |
+ ret=run_actions(h, mct->def, msg); |
|
965 |
+ h->run_flags &= ~BREAK_R_F; /* catch breaks, but let |
|
966 |
+ returns passthrough */ |
|
967 |
+ } |
|
968 |
+match_cleanup: |
|
969 |
+ if (rv1){ |
|
970 |
+ rval_destroy(rv1); |
|
971 |
+ rval_destroy(rv); |
|
972 |
+ rval_cache_clean(&c1); |
|
973 |
+ }else if (rv){ |
|
974 |
+ rval_destroy(rv); |
|
975 |
+ rval_cache_clean(&c1); |
|
976 |
+ } |
|
977 |
+ break; |
|
911 | 978 |
case WHILE_T: |
912 | 979 |
i=0; |
913 | 980 |
flags=0; |
... | ... |
@@ -216,7 +216,11 @@ static struct socket_id* mk_listen_id2(struct name_lst*, int, int); |
216 | 216 |
static void free_name_lst(struct name_lst* lst); |
217 | 217 |
static void free_socket_id_lst(struct socket_id* i); |
218 | 218 |
|
219 |
-static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a); |
|
219 |
+static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re, |
|
220 |
+ struct action* a, int* err); |
|
221 |
+static int case_check_type(struct case_stms* stms); |
|
222 |
+static int case_check_default(struct case_stms* stms); |
|
223 |
+ |
|
220 | 224 |
|
221 | 225 |
%} |
222 | 226 |
|
... | ... |
@@ -1805,10 +1809,12 @@ ct_rval: rval_expr { |
1805 | 1809 |
$$=0; |
1806 | 1810 |
if (!rve_is_constant($1)){ |
1807 | 1811 |
yyerror("constant expected"); |
1808 |
- }else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){ |
|
1812 |
+ /* |
|
1813 |
+ } else if (!rve_check_type((enum rval_type*)&i_tmp, $1, 0, 0 ,0)){ |
|
1809 | 1814 |
yyerror("invalid expression (bad type)"); |
1810 | 1815 |
}else if (i_tmp!=RV_INT){ |
1811 | 1816 |
yyerror("invalid expression type, int expected\n"); |
1817 |
+ */ |
|
1812 | 1818 |
}else |
1813 | 1819 |
$$=$1; |
1814 | 1820 |
} |
... | ... |
@@ -1817,28 +1823,38 @@ single_case: |
1817 | 1823 |
CASE ct_rval COLON actions { |
1818 | 1824 |
$$=0; |
1819 | 1825 |
if ($2==0) yyerror ("bad case label"); |
1820 |
- else if (($$=mk_case_stm($2, $4))==0){ |
|
1821 |
- yyerror("internal error: memory allocation failure"); |
|
1826 |
+ else if ((($$=mk_case_stm($2, 0, $4, &i_tmp))==0) && (i_tmp==-10)){ |
|
1827 |
+ YYABORT; |
|
1828 |
+ } |
|
1829 |
+ } |
|
1830 |
+| CASE SLASH ct_rval COLON actions { |
|
1831 |
+ $$=0; |
|
1832 |
+ if ($3==0) yyerror ("bad case label"); |
|
1833 |
+ else if ((($$=mk_case_stm($3, 1, $5, &i_tmp))==0) && (i_tmp==-10)){ |
|
1822 | 1834 |
YYABORT; |
1823 | 1835 |
} |
1824 | 1836 |
} |
1825 | 1837 |
| CASE ct_rval COLON { |
1826 | 1838 |
$$=0; |
1827 | 1839 |
if ($2==0) yyerror ("bad case label"); |
1828 |
- else if (($$=mk_case_stm($2, 0))==0){ |
|
1829 |
- yyerror("internal error: memory allocation failure"); |
|
1840 |
+ else if ((($$=mk_case_stm($2, 0, 0, &i_tmp))==0) && (i_tmp==-10)){ |
|
1841 |
+ YYABORT; |
|
1842 |
+ } |
|
1843 |
+ } |
|
1844 |
+ | CASE SLASH ct_rval COLON { |
|
1845 |
+ $$=0; |
|
1846 |
+ if ($3==0) yyerror ("bad case label"); |
|
1847 |
+ else if ((($$=mk_case_stm($3, 1, 0, &i_tmp))==0) && (i_tmp==-10)){ |
|
1830 | 1848 |
YYABORT; |
1831 | 1849 |
} |
1832 | 1850 |
} |
1833 | 1851 |
| DEFAULT COLON actions { |
1834 |
- if (($$=mk_case_stm(0, $3))==0){ |
|
1835 |
- yyerror("internal error: memory allocation failure"); |
|
1852 |
+ if ((($$=mk_case_stm(0, 0, $3, &i_tmp))==0) && (i_tmp=-10)){ |
|
1836 | 1853 |
YYABORT; |
1837 | 1854 |
} |
1838 | 1855 |
} |
1839 | 1856 |
| DEFAULT COLON { |
1840 |
- if (($$=mk_case_stm(0, 0))==0){ |
|
1841 |
- yyerror("internal error: memory allocation failure"); |
|
1857 |
+ if ((($$=mk_case_stm(0, 0, 0, &i_tmp))==0) && (i_tmp==-10)){ |
|
1842 | 1858 |
YYABORT; |
1843 | 1859 |
} |
1844 | 1860 |
} |
... | ... |
@@ -1866,6 +1882,12 @@ switch_cmd: |
1866 | 1882 |
$$=0; |
1867 | 1883 |
if ($2==0) yyerror("bad expression in switch(...)"); |
1868 | 1884 |
else if ($4==0) yyerror ("bad switch body"); |
1885 |
+ else if (case_check_default($4)!=0) |
|
1886 |
+ yyerror_at(&$2->fpos, "bad switch(): too many " |
|
1887 |
+ "\"default:\" labels\n"); |
|
1888 |
+ else if (case_check_type($4)!=0) |
|
1889 |
+ yyerror_at(&$2->fpos, "bad switch(): mixed integer and" |
|
1890 |
+ " string/RE cases not allowed\n"); |
|
1869 | 1891 |
else{ |
1870 | 1892 |
$$=mk_action(SWITCH_T, 2, RVE_ST, $2, CASE_ST, $4); |
1871 | 1893 |
if ($$==0) { |
... | ... |
@@ -2926,15 +2948,52 @@ static void free_socket_id_lst(struct socket_id* lst) |
2926 | 2948 |
} |
2927 | 2949 |
|
2928 | 2950 |
|
2929 |
-static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a) |
|
2951 |
+/** create a temporary case statmenet structure. |
|
2952 |
+ * *err will be filled in case of error (return == 0): |
|
2953 |
+ * -1 - non constant expression |
|
2954 |
+ * -2 - expression error (bad type) |
|
2955 |
+ * -10 - memory allocation error |
|
2956 |
+ */ |
|
2957 |
+static struct case_stms* mk_case_stm(struct rval_expr* ct, int is_re, |
|
2958 |
+ struct action* a, int* err) |
|
2930 | 2959 |
{ |
2931 | 2960 |
struct case_stms* s; |
2961 |
+ struct rval_expr* bad_rve; |
|
2962 |
+ enum rval_type type, bad_t, exp_t; |
|
2963 |
+ enum match_str_type t; |
|
2964 |
+ |
|
2965 |
+ t=MATCH_UNKNOWN; |
|
2966 |
+ if (ct){ |
|
2967 |
+ /* if ct!=0 => case, else if ct==0 is a default */ |
|
2968 |
+ if (!rve_is_constant(ct)){ |
|
2969 |
+ yyerror_at(&ct->fpos, "non constant expression in case"); |
|
2970 |
+ *err=-1; |
|
2971 |
+ return 0; |
|
2972 |
+ } |
|
2973 |
+ if (rve_check_type(&type, ct, &bad_rve, &bad_t, &exp_t)!=1){ |
|
2974 |
+ yyerror_at(&ct->fpos, "bad expression: type mismatch:" |
|
2975 |
+ " %s instead of %s at (%d,%d)", |
|
2976 |
+ rval_type_name(bad_t), rval_type_name(exp_t), |
|
2977 |
+ bad_rve->fpos.s_line, bad_rve->fpos.s_col); |
|
2978 |
+ *err=-2; |
|
2979 |
+ return 0; |
|
2980 |
+ } |
|
2981 |
+ if (is_re) |
|
2982 |
+ t=MATCH_RE; |
|
2983 |
+ else if (type==RV_STR) |
|
2984 |
+ t=MATCH_STR; |
|
2985 |
+ else |
|
2986 |
+ t=MATCH_INT; |
|
2987 |
+ } |
|
2988 |
+ |
|
2932 | 2989 |
s=pkg_malloc(sizeof(*s)); |
2933 | 2990 |
if (s==0) { |
2934 |
- LOG(L_CRIT,"ERROR: cfg. parser: out of memory.\n"); |
|
2991 |
+ yyerror("internal error: memory allocation failure"); |
|
2992 |
+ *err=-10; |
|
2935 | 2993 |
} else { |
2936 | 2994 |
memset(s, 0, sizeof(*s)); |
2937 | 2995 |
s->ct_rve=ct; |
2996 |
+ s->type=t; |
|
2938 | 2997 |
s->actions=a; |
2939 | 2998 |
s->next=0; |
2940 | 2999 |
s->append=0; |
... | ... |
@@ -2942,6 +3001,47 @@ static struct case_stms* mk_case_stm(struct rval_expr* ct, struct action* a) |
2942 | 3001 |
return s; |
2943 | 3002 |
} |
2944 | 3003 |
|
3004 |
+ |
|
3005 |
+/* |
|
3006 |
+ * @return 0 on success, -1 on error. |
|
3007 |
+ */ |
|
3008 |
+static int case_check_type(struct case_stms* stms) |
|
3009 |
+{ |
|
3010 |
+ struct case_stms* c; |
|
3011 |
+ struct case_stms* s; |
|
3012 |
+ |
|
3013 |
+ for(c=stms; c ; c=c->next){ |
|
3014 |
+ if (!c->ct_rve) continue; |
|
3015 |
+ for (s=c->next; s; s=s->next){ |
|
3016 |
+ if (!s->ct_rve) continue; |
|
3017 |
+ if ((s->type!=c->type) && |
|
3018 |
+ !( (c->type==MATCH_STR || c->type==MATCH_RE) && |
|
3019 |
+ (s->type==MATCH_STR || s->type==MATCH_RE) ) ){ |
|
3020 |
+ yyerror_at(&s->ct_rve->fpos, "type mismatch in case"); |
|
3021 |
+ return -1; |
|
3022 |
+ } |
|
3023 |
+ } |
|
3024 |
+ } |
|
3025 |
+ return 0; |
|
3026 |
+} |
|
3027 |
+ |
|
3028 |
+ |
|
3029 |
+/* |
|
3030 |
+ * @return 0 on success, -1 on error. |
|
3031 |
+ */ |
|
3032 |
+static int case_check_default(struct case_stms* stms) |
|
3033 |
+{ |
|
3034 |
+ struct case_stms* c; |
|
3035 |
+ int default_no; |
|
3036 |
+ |
|
3037 |
+ default_no=0; |
|
3038 |
+ for(c=stms; c ; c=c->next) |
|
3039 |
+ if (c->ct_rve==0) default_no++; |
|
3040 |
+ return (default_no<=1)?0:-1; |
|
3041 |
+} |
|
3042 |
+ |
|
3043 |
+ |
|
3044 |
+ |
|
2945 | 3045 |
/* |
2946 | 3046 |
int main(int argc, char ** argv) |
2947 | 3047 |
{ |
... | ... |
@@ -72,7 +72,7 @@ enum { FORWARD_T=1, SEND_T, DROP_T, LOG_T, ERROR_T, ROUTE_T, EXEC_T, |
72 | 72 |
SET_HOST_T, SET_HOSTPORT_T, SET_USER_T, SET_USERPASS_T, |
73 | 73 |
SET_PORT_T, SET_URI_T, SET_HOSTPORTTRANS_T, |
74 | 74 |
IF_T, SWITCH_T /* only until fixup*/, |
75 |
- BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, WHILE_T, |
|
75 |
+ BLOCK_T, EVAL_T, SWITCH_JT_T, SWITCH_COND_T, MATCH_COND_T, WHILE_T, |
|
76 | 76 |
MODULE_T, MODULE3_T, MODULE4_T, MODULE5_T, MODULE6_T, MODULEX_T, |
77 | 77 |
SETFLAG_T, RESETFLAG_T, ISFLAGSET_T , |
78 | 78 |
AVPFLAG_OPER_T, |
... | ... |
@@ -101,7 +101,7 @@ enum { NOSUBTYPE=0, STRING_ST, NET_ST, NUMBER_ST, IP_ST, RE_ST, PROXY_ST, |
101 | 101 |
SELECT_ST, PVAR_ST, |
102 | 102 |
LVAL_ST, RVE_ST, |
103 | 103 |
RETCODE_ST, CASE_ST, |
104 |
- BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST |
|
104 |
+ BLOCK_ST, JUMPTABLE_ST, CONDTABLE_ST, MATCH_CONDTABLE_ST |
|
105 | 105 |
}; |
106 | 106 |
|
107 | 107 |
/* run flags */ |
... | ... |
@@ -233,13 +233,17 @@ struct rvalue* rval_new(enum rval_type t, union rval_val* v, int extra_size) |
233 | 233 |
{ |
234 | 234 |
struct rvalue* rv; |
235 | 235 |
|
236 |
- if (t==RV_STR && v && v->s.len) |
|
236 |
+ if (t==RV_STR && v && v->s.s) |
|
237 | 237 |
return rval_new_str(&v->s, extra_size); |
238 | 238 |
rv=rval_new_empty(extra_size); |
239 | 239 |
if (likely(rv)){ |
240 | 240 |
rv->type=t; |
241 |
- if (likely(v)){ |
|
241 |
+ if (likely(v && t!=RV_STR)){ |
|
242 | 242 |
rv->v=*v; |
243 |
+ }else if (t==RV_STR){ |
|
244 |
+ rv->v.s.s=&rv->buf[0]; |
|
245 |
+ rv->v.s.len=0; |
|
246 |
+ if (likely(extra_size)) rv->v.s.s[0]=0; |
|
243 | 247 |
}else |
244 | 248 |
memset (&rv->v, 0, sizeof(rv->v)); |
245 | 249 |
} |
... | ... |
@@ -22,6 +22,7 @@ |
22 | 22 |
* History: |
23 | 23 |
* -------- |
24 | 24 |
* 2009-02-02 initial version (andrei) |
25 |
+ * 2009-02-19 string and RE switch support added (andrei) |
|
25 | 26 |
*/ |
26 | 27 |
|
27 | 28 |
#include "switch.h" |
... | ... |
@@ -84,6 +85,32 @@ static struct switch_jmp_table* mk_switch_jmp_table(int jmp_size, int rest) |
84 | 85 |
|
85 | 86 |
|
86 | 87 |
|
88 |
+/** create a match cond table structure (pkg_malloc'ed). |
|
89 |
+ * @return 0 on error, pointer on success |
|
90 |
+ */ |
|
91 |
+static struct match_cond_table* mk_match_cond_table(int n) |
|
92 |
+{ |
|
93 |
+ struct match_cond_table* mct; |
|
94 |
+ |
|
95 |
+ /* allocate everything in a single block, better for cache locality */ |
|
96 |
+ mct=pkg_malloc(ROUND_POINTER(sizeof(*mct))+ |
|
97 |
+ ROUND_POINTER(n*sizeof(mct->match[0]))+ |
|
98 |
+ n*sizeof(mct->jump[0])); |
|
99 |
+ if (mct==0) return 0; |
|
100 |
+ mct->n=n; |
|
101 |
+ mct->match=(struct match_str*)((char*)mct+ROUND_POINTER(sizeof(*mct))); |
|
102 |
+ mct->jump=(struct action**) |
|
103 |
+ ((char*)mct->match+ROUND_POINTER(n*sizeof(mct->match[0]))); |
|
104 |
+ mct->def=0; |
|
105 |
+ return mct; |
|
106 |
+} |
|
107 |
+ |
|
108 |
+ |
|
109 |
+ |
|
110 |
+static int fix_match(struct action* t); |
|
111 |
+ |
|
112 |
+ |
|
113 |
+ |
|
87 | 114 |
void destroy_case_stms(struct case_stms *lst) |
88 | 115 |
{ |
89 | 116 |
struct case_stms* l; |
... | ... |
@@ -138,6 +165,11 @@ int fix_switch(struct action* t) |
138 | 165 |
def_jmp_bm=0; |
139 | 166 |
labels_no=0; |
140 | 167 |
default_found=0; |
168 |
+ /* check if string switch (first case is string or RE) */ |
|
169 |
+ for (c=(struct case_stms*)t->val[1].u.data; c && c->is_default; c=c->next); |
|
170 |
+ if (c && (c->type==MATCH_STR || c->type==MATCH_RE)) |
|
171 |
+ return fix_match(t); |
|
172 |
+ |
|
141 | 173 |
sw_rve=(struct rval_expr*)t->val[0].u.data; |
142 | 174 |
/* handle null actions: optimize away if no |
143 | 175 |
sideffects */ |
... | ... |
@@ -159,12 +191,17 @@ int fix_switch(struct action* t) |
159 | 191 |
n=0; |
160 | 192 |
for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){ |
161 | 193 |
if (c->ct_rve){ |
194 |
+ if (c->type!=MATCH_INT){ |
|
195 |
+ LOG(L_ERR, "ERROR: fix_switch: wrong case type %d (int" |
|
196 |
+ "expected)\n", c->type); |
|
197 |
+ return E_UNSPEC; |
|
198 |
+ } |
|
162 | 199 |
if (!rve_is_constant(c->ct_rve)){ |
163 | 200 |
LOG(L_ERR, "ERROR: fix_switch: non constant " |
164 | 201 |
"expression in case\n"); |
165 | 202 |
return E_BUG; |
166 | 203 |
} |
167 |
- if (rval_expr_eval_int(0, 0, &c->int_label, c->ct_rve) |
|
204 |
+ if (rval_expr_eval_int(0, 0, &c->label.match_int, c->ct_rve) |
|
168 | 205 |
<0){ |
169 | 206 |
LOG(L_ERR, "ERROR: fix_switch: case expression" |
170 | 207 |
" (%d,%d) has non-interger type\n", |
... | ... |
@@ -183,7 +220,7 @@ int fix_switch(struct action* t) |
183 | 220 |
return E_UNSPEC; |
184 | 221 |
} |
185 | 222 |
default_found=1; |
186 |
- c->int_label=-1; |
|
223 |
+ c->label.match_int=-1; |
|
187 | 224 |
c->is_default=1; |
188 | 225 |
def_a=c->actions; |
189 | 226 |
} |
... | ... |
@@ -241,14 +278,14 @@ int fix_switch(struct action* t) |
241 | 278 |
def_jmp_bm=tail; |
242 | 279 |
} else { |
243 | 280 |
for (j=0; j<n; j++){ |
244 |
- if (cond[j]==c->int_label){ |
|
281 |
+ if (cond[j]==c->label.match_int){ |
|
245 | 282 |
LOG(L_ERR, "ERROR: fix_switch: duplicate case (%d,%d)\n", |
246 | 283 |
c->ct_rve->fpos.s_line, c->ct_rve->fpos.s_col); |
247 | 284 |
ret=E_UNSPEC; |
248 | 285 |
goto error; |
249 | 286 |
} |
250 | 287 |
} |
251 |
- cond[n]=c->int_label; |
|
288 |
+ cond[n]=c->label.match_int; |
|
252 | 289 |
jmp_bm[n]=tail; |
253 | 290 |
n++; |
254 | 291 |
} |
... | ... |
@@ -371,4 +408,300 @@ error: |
371 | 408 |
return ret; |
372 | 409 |
} |
373 | 410 |
|
411 |
+ |
|
412 |
+ |
|
413 |
+/** fixup function for MATCH_T actions. |
|
414 |
+ * can produce 3 different action types: |
|
415 |
+ * - BLOCK_T (actions) - actions grouped in a block, break ends the block |
|
416 |
+ * execution. |
|
417 |
+ * - EVAL_T (cond) - null switch block, but the condition has to be |
|
418 |
+ * evaluated due to possible side-effects. |
|
419 |
+ * - MATCH_COND_T(cond, jumps) - condition table |
|
420 |
+ */ |
|
421 |
+static int fix_match(struct action* t) |
|
422 |
+{ |
|
423 |
+ struct case_stms* c; |
|
424 |
+ int n, i, j, ret; |
|
425 |
+ struct action* a; |
|
426 |
+ struct action* block; |
|
427 |
+ struct action* def_a; |
|
428 |
+ struct action* action_lst; |
|
429 |
+ struct action** tail; |
|
430 |
+ struct match_cond_table* mct; |
|
431 |
+ int labels_no; |
|
432 |
+ struct action** def_jmp_bm; |
|
433 |
+ struct match_str* match; |
|
434 |
+ struct action*** jmp_bm; |
|
435 |
+ int default_found; |
|
436 |
+ struct rval_expr* m_rve; |
|
437 |
+ struct rvalue* rv; |
|
438 |
+ regex_t* regex; |
|
439 |
+ str s; |
|
440 |
+ |
|
441 |
+ ret=E_BUG; |
|
442 |
+ match=0; |
|
443 |
+ jmp_bm=0; |
|
444 |
+ def_jmp_bm=0; |
|
445 |
+ labels_no=0; |
|
446 |
+ default_found=0; |
|
447 |
+ rv=0; |
|
448 |
+ s.s=0; |
|
449 |
+ s.len=0; |
|
450 |
+ m_rve=(struct rval_expr*)t->val[0].u.data; |
|
451 |
+ /* handle null actions: optimize away if no |
|
452 |
+ sideffects */ |
|
453 |
+ if (t->val[1].u.data==0){ |
|
454 |
+ if (!rve_has_side_effects(m_rve)){ |
|
455 |
+ t->type=BLOCK_T; |
|
456 |
+ rve_destroy(m_rve); |
|
457 |
+ t->val[0].type=BLOCK_ST; |
|
458 |
+ t->val[0].u.data=0; |
|
459 |
+ DBG("MATCH: null switch optimized away\n"); |
|
460 |
+ }else{ |
|
461 |
+ t->type=EVAL_T; |
|
462 |
+ t->val[0].type=RVE_ST; |
|
463 |
+ DBG("MATCH: null switch turned to EVAL_T\n"); |
|
464 |
+ } |
|
465 |
+ return 0; |
|
466 |
+ } |
|
467 |
+ def_a=0; |
|
468 |
+ n=0; |
|
469 |
+ for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){ |
|
470 |
+ if (c->ct_rve){ |
|
471 |
+ if (c->type!=MATCH_STR && c->type!=MATCH_RE){ |
|
472 |
+ LOG(L_ERR, "ERROR: fix_match: wrong case type %d (string" |
|
473 |
+ "or RE expected)\n", c->type); |
|
474 |
+ return E_UNSPEC; |
|
475 |
+ } |
|
476 |
+ if (!rve_is_constant(c->ct_rve)){ |
|
477 |
+ LOG(L_ERR, "ERROR: fix_match: non constant " |
|
478 |
+ "expression in case\n"); |
|
479 |
+ ret=E_BUG; |
|
480 |
+ goto error; |
|
481 |
+ } |
|
482 |
+ if ((rv=rval_expr_eval(0, 0, c->ct_rve)) == 0 ){ |
|
483 |
+ LOG(L_ERR, "ERROR: fix_match: bad case expression" |
|
484 |
+ " (%d,%d)\n", |
|
485 |
+ c->ct_rve->fpos.s_line, |
|
486 |
+ c->ct_rve->fpos.s_col); |
|
487 |
+ ret=E_BUG; |
|
488 |
+ goto error; |
|
489 |
+ } |
|
490 |
+ if (rval_get_str(0, 0, &s, rv, 0)<0){ |
|
491 |
+ LOG(L_ERR, "ERROR: fix_match (%d,%d): out of memory?\n", |
|
492 |
+ c->ct_rve->fpos.s_line, |
|
493 |
+ c->ct_rve->fpos.s_col); |
|
494 |
+ ret=E_BUG; |
|
495 |
+ goto error; |
|
496 |
+ } |
|
497 |
+ if (c->type==MATCH_RE){ |
|
498 |
+ if ((regex=pkg_malloc(sizeof(regex_t))) == 0){ |
|
499 |
+ LOG(L_ERR, "ERROR: fix_match: out of memory\n"); |
|
500 |
+ ret=E_OUT_OF_MEM; |
|
501 |
+ goto error; |
|
502 |
+ } |
|
503 |
+ if (regcomp(regex, s.s, |
|
504 |
+ REG_EXTENDED | REG_NOSUB | c->re_flags) !=0){ |
|
505 |
+ pkg_free(regex); |
|
506 |
+ regex=0; |
|
507 |
+ LOG(L_ERR, "ERROR: fix_match (%d, %d): bad regular" |
|
508 |
+ " expression %.*s\n", |
|
509 |
+ c->ct_rve->fpos.s_line, |
|
510 |
+ c->ct_rve->fpos.s_col, |
|
511 |
+ s.len, ZSW(s.s)); |
|
512 |
+ ret=E_UNSPEC; |
|
513 |
+ goto error; |
|
514 |
+ } |
|
515 |
+ c->label.match_re=regex; |
|
516 |
+ regex=0; |
|
517 |
+ }else if (c->type==MATCH_STR){ |
|
518 |
+ c->label.match_str=s; |
|
519 |
+ s.s=0; |
|
520 |
+ s.len=0; |
|
521 |
+ }else{ |
|
522 |
+ LOG(L_CRIT, "BUG: fix_match (%d,%d): wrong case type %d\n", |
|
523 |
+ c->ct_rve->fpos.s_line, c->ct_rve->fpos.s_col, |
|
524 |
+ c->type); |
|
525 |
+ ret=E_BUG; |
|
526 |
+ goto error; |
|
527 |
+ } |
|
528 |
+ c->is_default=0; |
|
529 |
+ n++; /* count only non-default cases */ |
|
530 |
+ /* cleanup */ |
|
531 |
+ rval_destroy(rv); |
|
532 |
+ rv=0; |
|
533 |
+ if (s.s){ |
|
534 |
+ pkg_free(s.s); |
|
535 |
+ s.s=0; |
|
536 |
+ s.len=0; |
|
537 |
+ } |
|
538 |
+ }else{ |
|
539 |
+ if (default_found){ |
|
540 |
+ LOG(L_ERR, "ERROR: fix_match: more then one \"default\"" |
|
541 |
+ " label found (%d, %d)\n", |
|
542 |
+ c->ct_rve->fpos.s_line, |
|
543 |
+ c->ct_rve->fpos.s_col); |
|
544 |
+ ret=E_UNSPEC; |
|
545 |
+ goto error; |
|
546 |
+ } |
|
547 |
+ default_found=1; |
|
548 |
+ c->is_default=1; |
|
549 |
+ def_a=c->actions; |
|
550 |
+ } |
|
551 |
+ if ( c->actions && ((ret=fix_actions(c->actions))<0)) |
|
552 |
+ goto error; |
|
553 |
+ } |
|
554 |
+ DBG("MATCH: %d cases, %d default\n", n, default_found); |
|
555 |
+ /*: handle n==0 (no case only a default:) */ |
|
556 |
+ if (n==0){ |
|
557 |
+ if (default_found){ |
|
558 |
+ if (!rve_has_side_effects(m_rve)){ |
|
559 |
+ t->type=BLOCK_T; |
|
560 |
+ rve_destroy(m_rve); |
|
561 |
+ destroy_case_stms(t->val[1].u.data); |
|
562 |
+ t->val[0].type=BLOCK_ST; |
|
563 |
+ t->val[0].u.data=def_a; |
|
564 |
+ t->val[1].type=0; |
|
565 |
+ t->val[1].u.data=0; |
|
566 |
+ DBG("MATCH: default only switch optimized away (BLOCK_T)\n"); |
|
567 |
+ return 0; |
|
568 |
+ } |
|
569 |
+ DBG("MATCH: default only switch with side-effect...\n"); |
|
570 |
+ }else{ |
|
571 |
+ LOG(L_CRIT, "BUG: fix_match: empty switch not expected at this" |
|
572 |
+ " point\n"); |
|
573 |
+ ret=E_BUG; |
|
574 |
+ goto error; |
|
575 |
+ } |
|
576 |
+ } |
|
577 |
+ labels_no=n; |
|
578 |
+ match=pkg_malloc(sizeof(match[0])*n); |
|
579 |
+ jmp_bm=pkg_malloc(sizeof(jmp_bm[0])*n); |
|
580 |
+ if (match==0 || jmp_bm==0){ |
|
581 |
+ LOG(L_ERR, "ERROR: fix_match: memory allocation failure\n"); |
|
582 |
+ ret=E_OUT_OF_MEM; |
|
583 |
+ goto error; |
|
584 |
+ } |
|
585 |
+ |
|
586 |
+ /* fill condition table and jump point bookmarks and "flatten" the action |
|
587 |
+ lists (transform them into a single list for the entire switch, rather |
|
588 |
+ then one block per case ) */ |
|
589 |
+ n=0; |
|
590 |
+ action_lst=0; |
|
591 |
+ tail=&action_lst; |
|
592 |
+ for (c=(struct case_stms*)t->val[1].u.data; c; c=c->next){ |
|
593 |
+ a=c->actions; |
|
594 |
+ if (a){ |
|
595 |
+ for (; a->next; a=a->next); |
|
596 |
+ if (action_lst==0) |
|
597 |
+ action_lst=c->actions; |
|
598 |
+ else |
|
599 |
+ *tail=c->actions; |
|
600 |
+ } |
|
601 |
+ if (c->is_default){ |
|
602 |
+ def_jmp_bm=tail; |
|
603 |
+ } else{ |
|
604 |
+ match[n].type=c->type; |
|
605 |
+ if (match[n].type == MATCH_STR){ |
|
606 |
+ for (j=0; j<n; j++){ |
|
607 |
+ if ( match[j].type == c->type && |
|
608 |
+ match[j].l.s.len == c->label.match_str.len && |
|
609 |
+ memcmp(match[j].l.s.s, c->label.match_str.s, |
|
610 |
+ match[j].l.s.len) == 0 ){ |
|
611 |
+ LOG(L_ERR, "ERROR: fix_match: duplicate case" |
|
612 |
+ " (%d,%d)\n", c->ct_rve->fpos.s_line, |
|
613 |
+ c->ct_rve->fpos.s_col); |
|
614 |
+ ret=E_UNSPEC; |
|
615 |
+ goto error; |
|
616 |
+ } |
|
617 |
+ } |
|
618 |
+ match[n].flags=0; |
|
619 |
+ match[n].l.s=c->label.match_str; |
|
620 |
+ c->label.match_str.s=0; /* prevent s being freed */ |
|
621 |
+ c->label.match_str.len=0; |
|
622 |
+ } else { |
|
623 |
+ match[n].flags=c->re_flags | REG_EXTENDED | REG_NOSUB; |
|
624 |
+ match[n].l.regex=c->label.match_re; |
|
625 |
+ c->label.match_re=0; |
|
626 |
+ } |
|
627 |
+ jmp_bm[n]=tail; |
|
628 |
+ n++; |
|
629 |
+ } |
|
630 |
+ if (c->actions) |
|
631 |
+ tail=&a->next; |
|
632 |
+ } |
|
633 |
+ /* handle constant rve w/ no side-effects: replace the whole case |
|
634 |
+ with the case rve block */ |
|
635 |
+ if ( (scr_opt_lev>=2) && |
|
636 |
+ !rve_has_side_effects(m_rve) && rve_is_constant(m_rve)){ |
|
637 |
+ if ((rv=rval_expr_eval(0, 0, m_rve)) == 0){ |
|
638 |
+ LOG(L_ERR, "ERROR: fix_match: bad expression (%d,%d)\n", |
|
639 |
+ m_rve->fpos.s_line, m_rve->fpos.s_col); |
|
640 |
+ ret=E_UNSPEC; |
|
641 |
+ goto error; |
|
642 |
+ } |
|
643 |
+ if (rval_get_str(0, 0, &s, rv, 0) < 0 ){ |
|
644 |
+ LOG(L_ERR, "ERROR: fix_match (%d,%d): bad string expression\n", |
|
645 |
+ m_rve->fpos.s_line, |
|
646 |
+ m_rve->fpos.s_col); |
|
647 |
+ ret=E_UNSPEC; |
|
648 |
+ goto error; |
|
649 |
+ } |
|
650 |
+ /* start with the "default:" value in case nothing is found */ |
|
651 |
+ block=def_jmp_bm?*def_jmp_bm:0; |
|
652 |
+ for (i=0; i<n; i++){ |
|
653 |
+ if (((match[i].type == MATCH_STR) && (match[i].l.s.len == s.len) && |
|
654 |
+ (memcmp(match[i].l.s.s, s.s, s.len) == 0)) || |
|
655 |
+ ((match[i].type == MATCH_RE) && |
|
656 |
+ regexec(match[i].l.regex, s.s, 0, 0, 0) == 0) ) { |
|
657 |
+ block=*jmp_bm[i]; |
|
658 |
+ break; |
|
659 |
+ } |
|
660 |
+ } |
|
661 |
+ DBG("MATCH: constant switch(\"%.*s\") with %d cases optimized away" |
|
662 |
+ " to case no. %d\n", s.len, ZSW(s.s), n, i); |
|
663 |
+ /* cleanup */ |
|
664 |
+ rval_destroy(rv); |
|
665 |
+ rv=0; |
|
666 |
+ pkg_free(s.s); |
|
667 |
+ s.s=0; |
|
668 |
+ s.len=0; |
|
669 |
+ ret=0; |
|
670 |
+ /* replace with BLOCK_ST */ |
|
671 |
+ rve_destroy(m_rve); |
|
672 |
+ destroy_case_stms(t->val[1].u.data); |
|
673 |
+ t->type=BLOCK_T; |
|
674 |
+ t->val[0].type=BLOCK_ST; |
|
675 |
+ t->val[0].u.data=block; |
|
676 |
+ t->val[1].type=0; |
|
677 |
+ t->val[1].u.data=0; |
|
678 |
+ goto end; |
|
679 |
+ } |
|
680 |
+ mct=mk_match_cond_table(n); |
|
681 |
+ if (mct==0){ |
|
682 |
+ LOG(L_ERR, "ERROR: fix_match: memory allocation error\n"); |
|
683 |
+ ret=E_OUT_OF_MEM; |
|
684 |
+ goto error; |
|
685 |
+ } |
|
686 |
+ mct->n=n; |
|
687 |
+ for (i=0; i<n; i++){ |
|
688 |
+ mct->match[i]=match[i]; |
|
689 |
+ mct->jump[i]=*jmp_bm[i]; |
|
690 |
+ } |
|
691 |
+ mct->def=def_jmp_bm?*def_jmp_bm:0; |
|
692 |
+ t->type=MATCH_COND_T; |
|
693 |
+ t->val[1].type=MATCH_CONDTABLE_ST; |
|
694 |
+ t->val[1].u.data=mct; |
|
695 |
+ DBG("MATCH: optimized to match condtable (%d) default: %s\n ", |
|
696 |
+ mct->n, mct->def?"yes":"no"); |
|
697 |
+ ret=0; |
|
698 |
+end: |
|
699 |
+error: |
|
700 |
+ if (match) pkg_free(match); |
|
701 |
+ if (jmp_bm) pkg_free(jmp_bm); |
|
702 |
+ /* cleanup rv & s*/ |
|
703 |
+ if (rv) rval_destroy(rv); |
|
704 |
+ if (s.s) pkg_free(s.s); |
|
705 |
+ return ret; |
|
706 |
+} |
|
374 | 707 |
/* vi: set ts=4 sw=4 tw=79:ai:cindent: */ |
... | ... |
@@ -27,15 +27,24 @@ |
27 | 27 |
#ifndef __switch_h |
28 | 28 |
#define __switch_h |
29 | 29 |
|
30 |
+#include <regex.h> |
|
31 |
+ |
|
30 | 32 |
#include "route_struct.h" |
31 | 33 |
|
34 |
+ |
|
32 | 35 |
struct case_stms{ |
33 | 36 |
struct rval_expr* ct_rve; |
34 | 37 |
struct action* actions; |
35 | 38 |
struct case_stms* next; |
36 | 39 |
struct case_stms** append; |
37 |
- int int_label; |
|
40 |
+ int type; /**< type: MATCH_UNKOWN, MATCH_INT, MATCH_STR, MATCH_RE */ |
|
41 |
+ int re_flags; /**< used only for REs */ |
|
38 | 42 |
int is_default; |
43 |
+ union { |
|
44 |
+ int match_int; |
|
45 |
+ str match_str; |
|
46 |
+ regex_t* match_re; |
|
47 |
+ } label; /**< fixed case argument */ |
|
39 | 48 |
}; |
40 | 49 |
|
41 | 50 |
|
... | ... |
@@ -43,7 +52,7 @@ struct switch_cond_table{ |
43 | 52 |
int n; /**< size */ |
44 | 53 |
int* cond; /**< int labels array */ |
45 | 54 |
struct action** jump; /**< jump points array */ |
46 |
- struct action* def; /**< default jump */ |
|
55 |
+ struct action* def; /**< default jump */ |
|
47 | 56 |
}; |
48 | 57 |
|
49 | 58 |
|
... | ... |
@@ -54,6 +63,25 @@ struct switch_jmp_table{ |
54 | 63 |
struct switch_cond_table rest; /**< normal cond. table for the rest */ |
55 | 64 |
}; |
56 | 65 |
|
66 |
+ |
|
67 |
+enum match_str_type { MATCH_UNKNOWN, MATCH_INT, MATCH_STR, MATCH_RE }; |
|
68 |
+ |
|
69 |
+struct match_str{ |
|
70 |
+ enum match_str_type type;/**< string or RE */ |
|
71 |
+ int flags; /**< flags for re */ |
|
72 |
+ union{ |
|
73 |
+ str s; /* string */ |
|
74 |
+ regex_t* regex; /**< compiled regex */ |
|
75 |
+ }l; |
|
76 |
+}; |
|
77 |
+ |
|
78 |
+struct match_cond_table{ |
|
79 |
+ int n; /**< size */ |
|
80 |
+ struct match_str* match; /**< match array */ |
|
81 |
+ struct action** jump; /**< jump points array */ |
|
82 |
+ struct action* def; /**< default jmp */ |
|
83 |
+}; |
|
84 |
+ |
|
57 | 85 |
int fix_switch(struct action* t); |
58 | 86 |
|
59 | 87 |
#endif /*__switch_h*/ |