cmd.exe: Support IF..ELSE processing tolerate multiline/part lines.

This commit is contained in:
Jason Edmeades 2007-06-15 20:59:27 +01:00 committed by Alexandre Julliard
parent 345cb89175
commit d2e7b401a2
4 changed files with 130 additions and 24 deletions

View File

@ -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;
}

View File

@ -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;
}
}
}
}
}

View File

@ -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 */

View File

@ -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;
}
/***************************************************************************