some time ago the safe return technic was developed, to return any text over an endlocal barrier.
But it has one big disadvantage, you need to copy the part to each function, as it can't be used in a general subfunction.
The cause is simple.
If the safe return would reside in a subfunction like :generalReturn this can't remove the SETLOCAL contexts, as a function can only remove the own SETLOCALs but not the ones, defined outside of the function
Code: Select all
@echo off
set var=origin
setlocal
set var=second
call :test
echo %var%
exit /b
:test
endlocal
exit /b
This code outputs second not origin as the endlocal in the :test function has no effect.
So I decide to write a macro for the return technic.
But this is also a bit tricky, as at some points (escaping exclamation marks) you need to use percent expansion, which will not work in a macro.
You could use a CALL, but then you can't place a single escape caret in front of the exclamation marks anymore.
And later you get the problem to decide, if the context you return to is a disabled or enabled one.
For the enabled context, it's possible to solve it inside the macro, but for the disabled context it seems to be impossible.
So I decide to call in that case a helper function.
Long explanation, but simply to use without the need to understand anything of the macro.
A function can use the macro
Code: Select all
:myFunction <returnVariable>
setlocal EnableDelayedExpansion
set "var=Hello &^^ "^^^^ ^&" world^!!CR!*!LF!X"
%jebReturn% %1 var
exit /b
But it can also be used in a normal FOR loop
Code: Select all
setlocal DisableDelayedExpansion
set "line="
for /L %%n in (1 1 3) do (
setlocal DisableDelayedExpansion
set "text=critical&<>| !" ^&^<^>^| ^^ !"
setlocal EnableDelayedExpansion
set full=!line!!text!
%jebReturn% line full 2
setlocal EnableDelayedExpansion
echo result=!line!
endlocal
)
You only need to initialize the macro and when using the macro you can optional define the number of ENDLOCAL barriers to overcome.
Code: Select all
@echo off
setlocal DisableDelayedExpansion
call :initReturn
rem ** Now the macro can be used
setlocal EnableDelayedExpansion
call :test result
echo !result!
:test
setlocal EnableDelayedExpansion
setlocal EnableDelayedExpansion
setlocal EnableDelayedExpansion
set "var=Hello &^^ "^^^^ ^&" world^!!CR!*!LF!X"
%jebReturn% result var 3
exit /b
:::------------------
:initReturn
set LF=^
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set "CR=%%C"
REM ** jebReturn <resultVariable> <TranferVariable> [endlocalCount]
REM ** return a <TranferVariable> over one or more endlocal barriers to a resultVariable
REM ** endlocalCount default=1, is the number of endlocals that should be executed
REM ** All special characters can be transferd from TranferVariable to the result variable, even CR and LF and also "<>&|!^"
REM ** The result is correct, independent of the delayed expansion mode after the endlocals
set ^"jebReturn=for %%# in (1 2) do if %%#==2 (%\n%
setlocal EnableDelayedExpansion%\n%
set safeReturn_count=0%\n%
for %%C in (!args!) do set "safeReturn[!safeReturn_count!]=%%~C" ^& set /a safeReturn_count+=1%\n%
if not defined safeReturn[2] set "safeReturn[2]=1"%\n%
set /a safeReturn[2]+=1%\n%
for /F "delims=" %%T in ("!safeReturn[1]!") DO set "safeReturn_Ena=!%%T!"%\n%
set ^"safeReturn_Ena=!safeReturn_Ena:"=""q!"%\n%
FOR /F %%R in ("!CR! #") DO set "safeReturn_Ena=!safeReturn_Ena:%%~R=""r!"%\n%
FOR %%L in ("!LF!") DO set "safeReturn_Ena=!safeReturn_Ena:%%~L=""n!"%\n%
set "safeReturn_Dis=!safeReturn_Ena!"%\n%
set "safeReturn_Ena=!safeReturn_Ena:^=^^!"%\n%
set "path="%\n%
set "pathExt=;"%\n%
call set "safeReturn_Ena=%%safeReturn_Ena:^!=""c^!%%"%\n%
set "safeReturn_Ena=!safeReturn_Ena:""c=^!"%\n%
FOR %%L in ("!LF!") DO (%\n%
for /F "delims=" %%N in (""!safeReturn[0]!"") DO (%\n%
for /F "delims=" %%D in (""!safeReturn_Dis!"") DO (%\n%
for /F "delims=" %%E in (""!safeReturn_Ena!"") DO (%\n%
FOR /L %%n in (1 1 !safeReturn[2]!) do endlocal%\n%
if "!"=="" (%\n%
set "%%~N=%%~E" !%\n%
set "%%~N=!%%~N:""n=%%~L!"%\n%
FOR /F %%R in ("!CR! #") DO set "%%~N=!%%~N:""r=%%~R!"%\n%
set ^"%%~N=!%%~N:""q="!"%\n%
) ELSE (%\n%
set "%%~N=%%~D"%\n%
call :__disDelayedReturn %%~N%\n%
)%\n%
)%\n%
)%\n%
)%\n%
)%\n%
) else set args="
exit /b
REM ** Helper function for correct returning when delayed expansion is disabled
:__disDelayedReturn
setlocal EnableDelayedExpansion
set "safeReturn_text=!%~1!"
set "safeReturn_text=!safeReturn_text:%%=%%~2!"
set "safeReturn_text=!safeReturn_text:""n=%%~L!"
set "safeReturn_text=!safeReturn_text:""r=%%~3!"
set "safeReturn_text=!safeReturn_text:""q=%%~4!"
FOR %%L in ("!LF!") DO (
FOR /F "tokens=1-4" %%2 in (^"%% !CR! """") DO (
endlocal
set "%1=%safeReturn_text%"
)
)
exit /b
hope it helps
jeb