cmd: Add support for calling a built in command.

This commit is contained in:
Jason Edmeades 2012-10-14 00:38:53 +01:00 committed by Alexandre Julliard
parent 9f83165efb
commit 3c05818515
6 changed files with 79 additions and 27 deletions

View file

@ -89,7 +89,10 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
CMD_LIST *toExecute = NULL; /* Commands left to be executed */
if (!WCMD_ReadAndParseLine(NULL, &toExecute, h))
break;
WCMD_process_commands(toExecute, FALSE, NULL, NULL);
/* Note: although this batch program itself may be called, we are not retrying
the command as a result of a call failing to find a program, hence the
retryCall parameter below is FALSE */
WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
WCMD_free_commands(toExecute);
toExecute = NULL;
}
@ -649,6 +652,8 @@ void WCMD_call (WCHAR *command) {
/* Run other program if no leading ':' */
if (*command != ':') {
WCMD_run_program(command, TRUE);
/* If the thing we try to run does not exist, call returns 1 */
if (errorlevel) errorlevel=1;
} else {
WCHAR gotoLabel[MAX_PATH];

View file

@ -1439,7 +1439,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
/* Process the first command, if there is one */
if (executecmds && firstcmd && *firstcmd) {
WCHAR *command = WCMD_strdupW(firstcmd);
WCMD_execute (firstcmd, (*cmdList)->redirects, variable, value, cmdList);
WCMD_execute (firstcmd, (*cmdList)->redirects, variable, value, cmdList, FALSE);
HeapFree(GetProcessHeap(), 0, command);
}
@ -1468,14 +1468,14 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
(*cmdList)->prevDelim == CMD_ONSUCCESS) {
if (processThese && (*cmdList)->command) {
WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, variable,
value, cmdList);
value, cmdList, FALSE);
}
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, variable, value);
*cmdList = WCMD_process_commands(*cmdList, TRUE, variable, value, FALSE);
WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
}
if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
@ -1497,7 +1497,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
/* Skip leading whitespace between condition and the command */
while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
if (*cmd) {
WCMD_execute (cmd, (*cmdList)->redirects, variable, value, cmdList);
WCMD_execute (cmd, (*cmdList)->redirects, variable, value, cmdList, FALSE);
}
}
if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
@ -1809,7 +1809,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
/* Execute program and redirect output */
wsprintfW(temp_cmd, redirOut, (itemStart+1), temp_file);
WCMD_execute (itemStart, temp_cmd, NULL, NULL, NULL);
WCMD_execute (itemStart, temp_cmd, NULL, NULL, NULL, FALSE);
/* Open the file, read line by line and process */
input = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ,

View file

@ -1599,6 +1599,22 @@ echo %ErrorLevel%
rem First look for programs in the path before trying a builtin
echo echo non-builtin dir> dir.cmd
call dir /b
del dir.cmd
rem The below line equates to call (, which does nothing, then the
rem subsequent lines are executed.
call (
echo Line one
echo Line two
)
rem The below line equates to call if, which always fails, then the
rem subsequent lines are executed. Note cmd.exe swallows all lines
rem starting with )
call if 1==1 (
echo Get if
) else (
echo ... and else!
)
call call call echo passed
cd .. & rd /s/q foobar
echo ------------ Testing SHIFT ------------

View file

@ -824,14 +824,19 @@ foo ""
foo ''
'' bar
--- with builtins
@todo_wine@0
@todo_wine@foo created
@todo_wine@Should expand foobaz
@todo_wine@batfile
@todo_wine@robinfile
@todo_wine@1
@todo_wine@1
0
foo created
Should expand foobaz
batfile
robinfile
1
1
non-builtin dir
Line one
Line two
Get if
... and else!
passed
------------ Testing SHIFT ------------
'p1' 'p2' 'p3' 'p4' 'p5'
'p2' 'p3' 'p4' 'p5' ''

View file

@ -121,11 +121,11 @@ BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWO
WCHAR *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_LIST **output, HANDLE readFrom);
CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
const WCHAR *var, const WCHAR *val);
const WCHAR *var, const WCHAR *val, BOOL retrycall);
void WCMD_free_commands(CMD_LIST *cmds);
void WCMD_execute (const WCHAR *orig_command, const WCHAR *redirects,
const WCHAR *parameter, const WCHAR *substitution,
CMD_LIST **cmdList);
CMD_LIST **cmdList, BOOL retrycall);
/* Data structure to hold context when executing batch files */

