From: Eric Pouech epouech@codeweavers.com
Fix several incorrect behaviors that (partly) compensate each others: - when replacing %XXX% by %YYY%, advance current position after the copied %YYY% (otherwise it will expand %YYY% in same run), - don't parse the command in run_program(), - always expand full content in CALL command.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 78 ++++++++++---------- programs/cmd/tests/test_builtins.cmd.exp | 6 +- programs/cmd/tests/test_cmdline.cmd.exp | 2 +- programs/cmd/wcmd.h | 4 +- programs/cmd/wcmdmain.c | 90 ++++++++++++------------ 5 files changed, 88 insertions(+), 92 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 402a6f649d2..fe859dd52c4 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -42,7 +42,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(cmd); * a label to goto once opened. */
-void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HANDLE pgmHandle) +void WCMD_batch (WCHAR *file, WCHAR *command, WCHAR *startLabel, HANDLE pgmHandle) { HANDLE h = INVALID_HANDLE_VALUE; BATCH_CONTEXT *prev_context; @@ -112,11 +112,6 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
free(context->batchfileW); LocalFree(context); - if ((prev_context != NULL) && (!called)) { - WINE_TRACE("Batch completed, but was not 'called' so skipping outer batch too\n"); - prev_context -> skip_rest = TRUE; - context = prev_context; - } context = prev_context; }
@@ -642,46 +637,47 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute) WCMD_strsubstW(*start, lastModifier+1, finaloutput, -1); }
+extern void WCMD_expand(const WCHAR *, WCHAR *); + /******************************************************************* * WCMD_call - processes a batch call statement * * If there is a leading ':', calls within this batch program * otherwise launches another program. */ -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 = ERROR_INVALID_FUNCTION; - } else { - - WCHAR gotoLabel[MAX_PATH]; - - lstrcpyW(gotoLabel, param1); - - if (context) { - - LARGE_INTEGER li; - - /* Save the for variable context, then start with an empty context - as for loop variables do not survive a call */ - WCMD_save_for_loop_context(TRUE); - - /* Save the current file position, call the same file, - restore position */ - li.QuadPart = 0; - li.u.LowPart = SetFilePointer(context -> h, li.u.LowPart, - &li.u.HighPart, FILE_CURRENT); - WCMD_batch (context->batchfileW, command, TRUE, gotoLabel, context->h); - SetFilePointer(context -> h, li.u.LowPart, - &li.u.HighPart, FILE_BEGIN); - - /* Restore the for loop context */ - WCMD_restore_for_loop_context(); - } else { - WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_CALLINSCRIPT)); +void WCMD_call(WCHAR *command) +{ + WCHAR buffer[MAXSTRING]; + WCMD_expand(command, buffer); + + /* Run other program if no leading ':' */ + if (*command != ':') + { + WCMD_run_program(buffer, TRUE); + /* If the thing we try to run does not exist, call returns 1 */ + if (errorlevel) errorlevel = ERROR_INVALID_FUNCTION; } - } + else if (context) + { + LARGE_INTEGER li; + WCHAR gotoLabel[MAX_PATH]; + + lstrcpyW(gotoLabel, param1); + + /* Save the for variable context, then start with an empty context + as for loop variables do not survive a call */ + WCMD_save_for_loop_context(TRUE); + + /* Save the current file position, call the same file, + restore position */ + li.QuadPart = 0; + li.u.LowPart = SetFilePointer(context->h, li.u.LowPart, + &li.u.HighPart, FILE_CURRENT); + WCMD_batch(context->batchfileW, buffer, gotoLabel, context->h); + SetFilePointer(context->h, li.u.LowPart, &li.u.HighPart, FILE_BEGIN); + + /* Restore the for loop context */ + WCMD_restore_for_loop_context(); + } else + WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_CALLINSCRIPT)); } diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 146dbb5ad0a..31ed5b92765 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1588,9 +1588,9 @@ passed abc abc %WINE_BAR% -@todo_wine@abc -@todo_wine@abc %WINE_BAR% %WINE_BAR% -@todo_wine@abc def def +abc +abc %WINE_BAR% %WINE_BAR% +abc def def --- mixing batch and builtins bar@space@ @todo_wine@foo diff --git a/programs/cmd/tests/test_cmdline.cmd.exp b/programs/cmd/tests/test_cmdline.cmd.exp index afd4a4eefdf..58cd5480292 100644 --- a/programs/cmd/tests/test_cmdline.cmd.exp +++ b/programs/cmd/tests/test_cmdline.cmd.exp @@ -115,7 +115,7 @@ bar@space@ 0@space@ !@space@ 0@space@@or_broken@!@space@ -@todo_wine@0@space@ +0@space@ '@space@ +@space@ 0@space@ diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index e2ee0fc02ac..19d82c0f062 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -182,7 +182,7 @@ typedef int RETURN_CODE; #define RETURN_CODE_ABORTED (-999999)
void WCMD_assoc (const WCHAR *, BOOL); -void WCMD_batch (WCHAR *, WCHAR *, BOOL, WCHAR *, HANDLE); +void WCMD_batch(WCHAR *, WCHAR *, WCHAR *, HANDLE); void WCMD_call (WCHAR *command); void WCMD_change_tty (void); void WCMD_choice (const WCHAR *); @@ -242,7 +242,7 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute);
WCHAR *WCMD_strip_quotes(WCHAR *cmd); WCHAR *WCMD_LoadMessage(UINT id); -void WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int len); +WCHAR *WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int len); BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead);
WCHAR *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_NODE **output, HANDLE readFrom); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 1fe519aa149..f921f771b52 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -438,7 +438,7 @@ void *xrealloc(void *ptr, size_t size) * It's up to the caller to ensure there is enough space in the * destination buffer. */ -void WCMD_strsubstW(WCHAR *start, const WCHAR *next, const WCHAR *insert, int len) { +WCHAR *WCMD_strsubstW(WCHAR *start, const WCHAR *next, const WCHAR *insert, int len) {
if (len < 0) len=insert ? lstrlenW(insert) : 0; @@ -446,6 +446,7 @@ void WCMD_strsubstW(WCHAR *start, const WCHAR *next, const WCHAR *insert, int le memmove(start+len, next, (lstrlenW(next) + 1) * sizeof(*next)); if (insert) memcpy(start, insert, len * sizeof(*insert)); + return start + len; }
BOOL WCMD_get_fullpath(const WCHAR* in, SIZE_T outsize, WCHAR* out, WCHAR** start) @@ -568,8 +569,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) /* In batch program, missing terminator for % and no following ':' just removes the '%' */ if (context) { - WCMD_strsubstW(start, start + 1, NULL, 0); - return start; + return WCMD_strsubstW(start, start + 1, NULL, 0); } else {
/* In command processing, just ignore it - allows command line @@ -643,30 +643,21 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) /* Command line - just ignore this */ if (context == NULL) return endOfVar+1;
- /* Batch - replace unknown env var with nothing */ - if (colonpos == NULL) { - WCMD_strsubstW(start, endOfVar + 1, NULL, 0); - } else { - len = lstrlenW(thisVar); - thisVar[len-1] = 0x00; - /* If %:...% supplied, : is retained */ - if (colonpos == thisVar+1) { - WCMD_strsubstW(start, endOfVar + 1, colonpos, -1); - } else { - WCMD_strsubstW(start, endOfVar + 1, colonpos + 1, -1); - } - } - return start; - + if (colonpos == NULL) + return WCMD_strsubstW(start, endOfVar + 1, NULL, 0); + len = lstrlenW(thisVar); + thisVar[len-1] = 0x00; + /* If %:...% supplied, : is retained */ + if (colonpos == thisVar+1) + return WCMD_strsubstW(start, endOfVar + 1, colonpos, -1); + return WCMD_strsubstW(start, endOfVar + 1, colonpos + 1, -1); }
/* See if we need to do complex substitution (any ':'s), if not then our work here is done */ - if (colonpos == NULL) { - WCMD_strsubstW(start, endOfVar + 1, thisVarContents, -1); - return start; - } + if (colonpos == NULL) + return WCMD_strsubstW(start, endOfVar + 1, thisVarContents, -1);
/* Restore complex bit */ *colonpos = ':'; @@ -699,20 +690,18 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) startCopy = &thisVarContents[max(0, len + substrposition)]; }
- if (commapos == NULL) { + if (commapos == NULL) /* Copy the lot */ - WCMD_strsubstW(start, endOfVar + 1, startCopy, -1); - } else if (substrlength < 0) { + return WCMD_strsubstW(start, endOfVar + 1, startCopy, -1); + if (substrlength < 0) {
int copybytes = len + substrlength - (startCopy - thisVarContents); if (copybytes >= len) copybytes = len - 1; else if (copybytes < 0) copybytes = 0; - WCMD_strsubstW(start, endOfVar + 1, startCopy, copybytes); - } else { - substrlength = min(substrlength, len - (startCopy - thisVarContents)); - WCMD_strsubstW(start, endOfVar + 1, startCopy, substrlength); + return WCMD_strsubstW(start, endOfVar + 1, startCopy, copybytes); } - + substrlength = min(substrlength, len - (startCopy - thisVarContents)); + return WCMD_strsubstW(start, endOfVar + 1, startCopy, substrlength); /* search and replace manipulation */ } else { WCHAR *equalspos = wcsstr(colonpos, L"="); @@ -720,6 +709,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) WCHAR *found = NULL; WCHAR *searchIn; WCHAR *searchFor; + WCHAR *ret;
if (equalspos == NULL) return start+1; s = xstrdupW(endOfVar + 1); @@ -743,13 +733,14 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) /* Do replacement */ lstrcpyW(start, replacewith); lstrcatW(start, thisVarContents + (found-searchIn) + lstrlenW(searchFor+1)); + ret = start + wcslen(start); lstrcatW(start, s); } else { /* Copy as is */ lstrcpyW(start, thisVarContents); + ret = start + wcslen(start); lstrcatW(start, s); } - } else { /* Loop replacing all instances */ WCHAR *lastFound = searchIn; @@ -767,13 +758,14 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) } lstrcatW(outputposn, thisVarContents + (lastFound-searchIn)); + ret = outputposn + wcslen(outputposn); lstrcatW(outputposn, s); } free(s); free(searchIn); free(searchFor); + return ret; } - return start; }
/***************************************************************************** @@ -831,8 +823,9 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { if (!atExecute && *(p+1) == startchar) { if (context) { WCMD_strsubstW(p, p+1, NULL, 0); + p++; } - p+=1; + else p+=2;
/* Replace %~ modifications if in batch program */ } else if (*(p+1) == '~') { @@ -843,7 +836,7 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { } else if (!atExecute && context && (i >= 0) && (i <= 9) && startchar == '%') { t = WCMD_parameter(context -> command, i + context -> shift_count[i], NULL, TRUE, TRUE); - WCMD_strsubstW(p, p+2, t, -1); + p = WCMD_strsubstW(p, p+2, t, -1);
/* Replace use of %* if in batch program*/ } else if (!atExecute && context && *(p+1)=='*' && startchar == '%') { @@ -852,15 +845,15 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { if (startOfParms != NULL) { startOfParms += lstrlenW(thisParm); while (*startOfParms==' ' || *startOfParms == '\t') startOfParms++; - WCMD_strsubstW(p, p+2, startOfParms, -1); + p = WCMD_strsubstW(p, p+2, startOfParms, -1); } else - WCMD_strsubstW(p, p+2, NULL, 0); + p = WCMD_strsubstW(p, p+2, NULL, 0);
} else { int forvaridx = for_var_char_to_index(*(p+1)); if (startchar == '%' && forvaridx != -1 && forloopcontext->variable[forvaridx]) { /* Replace the 2 characters, % and for variable character */ - WCMD_strsubstW(p, p + 2, forloopcontext->variable[forvaridx], -1); + p = WCMD_strsubstW(p, p + 2, forloopcontext->variable[forvaridx], -1); } else if (!atExecute || startchar == '!') { p = WCMD_expand_envvar(p, startchar);
@@ -930,6 +923,13 @@ static void WCMD_parse (const WCHAR *s, WCHAR *q, WCHAR *p1, WCHAR *p2) } }
+void WCMD_expand(const WCHAR *src, WCHAR *dst) +{ + wcscpy(dst, src); + handleExpansion(dst, FALSE); + WCMD_parse(dst, quals, param1, param2); +} + /* ============================== */ /* Data structures for commands */ /* ============================== */ @@ -1317,6 +1317,8 @@ static void init_msvcrt_io_block(STARTUPINFOW* st) } }
+static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOOL iscalled); + /****************************************************************************** * WCMD_run_program * @@ -1524,8 +1526,12 @@ void WCMD_run_program (WCHAR *command, BOOL called) if (ext && (!wcsicmp(ext, L".bat") || !wcsicmp(ext, L".cmd"))) { BOOL oldinteractive = interactive; interactive = FALSE; - WCMD_batch (thisDir, command, called, NULL, INVALID_HANDLE_VALUE); + WCMD_batch(thisDir, command, NULL, INVALID_HANDLE_VALUE); interactive = oldinteractive; + if (context && !called) { + TRACE("Batch completed, but was not 'called' so skipping outer batch too\n"); + context->skip_rest = TRUE; + } return; } else { DWORD exit_code; @@ -1574,13 +1580,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
/* Not found anywhere - were we called? */ if (called) { - CMD_NODE *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, called); - node_dispose_tree(toExecute); - toExecute = NULL; + execute_single_command(command, NULL, TRUE); return; }