core expr eval: special handling for undef cmp expr
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 28 Apr 2009 14:07:23 +0000 (16:07 +0200)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 28 Apr 2009 14:07:23 +0000 (16:07 +0200)
undef == expression is evaluated in the following way:
 - default:
           (type_of(expression))(undef) == expression.
   If the expression is undef too, the whole comparison it's
   evaluated to true (internally it's evaluated to
   (str) undef == (str) undef =>    "" == "" => true).
   E.g.:  undef == 0   -> (int)undef == 0   -> 0 == 0  -> true
          undef == "a" -> (str)undef == "a" -> ""=="a" -> false
          undef == undef -> true
 - if UNDEF_EQ_UNDEF_TRUE is defined, the == comparison is always
   false except for undef == undef which is true.
   E.g.:  undef == 0 -> false ; undef == "" false
 - if UNDEF_EQ_ALWAY_FALSE is defined, the result is always false

!= is always the reverse of ==.

Note: this covers only the case when undef is on the _left_ side
 (the case in which we do not know what to convert undef to). If
 the undef is on the right side, it will work like for any other
 comparison: expr == undef => expr == (type_of(expr)) undef.

rvalue.c

index 14c1f16..2961ee9 100644 (file)
--- a/rvalue.c
+++ b/rvalue.c
  *  2009-04-24  added support for defined, strempty, strlen (andrei)
  *  2009-04-28  int and str automatic conversions: (int)undef=0,
  *               (str)undef="", (int)""=0, (int)"123"=123, (int)"abc"=0
- *               (andrei)
+ *              handle undef == expr, in function of the UNDEF_EQ_* defines.
+ *              (andrei)
+ */
+
+/* special defines:
+ *
+ *  UNDEF_EQ_* - how to behave when undef is on the right side of a generic
+ *               compare operator
+ *  UNDEF_EQ_ALWAYS_FALSE:  undef  == something  is always false
+ *  UNDEF_EQ_UNDEF_TRUE  :  undef == something false except for undef==undef
+ *                          which is true
+ *  no UNDEF_EQ* define  :  undef == expr => convert undef to typeof(expr)
+ *                          and perorm normal ==. undef == undef will be
+ *                          converted to string and it will be true
+ *                          ("" == "")
+ * NOTE: expr == undef, with defined(expr) is always evaluated this way:
+         expr == (type_of(expr))undef
  */
 
 #include "rvalue.h"
@@ -1549,7 +1565,7 @@ 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 rval_cache c1, c2;
        struct rvalue* rv1;
        struct rvalue* rv2;
        
@@ -1616,10 +1632,14 @@ int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
                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 */
+                        *   use string diff.
+                        * if left is int eval as int using int diff
+                        * if left is undef, look at right and convert to right type
+                        */
                        rval_cache_init(&c1);
                        if (unlikely( (ret=rval_expr_eval_rvint(h, msg, &rv1, &i1,
                                                                                                        rve->left.rve, &c1))<0)){
+                               /* error */
                                rval_cache_clean(&c1);
                                break;
                        }
@@ -1628,20 +1648,67 @@ int rval_expr_eval_int( struct run_act_ctx* h, struct sip_msg* msg,
                                rval_cache_clean(&c1);
                                if (unlikely( (ret=rval_expr_eval_int(h, msg, &i2,
                                                                                                                rve->right.rve)) <0) )
-                                       break;
+                                       break;  /* error */
                                ret=int_intop2(res, rve->op, i1, i2);
                        }else{
-                               if (unlikely((rv2=rval_expr_eval(h, msg,
-                                                                                                       rve->right.rve))==0)){
+                               /* not int => str or undef */
+                               /* check for undefined left operand */
+                               if (unlikely( c1.cache_type!=RV_CACHE_EMPTY &&
+                                                               c1.val_type==RV_NONE)){
+#ifdef UNDEF_EQ_ALWAYS_FALSE
+                                       /* undef == something  always false
+                                          undef != something  always true*/
+                                       ret=(rve->op==RVE_DIFF_OP);
+#elif defined UNDEF_EQ_UNDEF_TRUE
+                                       /* undef == something defined always false
+                                          undef == undef true */
+                                       if (int_rve_defined(h, msg, &i2, rve->right.rve)<0){
+                                               /* error */
+                                               rval_cache_clean(&c1);
+                                               rval_destroy(rv1);
+                                               break;
+                                       }
+                                       ret=(!i2) ^ (rve->op==RVE_DIFF_OP);
+#else  /* ! UNDEF_EQ_* */
+                                       /*  undef == val
+                                        *  => convert to (type_of(val)) (undef) == val */
+                                       rval_cache_init(&c2);
+                                       if (unlikely( (ret=rval_expr_eval_rvint(h, msg, &rv2, &i2,
+                                                                                                       rve->right.rve, &c2))<0)){
+                                               /* error */
+                                               rval_cache_clean(&c1);
+                                               rval_cache_clean(&c2);
+                                               rval_destroy(rv1);
+                                               break;
+                                       }
+                                       if (rv2==0){
+                                               /* int */
+                                               ret=int_intop2(res, rve->op, 0 /* undef */, i2);
+                                       }else{
+                                               /* str or undef */
+                                               ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1,
+                                                                                       rv2, &c2);
+                                               rval_cache_clean(&c2);
+                                               rval_destroy(rv2);
+                                       }
+#endif /* UNDEF_EQ_* */
+                                       rval_cache_clean(&c1);
                                        rval_destroy(rv1);
+                               }else{ 
+                                       /* left value == defined and != int => str
+                                        * => lval == (str) val */
+                                       if (unlikely((rv2=rval_expr_eval(h, msg,
+                                                                                                               rve->right.rve))==0)){
+                                               /* error */
+                                               rval_destroy(rv1);
+                                               rval_cache_clean(&c1);
+                                               break;
+                                       }
+                                       ret=rval_str_lop2(h, msg, res, rve->op, rv1, &c1, rv2, 0);
                                        rval_cache_clean(&c1);
-                                       ret=-1;
-                                       break;
+                                       rval_destroy(rv1);
+                                       rval_destroy(rv2);
                                }
-                               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