[PATCH 02/12] CMD.exe: Add basic support for && and (...) syntax

Jason Edmeades jason.edmeades at googlemail.com
Thu Jun 14 17:06:38 CDT 2007


This adds the beginnings of multiple part commands and multiple line
commands. It supports the basics, but some of them are not 100% correct.

The infrastructure is there to correctly support && (which terminates
all commands when a non-0 error level is found, I think) and multiline
if statements (which is why the bracketDepth is maintained as well
as the NULL end of bracket entries). These should follow later in this
patchset
---
 programs/cmd/Cs.rc      |    1 +
 programs/cmd/De.rc      |    1 +
 programs/cmd/En.rc      |    1 +
 programs/cmd/Es.rc      |    1 +
 programs/cmd/Fr.rc      |    1 +
 programs/cmd/Ja.rc      |    1 +
 programs/cmd/Ko.rc      |    1 +
 programs/cmd/Nl.rc      |    1 +
 programs/cmd/No.rc      |    1 +
 programs/cmd/Pl.rc      |    1 +
 programs/cmd/Pt.rc      |    1 +
 programs/cmd/Ru.rc      |    1 +
 programs/cmd/Si.rc      |    1 +
 programs/cmd/Tr.rc      |    1 +
 programs/cmd/batch.c    |   16 +--
 programs/cmd/wcmd.h     |   16 +++-
 programs/cmd/wcmdmain.c |  292 +++++++++++++++++++++++++++++++++++++++++++----
 17 files changed, 308 insertions(+), 30 deletions(-)

diff --git a/programs/cmd/Cs.rc b/programs/cmd/Cs.rc
index 302659f..4db0e0d 100644
--- a/programs/cmd/Cs.rc
+++ b/programs/cmd/Cs.rc
@@ -267,4 +267,5 @@ Zadejte HELP <pøíkaz> pro podrobnìjší informace o nìkterém z výše uvedených pøík
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/De.rc b/programs/cmd/De.rc
index 55799ea..19b590d 100644
--- a/programs/cmd/De.rc
+++ b/programs/cmd/De.rc
@@ -291,4 +291,5 @@ obigen Befehle erhalten.\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/En.rc b/programs/cmd/En.rc
index 6f384ca..72d0dd9 100644
--- a/programs/cmd/En.rc
+++ b/programs/cmd/En.rc
@@ -271,4 +271,5 @@ Enter HELP <command> for further information on any of the above commands\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Es.rc b/programs/cmd/Es.rc
index 02bd865..c6842b7 100644
--- a/programs/cmd/Es.rc
+++ b/programs/cmd/Es.rc
@@ -288,4 +288,5 @@ Introduzca HELP <comando> para más información sobre cualquiera de los comandos\
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Fr.rc b/programs/cmd/Fr.rc
index 0e3dcc7..e783186 100644
--- a/programs/cmd/Fr.rc
+++ b/programs/cmd/Fr.rc
@@ -261,4 +261,5 @@ Entrez HELP <commande> pour plus d'informations sur les commandes ci-dessus\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Ja.rc b/programs/cmd/Ja.rc
index b09715a..1576841 100644
--- a/programs/cmd/Ja.rc
+++ b/programs/cmd/Ja.rc
@@ -265,4 +265,5 @@ EXIT\t\tCMD‚ðI—¹\n\n\
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Ko.rc b/programs/cmd/Ko.rc
index fd568d2..88f0626 100644
--- a/programs/cmd/Ko.rc
+++ b/programs/cmd/Ko.rc
@@ -263,4 +263,5 @@ HELP <¸í·É>À» Ä¡¸é ±× ¸í·ÉÀÇ »ó¼¼ÇÑ Á¤º¸¸¦ º¸¿©ÁÜ\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Nl.rc b/programs/cmd/Nl.rc
index b06651a..9704d4d 100644
--- a/programs/cmd/Nl.rc
+++ b/programs/cmd/Nl.rc
@@ -264,4 +264,5 @@ type HELP <opdracht> voor meer informatie over bovengenoemde opdrachten\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/No.rc b/programs/cmd/No.rc
index 571cc47..5997130 100644
--- a/programs/cmd/No.rc
+++ b/programs/cmd/No.rc
@@ -269,4 +269,5 @@ Skriv «HELP <kommando>» for mer informasjon om kommandoene ovenfor\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Pl.rc b/programs/cmd/Pl.rc
index 6d56af1..01ac02a 100644
--- a/programs/cmd/Pl.rc
+++ b/programs/cmd/Pl.rc
@@ -266,4 +266,5 @@ Wpisz HELP <komenda> dla dok³adniejszych informacji o komendzie\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Pt.rc b/programs/cmd/Pt.rc
index 808bd11..3dcc089 100644
--- a/programs/cmd/Pt.rc
+++ b/programs/cmd/Pt.rc
@@ -474,4 +474,5 @@ Digite HELP <comando> para mais informações sobre alguns dos comandos acima\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Ru.rc b/programs/cmd/Ru.rc
index 852f9ff..1719d11 100644
--- a/programs/cmd/Ru.rc
+++ b/programs/cmd/Ru.rc
@@ -277,4 +277,5 @@ EXIT\t\tÂûéòè èç CMD\n\n\
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Si.rc b/programs/cmd/Si.rc
index e2243f5..a7b37df 100644
--- a/programs/cmd/Si.rc
+++ b/programs/cmd/Si.rc
@@ -263,4 +263,5 @@ Enter HELP <command> for further information on any of the above commands\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/Tr.rc b/programs/cmd/Tr.rc
index e14c2a7..f70ad05 100644
--- a/programs/cmd/Tr.rc
+++ b/programs/cmd/Tr.rc
@@ -265,4 +265,5 @@ Yukarýdaki komutlar hakkýnda daha fazla bilgi için HELP <komut> girin\n"
   WCMD_ANYKEY,"Press Return key to continue: "
   WCMD_CONSTITLE,"Wine Command Prompt"
   WCMD_VERSION,"CMD Version %s\n\n"
