RESEND [PATCH 09/11] CMD.exe: Support IF..ELSE processing tollerate multiline/part lines

Jason Edmeades jason.edmeades at googlemail.com
Fri Jun 15 14:59:27 CDT 2007


This handles almost all the cases I can throw at it identically to the
way windows does, other than those mentioned earlier because of the
lack of actual parsing of the operands when splitting up the line.

Note: I have not changes the behaviour of the if condition testing so
it is still as good or as bad as previously, but it now supports else.
---
 programs/cmd/batch.c    |    2 +-
 programs/cmd/builtins.c |   89 +++++++++++++++++++++++++++++++++++++++++++++--
 programs/cmd/wcmd.h     |    2 +-
 programs/cmd/wcmdmain.c |   61 ++++++++++++++++++++++----------
 4 files changed, 130 insertions(+), 24 deletions(-)

diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index cc9d835..0990b7e 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -109,7 +109,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, int called, WCHAR *startLabel, HAN
       CMD_LIST *toExecute = NULL;         /* Commands left to be executed */
       if (WCMD_ReadAndParseLine(NULL, &toExecute, h) == NULL)
         break;
-      WCMD_process_commands(toExecute);
+      WCMD_process_commands(toExecute, FALSE);
       WCMD_free_commands(toExecute);
       toExecute = NULL;
   }
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index ebbef1b..0ec86e5 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -790,6 +790,14 @@ void WCMD_popd (void) {
  * WCMD_if
  *
  * Batch file conditional.
+ *
+ * On entry, cmdlist will point to command containing the IF, and optionally
+ *   the first command to execute (if brackets not found)
+ *   If &&'s were found, this may be followed by a record flagged as isAmpersand
+ *   If ('s were found, execute all within that bracket
+ *   Command may optionally be followed by an ELSE - need to skip instructions
+ *   in the else using the same logic
+ *
  * FIXME: Much more syntax checking needed!
  */
 
@@ -802,6 +810,8 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
   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_LIST *curPosition;
+  int myDepth;
 
   if (!lstrcmpiW (param1, notW)) {
     negate = 1;
@@ -835,10 +845,83 @@ void WCMD_if (WCHAR *p, CMD_LIST **cmdList) {
     WCMD_output (WCMD_LoadMessage(WCMD_SYNTAXERR));
     return;
   }
+
+  /* Process rest of IF statement which is on the same line
+     Note: This may process all or some of the cmdList (eg a GOTO) */
+  curPosition = *cmdList;
+  myDepth     = (*cmdList)->bracketDepth;
+
   if (test != negate) {
-    command = WCMD_strdupW(command);
-    WCMD_process_command (command, cmdList);
-    free (command);
+    WCHAR *cmd = command;
+
+    /* Skip leading whitespace between condition and the command */
+    while (cmd && *cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
+
+    if (cmd && *cmd) {
+      command = WCMD_strdupW(cmd);
+      WCMD_process_command (command, cmdList);
+      free (command);
+    }
+  }
+
+  /* If it didnt move the position, step to next command */
+  if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+
+  /* Process any other parts of the IF */
+  if (*cmdList) {
+    BOOL processThese = (test != negate);
+
+    while (*cmdList) {
+      const WCHAR ifElse[] = {'e','l','s','e',' ','\0'};
+
+      /* execute all appropriate commands */
+      curPosition = *cmdList;
+
+      WINE_TRACE("Processing cmdList(%p) - &(%d) bd(%d / %d)\n",
+                 *cmdList,
+                 (*cmdList)->isAmphersand,
+                 (*cmdList)->bracketDepth, myDepth);
+
+      /* Execute any appended to the statement with &&'s */
+      if ((*cmdList)->isAmphersand) {
+        if (processThese) {
+          WCMD_process_command((*cmdList)->command, cmdList);
+        }
+        if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+
+      /* Execute any appended to the statement with (...) */
+      } else if ((*cmdList)->bracketDepth > myDepth) {
+        if (processThese) {
+          *cmdList = WCMD_process_commands(*cmdList, TRUE);
+          WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
+        }
+        if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+
+      /* End of the command - does 'ELSE ' follow as the next command? */
+      } else {
+        if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                           (*cmdList)->command, 5, ifElse, -1) == 2) {
+
+            /* Swap between if and else processing */
+            processThese = !processThese;
+
+            /* Process the ELSE part */
+            if (processThese) {
+              WCHAR *cmd = ((*cmdList)->command) + strlenW(ifElse);
+
+              /* Skip leading whitespace between condition and the command */
+              while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
+              if (*cmd) {
+                WCMD_process_command(cmd, cmdList);
+              }
+            }
+            if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
+        } else {
+          WINE_TRACE("Found end of this IF statement (next = %p)\n", *cmdList);
+          break;
+        }
+      }
+    }
   }
 }
 
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index ebb0fde..90f0179 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -102,7 +102,7 @@ BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars,
                    LPDWORD charsRead, const LPOVERLAPPED unused);
 
 WCHAR *WCMD_ReadAndParseLine(WCHAR *initialcmd, CMD_LIST **output, HANDLE readFrom);
