Index: /trunk/src/kmk/expreval.c
===================================================================
--- /trunk/src/kmk/expreval.c	(revision 3397)
+++ /trunk/src/kmk/expreval.c	(revision 3398)
@@ -41,4 +41,5 @@
 #include "debug.h"
 #include "hash.h"
+#include "version_compare.h"
 #include <ctype.h>
 #ifndef _MSC_VER
@@ -181,5 +182,5 @@
 /** Operator start character map.
  * This indicates which characters that are starting operators and which aren't. */
-static char g_auchOpStartCharMap[256];
+static unsigned char g_auchOpStartCharMap[256];
 /** Whether we've initialized the map. */
 static int g_fExprInitializedMap = 0;
@@ -1239,5 +1240,31 @@
 
 /**
- * Less than or equal
+ * Less than or equal, version string.
+ *
+ * @returns Status code.
+ * @param   pThis       The instance.
+ */
+static EXPRRET expr_op_ver_less_or_equal_than(PEXPR pThis)
+{
+    EXPRRET     rc = kExprRet_Ok;
+    PEXPRVAR    pVar1 = &pThis->aVars[pThis->iVar - 1];
+    PEXPRVAR    pVar2 = &pThis->aVars[pThis->iVar];
+
+    rc = expr_var_unify_types(pThis, pVar1, pVar2, "vle");
+    if (rc >= kExprRet_Ok)
+    {
+        if (!expr_var_is_string(pVar1))
+            expr_var_assign_bool(pVar1, pVar1->uVal.i <= pVar2->uVal.i);
+        else
+            expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) <= 0);
+    }
+
+    expr_pop_and_delete_var(pThis);
+    return rc;
+}
+
+
+/**
+ * Less than or equal.
  *
  * @returns Status code.
@@ -1265,4 +1292,30 @@
 
 /**
+ * Less than, version string.
+ *
+ * @returns Status code.
+ * @param   pThis       The instance.
+ */
+static EXPRRET expr_op_ver_less_than(PEXPR pThis)
+{
+    EXPRRET     rc = kExprRet_Ok;
+    PEXPRVAR    pVar1 = &pThis->aVars[pThis->iVar - 1];
+    PEXPRVAR    pVar2 = &pThis->aVars[pThis->iVar];
+
+    rc = expr_var_unify_types(pThis, pVar1, pVar2, "vlt");
+    if (rc >= kExprRet_Ok)
+    {
+        if (!expr_var_is_string(pVar1))
+            expr_var_assign_bool(pVar1, pVar1->uVal.i < pVar2->uVal.i);
+        else
+            expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) < 0);
+    }
+
+    expr_pop_and_delete_var(pThis);
+    return rc;
+}
+
+
+/**
  * Less than.
  *
@@ -1291,5 +1344,31 @@
 
 /**
- * Greater or equal than
+ * Greater or equal than, version string.
+ *
+ * @returns Status code.
+ * @param   pThis       The instance.
+ */
+static EXPRRET expr_op_ver_greater_or_equal_than(PEXPR pThis)
+{
+    EXPRRET     rc = kExprRet_Ok;
+    PEXPRVAR    pVar1 = &pThis->aVars[pThis->iVar - 1];
+    PEXPRVAR    pVar2 = &pThis->aVars[pThis->iVar];
+
+    rc = expr_var_unify_types(pThis, pVar1, pVar2, "vge");
+    if (rc >= kExprRet_Ok)
+    {
+        if (!expr_var_is_string(pVar1))
+            expr_var_assign_bool(pVar1, pVar1->uVal.i >= pVar2->uVal.i);
+        else
+            expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) >= 0);
+    }
+
+    expr_pop_and_delete_var(pThis);
+    return rc;
+}
+
+
+/**
+ * Greater or equal than.
  *
  * @returns Status code.
@@ -1317,4 +1396,30 @@
 
 /**
+ * Greater than, version string.
+ *
+ * @returns Status code.
+ * @param   pThis       The instance.
+ */
+static EXPRRET expr_op_ver_greater_than(PEXPR pThis)
+{
+    EXPRRET     rc = kExprRet_Ok;
+    PEXPRVAR    pVar1 = &pThis->aVars[pThis->iVar - 1];
+    PEXPRVAR    pVar2 = &pThis->aVars[pThis->iVar];
+
+    rc = expr_var_unify_types(pThis, pVar1, pVar2, "vgt");
+    if (rc >= kExprRet_Ok)
+    {
+        if (!expr_var_is_string(pVar1))
+            expr_var_assign_bool(pVar1, pVar1->uVal.i > pVar2->uVal.i);
+        else
+            expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) > 0);
+    }
+
+    expr_pop_and_delete_var(pThis);
+    return rc;
+}
+
+
+/**
  * Greater than.
  *
@@ -1343,21 +1448,97 @@
 
 /**
- * Equal.
- *
- * @returns Status code.
- * @param   pThis       The instance.
- */
-static EXPRRET expr_op_equal(PEXPR pThis)
+ * Equal, version strings.
+ *
+ * @returns Status code.
+ * @param   pThis       The instance.
+ */
+static EXPRRET expr_op_ver_equal(PEXPR pThis)
 {
     EXPRRET     rc = kExprRet_Ok;
     PEXPRVAR    pVar1 = &pThis->aVars[pThis->iVar - 1];
     PEXPRVAR    pVar2 = &pThis->aVars[pThis->iVar];
+    int const   fIsString1 = expr_var_is_string(pVar1);
 
     /*
      * The same type?
      */
-    if (expr_var_is_string(pVar1) == expr_var_is_string(pVar2))
-    {
-        if (!expr_var_is_string(pVar1))
+    if (fIsString1 == expr_var_is_string(pVar2))
+    {
+        if (!fIsString1)
+            /* numbers are simple */
+            expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
+        else
+        {
+            /* try a normal string compare. */
+            expr_var_make_simple_string(pVar1);
+            expr_var_make_simple_string(pVar2);
+            if (!version_compare(pVar1->uVal.psz, pVar2->uVal.psz))
+                expr_var_assign_bool(pVar1, 1);
+            /* try convert and compare as number instead. */
+            else if (   expr_var_try_make_num(pVar1) >= kExprRet_Ok
+                     && expr_var_try_make_num(pVar2) >= kExprRet_Ok)
+                expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
+            /* ok, they really aren't equal. */
+            else
+                expr_var_assign_bool(pVar1, 0);
+        }
+    }
+    else
+    {
+        /*
+         * If the type differs, there are now two options:
+         *  1. Try convert the string to a valid number and compare the numbers.
+         *  2. Convert the non-string to a number and compare the strings.
+         */
+        if (   expr_var_try_make_num(pVar1) >= kExprRet_Ok
+            && expr_var_try_make_num(pVar2) >= kExprRet_Ok)
+            expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
+        else
+        {
+            expr_var_make_simple_string(pVar1);
+            expr_var_make_simple_string(pVar2);
+            expr_var_assign_bool(pVar1, version_compare(pVar1->uVal.psz, pVar2->uVal.psz) == 0);
+        }
+    }
+
+    expr_pop_and_delete_var(pThis);
+    return rc;
+}
+
+
+/**
+ * Not equal, version string.
+ *
+ * @returns Status code.
+ * @param   pThis       The instance.
+ */
+static EXPRRET expr_op_ver_not_equal(PEXPR pThis)
+{
+    EXPRRET rc = expr_op_ver_equal(pThis);
+    if (rc >= kExprRet_Ok)
+        rc = expr_op_logical_not(pThis);
+    return rc;
+}
+
+
+/**
+ * Equal.
+ *
+ * @returns Status code.
+ * @param   pThis       The instance.
+ */
+static EXPRRET expr_op_equal(PEXPR pThis)
+{
+    EXPRRET     rc = kExprRet_Ok;
+    PEXPRVAR    pVar1 = &pThis->aVars[pThis->iVar - 1];
+    PEXPRVAR    pVar2 = &pThis->aVars[pThis->iVar];
+    int const   fIsString1 = expr_var_is_string(pVar1);
+
+    /*
+     * The same type?
+     */
+    if (fIsString1 == expr_var_is_string(pVar2))
+    {
+        if (!fIsString1)
             /* numbers are simple */
             expr_var_assign_bool(pVar1, pVar1->uVal.i == pVar2->uVal.i);
@@ -1615,6 +1796,12 @@
     EXPR_OP(">=",          60,      2,    expr_op_greater_or_equal_than),
     EXPR_OP(">",           60,      2,    expr_op_greater_than),
+    EXPR_OP("vle",         60,      2,    expr_op_ver_less_or_equal_than),
+    EXPR_OP("vlt",         60,      2,    expr_op_ver_less_than),
+    EXPR_OP("vge",         60,      2,    expr_op_ver_greater_or_equal_than),
+    EXPR_OP("vgt",         60,      2,    expr_op_ver_greater_than),
     EXPR_OP("==",          55,      2,    expr_op_equal),
+    EXPR_OP("veq",         55,      2,    expr_op_ver_equal),
     EXPR_OP("!=",          55,      2,    expr_op_not_equal),
+    EXPR_OP("vne",         55,      2,    expr_op_ver_not_equal),
     EXPR_OP("!",           80,      1,    expr_op_logical_not),
     EXPR_OP("^",           45,      2,    expr_op_bitwise_xor),
@@ -1623,8 +1810,8 @@
     EXPR_OP("||",          30,      2,    expr_op_logical_or),
     EXPR_OP("|",           40,      2,    expr_op_bitwise_or),
-            { "(", 1, ')',   10,      1,    expr_op_left_parenthesis },
-            { ")", 1, '(',   10,      0,    expr_op_right_parenthesis },
- /*         { "?", 1, ':',    5,      2,    expr_op_question },
-            { ":", 1, '?',    5,      2,    expr_op_colon }, -- too weird for now. */
+          { "(", 1, ')',   10,      1,    expr_op_left_parenthesis },
+          { ")", 1, '(',   10,      0,    expr_op_right_parenthesis },
+ /*       { "?", 1, ':',    5,      2,    expr_op_question },
+          { ":", 1, '?',    5,      2,    expr_op_colon }, -- too weird for now. */
 #undef EXPR_OP
 };
@@ -1694,18 +1881,16 @@
     {
         /* compare the string... */
+        if (g_aExprOps[i].szOp[0] != ch)
+            continue;
         switch (g_aExprOps[i].cchOp)
         {
             case 1:
-                if (g_aExprOps[i].szOp[0] != ch)
-                    continue;
                 break;
             case 2:
-                if (    g_aExprOps[i].szOp[0] != ch
-                    ||  g_aExprOps[i].szOp[1] != psz[1])
+                if (g_aExprOps[i].szOp[1] != psz[1])
                     continue;
                 break;
             default:
-                if (    g_aExprOps[i].szOp[0] != ch
-                    ||  strncmp(&g_aExprOps[i].szOp[1], psz + 1, g_aExprOps[i].cchOp - 1))
+                if (strncmp(&g_aExprOps[i].szOp[1], psz + 1, g_aExprOps[i].cchOp - 1))
                     continue;
                 break;
