[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