Mask password with Asterisk

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
AramSarkisyan
Posts: 4
Joined: 14 Mar 2018 20:35

Mask password with Asterisk

#1 Post by AramSarkisyan » 14 Mar 2018 20:52

Hi everyone,
I'm new here and in coding also :).
Any advise for the below issue would be greatly appreciated.
Here is what i need:

Need to run batch file and release access to some file via "Cacls" cmd for user. Batch file should ask for password and before execution of cacls command need confirmation.
With my beginner coding knowledge and with the help of inet i have done something here:

Code: Select all

@Echo off
color a
set tries=6
:top
cls
set /a tries =%tries%-1
if %tries%==0 (
goto :end)
Echo Please enter password to proceed.
Echo You have %tries% attempts left.
set/p "pass=>"
if %pass%== HELLO (
icacls C:\TEST.txt /grant Administrator:F
cls
) else (
goto :top)
:end
Above code allows me to do what i want, however password can be seen ((((.
Looking for any advise..
Thanks
Aram

Squashman
Expert
Posts: 4150
Joined: 23 Dec 2011 13:59

Re: Mask password with Asterisk

#2 Post by Squashman » 14 Mar 2018 21:00

Yes. This question has been covered a few times on the forums.
viewtopic.php?t=4664#p26788

AramSarkisyan
Posts: 4
Joined: 14 Mar 2018 20:35

Re: Mask password with Asterisk

#3 Post by AramSarkisyan » 15 Mar 2018 02:25

Thanks Squashman
I'v gone thru all codes from your link. Most of them uses asterisk, but unfortunately they are too complicated for me :D and i couldn't merge any of them with my "code". None off them executes any command (not sure if i'm correct)..

dbenham
Expert
Posts: 2369
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Mask password with Asterisk

#4 Post by dbenham » 15 Mar 2018 05:54

That link from Squashman uses XCOPY to read key presses.

Carlos discovered an alternate way to read key presses using REPLACE at viewtopic.php?f=3&t=6382.

I then used Carlos' work to develop a my own masked input routine called :getMaskedInput, which in turn calls :getKey, both found at viewtopic.php?f=3&t=7396.

I continued to refine those routines to be extremely robust, and the final versions are available at viewtopic.php?f=3&t=7396&p=50819#p50819

@AramSarkisyan - You don't have to understand the code to use it :!:

Simply copy the :getMaskedInput and :getKey functions into your code. Then if you want to prompt the user to enter a password, and capture the value in variable PASS, then simply use the following call:

Code: Select all

call :getMaskedInput /p "Password: " PASS
Read the documentation at the top of the source code to learn how to restrict what characters are accepted.


Dave Benham

AramSarkisyan
Posts: 4
Joined: 14 Mar 2018 20:35

Re: Mask password with Asterisk

#5 Post by AramSarkisyan » 15 Mar 2018 20:02

Dave,
Thanks... I read articles you recommended.
The first issue I got is that <<call :getMaskedInput /p "Password: " PASS>> didn't recognized, so i changed <<:getMaskedInput>> to the <<:getMaskedInputLoop>>.
But still, i couldn't link <<:getMaskedInputLoop>> and <<:getKey>> with my code. I am not sure how correctly combine them ((.
I don't mind if the password can be seen in the batch file (will convert it to exe).
Will I looks stupid if I request someone's help to combine them? :?

Squashman
Expert
Posts: 4150
Joined: 23 Dec 2011 13:59

Re: Mask password with Asterisk

#6 Post by Squashman » 15 Mar 2018 22:23

Wasn't overly difficult to combine them. You could have at least made an attempt and showed us what you tried. I will give you a fish today.

Code: Select all

@echo off

set tries=6
:top
cls
set /a tries-=1
if %tries% equ 0 goto end
Echo Please enter password to proceed.
Echo You have %tries% attempts left.
call :getMaskedInput /p "Password: " pass
if "%pass%"=="HELLO" (
	icacls C:\TEST.txt /grant Administrator:F
	cls
) else (
	goto :top
)
:end

GOTO :EOF

::getMaskedInput  [/P "Prompt"]  StrVar  [ValidVar [/I]]
::
:: Get a user input string, echoing * for each key pressed. [Backspace] erases
:: the previous character. [Enter] completes the string. Additionally, any
:: method that generates Null (0x00), LineFeed (0x0A) or Carriage Return (0x0D)
:: will also terminate the string. On Windows 10 a [Ctrl-Z] (0x1A) will also
:: terminate the string. The final string may contain any characters between
:: 0x01 and 0xFF except Backspace, LineFeed, and Carriage Return. On Windows 10
:: Ctrl-Z is also excluded.
::
:: The optional /P parameter is used to specify a "Prompt" that is written to
:: stdout, without a newline.
::
:: The optional ValidVar parameter defines the characters that will be accepted.
:: If the variable is not given or not defined, then all characters are accepted.
:: If given and defined, then only characters within ValidVar are accepted.
::
:: If ValidVar is followed by the optional /I switch, then case of standard
:: English letters is ignored. The case of the pressed key is preserved in
:: the result, but English letters A-Z and a-z are not rejected due to case
:: differences when the /I switch is added.
::
:: Any value (except null) may be entered by holding the [Alt] key and pressing
:: the appropriate decimal code on the numeric keypad. For example, holding
:: [Alt] and pressing numeric keypad [1] and [0], and then releasing [Alt] will
:: result in a LineFeed.
::
:: The only way to enter a Null is by holding [Ctrl] and pressing the normal [2]
::
:: An alternate way to enter control characters 0x01 through 0x1A is by holding
:: the [Ctrl] key and pressing any one of the letter keys [A] through [Z].
:: However, [Ctrl-A], [Ctrl-F], [Ctrl-M], and [Ctrl-V] will be blocked on Win 10
:: if the console has Ctrl key shortcuts enabled.
::
:: This function works properly regardless whether delayed expansion
:: is enabled or disabled.
::
:: :getMaskedInput version 2.0 was written by Dave Benham, and originally
:: posted at http://www.dostips.com/forum/viewtopic.php?f=3&t=7396
::
:: This work was inspired by posts from carlos and others at
:: http://www.dostips.com/forum/viewtopic.php?f=3&t=6382
::
:getMaskedInput
setlocal
set "notDelayed=!"
if /i "%~1" equ "/P" (
  <nul set /p ^"=%2"
  shift /1
  shift /1
)
setlocal enableDelayedExpansion
set "mask=!%2!"
for /f %%A in ('"Prompt;$H&for %%A in (1) do rem"') do set "BS=%%A"
if defined mask set "mask=1!BS!!mask!"
set "str="
:getMaskedInputLoop
(
  call :getKey key mask %3
  if defined key (
    if not defined notDelayed (
      if "!key!" equ "^!" set "key=^^^!"
      if "!key!" equ "^" set "key=^^"
    )
    if "!key!" equ "!BS!" (
      if defined str (
        set "str=!str:~0,-1!"
        <nul set /p "=%BS% %BS%"
      )
    ) else (
      set "str=!str!!key!"
      <nul set /p "=*"
    )
    goto :getMaskedInputLoop
  )
  for /f "delims=" %%A in (""!str!"") do (
    endlocal
    endlocal
    set "%1=%%~A" !
    echo(
    exit /b
  )
)

::getKey  [/P "Prompt"]  KeyVar  [ValidVar [/I]]
::
:: Read a keypress representing a character between 0x00 and 0xFF and store the
:: value in variable KeyVar. Null (0x00), LineFeed (0x0A), and Carriage Return
:: (0x0D) will result in an undefined KeyVar. On Windows 10, Ctrl-Z (0x1A) will
:: also result in an undefined KeyVar. The simplest way to get an undefined
:: KeyVar is to press the [Enter] key.
::
:: The optional /P parameter is used to specify a "Prompt" that is written to
:: stdout, without a newline. Also, the accepted character is ECHOed after the
:: prompt if the /P option was used.
::
:: The optional ValidVar parameter defines the values that will be accepted.
:: If the variable is not given or not defined, then all characters are accepted.
:: If given and defined, then only characters within ValidVar are accepted. The
:: first character within ValidVar should either be 0, meaning ignore undefined
:: KeyVar, or 1, meaning accept undefined KeyVar. The remaining characters
:: represent themselves. For example, a ValidVar value of 0YN will only accept
:: uppercase Y or N. A value of 1YN will additionally accept [Enter] etc.
::
:: If ValidVar is followed by the optional /I switch, then case of standard
:: English letters is ignored. The case of the pressed key is preserved in
:: the result, but English letters A-Z and a-z are not rejected due to case
:: differences when the /I switch is added.
::
:: Any value (except null) may be entered by holding the [Alt] key and pressing
:: the appropriate decimal code on the numeric keypad. For example, holding
:: [Alt] and pressing numeric keypad [1] and [0], and then releasing [Alt] will
:: result in a LineFeed.
::
:: The only way to enter a Null is by holding [Ctrl] and pressing the normal [2]
::
:: An alternate way to enter control characters 0x01 through 0x1A is by holding
:: the [Ctrl] key and pressing any one of the letter keys [A] through [Z].
:: However, [Ctrl-A], [Ctrl-F], [Ctrl-M], and [Ctrl-V] will be blocked on Win 10
:: if the console has Ctrl key shortcuts enabled.
::
:: This function works properly regardless whether delayed expansion is enabled
:: or disabled.
::
:: :getKey version 2.0 was written by Dave Benham, and originally posted at
:: http://www.dostips.com/forum/viewtopic.php?f=3&t=7396
::
:: This work was inspired by posts from carlos and others at
:: http://www.dostips.com/forum/viewtopic.php?f=3&t=6382
::
:getKey
setlocal disableDelayedExpansion
if /i "%~1" equ "/P" (
  <nul set /p ^"=%2"
  shift /1
  shift /1
  set "getKey./P=1"
) else (
  set "getKey./P="
)
:getKeyRetry
(
  endlocal&setlocal disableDelayedExpansion
  (for /f skip^=1^ delims^=^ eol^= %%A in ('replace.exe ? . /u /w') do for /f delims^=^ eol^= %%B in ("%%A") do (
    endlocal
    if "%%B" equ "" (set "%1=^!") else set "%1=%%B"
    setlocal enableDelayedExpansion
  )) || (
    endlocal
    set "%1="
    setlocal enableDelayedExpansion
  )
  set "getKey./P=%getKey./P%"
  if defined %1 (set "getKey.key=!%1!") else set "getKey.key=x"
)
(
  if "!%2!" neq "" (
    if defined %1 (
      set "getKey.mask=!%2:~1!"
      if not defined getKey.mask goto :getKeyRetry
      if /i "%~3" equ "/I" (
        if "!%1!" equ "=" (
          set "getKey.mask=a!getKey.mask!"
          for /f "delims=" %%A in ("!getKey.mask!") do if /i "!getKey.mask:%%A=%%A!" equ "!getKey.mask!" goto :getKeyRetry
        ) else for /f delims^=^ eol^= %%A in ("!%1!") do if "!getKey.mask:*%%A=!" equ "!getKey.mask!" goto :getKeyRetry
      ) else (
        for /f tokens^=1*^ eol^=^%getKey.key%^ delims^=^%getKey.key% %%A in ("!getKey.mask!!getKey.mask!") do if "%%B" equ "" goto :getKeyRetry
      )
    ) else if "!%2:~0,1!" equ "0" goto :getKeyRetry
  )
  if defined getKey./P echo(!%1!
  exit /b
)

::getAnyKey  [/P "Prompt"]  KeyVar  [ValidVar [/I]]
::
:: Read a keypress representing any character between 0x00 and 0xFF and store
:: the character in variable KeyVar. A Null value of 0x00 is represented as an
:: undefined KeyVar.
::
:: Calling :getAnyKey will also define the following three variables:
::   getAnyKey.LF    = Linefeed        0x0A, decimal 10
::   getAnyKey.CR    = Carriage Return 0x0D, decimal 13
::   getAnyKey.CtrlZ = Control-Z       0x1A, decimal 26
:: The three variables can be defined in advance by calling :getAnyKeyInit.
::
:: The optional /P parameter is used to specify a "Prompt" that is written to
:: stdout, without a newline. Also, the accepted character is ECHOed after the
:: prompt if the /P option was used.
::
:: The optional ValidVar parameter defines the characters that will be accepted.
:: If the variable is not given or not defined, then all values are accepted.
:: If given and defined, then only characters within ValidVar are accepted. The
:: first character indicates whether Null (0x00) is accepted. A value of 1 means
:: acceptance, and 0 means rejection. The remaining characters represent
:: themselves. For example, a ValidVar value of 0YN will only accept upper case
:: Y or N. A value of 1YN will additionally accept Null.
::
:: Linefeed, Carriage Return, and/or Control-Z may be added to ValidVar by
:: enabling delayedExpansion, calling :getAnyKeyInit, and then appending
:: !getAnyKey.LF!, !getAnyKey.CR!, and/or !getAnyKey.CtrlZ! respectively.
::
:: If ValidVar is followed by the optional /I switch, then case of standard
:: English letters is ignored. The case of the pressed key is preserved in
:: the result, but English letters A-Z and a-z are not rejected due to case
:: differences when the /I switch is added.
::
:: Note that [Enter] is interpreted as a Carriage Return.
::
:: Any value (except null) may be entered by holding the [Alt] key and pressing
:: the appropriate decimal code on the numeric keypad. For example, holding
:: [Alt] and pressing numeric keypad [1] and [0], and then releasing [Alt] will
:: result in a Linefeed.
::
:: The only way to enter a Null is by holding [Ctrl] and pressing the normal [2]
::
:: An alternate way to enter control characters 0x01 through 0x1A is by holding
:: the [Ctrl] key and pressing any one of the letter keys [A] through [Z].
:: However, [Ctrl-A], [Ctrl-F], [Ctrl-M], and [Ctrl-V] will be blocked on Win 10
:: if the console has Ctrl key shortcuts enabled.
::
:: This function works properly regardless whether delayed expansion is enabled
:: or disabled.
::
:: :getAnyKey version 2.0 was written by Dave Benham, and originally posted at
:: http://www.dostips.com/forum/viewtopic.php?f=3&t=7396
::
:: This work was inspired by posts from carlos and others at
:: http://www.dostips.com/forum/viewtopic.php?f=3&t=6382
::
:getAnyKey
if not defined getAnyKey.CtrlZ call :getAnyKeyInit
setlocal
if "!!" equ "" set "getAnyKey.delayed=1"
(
  endlocal&setlocal disableDelayedExpansion
  set "getAnyKey.delayed=%getAnyKey.delayed%"
  if /i "%~1" equ "/P" (
    set "getAnyKey./P=1"
    <nul set /p ^"=%2"
    shift /1
    shift /1
  ) else (
    set "getAnyKey./P="
  )
)
:getAnyKeyRetry
(
  endlocal&setlocal disableDelayedExpansion
  for /f "skip=1 delims=" %%A in (
    'replace.exe ? . /u /w ^| findstr /n "^" ^| find /n /v ""'
  ) do set "str=%%A"
  setlocal enableDelayedExpansion
  if "!str!" equ "[3]3:" (  %= LineFeed =%
    endlocal&endlocal
    set ^"%1=^%getAnyKey.LF%%getAnyKey.LF%"
  ) else (                  %= Not LineFeed =%
    if "!str!" equ "[2]2:" (         %= Ctrl-Z on Win 10 =%
      set "key=!getAnyKey.CtrlZ!
    ) else if "!str!" equ "[3]" (    %= Null = %
      set "key="
    ) else (                         %= All others =%
      set "key=!str:~-1!"
      if "%getAnyKey.delayed%"=="1" if "!key!" equ "^!" set "key=^^^!"
    )
    for /f "delims=" %%A in (""!key!"") do endlocal&endlocal&set "%1=%%~A"
  )
  setlocal enableDelayedExpansion
  set "getAnyKey.delayed=%getAnyKey.delayed%"
  set "getAnyKey./P=%getAnyKey./P%"
  set "getAnyKey.key=x"
  if defined %1 if !%1! neq !getAnyKey.LF! if !%1! neq !getAnyKey.CR! set "getAnyKey.key=!%1!"
)
(
  if "!%2!" neq "" (
    if defined %1 (
      set "getAnyKey.mask=!%2:~1!"
      if not defined getAnyKey.mask goto :getAnyKeyRetry
      if "!%1!" equ "!getAnyKey.LF!" (
        for %%A in ("!%1!") do if "!getAnyKey.mask:%%~A=!" equ "!getAnyKey.mask!" goto :getAnyKeyRetry
      ) else if "!%1!" equ "!getAnyKey.CR!" (
        for /f %%A in (""!%1!"") do if "!getAnyKey.mask:%%~A=!" equ "!getAnyKey.mask!" goto :getAnyKeyRetry
      ) else if /i "%~3" equ "/I" (
        if "!%1!" neq "=" (
          for /f "delims=" %%A in (""!%1!"") do if "!getAnyKey.mask:*%%~A=!" equ "!getAnyKey.mask!" goto :getAnyKeyRetry
        ) else (
          for %%A in ("!getAnyKey.LF!") do set "getAnyKey.mask=a!getAnyKey.mask:%%~A=!"
          for /f "delims=" %%A in (""!getAnyKey.mask!"") do if /i "!getAnyKey.mask:%%~A=%%~A!" equ "!getAnyKey.mask!" goto :getKeyRetry
        )
      ) else (
        for %%A in ("!getAnyKey.LF!") do set "getAnyKey.mask=!getAnyKey.mask:%%~A=!"
        for /f tokens^=1*^ eol^=^%getAnyKey.key%^ delims^=^%getAnyKey.key% %%A in (
          "!getAnyKey.mask!!getAnyKey.mask!!getAnyKey.CR!"
        ) do if "%%B" equ "" goto :getAnyKeyRetry
      )
    ) else if "!%2:~0,1!" equ "0" goto :getAnyKeyRetry
  )
  if defined getAnyKey./P echo(!%1!
  exit /b
)

:getAnyKeyInit
:: Ctrl-Z  0x1A  decimal 26
copy nul "%temp%\ctrlZ.tmp" /a <nul >nul
(for /f "usebackq" %%A in ("%temp%\ctrlZ.tmp") do set "getAnyKey.CtrlZ=%%A")2>nul||goto :getAnyKeyInit
del "%temp%\ctrlZ.tmp" 2>nul
:: Linefeed  0x0A  decimal 10
(set getAnyKey.LF=^
%= Do not remove or alter this line =%
)
:: Carriage Return  0x0D  decimal 13
for /f %%A in ('copy /z "%~dpf0" nul') do set "getAnyKey.CR=%%A"
exit /b

Aacini
Expert
Posts: 1631
Joined: 06 Dec 2011 22:15
Location: México City, México

Re: Mask password with Asterisk

#7 Post by Aacini » 16 Mar 2018 00:07

There are several solutions to this problem, like my ReadFormattedLine.bat subroutine, and simpler versions of that code, like this one that read just a line emulating SET /P command.

I copied the code from this post, that is a modified version of previous code that read a password, and slightly modified it:

Code: Select all

@echo off
setlocal

call :ReadPassword pass="Enter password: "
echo Password read: "%pass%"
goto :EOF


:ReadPassword var="prompt"

rem Read a password
rem Antonio Perez Ayala

rem Initialize variables
setlocal EnableDelayedExpansion
rem Get a CarriageReturn (ASCII 13) character
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
rem Get a BackSpace (ASCII 8) character
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"

rem Show the prompt and start reading
set /P "=%~2" < NUL
set "input="
set i=0

:nextKey
   set "key="
   for /F "delims=" %%a in ('xcopy /W "%~F0" "%~F0" 2^>NUL') do if not defined key set "key=%%a"

   rem If key is CR: terminate input
   if "!key:~-1!" equ "!CR!" goto endRead

   rem If key is BS: delete last char, if any
   set "key=!key:~-1!"
   if "!key!" equ "!BS!" (
      if %i% gtr 0 (
         set /P "=!BS! !BS!" < NUL
         set "input=%input:~0,-1%"
         set /A i-=1
      )
      goto nextKey
   )

   rem Else: show and accept the key
   set /P "=*" < NUL
   set "input=%input%%key%"
   set /A i+=1

goto nextKey

:endRead
echo/
endlocal & set "%~1=%input%"
exit /B
Antonio

npocmaka_
Posts: 493
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: Mask password with Asterisk

#8 Post by npocmaka_ » 16 Mar 2018 05:38


elzooilogico
Posts: 118
Joined: 23 May 2016 15:39
Location: Spain

Re: Mask password with Asterisk

#9 Post by elzooilogico » 17 Mar 2018 03:48

Also, if using powershell is allowed

Code: Select all

@echo off
SetLocal EnableDelayedExpansion EnableExtensions

set/P user="*     User: "
call:getPass pass

echo %user% %pass%
EndLocal
exit/B 1

:getPass
SetLocal
set "psCmd=powershell -Command "$pwd = read-host '* Password' -AsSecureString; $BSTR=[System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd); [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)""
for /F "usebackq delims=" %%# in (`%psCmd%`) do set "pwd=%%#"
if "%pwd%" EQU "" EndLocal & exit/B 1
EndLocal & set "%1=%pwd%"
doskey /listsize=0 >NUL 2>&1 & doskey /listsize=50 >NUL 2>&1        & rem Clear keyboard buffer
exit/B 0

AramSarkisyan
Posts: 4
Joined: 14 Mar 2018 20:35

Re: Mask password with Asterisk

#10 Post by AramSarkisyan » 18 Mar 2018 23:57

Thanks everyone for the help,
Antonio's reference looks works for me fine.
Here is the final version that i will use:

Code: Select all

@echo off
setlocal
color a
set tries=6
:top
cls
set /a tries=%tries%-1
if %tries%==0 (
goto :end)
Echo  Please enter password to proceed.
Echo  You have %tries% attempts left.
call :ReadPassword pass="Enter password: "
if %pass%== PASSWORD (
icacls D:\XXX\XXX\XXX\2MSTXS684B.jpg /grant Administrator:F 
cls
) else (
goto :top)
:end
goto :EOF


:ReadPassword var="prompt"

rem Read a password
rem Antonio Perez Ayala

rem Initialize variables
setlocal EnableDelayedExpansion
rem Get a CarriageReturn (ASCII 13) character
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
rem Get a BackSpace (ASCII 8) character
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"

rem Show the prompt and start reading
set /P "=%~2" < NUL
set "input="
set i=0

:nextKey
   set "key="
   for /F "delims=" %%a in ('xcopy /W "%~F0" "%~F0" 2^>NUL') do if not defined key set "key=%%a"

   rem If key is CR: terminate input
   if "!key:~-1!" equ "!CR!" goto endRead

   rem If key is BS: delete last char, if any
   set "key=!key:~-1!"
   if "!key!" equ "!BS!" (
      if %i% gtr 0 (
         set /P "=!BS! !BS!" < NUL
         set "input=%input:~0,-1%"
         set /A i-=1
      )
      goto nextKey
   )

   rem Else: show and accept the key
   set /P "=*" < NUL
   set "input=%input%%key%"
   set /A i+=1

goto nextKey

:endRead
echo/
endlocal & set "%~1=%input%"
exit /B

dkernel
Posts: 1
Joined: 22 Mar 2018 05:30

Re: Mask password with Asterisk

#11 Post by dkernel » 22 Mar 2018 05:42

@AramSarkisyan, there is a little piece of code you must modify to avoid problems with especial characters. The %pass% and PASSWORD must be into quotation marks to avoid problems, exmp: intro or &.

Code: Select all

if "%pass%" == "PASSWORD" (

jfl
Posts: 130
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: Mask password with Asterisk

#12 Post by jfl » 26 Mar 2018 10:12

Antonio's routine in post #7 above fails when a password contains the ! character. (The first actual password I tried contains one!)
Here's an updated version with a fix.

Code: Select all

@echo off
setlocal

call :ReadPassword pass="Enter password: "
setlocal EnableDelayedExpansion
echo Password read: "!pass!"
goto :EOF


:ReadPassword var="prompt"

rem Read a password
rem Antonio Perez Ayala

rem Initialize variables
setlocal EnableDelayedExpansion
rem Get a CarriageReturn (ASCII 13) character
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
rem Get a BackSpace (ASCII 8) character
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"

rem Show the prompt and start reading
set /P "=%~2" < NUL
set "input="
set i=0

rem Get the localized xcopy prompt
set "msg="
for /F "delims=" %%a in ('echo.^|xcopy /W "%~F0" "%~F0" 2^>NUL') do if not defined msg set "msg=%%a"

:nextKey
   set "key="
   for /F "delims=" %%a in ('xcopy /W "%~F0" "%~F0" 2^>NUL') do if not defined key set "key=%%a"

   rem Remove the localized xcopy prompt from the beginning of the string
   set key=!key:%msg%=!
   rem If the key is a question mark, it'll have been lost in the set "key=%%a" above
   if not defined key set "key=^!"

   rem If key is CR: terminate input
   if "!key:~-1!" equ "!CR!" goto endRead

   rem If key is BS: delete last char, if any
   set "key=!key:~-1!"
   if "!key!" equ "!BS!" (
      if %i% gtr 0 (
         set /P "=!BS! !BS!" < NUL
         set "input=!input:~0,-1!"
         set /A i-=1
      )
      goto nextKey
   )

   rem Else: show and accept the key
   set /P "=*" < NUL
   set "input=!input!!key!"
   set /A i+=1

goto nextKey

:endRead
echo/
endlocal & set "%~1=%input%"
exit /B

Post Reply