Code: Select all
@echo off & rem if defined retLocals goto :eof (remove 'rem' to block re-loads)
if "!" equ "" (
>&2 echo *** 'retLocals' macro must be loaded under 'disableDelayedExpansion'
exit /b 1
)
@rem single linefeed char 0x0A (two blank lines required below)
set LF=^
@rem newline macro (linefeed + line continuation)
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
@rem single carriage-return char 0x0D (usable as !CR! under edx, only)
for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set "CR=%%C"
:: macro to return multiple LF-free variables across endlocal barriers
::
:: syntax: %retLocals%[*cnt] out1[=in1] [, out2[=in2] [, out3[=in3]] ...]
:: - 'out#' (#=1,2,3,...) are variables being returned/set in the outer context
:: - 'in#' are local/inner context variables whose values are returned
:: - optional [=in#] defaults to 'out#=out#'
:: - optional [*cnt] is the nesting depth, default '*1' for one 'endlocal' call
:: - spaces around ',' commas and '=' equal signs are optional
::
:: e.g. %retLocals% var copies inner 'var' across 1 endlocal
:: to namesake 'var' in the outer context
::
:: %retLocals%*2 out1=in1, out2 copies 'in1', 'out2' across 2 endlocal's
:: to 'out1', 'out2' in the outer context
::
:: %retLocals%*0 out=in copies 'in' to 'out' within same context
:: like 'set out=%in%' but safe against
:: embedded quotes and funny chars except LF
::
:: ! names of variables 'out#', 'in#' are expected to be plain quote-less
:: well behaved strings, with no funny (<|>^%!=,;) characters
:: ! 'in#' names must not be 'cd' since that's used by %retLocals% internally
:: ! outer environment other than 'out#' is not modified
:: except for '%retLocals%*0' with '*0' de-nesting, which clears 'cd'
:: ! errorlevel is reset to '0' but the caller can preserve the inner value
:: if needed via '(%retLocals% ...) & set outErr=%errorlevel%'
set ^"retLocals=for %%# in (1 2) do if %%#==2 (%\n%
setlocal enableDelayedExpansion%\n%
set x=%\n%
for %%L in ("!LF!") do for %%R in ("!CR!") do ^
for /f "tokens=1,*" %%U in ("!cd!") do ^
for /f "tokens=2 delims=*" %%U in ("%%U*%%U") do (%\n%
set v=%%~V%\n%
for /f "delims=" %%V in ("!v:,=%%~L!") do ^
for /f "tokens=1,2 delims== " %%V in ("%%~V=%%~V") do (%\n%
set w=!%%~W!%\n%
if defined w (%\n%
set w=!w:^"=""q!%\n%
set w=!w:%%~R=""r!%\n%
set "path=" ^& set "pathExt=;"%\n%
set "w=!w:^=^^^^!"%\n%
call set "w=%%w:^!=^^^!%%"%\n%
set "w=!w:^^=^!")%\n%
if defined x set x=!x!!LF!%\n%
set x=!x!"%%~V=!%%~W!"!LF!"%%~V=!w!")%\n%
for /f "delims=" %%X in ("!x!") do (%\n%
if "!cd:~0,1!"=="*" (%\n%
for /l %%U in (0 1 %%U) do endlocal%\n%
set "cd=" ^& call;)%\n%
if errorlevel 1 ((if "!"=="" (%\n%
set "%%~X"!%\n%
for /f "delims==" %%V in ("%%~X") do (%\n%
if defined %%~V (%\n%
set %%~V=!%%~V:""r=%%~R!%\n%
set %%~V=!%%~V:^""q="!%\n%
)))) ^& call;%\n%
) else (if not "!"=="" set "%%~X") ^& call%\n%
))) else set cd=*1^"
exit /b 0
For a made-up and completely useless test case, the following
Code: Select all
@echo off & setlocal disableDelayedExpansion
@rem load 'retLocals' macro
call retLocals
@rem run silly :login
set "user="
set "pass="
call :login user pass
setlocal enableDelayedExpansion
echo(
echo user '!user!' / pass '!pass!'
endlocal & endlocal & goto :eof
@rem random code using 'retLocals'
:login
setlocal enableDelayedExpansion
echo(
set/p name="enter name: "
set/p %~2="enter pass: "
(%retLocals% %~1=name, %~2) & goto :eof
Code: Select all
C:\tmp>retLocals.test.login
enter name: Λi√ıū
enter pass: (%!"^,""^^;&>>^
user 'Λi√ıū' / pass '(%!"^,""^^;&>>^'
C:\tmp>
I believe it's technically possible to write a similar macro with LF support, too, probably at the expense of an external batch call. That said, I don't have the dedication (or much interest) to follow that through myself. Feel free to run with it
Liviu