From 3c058185155a24f0e059d55c5d6318bc9a3f6400 Mon Sep 17 00:00:00 2001 From: Jason Edmeades Date: Sun, 14 Oct 2012 00:38:53 +0100 Subject: [PATCH] cmd: Add support for calling a built in command. --- programs/cmd/batch.c | 7 ++++- programs/cmd/builtins.c | 10 +++---- programs/cmd/tests/test_builtins.cmd | 16 ++++++++++ programs/cmd/tests/test_builtins.cmd.exp | 19 +++++++----- programs/cmd/wcmd.h | 4 +-- programs/cmd/wcmdmain.c | 50 ++++++++++++++++++++++++-------- 6 files changed, 79 insertions(+), 27 deletions(-) diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 7b17ea851d5..bacb6cdf557 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -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]; diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 866281b3803..8ae9eb2fdcb 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -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, diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 1956c3f11ec..3d01151b7e6 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -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 ------------ diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index f6e7d110e44..b7e8397e6d5 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -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' '' diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index e303efe2788..d4d148f72d7 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -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 */ diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 07b8321fe77..69317551527 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -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; } -- 2.11.4.GIT