[1/6] cmd: Add support for LSS comparison operator in if statements (try 2)

Frédéric Delanoy frederic.delanoy at gmail.com
Sun Oct 16 17:03:09 CDT 2011


try 2: cleanup; added extended operators enum with initially one element.
Will be completed by the next patches in this series

---
 programs/cmd/builtins.c                  |   81 ++++++++++++++++++++++++++++--
 programs/cmd/tests/test_builtins.cmd     |   60 ++++++++++++++++++++--
 programs/cmd/tests/test_builtins.cmd.exp |   44 +++++++++++++++--
 programs/cmd/wcmd.h                      |    6 ++
 4 files changed, 179 insertions(+), 12 deletions(-)

diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 5a890c7..ffaea4c 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1479,6 +1479,25 @@ void WCMD_popd (void) {
     LocalFree (temp);
 }
 
+/* Returns CMD_IF_OPERATOR corresponding to 'operator', or NULL if no match found */
+static CMD_IF_OPERATOR *match_if_operator(const WCHAR *operator)
+{
+    static const WCHAR lssW[] = {'l','s','s','\0'};
+    CMD_IF_OPERATOR *result = NULL;
+
+    if (!operator) return NULL;
+    result = HeapAlloc(GetProcessHeap(), 0, sizeof(*result));
+    if (!result) return NULL;
+
+    if (!lstrcmpiW(operator, lssW))
+        *result = CMD_IF_LSS;
+    else {
+        HeapFree(GetProcessHeap(), 0, result);
+        result = NULL;
+    }
+    return result;
+}
+
 /****************************************************************************
  * WCMD_if
  *
@@ -1494,16 +1513,18 @@ void WCMD_popd (void) {
  * FIXME: Much more syntax checking needed!
  */
 
-void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
-
+void WCMD_if (WCHAR *p, CMD_LIST **cmdList)
+{
   int negate; /* Negate condition */
   int test;   /* Condition evaluation result */
   WCHAR condition[MAX_PATH], *command, *s;
+  WCHAR *leftOperand = NULL, *rightOperand = NULL;
   static const WCHAR notW[]    = {'n','o','t','\0'};
   static const WCHAR errlvlW[] = {'e','r','r','o','r','l','e','v','e','l','\0'};
   static const WCHAR existW[]  = {'e','x','i','s','t','\0'};
   static const WCHAR defdW[]   = {'d','e','f','i','n','e','d','\0'};
   static const WCHAR eqeqW[]   = {'=','=','\0'};
+  CMD_IF_OPERATOR *operator = NULL;
   static const WCHAR parmI[]   = {'/','I','\0'};
   int caseInsensitive = (strstrW(quals, parmI) != NULL);
 
@@ -1539,13 +1560,65 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
     WCMD_parameter(s, 1, &command, NULL);
   }
   else {
-    WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR));
-    return;
+    /* Handle extended comparison operators */
+    WCHAR *leftOpStart, *leftOpEnd, *rightOpStart, *rightOpEnd;
+    long int leftOperand_int, rightOperand_int;
+    WCHAR *endptr_leftOp, *endptr_rightOp;
+    BOOL int_operands;
+
+    /* Parse operands and comparison operator with potential surrounding quotes, so param1/2 can't
+     * be used */
+    WCMD_parameter(p, negate+caseInsensitive, &leftOpStart, &leftOpEnd);
+    if (!(leftOperand = HeapAlloc(GetProcessHeap(), 0, (leftOpEnd-leftOpStart+2)*sizeof(WCHAR))))
+      goto err;
+    memcpy(leftOperand, leftOpStart, (leftOpEnd-leftOpStart+1)*sizeof(WCHAR));
+    leftOperand[leftOpEnd-leftOpStart+1] = '\0';
+
+    operator = match_if_operator(WCMD_parameter(p, 1+negate+caseInsensitive, NULL, NULL));
+    if (!operator) {
+      WCMD_output(WCMD_LoadMessage(WCMD_SYNTAXERR));
+      HeapFree(GetProcessHeap(), 0, leftOperand);
+      return;
+    }
+
+    WCMD_parameter(p, 2+negate+caseInsensitive, &rightOpStart, &rightOpEnd);
+    if (!(rightOperand = HeapAlloc(GetProcessHeap(), 0, (rightOpEnd-rightOpStart+2)*sizeof(WCHAR))))
+      goto err;
+    memcpy(rightOperand, rightOpStart, (rightOpEnd-rightOpStart+1)*sizeof(WCHAR));
+    rightOperand[rightOpEnd-rightOpStart+1] = '\0';
+
+    /* Check if we have plain integers (in decimal, octal or hexadecimal notation) */
+    leftOperand_int = strtolW(leftOperand, &endptr_leftOp, 0);
+    rightOperand_int = strtolW(rightOperand, &endptr_rightOp, 0);
+    int_operands = (!*endptr_leftOp) && (!*endptr_rightOp);
+
+    /* Perform actual (integer or string) comparison according to detected operator */
+    switch (*operator) {
+      case CMD_IF_LSS:
+        if (int_operands)
+          test = (leftOperand_int < rightOperand_int);
+        else
+          test = caseInsensitive ? (lstrcmpiW(leftOperand, rightOperand) < 0)
+                                 : (lstrcmpW (leftOperand, rightOperand) < 0);
+        break;
+    }
+
+    HeapFree(GetProcessHeap(), 0, leftOperand);
+    HeapFree(GetProcessHeap(), 0, operator);
+    HeapFree(GetProcessHeap(), 0, rightOperand);
+    WCMD_parameter(p, 3+negate+caseInsensitive, &command, NULL);
   }
 
   /* Process rest of IF statement which is on the same line
      Note: This may process all or some of the cmdList (eg a GOTO) */
   WCMD_part_execute(cmdList, command, NULL, NULL, TRUE, (test != negate));
+  return;
+
+err:
+  HeapFree(GetProcessHeap(), 0, leftOperand);
+  HeapFree(GetProcessHeap(), 0, operator);
+  HeapFree(GetProcessHeap(), 0, rightOperand);
+  WINE_ERR("Insufficient memory!\n");
 }
 
 /****************************************************************************
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index 02e264d..1244fb4 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -552,7 +552,7 @@ if exist foo (
 )
 cd .. & rd foobar
 
-echo ------------ Testing if/else --------------
+echo ------------ Testing if/else ------------
 echo if/else should work with blocks
 if 0 == 0 (
   echo if seems to work
@@ -569,19 +569,19 @@ if /c==/c (
 ) else (
   echo parameter detection seems to be broken
 )
-echo Testing case sensitivity with and without /i option
+echo ... case sensitivity with and without /i option ...
 if bar==BAR echo if does not default to case sensitivity
 if not bar==BAR echo if seems to default to case sensitivity
 if /i foo==FOO echo if /i seems to work
 if /i not foo==FOO echo if /i seems to be broken
 if /I foo==FOO echo if /I seems to work
 if /I not foo==FOO echo if /I seems to be broken
-echo Testing string comparisons
+echo ... string comparisons ...
 if abc == abc  (echo equal) else echo non equal
 if abc =="abc" (echo equal) else echo non equal
 if "abc"== abc (echo equal) else echo non equal
 if "abc"== "abc" (echo equal) else echo non equal
-echo Testing tabs handling
+echo ... tabs handling ...
 if at tab@1==1 echo doom
 if @tab at 1==1 echo doom
 if 1==1 (echo doom) else at tab@echo quake
@@ -590,6 +590,58 @@ if 1==0 at tab@(echo doom) else echo quake
 if 1==0 (echo doom)@tab at else echo quake
 if 1==0 (echo doom) else at tab@echo quake
 
+echo ... comparison operators ...
+rem NT4 misevaluates conditionals in for loops so we have to use subroutines as workarounds
+rem Imbricated for loops parameters are currently not expanded correctly; this prevents usage of simpler imbricated for loops in tests
+echo ...... for strings ......
+rem NT4 stops processing of the whole batch file as soon as it finds a
+rem comparison operator non fully uppercased, such as lss instead of LSS, so we
+rem can't test those here.
+if LSS LSS LSSfoo (echo LSS operator can be used as operand)
+if LSS LSS LSS (echo bar)
+if 1.1 LSS 1.10 (echo floats are handled as strings)
+if "9" LSS "10" (echo numbers in quotes recognized!) else echo numbers in quotes are handled as strings
+if not "-1" LSS "1" (echo negative numbers as well) else echo NT
+if /i foo LSS FoOc echo if /i seems to work for LSS
+if /I not foo LSS FOOb echo if /I seems to be broken for LSS
+set STR_PARMS=A B AB BA AA
+for %%i in (%STR_PARMS%) do call :LSStest %%i A
+for %%i in (%STR_PARMS%) do call :LSStest %%i B
+for %%i in (%STR_PARMS%) do call :LSStest %%i AB
+for %%i in (%STR_PARMS%) do call :LSStest %%i BA
+for %%i in (%STR_PARMS%) do call :LSStest %%i AA
+if b LSS B (echo b LSS B) else echo NT4
+if /I b LSS B echo b LSS B insensitive
+if b LSS A echo b LSS A
+if /I b LSS A echo b LSS A insensitive
+if a LSS B (echo a LSS B) else echo NT4
+if /I a LSS B echo a LSS B insensitive
+if A LSS b echo A LSS b
+if /I A LSS b echo A LSS b insensitive
+echo ...... for numbers ......
+if -1 LSS 1 (echo negative numbers handled)
+if not -1 LSS -10 (echo negative numbers handled)
+if not 9 LSS 010 (echo octal handled)
+if not -010 LSS -8 (echo also in negative form)
+if 4 LSS 0x5 (echo hexa handled)
+if not -1 LSS -0x1A (echo also in negative form)
+if 11 LSS 101 (echo 11 LSS 101)
+set INT_PARMS=0 1 10 9
+for %%i in (%INT_PARMS%) do call :LSStest %%i 0
+for %%i in (%INT_PARMS%) do call :LSStest %%i 1
+for %%i in (%INT_PARMS%) do call :LSStest %%i 10
+for %%i in (%INT_PARMS%) do call :LSStest %%i 9
+goto :endIfCompOpsSubroutines
+
+rem IF subroutines helpers
+:LSStest
+if %1 LSS %2 echo %1 LSS %2
+goto :eof
+
+:endIfCompOpsSubroutines
+set STR_PARMS=
+set INT_PARMS=
+
 echo -----------Testing for -----------
 echo ...plain FOR
 for %%i in (A B C) do echo %%i
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index 20e5d12..e78b211 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -378,21 +378,21 @@ bar
 bar
 NUL
 @todo_wine at foo created
------------- Testing if/else --------------
+------------ Testing if/else ------------
 if/else should work with blocks
 if seems to work
 else seems to work
 if seems not to detect /c as parameter
-Testing case sensitivity with and without /i option
+... case sensitivity with and without /i option ...
 if seems to default to case sensitivity
 if /i seems to work
 if /I seems to work
-Testing string comparisons
+... string comparisons ...
 equal
 non equal
 non equal
 equal
-Testing tabs handling
+... tabs handling ...
 doom
 doom
 doom
@@ -400,6 +400,42 @@ lol
 quake
 quake
 quake
+... comparison operators ...
+...... for strings ......
+LSS operator can be used as operand
+floats are handled as strings
+numbers in quotes are handled as strings
+negative numbers as well at or_broken@NT
+if /i seems to work for LSS
+A LSS B
+AB LSS B
+AA LSS B
+A LSS AB
+AA LSS AB
+A LSS BA
+B LSS BA
+AB LSS BA
+AA LSS BA
+A LSS AA
+b LSS B at or_broken@NT4
+a LSS B at or_broken@NT4
+a LSS B insensitive
+A LSS b
+A LSS b insensitive
+...... for numbers ......
+negative numbers handled
+negative numbers handled
+octal handled
+also in negative form
+hexa handled
+also in negative form
+11 LSS 101
+0 LSS 1
+0 LSS 10
+1 LSS 10
+9 LSS 10
+0 LSS 9
+1 LSS 9
 -----------Testing for -----------
 ...plain FOR
 A
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 61cef8f..b5e3d0b 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -50,6 +50,12 @@ typedef struct _CMD_LIST {
   WCHAR               pipeFile[MAX_PATH]; /* Where to get input from for pipes */
 } CMD_LIST;
 
+/* Data structure to hold IF extended comparison operators */
+
+typedef enum _CMD_IF_OPERATOR {
+    CMD_IF_LSS /* < */
+} CMD_IF_OPERATOR;
+
 void WCMD_assoc (const WCHAR *, BOOL);
 void WCMD_batch (WCHAR *, WCHAR *, int, WCHAR *, HANDLE);
 void WCMD_call (WCHAR *command);
-- 
1.7.7




More information about the wine-patches mailing list