View file

@ -1000,6 +1000,9 @@ static void init_msvcrt_io_block(STARTUPINFOW* st)
* Launching
* Once a match has been found, it is launched - Code currently uses
* findexecutable to achieve this which is left untouched.
* If an executable has not been found, and we were launched through
* a call, we need to check if the command is an internal command,
* so go back through wcmd_execute.
*/
void WCMD_run_program (WCHAR *command, BOOL called)
@ -1197,6 +1200,8 @@ void WCMD_run_program (WCHAR *command, BOOL called)
if (!status)
break;
called = FALSE; /* No need to retry as we launched something */
if (!assumeInternal && !console) errorlevel = 0;
else
{
@ -1212,6 +1217,18 @@ void WCMD_run_program (WCHAR *command, BOOL called)
}
}
/* Not found anywhere - were we called? */
if (called) {
CMD_LIST *toExecute = NULL; /* Commands left to be executed */
/* Parse the command string, without reading any more input */
WCMD_ReadAndParseLine(command, &toExecute, INVALID_HANDLE_VALUE);
WCMD_process_commands(toExecute, FALSE, NULL, NULL, called);
WCMD_free_commands(toExecute);
toExecute = NULL;
return;
}
/* Not found anywhere - give up */
SetLastError(ERROR_FILE_NOT_FOUND);
WCMD_print_error ();
@ -1226,10 +1243,13 @@ void WCMD_run_program (WCHAR *command, BOOL called)
/*****************************************************************************
* Process one command. If the command is EXIT this routine does not return.
* We will recurse through here executing batch files.
* Note: If call is used to a non-existing program, we reparse the line and
* try to run it as an internal command. 'retrycall' represents whether
* we are attempting this retry.
*/
void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
const WCHAR *forVariable, const WCHAR *forValue,
CMD_LIST **cmdList)
CMD_LIST **cmdList, BOOL retrycall)
{
WCHAR *cmd, *p, *redir;
int status, i;
@ -1487,18 +1507,12 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
case WCMD_ECHO:
WCMD_echo(&whichcmd[count]);
break;
case WCMD_FOR:
WCMD_for (p, cmdList);
break;
case WCMD_GOTO:
WCMD_goto (cmdList);
break;
case WCMD_HELP:
WCMD_give_help (p);
break;
case WCMD_IF:
WCMD_if (p, cmdList);
break;
case WCMD_LABEL:
WCMD_volume (TRUE, p);
break;
@ -1587,6 +1601,17 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
case WCMD_EXIT:
WCMD_exit (cmdList);
break;
case WCMD_FOR:
case WCMD_IF:
/* Very oddly, probably because of all the special parsing required for
these two commands, neither for nor if are supported when called,
ie call if 1==1... will fail. */
if (!retrycall) {
if (i==WCMD_FOR) WCMD_for (p, cmdList);
else if (i==WCMD_IF) WCMD_if (p, cmdList);
break;
}
/* else: drop through */
default:
prev_echo_mode = echo_mode;
WCMD_run_program (whichcmd, FALSE);
@ -2240,7 +2265,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
* Process all the commands read in so far
*/
CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
const WCHAR *var, const WCHAR *val) {
const WCHAR *var, const WCHAR *val,
BOOL retrycall) {
int bdepth = -1;
@ -2265,7 +2291,7 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
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));
WCMD_execute (thisCmd->command, thisCmd->redirects, var, val, &thisCmd);
WCMD_execute (thisCmd->command, thisCmd->redirects, var, val, &thisCmd, retrycall);
}
/* Step on unless the command itself already stepped on */
@ -2555,7 +2581,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, FALSE, NULL, NULL);
WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
WCMD_free_commands(toExecute);
toExecute = NULL;
@ -2642,7 +2668,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, FALSE, NULL, NULL);
WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
WCMD_free_commands(toExecute);
toExecute = NULL;
HeapFree(GetProcessHeap(), 0, cmd);
@ -2662,7 +2688,7 @@ int wmain (int argc, WCHAR *argvW[])
if (echo_mode) WCMD_show_prompt();
if (!WCMD_ReadAndParseLine(NULL, &toExecute, GetStdHandle(STD_INPUT_HANDLE)))
break;
WCMD_process_commands(toExecute, FALSE, NULL, NULL);
WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
WCMD_free_commands(toExecute);
toExecute = NULL;
}