+  WCMD_MOREPROMPT, "More? "
 }
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index c301bcc..b2a07cb 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -105,15 +105,13 @@ void WCMD_batch (WCHAR *file, WCHAR *command, int called, WCHAR *startLabel, HAN
  * 	the rest are handled by the main command processor.
  */
 
-  while (context -> skip_rest == FALSE && WCMD_fgets (string, sizeof(string), h)) {
-      if (strlenW(string) == MAXSTRING -1) {
-          WCMD_output_asis( WCMD_LoadMessage(WCMD_TRUNCATEDLINE));
-          WCMD_output_asis( string);
-          WCMD_output_asis( newline);
-      }
-      if (string[0] != ':') {                      /* Skip over labels */
-          WCMD_process_command (string);
-      }
+  while (context -> skip_rest == FALSE) {
+      CMD_LIST *toExecute = NULL;         /* Commands left to be executed */
+      if (WCMD_ReadAndParseLine(NULL, &toExecute, h) == NULL)
+        break;
+      WCMD_process_commands(toExecute);
+      WCMD_free_commands(toExecute);
+      toExecute = NULL;
   }
   CloseHandle (h);
 
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index a9abfe6..4ab365c 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -92,6 +92,19 @@ WCHAR *WCMD_strdupW(WCHAR *input);
 BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars,
                    LPDWORD charsRead, const LPOVERLAPPED unused);
 
+/*	Data structure to hold commands to be processed */
+
+typedef struct _CMD_LIST {
+  WCHAR              *command;     /* Command string to execute                */
+  struct _CMD_LIST   *nextcommand; /* Next command string to execute           */
+  BOOL                isAmphersand;/* Whether follows &&                       */
+  int                 bracketDepth;/* How deep bracketing have we got to       */
+} CMD_LIST;
+
+WCHAR *WCMD_ReadAndParseLine(WCHAR *initialcmd, CMD_LIST **output, HANDLE readFrom);
+void   WCMD_process_commands(CMD_LIST *thisCmd);
+void   WCMD_free_commands(CMD_LIST *cmds);
+
 /*	Data structure to hold context when executing batch files */
 
 typedef struct {
@@ -100,6 +113,7 @@ typedef struct {
   int shift_count[10];	/* Offset in terms of shifts for %0 - %9 */
   void *prev_context;	/* Pointer to the previous context block */
   BOOL  skip_rest;      /* Skip the rest of the batch program and exit */
+  CMD_LIST *toExecute;  /* Commands left to be executed */
 } BATCH_CONTEXT;
 
 /* Data structure to save setlocal and pushd information */
@@ -223,7 +237,7 @@ extern WCHAR version_string[];
 #define WCMD_ANYKEY           1031
 #define WCMD_CONSTITLE        1032
 #define WCMD_VERSION          1033
-
+#define WCMD_MOREPROMPT       1034
 
 /* msdn specified max for Win XP */
 #define MAXSTRING 8192
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 3f4cdd0..245556e 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -84,6 +84,7 @@ int echo_mode = 1, verify_mode = 0, defaultColor = 7;
 static int opt_c, opt_k, opt_s;
 const WCHAR newline[] = {'\n','\0'};
 static const WCHAR equalsW[] = {'=','\0'};
+static const WCHAR closeBW[] = {')','\0'};
 WCHAR anykey[100];
 WCHAR version_string[100];
 WCHAR quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
@@ -107,13 +108,13 @@ int wmain (int argc, WCHAR *argvW[])
   WCHAR  *cmd   = NULL;
   WCHAR string[1024];
   WCHAR envvar[4];
-  DWORD count;
   HANDLE h;
   int opt_q;
   int opt_t = 0;
   static const WCHAR autoexec[] = {'\\','a','u','t','o','e','x','e','c','.',
                                    'b','a','t','\0'};
   char ansiVersion[100];
+  CMD_LIST *toExecute = NULL;         /* Commands left to be executed */
 
   /* Pre initialize some messages */
   strcpy(ansiVersion, PACKAGE_VERSION);
@@ -320,10 +321,13 @@ int wmain (int argc, WCHAR *argvW[])
        * the currently allocated input and output handles. This allows
        * us to pipe to and read from the command interpreter.
        */
-      if (strchrW(cmd,'|') != NULL)
-          WCMD_pipe(cmd);
-      else
-          WCMD_process_command(cmd);
+
+      /* Parse the command string, without reading any more input */
+      WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
+      WCMD_process_commands(toExecute);
+      WCMD_free_commands(toExecute);
+      toExecute = NULL;
+
       HeapFree(GetProcessHeap(), 0, cmd);
       return errorlevel;
   }
@@ -411,7 +415,11 @@ int wmain (int argc, WCHAR *argvW[])
   }
 
   if (opt_k) {
-      WCMD_process_command(cmd);
+      /* Parse the command string, without reading any more input */
+      WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
+      WCMD_process_commands(toExecute);
+      WCMD_free_commands(toExecute);
+      toExecute = NULL;
       HeapFree(GetProcessHeap(), 0, cmd);
   }
 
@@ -434,22 +442,18 @@ int wmain (int argc, WCHAR *argvW[])
 
   WCMD_version ();
   while (TRUE) {
+
+    /* Read until EOF (which for std input is never, but if redirect
+       in place, may occur                                          */
     WCMD_show_prompt ();
-    WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
-                   sizeof(string)/sizeof(WCHAR), &count, NULL);
-    if (count > 1) {
-      string[count-1] = '\0'; /* ReadFile output is not null-terminated! */
-      if (string[count-2] == '\r') string[count-2] = '\0'; /* Under Windoze we get CRLF! */
-      if (strlenW (string) != 0) {
-        if (strchrW(string,'|') != NULL) {
-          WCMD_pipe (string);
-        }
-        else {
-          WCMD_process_command (string);
-        }
-      }
-    }
+    if (WCMD_ReadAndParseLine(NULL, &toExecute,
+                              GetStdHandle(STD_INPUT_HANDLE)) == NULL)
+      break;
+    WCMD_process_commands(toExecute);
+    WCMD_free_commands(toExecute);
+    toExecute = NULL;
   }
+  return 0;
 }
 
 
@@ -1852,3 +1856,251 @@ BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars,
     }
     return res;
 }
+
+/***************************************************************************
+ * WCMD_DumpCommands
+ *
+ *	Domps out the parsed command line to ensure syntax is correct
+ */
+void WCMD_DumpCommands(CMD_LIST *commands) {
+    WCHAR buffer[MAXSTRING];
+    CMD_LIST *thisCmd = commands;
+    const WCHAR fmt[] = {'%','p',' ','%','c',' ','%','2','.','2','d',' ',
+                         '%','p',' ','%','s','\0'};
+
+    WINE_TRACE("Parsed line:\n");
+    while (thisCmd != NULL) {
+      sprintfW(buffer, fmt,
+               thisCmd,
+               thisCmd->isAmphersand?'Y':'N',
+               thisCmd->bracketDepth,
+               thisCmd->nextcommand,
+               thisCmd->command);
+      WINE_TRACE("%s\n", wine_dbgstr_w(buffer));
+      thisCmd = thisCmd->nextcommand;
+    }
+}
+
+/***************************************************************************
+ * WCMD_ReadAndParseLine
+ *
+ *   Either uses supplied input or
+ *     Reads a file from the handle, and then...
+ *   Parse the text buffer, spliting into seperate commands
+ *     - unquoted && strings split 2 commands but the 2nd is flagged as
+ *            following an &&
+ *     - ( as the first character just ups the bracket depth
+ *     - unquoted ) when bracket depth > 0 terminates a bracket and
+ *            adds a CMD_LIST structure with null command
+ *     - Anything else gets put into the command string (including
+ *            redirects)
+ */
+WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readFrom) {
+
+    WCHAR    *curPos;
+    BOOL      inQuotes = FALSE;
+    WCHAR     curString[MAXSTRING];
+    int       curLen   = 0;
+    int       curDepth = 0;
+    CMD_LIST *thisEntry = NULL;
+    CMD_LIST *lastEntry = NULL;
+    BOOL      isAmphersand = FALSE;
+    static WCHAR    *extraSpace = NULL;  /* Deliberately never freed */
+
+    /* Allocate working space for a command read from keyboard, file etc */
+    if (!extraSpace)
+      extraSpace = HeapAlloc(GetProcessHeap(), 0, (MAXSTRING+1) * sizeof(WCHAR));
+
+    /* If initial command read in, use that, otherwise get input from handle */
+    if (optionalcmd != NULL) {
+        strcpyW(extraSpace, optionalcmd);
+    } else if (readFrom == INVALID_HANDLE_VALUE) {
+        WINE_FIXME("No command nor handle supplied\n");
+    } else {
+        if (WCMD_fgets(extraSpace, MAXSTRING, readFrom) == NULL) return NULL;
+    }
+    curPos = extraSpace;
+
+    /* Handle truncated input - issue warning */
+    if (strlenW(extraSpace) == MAXSTRING -1) {
+        WCMD_output_asis(WCMD_LoadMessage(WCMD_TRUNCATEDLINE));
+        WCMD_output_asis(extraSpace);
+        WCMD_output_asis(newline);
+    }
+
+    /* Start with an empty string */
+    curLen = 0;
+
+    /* Parse every character on the line being processed */
+    while (*curPos != 0x00) {
+      switch (*curPos) {
+
+      case '\t':/* drop through - ignore whitespace at the start of a command */
+      case ' ': if (curLen > 0)
+                  curString[curLen++] = *curPos;
+                break;
+
+      case '"': inQuotes = !inQuotes;
+                break;
+
+      case '(': if (inQuotes || curLen > 0) {
+                  curString[curLen++] = *curPos;
+                } else {
+                  curDepth++;
+                }
+                break;
+
+      case '&': if (!inQuotes && *(curPos+1) == '&') {
+                  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;
+                  }
+                  lastEntry = thisEntry;
+                  isAmphersand = TRUE;
+                } else {
+                  curString[curLen++] = *curPos;
+                }
+                break;
+
+      case ')': if (!inQuotes && curDepth > 0) {
+
+                  /* Add the current command if there is one */
+                  if (curLen) {
+                    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;
+                  }
+
+                  /* Add an empty entry to the command list */
+                  thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
+                  thisEntry->command = NULL;
+                  thisEntry->nextcommand = NULL;
+                  thisEntry->isAmphersand = FALSE;
+                  thisEntry->bracketDepth = curDepth;
+                  curDepth--;
+                  if (lastEntry) {
+                    lastEntry->nextcommand = thisEntry;
+                  } else {
+                    *output = thisEntry;
+                  }
+                  lastEntry = thisEntry;
+                } else {
+                  curString[curLen++] = *curPos;
+                }
+                break;
+      default:
+                curString[curLen++] = *curPos;
+      }
+
+      curPos++;
+
+      /* If we have reached the end, add this command into the list */
+      if (*curPos == 0x00 && curLen > 0) {
+
+          /* 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;
+          }
+          lastEntry = thisEntry;
+      }
+
+      /* If we have reached the end of the string, see if bracketing outstanding */
+      if (*curPos == 0x00 && curDepth > 0 && readFrom != INVALID_HANDLE_VALUE) {
+        isAmphersand = FALSE;
+        inQuotes = FALSE;
+        memset(extraSpace, 0x00, (MAXSTRING+1) * sizeof(WCHAR));
+
+        /* Read more, skipping any blank lines */
+        while (*extraSpace == 0x00) {
+          if (!context) WCMD_output_asis( WCMD_LoadMessage(WCMD_MOREPROMPT));
+          if (WCMD_fgets(extraSpace, MAXSTRING, readFrom) == NULL) break;
+        }
+        curPos = extraSpace;
+      }
+    }
+
+    /* Dump out the parsed output */
+    WCMD_DumpCommands(*output);
+
+    return extraSpace;
+}
+
+/***************************************************************************
+ * WCMD_process_commands
+ *
+ * Process all the commands read in so far
+ */
+void WCMD_process_commands(CMD_LIST *thisCmd) {
+
+    /* Loop through the commands, processing them one by one */
+    while (thisCmd) {
+
+      /* 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->command);
+        } else {
+          WCMD_process_command (thisCmd->command);
+        }
+      }
+      thisCmd = thisCmd->nextcommand;
+    }
+}
+
+/***************************************************************************
+ * WCMD_free_commands
+ *
+ * Frees the storage held for a parsed command line
+ * - This is not done in the process_commands, as eventually the current
+ *   pointer will be modified within the commands, and hence a single free
+ *   routine is simpler
+ */
+void WCMD_free_commands(CMD_LIST *cmds) {
+
+    /* Loop through the commands, freeing them one by one */
+    while (cmds) {
+      CMD_LIST *thisCmd = cmds;
+      cmds = cmds->nextcommand;
+      HeapFree(GetProcessHeap(), 0, thisCmd->command);
+      HeapFree(GetProcessHeap(), 0, thisCmd);
+    }
+}
-- 
1.5.0




More information about the wine-patches mailing list