Commit c55cd876 authored by Jason Edmeades's avatar Jason Edmeades Committed by Alexandre Julliard

cmd: Fix setlocal/endlocal implementation.

parent 9dde62cb
......@@ -22,6 +22,8 @@
#include "wcmd.h"
#include "wine/debug.h"
extern struct env_stack *saved_environment;
WINE_DEFAULT_DEBUG_CHANNEL(cmd);
/****************************************************************************
......@@ -94,6 +96,13 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
CloseHandle (h);
/*
* If there are outstanding setlocal's to the current context, unwind them.
*/
while (saved_environment && saved_environment->batchhandle == context->h) {
WCMD_endlocal();
}
/*
* If invoked by a CALL, we return to the context of our caller. Otherwise return
* to the caller's caller.
*/
......
......@@ -103,7 +103,7 @@ static const WCHAR parmY[] = {'/','Y','\0'};
static const WCHAR parmNoY[] = {'/','-','Y','\0'};
static HINSTANCE hinst;
static struct env_stack *saved_environment;
struct env_stack *saved_environment;
static BOOL verify_mode = FALSE;
/**************************************************************************
......@@ -2087,6 +2087,9 @@ void WCMD_setlocal (const WCHAR *s) {
struct env_stack *env_copy;
WCHAR cwd[MAX_PATH];
/* setlocal does nothing outside of batch programs */
if (!context) return;
/* DISABLEEXTENSIONS ignored */
env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
......@@ -2097,10 +2100,10 @@ void WCMD_setlocal (const WCHAR *s) {
}
env = GetEnvironmentStringsW ();
env_copy->strings = WCMD_dupenv (env);
if (env_copy->strings)
{
env_copy->batchhandle = context->h;
env_copy->next = saved_environment;
saved_environment = env_copy;
......@@ -2127,7 +2130,12 @@ void WCMD_endlocal (void) {
struct env_stack *temp;
int len, n;
if (!saved_environment)
/* setlocal does nothing outside of batch programs */
if (!context) return;
/* setlocal needs a saved environment from within the same context (batch
program) as it was saved in */
if (!saved_environment || saved_environment->batchhandle != context->h)
return;
/* pop the old environment from the stack */
......
......@@ -1659,27 +1659,163 @@ cmd /e:oN /C tmp.cmd
rem FIXME: creating file before setting envvar value to prevent parsing-time evaluation (due to EnableDelayedExpansion not being implemented/available yet)
echo --- setlocal with corresponding endlocal
rem %CD% does not tork on NT4 so use the following workaround
for /d %%i in (.) do set CURDIR=%%~dpnxi
echo @echo off> test.cmd
echo echo %%VAR%%>> test.cmd
echo setlocal>> test.cmd
echo set VAR=localval>> test.cmd
echo md foobar2>> test.cmd
echo cd foobar2>> test.cmd
echo echo %%VAR%%>> test.cmd
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> test.cmd
echo endlocal>> test.cmd
echo echo %%VAR%%>> test.cmd
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> test.cmd
set VAR=globalval
call test.cmd
echo %VAR%
for /d %%i in (.) do echo %%~dpnxi
cd /d %curdir%
rd foobar2
set VAR=
echo --- setlocal with no corresponding endlocal
echo @echo off> test.cmd
echo echo %%VAR%%>> test.cmd
echo setlocal>> test.cmd
echo set VAR=localval>> test.cmd
echo md foobar2>> test.cmd
echo cd foobar2>> test.cmd
echo echo %%VAR%%>> test.cmd
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> test.cmd
set VAR=globalval
rem %CD% does not tork on NT4 so use the following workaround
for /d %%i in (.) do set CURDIR=%%~dpnxi
call test.cmd
echo %VAR%
for /d %%i in (.) do echo %%~dpnxi
cd /d %curdir%
rd foobar2
set VAR=
echo --- setlocal within same batch program
set var1=one
set var2=
set var3=
rem %CD% does not tork on NT4 so use the following workaround
for /d %%i in (.) do set CURDIR=%%~dpnxi
setlocal
set var2=two
mkdir foobar2
cd foobar2
setlocal
set var3=three
if "%var1%"=="one" echo Var1 ok 1
if "%var2%"=="two" echo Var2 ok 2
if "%var3%"=="three" echo Var3 ok 3
for /d %%i in (.) do set curdir2=%%~dpnxi
if "%curdir2%"=="%curdir%\foobar2" echo Directory is ok 1
endlocal
if "%var1%"=="one" echo Var1 ok 1
if "%var2%"=="two" echo Var2 ok 2
if "%var3%"=="" echo Var3 ok 3
for /d %%i in (.) do set curdir2=%%~dpnxi
if "%curdir2%"=="%curdir%\foobar2" echo Directory is ok 2
endlocal
if "%var1%"=="one" echo Var1 ok 1
if "%var2%"=="" echo Var2 ok 2
if "%var3%"=="" echo Var3 ok 3
for /d %%i in (.) do set curdir2=%%~dpnxi
if "%curdir2%"=="%curdir%" echo Directory is ok 3
rd foobar2 /s /q
set var1=
echo --- Mismatched set and end locals
mkdir foodir2 2>nul
mkdir foodir3 2>nul
mkdir foodir4 2>nul
rem %CD% does not tork on NT4 so use the following workaround
for /d %%i in (.) do set curdir=%%~dpnxi
echo @echo off> 2set1end.cmd
echo echo %%VAR%%>> 2set1end.cmd
echo setlocal>> 2set1end.cmd
echo set VAR=2set1endvalue1>> 2set1end.cmd
echo cd ..\foodir3>> 2set1end.cmd
echo setlocal>> 2set1end.cmd
echo set VAR=2set1endvalue2>> 2set1end.cmd
echo cd ..\foodir4>> 2set1end.cmd
echo endlocal>> 2set1end.cmd
echo echo %%VAR%%>> 2set1end.cmd
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> 2set1end.cmd
echo @echo off> 1set2end.cmd
echo echo %%VAR%%>> 1set2end.cmd
echo setlocal>> 1set2end.cmd
echo set VAR=1set2endvalue1>> 1set2end.cmd
echo cd ..\foodir3>> 1set2end.cmd
echo endlocal>> 1set2end.cmd
echo echo %%VAR%%>> 1set2end.cmd
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> 1set2end.cmd
echo endlocal>> 1set2end.cmd
echo echo %%VAR%%>> 1set2end.cmd
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> 1set2end.cmd
echo --- Extra setlocal in called batch
set VAR=value1
rem -- setlocal1 == this batch, should never be used inside a called routine
setlocal
set var=value2
cd foodir2
call %curdir%\2set1end.cmd
echo Finished:
echo %VAR%
for /d %%i in (.) do echo %%~dpnxi
endlocal
echo %VAR%
for /d %%i in (.) do echo %%~dpnxi
cd /d %curdir%
echo --- Extra endlocal in called batch
set VAR=value1
rem -- setlocal1 == this batch, should never be used inside a called routine
setlocal
set var=value2
cd foodir2
call %curdir%\1set2end.cmd
echo Finished:
echo %VAR%
for /d %%i in (.) do echo %%~dpnxi
endlocal
echo %VAR%
for /d %%i in (.) do echo %%~dpnxi
cd /d %curdir%
echo --- endlocal in called function rather than batch pgm is ineffective
@echo off
set var=1
set var2=1
setlocal
set var=2
call :endlocalroutine
echo %var%
endlocal
echo %var%
goto :endlocalfinished
:endlocalroutine
echo %var%
endlocal
echo %var%
setlocal
set var2=2
endlocal
echo %var2%
endlocal
echo %var%
echo %var2%
goto :eof
:endlocalfinished
echo %var%
cd .. & rd /q/s foobar
echo ------------ Testing Errorlevel ------------
......
......@@ -853,12 +853,60 @@ ErrLev: 0
--- setlocal with corresponding endlocal
globalval
localval
@pwd@\foobar\foobar2
globalval
@pwd@\foobar
globalval
@pwd@\foobar
--- setlocal with no corresponding endlocal
globalval
localval
@todo_wine@globalval
@pwd@\foobar\foobar2
globalval
@pwd@\foobar
--- setlocal within same batch program
Var1 ok 1
Var2 ok 2
Var3 ok 3
Directory is ok 1
Var1 ok 1
Var2 ok 2
Var3 ok 3
Directory is ok 2
Var1 ok 1
Var2 ok 2
Var3 ok 3
Directory is ok 3
--- Mismatched set and end locals
--- Extra setlocal in called batch
value2
2set1endvalue1
@pwd@\foobar\foodir3
Finished:
value2
@pwd@\foobar\foodir2
value1
@pwd@\foobar
--- Extra endlocal in called batch
value2
value2
@pwd@\foobar\foodir2
value2
@pwd@\foobar\foodir2
Finished:
value2
@pwd@\foobar\foodir2
value1
@pwd@\foobar
--- endlocal in called function rather than batch pgm is ineffective
2
2
1
2
1
2
1
1
------------ Testing Errorlevel ------------
9009
1
......
......@@ -146,9 +146,10 @@ struct env_stack
struct env_stack *next;
union {
int stackdepth; /* Only used for pushd and popd */
WCHAR cwd; /* Only used for set/endlocal */
WCHAR cwd; /* Only used for set/endlocal */
} u;
WCHAR *strings;
HANDLE batchhandle; /* Used to ensure set/endlocals stay in scope */
};
/* Data structure to save setlocal and pushd information */
......
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