-void   WCMD_process_commands(CMD_LIST *thisCmd);
+CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket);
 void   WCMD_free_commands(CMD_LIST *cmds);
 
 /*	Data structure to hold context when executing batch files */
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index a9beb61..0a9c4d2 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -324,7 +324,7 @@ int wmain (int argc, WCHAR *argvW[])
 
       /* Parse the command string, without reading any more input */
       WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
-      WCMD_process_commands(toExecute);
+      WCMD_process_commands(toExecute, FALSE);
       WCMD_free_commands(toExecute);
       toExecute = NULL;
 
@@ -417,7 +417,7 @@ int wmain (int argc, WCHAR *argvW[])
   if (opt_k) {
       /* Parse the command string, without reading any more input */
       WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
-      WCMD_process_commands(toExecute);
+      WCMD_process_commands(toExecute, FALSE);
       WCMD_free_commands(toExecute);
       toExecute = NULL;
       HeapFree(GetProcessHeap(), 0, cmd);
@@ -449,7 +449,7 @@ int wmain (int argc, WCHAR *argvW[])
     if (WCMD_ReadAndParseLine(NULL, &toExecute,
                               GetStdHandle(STD_INPUT_HANDLE)) == NULL)
       break;
-    WCMD_process_commands(toExecute);
+    WCMD_process_commands(toExecute, FALSE);
     WCMD_free_commands(toExecute);
     toExecute = NULL;
   }
@@ -2044,6 +2044,7 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
                 break;
 
       case '"': inQuotes = !inQuotes;
+                curString[curLen++] = *curPos;
                 break;
 
       case '(': /* If a '(' is the first non whitespace in a command portion
@@ -2101,21 +2102,23 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
                   curPos++; /* Skip other & */
 
                   /* Add an entry to the command list */
-                  thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
-                  thisEntry->command = HeapAlloc(GetProcessHeap(), 0,
-                                                 (curLen+1) * sizeof(WCHAR));
-                  memcpy(thisEntry->command, curString, curLen * sizeof(WCHAR));
-                  thisEntry->command[curLen] = 0x00;
-                  curLen = 0;
-                  thisEntry->nextcommand = NULL;
-                  thisEntry->isAmphersand = isAmphersand;
-                  thisEntry->bracketDepth = curDepth;
-                  if (lastEntry) {
-                    lastEntry->nextcommand = thisEntry;
-                  } else {
-                    *output = thisEntry;
+                  if (curLen > 0) {
+                    thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
+                    thisEntry->command = HeapAlloc(GetProcessHeap(), 0,
+                                                   (curLen+1) * sizeof(WCHAR));
+                    memcpy(thisEntry->command, curString, curLen * sizeof(WCHAR));
+                    thisEntry->command[curLen] = 0x00;
+                    curLen = 0;
+                    thisEntry->nextcommand = NULL;
+                    thisEntry->isAmphersand = isAmphersand;
+                    thisEntry->bracketDepth = curDepth;
+                    if (lastEntry) {
+                      lastEntry->nextcommand = thisEntry;
+                    } else {
+                      *output = thisEntry;
+                    }
+                    lastEntry = thisEntry;
                   }
-                  lastEntry = thisEntry;
                   isAmphersand = TRUE;
                 } else {
                   curString[curLen++] = *curPos;
@@ -2224,24 +2227,44 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
  *
  * Process all the commands read in so far
  */
-void WCMD_process_commands(CMD_LIST *thisCmd) {
+CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket) {
+
+    int bdepth = -1;
+
+    if (thisCmd && oneBracket) bdepth = thisCmd->bracketDepth;
 
     /* Loop through the commands, processing them one by one */
     while (thisCmd) {
 
+      CMD_LIST *origCmd = thisCmd;
+
+      /* If processing one bracket only, and we find the end bracket
+         entry (or less), return                                    */
+      if (oneBracket && !thisCmd->command &&
+          bdepth <= thisCmd->bracketDepth) {
+        WINE_TRACE("Finished bracket @ %p, next command is %p\n",
+                   thisCmd, thisCmd->nextcommand);
+        return thisCmd->nextcommand;
+      }
+
       /* Ignore the NULL entries a ')' inserts (Only 'if' cares
          about them and it will be handled in there)
          Also, skip over any batch labels (eg. :fred)          */
       if (thisCmd->command && thisCmd->command[0] != ':') {
+
         WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->command));
+
         if (strchrW(thisCmd->command,'|') != NULL) {
           WCMD_pipe (&thisCmd);
         } else {
           WCMD_process_command (thisCmd->command, &thisCmd);
         }
       }
-      if (thisCmd) thisCmd = thisCmd->nextcommand;
+
+      /* Step on unless the command itself already stepped on */
+      if (thisCmd == origCmd) thisCmd = thisCmd->nextcommand;
     }
+    return NULL;
 }
 
 /***************************************************************************
-- 
1.5.0




More information about the wine-patches mailing list