Jason Edmeades : cmd.exe: Parse multipart FOR statements.

Alexandre Julliard julliard at wine.codeweavers.com
Mon Jun 18 08:05:13 CDT 2007


Module: wine
Branch: master
Commit: d4afe81c3f839066ff5eaa292bd90db58aaf029f
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=d4afe81c3f839066ff5eaa292bd90db58aaf029f

Author: Jason Edmeades <jason.edmeades at googlemail.com>
Date:   Fri Jun 15 20:59:25 2007 +0100

cmd.exe: Parse multipart FOR statements.

---

 programs/cmd/wcmdmain.c |  118 +++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 110 insertions(+), 8 deletions(-)

diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index a690cb0..5ccd6d8 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -1907,8 +1907,14 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
     CMD_LIST *lastEntry = NULL;
     BOOL      isAmphersand = FALSE;
     static WCHAR    *extraSpace = NULL;  /* Deliberately never freed */
-    const WCHAR rem[] = {'r','e','m',' ','\0'};
+    const WCHAR remCmd[] = {'r','e','m',' ','\0'};
+    const WCHAR forCmd[] = {'f','o','r',' ','\0'};
     BOOL      inRem = FALSE;
+    BOOL      inFor = FALSE;
+    BOOL      onlyWhiteSpace = FALSE;
+    BOOL      lastWasWhiteSpace = FALSE;
+    BOOL      lastWasDo = FALSE;
+    BOOL      lastWasIn = FALSE;
 
     /* Allocate working space for a command read from keyboard, file etc */
     if (!extraSpace)
@@ -1939,13 +1945,57 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
 
       WCHAR thisChar;
 
-      /* If command starts with 'rem', ignore any &&, ( etc */
-      if (curLen == 0 && !inRem) {
+      /* Debugging AID:
+      WINE_TRACE("Looking at '%c' (len:%d, lws:%d, ows:%d)\n", *curPos, curLen,
+                 lastWasWhiteSpace, onlyWhiteSpace);
+      */
+
+     /* Certain commands need special handling */
+      if (curLen == 0) {
+        const WCHAR forDO[] = {'d','o',' ','\0'};
+
+        /* If command starts with 'rem', ignore any &&, ( etc */
         if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
-          curPos, 4, rem, -1) == 2) {
+          curPos, 4, remCmd, -1) == 2) {
           inRem = TRUE;
-        } else {
-          inRem = FALSE;
+
+        /* If command starts with 'for', handle ('s mid line after IN or DO */
+        } else if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+          curPos, 4, forCmd, -1) == 2) {
+          inFor = TRUE;
+
+        /* In a for loop, the DO command will follow a close bracket followed by
+           whitespace, followed by DO, ie closeBracket inserts a NULL entry, curLen
+           is then 0, and all whitespace is skipped                                */
+        } else if (inFor &&
+                   (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+                    curPos, 3, forDO, -1) == 2)) {
+          WINE_TRACE("Found DO\n");
+          lastWasDo = TRUE;
+          onlyWhiteSpace = TRUE;
+          memcpy(&curString[curLen], curPos, 3*sizeof(WCHAR));
+          curLen+=3;
+          curPos+=3;
+          continue;
+        }
+      } else {
+
+        /* Special handling for the 'FOR' command */
+        if (inFor && lastWasWhiteSpace) {
+          const WCHAR forIN[] = {'i','n',' ','\0'};
+
+          WINE_TRACE("Found 'FOR', comparing next parm: '%s'\n", wine_dbgstr_w(curPos));
+
+          if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+              curPos, 3, forIN, -1) == 2) {
+            WINE_TRACE("Found IN\n");
+            lastWasIn = TRUE;
+            onlyWhiteSpace = TRUE;
+            memcpy(&curString[curLen], curPos, 3*sizeof(WCHAR));
+            curLen+=3;
+            curPos+=3;
+            continue;
+          }
         }
       }
 
@@ -1955,20 +2005,61 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
       if (!inRem) thisChar = *curPos;
       else        thisChar = 'X';  /* Character with no special processing */
 
+      lastWasWhiteSpace = FALSE; /* Will be reset below */
+
       switch (thisChar) {
 
       case '\t':/* drop through - ignore whitespace at the start of a command */
       case ' ': if (curLen > 0)
                   curString[curLen++] = *curPos;
+
+                /* Remember just processed whitespace */
+                lastWasWhiteSpace = TRUE;
+
                 break;
 
       case '"': inQuotes = !inQuotes;
                 break;
 
-      case '(': if (inQuotes || curLen > 0) {
+      case '(': /* If a '(' is the first non whitespace in a command portion
+                   ie start of line or just after &&, then we read until an
+                   unquoted ) is found                                       */
+                WINE_TRACE("Found '(' conditions: curLen(%d), inQ(%d), onlyWS(%d)"
+                           ", for(%d, In:%d, Do:%d)\n",
+                           curLen, inQuotes,
+                           onlyWhiteSpace,
+                           inFor, lastWasIn, lastWasDo);
+                if (curLen == 0) {
+                  curDepth++;
+
+                /* If in quotes, ignore brackets */
+                } else if (inQuotes) {
                   curString[curLen++] = *curPos;
-                } else {
+
+                /* In a FOR loop, an unquoted '(' may occur straight after
+                   IN or DO                                                */
+                } else if (inFor && (lastWasIn || lastWasDo) && onlyWhiteSpace) {
+
+                  /* Add the current command */
+                  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;
+
                   curDepth++;
+                } else {
+                  curString[curLen++] = *curPos;
                 }
                 break;
 
@@ -2041,6 +2132,16 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
 
       curPos++;
 
+      /* At various times we need to know if we have only skipped whitespace,
+         so reset this variable and then it will remain true until a non
+         whitespace is found                                               */
+      if ((thisChar != ' ') && (thisChar != '\n')) onlyWhiteSpace = FALSE;
+
+      /* Flag end of interest in FOR DO and IN parms once something has been processed */
+      if (!lastWasWhiteSpace) {
+        lastWasIn = lastWasDo = FALSE;
+      }
+
       /* If we have reached the end, add this command into the list */
       if (*curPos == 0x00 && curLen > 0) {
 
@@ -2065,6 +2166,7 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
       /* If we have reached the end of the string, see if bracketing outstanding */
       if (*curPos == 0x00 && curDepth > 0 && readFrom != INVALID_HANDLE_VALUE) {
         inRem = FALSE;
+        inFor = FALSE;
         isAmphersand = FALSE;
         inQuotes = FALSE;
         memset(extraSpace, 0x00, (MAXSTRING+1) * sizeof(WCHAR));




More information about the wine-cvs mailing list