Module: wine
Branch: master
Commit: c55cd87632bdb54c0ea506edc279ebb9118def95
URL: http://source.winehq.org/git/wine.git/?a=commit;h=c55cd87632bdb54c0ea506edc…
Author: Jason Edmeades <jason(a)edmeades.me.uk>
Date: Sun Sep 30 23:07:01 2012 +0100
cmd: Fix setlocal/endlocal implementation.
---
programs/cmd/batch.c | 9 ++
programs/cmd/builtins.c | 14 +++-
programs/cmd/tests/test_builtins.cmd | 136 ++++++++++++++++++++++++++++++
programs/cmd/tests/test_builtins.cmd.exp | 50 +++++++++++-
programs/cmd/wcmd.h | 3 +-
5 files changed, 207 insertions(+), 5 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index 4926b1f..b77931d 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -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.
*/
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index f9e51e5..f6922ad 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -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 */
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index f564aed..d89721a 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -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 ------------
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index 435d0e6..4bab1ef 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -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
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 451c49b..32edc2b 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -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 */