Code: Select all
Set "key="
For /F "delims=" %%# In (
'Xcopy /L /W "%~f0" "%~f0" 2^>Nul'
) Do If Not Defined Key Set "Key=%%#"
Set "Key=%Key:~-1%"
SetLocal EnableDelayedExpansion
The string is built as long as 'Key' is defined by using:
Code: Select all
For /F delims^=^ eol^= %%# In ("!Line!") Do (
EndLocal&Set "Line=%%#%Key%"
)
Once key is undefined (after enter is pressed) the routine returns the result:
Code: Select all
For /F delims^=^ eol^= %%# In ("!Line!") Do (
EndLocal&EndLocal&(
Set "%~1=%%#"
exit/b %length%
)
)
OK so this works great as long as the routine is called from the command line or called from another script when delayed expansion is disabled. However if you call the routine from an enabled expansion environment, i.e. from another script then you run into problems handling ^'s and !'s when returning the final string.
It seems that one must determine the calling environment conditionally as has successfully been done by jeb and dbenham by setting a flag:
setlocal
set "NotDelayedFlag=!"
So if this flag is defined the routine was called from a 'DisableDelayedExapnsion' environment or the command line.
Conversly if the flag is not defined then the script has to deal with returning ^'s and !'s.
Here is the full routine:
Code: Select all
:GetVar
:: SYNTAX: GetVar ^<EnVar^> [="User Prompt: "] [length] [/m]
::
:: Assigns User input to the Environment variable passed as 1st argument
:: Allows unbalance quotes and 'poison' characters mixed with quotes
:: The only editing key available to the User is 'backspace'
:: The user string entered is set in the Environment variable, 'Envar'
:: If no arguments are passed the routine quits, stand alone version displays this help
:: The fisrt argument is mandatory, the rest are optional
:: To display a variable with Poison chars and quotes use: Eco.bat
:: To mask input with '*'s place /m at the very end of the argument list
:: This routine has flaws if called from a Delayed Expansion environment
::
:: Example:
::
:: GetVar.bat Pa$$word="Enter your 'Poison Password' string: " len /m
:: Eco.bat "Your Poison Password is: '" Pa$$word "' with length: " len
:::
::: Dependencies - None Author: Carl with ideas borrowed from Dostips.com
:::
if "%~1"=="" goto :EndGetVar
SetLocal DisableDelayedExpansion
set "NotDelayedFlag=!"
set "Masked="
echo(%*|find " /m">nul
if %errorlevel% equ 0 set "Masked=*"
For /F %%# In ('"Prompt;$H&For %%# in (1) Do Rem"') Do Set "BS=%%#"
Set "Line="
if /i "%~2" neq "/m" <Nul set/p=".%BS% %BS%%~2"
:Char_Loop_
Set "Key="
For /F "delims=" %%# In (
'Xcopy /L /W "%~f0" "%~f0" 2^>Nul'
) Do If Not Defined Key Set "Key=%%#"
Set "Key=%Key:~-1%"
SetLocal EnableDelayedExpansion
If Not Defined Key ( Rem Enter pressed
echo(
If not Defined Line EndLocal&EndLocal&(
If Defined %~1 Set "%~1="
If Defined %~3 Set/a "%~3=0
)&exit/b 0
For /F delims^=^ eol^= %%# In ("!Line!") Do (
EndLocal&EndLocal&(
If not "%~3"=="" Set/a "%~3=%length%"
If Not "%~1"=="" (Set "%~1=%%#")
exit/b %length%
)
)
)
If %BS%==^%Key% (
Set "Key="
If Defined Line set/a length-=1& Set "Line=!Line:~0,-1!"& Set /P "=%BS% %BS%" <Nul
) Else (
If defined Masked (Set "Display=*") Else (Set "Display=!Key!")
set/a length+=1& Set /p=".%BS%!Display!" <Nul
)
If Not Defined Line (
EndLocal& Set/a length=1& Set "Line=%Key%"
) Else For /F delims^=^ eol^= %%# In ("!Line!") Do (
EndLocal& Set/a length=%length%& Set "Line=%%#%Key%"
)
Goto :Char_Loop_
:EndGetVar
exit/b
:Eco "Here is a char: " char " and a string: " EnvarString /n "This is line two."
::
:: Echo's strings mixed with environment variables with or without CR/LF or embed CR/LF inside
:: Environment variables must be passed by name (not reference) and may contain poison chars
:: Quoted strings are echo'd as is and may have leading or trailing spaces do not quote Env vars
:: To place a newline (CF/LF) use '/n' unquoted
:::
::: Dependencies - None Author: Carl with ideas borrowed from Dostips.com
:::
SetLocal DisableDelayedExpansion
For /F %%# In ('"Prompt;$H&For %%# in (1) Do Rem"') Do Set "BS=%%#"
SetLocal EnableDelayedExpansion
:Start_Eco
if "%~1"=="" EndLocal&EndLocal&exit/b
if /i "/n" equ "%~1" (
echo(
) else if not defined %1 (
set "string=.%BS%%~1"
for /f delims^=^ eol^= %%# in ("!string!") do EndLocal& set/p="%%#" <nul
SetLocal EnableDelayedExpansion
) else ( <nul set/p=".%BS%!%~1!")
shift&goto :Start_Eco
:End_Eco
To make a long story short, the question is:
How to modify the routine so it returns any and all characters entered regardless if called from another routine with delayed expansion enabled?