Commit 3c058185 authored by Jason Edmeades's avatar Jason Edmeades Committed by Alexandre Julliard

cmd: Add support for calling a built in command.

parent 9f83165e
...@@ -89,7 +89,10 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA ...@@ -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 */ CMD_LIST *toExecute = NULL; /* Commands left to be executed */
if (!WCMD_ReadAndParseLine(NULL, &toExecute, h)) if (!WCMD_ReadAndParseLine(NULL, &toExecute, h))
break; 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); WCMD_free_commands(toExecute);
toExecute = NULL; toExecute = NULL;
} }
...@@ -649,6 +652,8 @@ void WCMD_call (WCHAR *command) { ...@@ -649,6 +652,8 @@ void WCMD_call (WCHAR *command) {
/* Run other program if no leading ':' */ /* Run other program if no leading ':' */
if (*command != ':') { if (*command != ':') {
WCMD_run_program(command, TRUE); WCMD_run_program(command, TRUE);
/* If the thing we try to run does not exist, call returns 1 */
if (errorlevel) errorlevel=1;
} else { } else {
WCHAR gotoLabel[MAX_PATH]; WCHAR gotoLabel[MAX_PATH];
......
...@@ -1439,7 +1439,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, ...@@ -1439,7 +1439,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
/* Process the first command, if there is one */ /* Process the first command, if there is one */
if (executecmds && firstcmd && *firstcmd) { if (executecmds && firstcmd && *firstcmd) {
WCHAR *command = WCMD_strdupW(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); HeapFree(GetProcessHeap(), 0, command);
} }
...@@ -1468,14 +1468,14 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, ...@@ -1468,14 +1468,14 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
(*cmdList)->prevDelim == CMD_ONSUCCESS) { (*cmdList)->prevDelim == CMD_ONSUCCESS) {
if (processThese && (*cmdList)->command) { if (processThese && (*cmdList)->command) {
WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, variable, WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, variable,
value, cmdList); value, cmdList, FALSE);
} }
if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand; if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
/* Execute any appended to the statement with (...) */ /* Execute any appended to the statement with (...) */
} else if ((*cmdList)->bracketDepth > myDepth) { } else if ((*cmdList)->bracketDepth > myDepth) {
if (processThese) { 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); WINE_TRACE("Back from processing commands, (next = %p)\n", *cmdList);
} }
if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand; if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
...@@ -1497,7 +1497,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, ...@@ -1497,7 +1497,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
/* Skip leading whitespace between condition and the command */ /* Skip leading whitespace between condition and the command */
while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++; while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
if (*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; if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
...@@ -1809,7 +1809,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { ...@@ -1809,7 +1809,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
/* Execute program and redirect output */ /* Execute program and redirect output */
wsprintfW(temp_cmd, redirOut, (itemStart+1), temp_file); 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 */ /* Open the file, read line by line and process */
input = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ, input = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ,
......
...@@ -1599,6 +1599,22 @@ echo %ErrorLevel% ...@@ -1599,6 +1599,22 @@ echo %ErrorLevel%
rem First look for programs in the path before trying a builtin rem First look for programs in the path before trying a builtin
echo echo non-builtin dir> dir.cmd echo echo non-builtin dir> dir.cmd
call dir /b 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 cd .. & rd /s/q foobar
echo ------------ Testing SHIFT ------------ echo ------------ Testing SHIFT ------------
......
...@@ -824,14 +824,19 @@ foo "" ...@@ -824,14 +824,19 @@ foo ""
foo '' foo ''
'' bar '' bar
--- with builtins --- with builtins
@todo_wine@0 0
@todo_wine@foo created foo created
@todo_wine@Should expand foobaz Should expand foobaz
@todo_wine@batfile batfile
@todo_wine@robinfile robinfile
@todo_wine@1 1
@todo_wine@1 1
non-builtin dir non-builtin dir
Line one
Line two
Get if
... and else!
passed
------------ Testing SHIFT ------------ ------------ Testing SHIFT ------------
'p1' 'p2' 'p3' 'p4' 'p5' 'p1' 'p2' 'p3' 'p4' 'p5'
'p2' 'p3' 'p4' 'p5' '' 'p2' 'p3' 'p4' 'p5' ''
......
...@@ -121,11 +121,11 @@ BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWO ...@@ -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); WCHAR *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_LIST **output, HANDLE readFrom);
CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, 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_free_commands(CMD_LIST *cmds);
void WCMD_execute (const WCHAR *orig_command, const WCHAR *redirects, void WCMD_execute (const WCHAR *orig_command, const WCHAR *redirects,
const WCHAR *parameter, const WCHAR *substitution, const WCHAR *parameter, const WCHAR *substitution,
CMD_LIST **cmdList); CMD_LIST **cmdList, BOOL retrycall);
/* Data structure to hold context when executing batch files */ /* Data structure to hold context when executing batch files */
......
...@@ -1000,6 +1000,9 @@ static void init_msvcrt_io_block(STARTUPINFOW* st) ...@@ -1000,6 +1000,9 @@ static void init_msvcrt_io_block(STARTUPINFOW* st)
* Launching * Launching
* Once a match has been found, it is launched - Code currently uses * Once a match has been found, it is launched - Code currently uses
* findexecutable to achieve this which is left untouched. * 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) void WCMD_run_program (WCHAR *command, BOOL called)
...@@ -1197,6 +1200,8 @@ void WCMD_run_program (WCHAR *command, BOOL called) ...@@ -1197,6 +1200,8 @@ void WCMD_run_program (WCHAR *command, BOOL called)
if (!status) if (!status)
break; break;
called = FALSE; /* No need to retry as we launched something */
if (!assumeInternal && !console) errorlevel = 0; if (!assumeInternal && !console) errorlevel = 0;
else else
{ {
...@@ -1212,6 +1217,18 @@ void WCMD_run_program (WCHAR *command, BOOL called) ...@@ -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 */ /* Not found anywhere - give up */
SetLastError(ERROR_FILE_NOT_FOUND); SetLastError(ERROR_FILE_NOT_FOUND);
WCMD_print_error (); WCMD_print_error ();
...@@ -1226,10 +1243,13 @@ void WCMD_run_program (WCHAR *command, BOOL called) ...@@ -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. * Process one command. If the command is EXIT this routine does not return.
* We will recurse through here executing batch files. * 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, void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
const WCHAR *forVariable, const WCHAR *forValue, const WCHAR *forVariable, const WCHAR *forValue,
CMD_LIST **cmdList) CMD_LIST **cmdList, BOOL retrycall)
{ {
WCHAR *cmd, *p, *redir; WCHAR *cmd, *p, *redir;
int status, i; int status, i;
...@@ -1487,18 +1507,12 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, ...@@ -1487,18 +1507,12 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
case WCMD_ECHO: case WCMD_ECHO:
WCMD_echo(&whichcmd[count]); WCMD_echo(&whichcmd[count]);
break; break;
case WCMD_FOR:
WCMD_for (p, cmdList);
break;
case WCMD_GOTO: case WCMD_GOTO:
WCMD_goto (cmdList); WCMD_goto (cmdList);
break; break;
case WCMD_HELP: case WCMD_HELP:
WCMD_give_help (p); WCMD_give_help (p);
break; break;
case WCMD_IF:
WCMD_if (p, cmdList);
break;
case WCMD_LABEL: case WCMD_LABEL:
WCMD_volume (TRUE, p); WCMD_volume (TRUE, p);
break; break;
...@@ -1587,6 +1601,17 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, ...@@ -1587,6 +1601,17 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
case WCMD_EXIT: case WCMD_EXIT:
WCMD_exit (cmdList); WCMD_exit (cmdList);
break; 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: default:
prev_echo_mode = echo_mode; prev_echo_mode = echo_mode;
WCMD_run_program (whichcmd, FALSE); WCMD_run_program (whichcmd, FALSE);
...@@ -2240,7 +2265,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE ...@@ -2240,7 +2265,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
* Process all the commands read in so far * Process all the commands read in so far
*/ */
CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, 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; int bdepth = -1;
...@@ -2265,7 +2291,7 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, ...@@ -2265,7 +2291,7 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
Also, skip over any batch labels (eg. :fred) */ Also, skip over any batch labels (eg. :fred) */
if (thisCmd->command && thisCmd->command[0] != ':') { if (thisCmd->command && thisCmd->command[0] != ':') {
WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->command)); 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 */ /* Step on unless the command itself already stepped on */
...@@ -2555,7 +2581,7 @@ int wmain (int argc, WCHAR *argvW[]) ...@@ -2555,7 +2581,7 @@ int wmain (int argc, WCHAR *argvW[])
/* Parse the command string, without reading any more input */ /* Parse the command string, without reading any more input */
WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE); 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); WCMD_free_commands(toExecute);
toExecute = NULL; toExecute = NULL;
...@@ -2642,7 +2668,7 @@ int wmain (int argc, WCHAR *argvW[]) ...@@ -2642,7 +2668,7 @@ int wmain (int argc, WCHAR *argvW[])
if (opt_k) { if (opt_k) {
/* Parse the command string, without reading any more input */ /* Parse the command string, without reading any more input */
WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE); 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); WCMD_free_commands(toExecute);
toExecute = NULL; toExecute = NULL;
HeapFree(GetProcessHeap(), 0, cmd); HeapFree(GetProcessHeap(), 0, cmd);
...@@ -2662,7 +2688,7 @@ int wmain (int argc, WCHAR *argvW[]) ...@@ -2662,7 +2688,7 @@ int wmain (int argc, WCHAR *argvW[])
if (echo_mode) WCMD_show_prompt(); if (echo_mode) WCMD_show_prompt();
if (!WCMD_ReadAndParseLine(NULL, &toExecute, GetStdHandle(STD_INPUT_HANDLE))) if (!WCMD_ReadAndParseLine(NULL, &toExecute, GetStdHandle(STD_INPUT_HANDLE)))
break; break;
WCMD_process_commands(toExecute, FALSE, NULL, NULL); WCMD_process_commands(toExecute, FALSE, NULL, NULL, FALSE);
WCMD_free_commands(toExecute); WCMD_free_commands(toExecute);
toExecute = NULL; toExecute = NULL;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment