forked from Mirrors/wine-wine
cmd: Fix setlocal/endlocal implementation.
parent
9dde62cb96
commit
c55cd87632
|
@ -22,6 +22,8 @@
|
||||||
#include "wcmd.h"
|
#include "wcmd.h"
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
|
|
||||||
|
extern struct env_stack *saved_environment;
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(cmd);
|
WINE_DEFAULT_DEBUG_CHANNEL(cmd);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
|
@ -93,6 +95,13 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
|
||||||
}
|
}
|
||||||
CloseHandle (h);
|
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
|
* If invoked by a CALL, we return to the context of our caller. Otherwise return
|
||||||
* to the caller's caller.
|
* to the caller's caller.
|
||||||
|
|
|
@ -103,7 +103,7 @@ static const WCHAR parmY[] = {'/','Y','\0'};
|
||||||
static const WCHAR parmNoY[] = {'/','-','Y','\0'};
|
static const WCHAR parmNoY[] = {'/','-','Y','\0'};
|
||||||
|
|
||||||
static HINSTANCE hinst;
|
static HINSTANCE hinst;
|
||||||
static struct env_stack *saved_environment;
|
struct env_stack *saved_environment;
|
||||||
static BOOL verify_mode = FALSE;
|
static BOOL verify_mode = FALSE;
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
@ -2087,6 +2087,9 @@ void WCMD_setlocal (const WCHAR *s) {
|
||||||
struct env_stack *env_copy;
|
struct env_stack *env_copy;
|
||||||
WCHAR cwd[MAX_PATH];
|
WCHAR cwd[MAX_PATH];
|
||||||
|
|
||||||
|
/* setlocal does nothing outside of batch programs */
|
||||||
|
if (!context) return;
|
||||||
|
|
||||||
/* DISABLEEXTENSIONS ignored */
|
/* DISABLEEXTENSIONS ignored */
|
||||||
|
|
||||||
env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
|
env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
|
||||||
|
@ -2097,10 +2100,10 @@ void WCMD_setlocal (const WCHAR *s) {
|
||||||
}
|
}
|
||||||
|
|
||||||
env = GetEnvironmentStringsW ();
|
env = GetEnvironmentStringsW ();
|
||||||
|
|
||||||
env_copy->strings = WCMD_dupenv (env);
|
env_copy->strings = WCMD_dupenv (env);
|
||||||
if (env_copy->strings)
|
if (env_copy->strings)
|
||||||
{
|
{
|
||||||
|
env_copy->batchhandle = context->h;
|
||||||
env_copy->next = saved_environment;
|
env_copy->next = saved_environment;
|
||||||
saved_environment = env_copy;
|
saved_environment = env_copy;
|
||||||
|
|
||||||
|
@ -2127,7 +2130,12 @@ void WCMD_endlocal (void) {
|
||||||
struct env_stack *temp;
|
struct env_stack *temp;
|
||||||
int len, n;
|
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;
|
return;
|
||||||
|
|
||||||
/* pop the old environment from the stack */
|
/* 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)
|
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
|
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 off> test.cmd
|
||||||
echo echo %%VAR%%>> test.cmd
|
echo echo %%VAR%%>> test.cmd
|
||||||
echo setlocal>> test.cmd
|
echo setlocal>> test.cmd
|
||||||
echo set VAR=localval>> test.cmd
|
echo set VAR=localval>> test.cmd
|
||||||
|
echo md foobar2>> test.cmd
|
||||||
|
echo cd foobar2>> test.cmd
|
||||||
echo echo %%VAR%%>> test.cmd
|
echo echo %%VAR%%>> test.cmd
|
||||||
|
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> test.cmd
|
||||||
echo endlocal>> test.cmd
|
echo endlocal>> test.cmd
|
||||||
echo echo %%VAR%%>> test.cmd
|
echo echo %%VAR%%>> test.cmd
|
||||||
|
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> test.cmd
|
||||||
set VAR=globalval
|
set VAR=globalval
|
||||||
call test.cmd
|
call test.cmd
|
||||||
echo %VAR%
|
echo %VAR%
|
||||||
|
for /d %%i in (.) do echo %%~dpnxi
|
||||||
|
cd /d %curdir%
|
||||||
|
rd foobar2
|
||||||
set VAR=
|
set VAR=
|
||||||
echo --- setlocal with no corresponding endlocal
|
echo --- setlocal with no corresponding endlocal
|
||||||
echo @echo off> test.cmd
|
echo @echo off> test.cmd
|
||||||
echo echo %%VAR%%>> test.cmd
|
echo echo %%VAR%%>> test.cmd
|
||||||
echo setlocal>> test.cmd
|
echo setlocal>> test.cmd
|
||||||
echo set VAR=localval>> test.cmd
|
echo set VAR=localval>> test.cmd
|
||||||
|
echo md foobar2>> test.cmd
|
||||||
|
echo cd foobar2>> test.cmd
|
||||||
echo echo %%VAR%%>> test.cmd
|
echo echo %%VAR%%>> test.cmd
|
||||||
|
echo for /d %%%%i in (.) do echo %%%%~dpnxi>> test.cmd
|
||||||
set VAR=globalval
|
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
|
call test.cmd
|
||||||
echo %VAR%
|
echo %VAR%
|
||||||
|
for /d %%i in (.) do echo %%~dpnxi
|
||||||
|
cd /d %curdir%
|
||||||
|
rd foobar2
|
||||||
set VAR=
|
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
|
cd .. & rd /q/s foobar
|
||||||
|
|
||||||
echo ------------ Testing Errorlevel ------------
|
echo ------------ Testing Errorlevel ------------
|
||||||
|
|
|
@ -853,12 +853,60 @@ ErrLev: 0
|
||||||
--- setlocal with corresponding endlocal
|
--- setlocal with corresponding endlocal
|
||||||
globalval
|
globalval
|
||||||
localval
|
localval
|
||||||
|
@pwd@\foobar\foobar2
|
||||||
globalval
|
globalval
|
||||||
|
@pwd@\foobar
|
||||||
globalval
|
globalval
|
||||||
|
@pwd@\foobar
|
||||||
--- setlocal with no corresponding endlocal
|
--- setlocal with no corresponding endlocal
|
||||||
globalval
|
globalval
|
||||||
localval
|
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 ------------
|
------------ Testing Errorlevel ------------
|
||||||
9009
|
9009
|
||||||
1
|
1
|
||||||
|
|
|
@ -146,9 +146,10 @@ struct env_stack
|
||||||
struct env_stack *next;
|
struct env_stack *next;
|
||||||
union {
|
union {
|
||||||
int stackdepth; /* Only used for pushd and popd */
|
int stackdepth; /* Only used for pushd and popd */
|
||||||
WCHAR cwd; /* Only used for set/endlocal */
|
WCHAR cwd; /* Only used for set/endlocal */
|
||||||
} u;
|
} u;
|
||||||
WCHAR *strings;
|
WCHAR *strings;
|
||||||
|
HANDLE batchhandle; /* Used to ensure set/endlocals stay in scope */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Data structure to save setlocal and pushd information */
|
/* Data structure to save setlocal and pushd information */
|
||||||
|
|
Loading…
Reference in New Issue