Bug in RENAME and KEYSTACK command?

#1
RENAME /P
If you want to end a RENAME /P command with <Esc>, you get some pretty strange behaviour:

If it is a filename without spaces, RENAME ignores <Esc> and asks again for Y/N/A/R.

If it is a filename with spaces, things get really interesting: it seems that the quotes around the filename are ignored and the filename is considered as multiple filenames.

Code:
[C:\Temp\ff]ren /p "test1 test2 test3.txt" "abc.txt"
C:\Temp\ff\test1 test2 test3.txt -> C:\Temp\ff\abc.txt (Y/N/A/R)?
TCC: (Sys) The system cannot find the file specified.
 "C:\Temp\ff\test1"
TCC: (Sys) The system cannot find the file specified.
 "C:\Temp\ff\test2"
TCC: (Sys) The system cannot find the file specified.
 "C:\Temp\ff\test3.txt"
     0 files renamed
COPY /P, DEL /P and MOVE /P work as one would expect: the command gets terminated.


KEYSTACK
In further testing I used KEYSTACK for replaying. KEYSTACK is supposed to begin with a clean buffer (between multiple KEYSTACK commands). This was not the case...
In the testscript below you can activate the
Code:
rem set KeySequence=Esc "N"
command to see the different effect it has on the second RENAME command.

( Tested on TCC/LE [email protected] 7 SP1 )


TESTSCRIPT
Code:
@echo off
setlocal
alias SHOWVAR=`echo.    %@UPPER[%1] : %[%1]`

echo.========================================================
            ver /r
echo.
echo.========================================================

pushd "%~dp0"


:: TEST1
    echos.>"dummy file name.txt"

    set KeySequence=Esc
    rem for testing keystack buffer flush:
    rem set KeySequence=Esc "N"
    gosub :TRY_RENAME


:: TEST2
    rem start /wait /C echos.> "dummy" > "file" > "name.txt"
    for %x in ("dummy" "file" "name.txt") Do echos.> %x
    
    set KeySequence=/W3 Esc /W3 "N" "N" Esc  "N" "N" "N"
    gosub :TRY_RENAME


:: DONE
del /q "dummy file name.txt" "dummy" "file" "name.txt"
goto :EOF



:TRY_RENAME

    echo.
    dir /hkm
    echo.
    echo.========================================================
    echo.
    SHOWVAR KeySequence
    echo.
    keystack %KeySequence% & ren /P "dummy file name.txt" "abc.txt"
    echo.
    echo.========================================================
RETURN

TESTSCRIPT OUTPUT
Code:
========================================================

TCC LE  14.00.9 x64   Windows 7 [Version 6.1.7601]
TCC LE Build 9   Windows 7 Build 7601  Service Pack 1

========================================================

 2-09-2016  17:39               0  dummy file name.txt
 2-09-2016  13:57             973  test.btm

========================================================

    KEYSEQUENCE : Esc

C:\Temp\ff2\dummy file name.txt -> C:\Temp\ff2\abc.txt (Y/N/A/R)?
TCC: (Sys) C:\Temp\ff2\test.btm [45]  The system cannot find the file specified.
 "C:\Temp\ff2\dummy"
TCC: (Sys) C:\Temp\ff2\test.btm [45]  The system cannot find the file specified.
 "C:\Temp\ff2\file"
TCC: (Sys) C:\Temp\ff2\test.btm [45]  The system cannot find the file specified.
 "C:\Temp\ff2\name.txt"
     0 files renamed

========================================================

 2-09-2016  17:39               0  dummy
 2-09-2016  17:39               0  dummy file name.txt
 2-09-2016  17:39               0  file
 2-09-2016  17:39               0  name.txt
 2-09-2016  13:57             973  test.btm

========================================================

    KEYSEQUENCE : /W3 Esc /W3 "N" "N" Esc  "N" "N" "N"

C:\Temp\ff2\dummy file name.txt -> C:\Temp\ff2\abc.txt (Y/N/A/R)?
C:\Temp\ff2\dummy -> C:\Temp\ff2\abc.txt (Y/N/A/R)? N
C:\Temp\ff2\file -> C:\Temp\ff2\abc.txt (Y/N/A/R)? N
C:\Temp\ff2\name.txt -> C:\Temp\ff2\abc.txt (Y/N/A/R)?
C:\Temp\ff2\dummy -> C:\Temp\ff2\abc.txt (Y/N/A/R)? N
C:\Temp\ff2\file -> C:\Temp\ff2\abc.txt (Y/N/A/R)? N
C:\Temp\ff2\name.txt -> C:\Temp\ff2\abc.txt (Y/N/A/R)? N
     0 files renamed

========================================================
 
#2
Interesting! I just get a new rename prompt when I press Esc.
Code:
p:\4sysutils\release> ren /p "test1 test2 test3.txt" "3tests.txt"
P:\4SysUtils\Release\test1 test2 test3.txt -> P:\4SysUtils\Release\3tests.txt (Y/N/A/R)?
P:\4SysUtils\Release\test1 test2 test3.txt -> P:\4SysUtils\Release\3tests.txt (Y/N/A/R)?
 

rconn

Administrator
Staff member
May 14, 2008
10,533
94
#4
KEYSTACK
In further testing I used KEYSTACK for replaying. KEYSTACK is supposed to begin with a clean buffer (between multiple KEYSTACK commands). This was not the case...
WAD -- this is a misunderstanding on how KEYSTACK works. KEYSTACK puts keys into the keyboard buffer and then it's up to whoever has the keyboard focus to read (& remove) the keys. Each time you run KEYSTACK, it does not attempt to clear the keyboard buffer (this would be very, very bad behavior), it simply adds the new keystrokes.
 
#5
And the RENAME command? Also WAD?


Regarding KEYSTACK:

  • The Help file explicitly states:

"Each time the KEYSTACK command is executed, it will clear any remaining keystrokes stored by a previous KEYSTACK command."
(under "Limitations")


  • It would indeed be a very bad idea to flush the KEYBOARD buffer, but ...
I was referring to the KEYSTACK buffer.

As far as I can tell, keys get stored in the keystack buffer first and then after the configured /Wx time passed to the keyboard buffer.
That's why you never know for sure what text is going to be typed with KEYSTACK, because the text gets "CapsLocked" before (or after?) going to the KEYBOARD buffer:
Keystack buffer = "Hello" + CapsLock OFF -> Output "Hello"
keyboard buffer = "Hello" + CapsLock ON -> Output "hELLO"

(No mention of this one in the help file, btw)


  • In KEYSTACK there is a difference between keys N and "N".
N gives you "n", whereas "N" gives you the expected "N"
(Whatever gets typed eventually is of course still dependent on the CapLock state)


  • Not flushing the KEYSTACK buffer leads to some strange behavior
For example:
keystack /w3 "HellO" & keystack /w6 "World" gives you: WWoorrlldd

OUTPUT TESTSCRIPT:
Code:
KeySequence1=/w54 A B C
KeySequence2=/w54 D E "F" G
KeySequence3=/w54 H I J K L

hhhiiijjkjklkll

------------------------------------------

KeySequence1=/w18 A B C
KeySequence2=/w54 H I J K L
KeySequence3=/w36 D E "F" G

ddedeeFFFggg

------------------------------------------

TESTSCRIPT:
Code:
@echo off
SETLOCAL
push "%~dp0"

:: TEST1
    set KeySequence1=/w54 A B C
    set KeySequence2=/w54 D E "F" G
    set KeySequence3=/w54 H I J K L

    gosub :NOTEPAD
    UNSET KeySequence*

:: TEST2
    set KeySequence1=/w18 A B C
    set KeySequence2=/w54 H I J K L
    set KeySequence3=/w36 D E "F" G

    gosub :NOTEPAD
    UNSET KeySequence*

:: DONE   
goto :EOF


:NOTEPAD
    SET KeySequence
    if DEFINED KeySequence1 KEYSTACK %KeySequence1%
    if DEFINED KeySequence2 KEYSTACK %KeySequence2%
    if DEFINED KeySequence3 KEYSTACK %KeySequence3%

    ECHO.> STACK.TXT
    START NOTEPAD.EXE stack.txt

    delay 5
    KEYSTACK Ctrl-S Alt-F4
    delay 1
    echo.
    type stack.txt
    echo.
    echo.----------------------------------------------------
    echo.

RETURN
 
#6
For example:
keystack /w3 "HellO" & keystack /w6 "World" gives you: WWoorrlldd
I can't imagine what's going on there. It's better here.
Code:
v:\> keystack /w3 "HellO" & keystack /w6 "World"

v:\> HellOWorld
I wouldn't expect three KEYSTACK's in a row with the same wait to work well. It is likely that there are three threads producing keystrokes all at once; and they're likely to get mixed up. When I change TEST1 to use waits of 18, 36, and 54, it works as expected.
Code:
v:\> kstest.btm
KeySequence1=/w18 A B C
KeySequence2=/w36 D E "F" G
KeySequence3=/w54 H I J K L

abcdeFghijkl

----------------------------------------------------

KeySequence1=/w18 A B C
KeySequence2=/w54 H I J K L
KeySequence3=/w36 D E "F" G

abcdeFghijkl

----------------------------------------------------
I don't know why you get doubled characters. I have not seen that in my testing.
Be aware of a subtle, but important, distinction:

a <--- the key
A <--- the key
"a" <--- the lowercase letter
"A" <--- the uppercase letter

The first two above will give the same result. If you want uppercase A without using quotes, use Shift-a.
 
#7
I wouldn't expect three KEYSTACK's in a row with the same wait to work well. It is likely that there are three threads producing keystrokes all at once; and they're likely to get mixed up.
A simple test shows that that (or something similar) is the case:
Code:
v:\> (keystack /w3 "1111111111") & (keystack /w3 "0000000000")

v:\> 01010101100110011001
 
#8
Maarten, what version of TCC are you using? I was testing with v20. If I go back to v16, I see the doubling of characters that you mentioned.
Code:
v:\> ver & keystack /w3 "HellO" & keystack /w6 "World"

TCC  16.03.55   Windows 7 [Version 6.1.7601]

v:\> WWoorrlldd
 
#9
Maarten, what version of TCC are you using? I was testing with v20. If I go back to v16, I see the doubling of characters that you mentioned.
I'm using TCC LE 14.00.9 x64 @ Windows 7 SP1 (x64)
Thanks for confirming I'm not going crazy (well, at least in this area :-)


"Be aware of a subtle, but important, distinction:

a <--- the key
A <--- the key
"a" <--- the lowercase letter
"A" <--- the uppercase letter"
Thanks for this helpful insight! ; makes it a lot easier to understand
 
#10
For anyone who want the KEYSTACK output be independent of the CapsLock state, here are some options:

( Testing if CapsLock is on: if %_CapsLock == 1 echo CapsLock ON )

  • If you use TCC, TPIPE would possibly be the easiest way: parameter /simple=9
    (Why is it called TPIPE? It's more like Hash-PIPE, but maybe that gives some unwanted connotations ;-)
    I didn't test TPIPE though, as I'm using TCC/LE
  • Force the CapsLock state OFF: KEYBD /C0 Might influence other programs
  • Script:
    • Convert every character to it's @ascii value
    • check if UPPERcase, then convert to lowercase @ascii value
    • Otherwise: convert to UPPERcase @ascii value
    • The usage of @ascii values instead of @char was needed to bypass some unwanted parsing effects (especially quotes and spaces).
      Only in the end the @ascii values get converted back to @char values
Code:
set KeySequence=tHIS "IS" 'a' rANDOM text /[email protected]#$%%^^*()^<^>\ 123
set KeySequence*

set TOGGLE=
:: X3 and X5 are needed
SETDOS /x-1246789


for %x in (%@ascii[%KeySequence]) (
   IFF "%@char[%x]" EQC "%@UPPER[%@char[%x]]" THEN
       set TOGGLE=%TOGGLE% %@ascii[%@lower[%@char[%x]]]
   ELSE  
       set TOGGLE=%TOGGLE% %@ascii[%@UPPER[%@char[%x]]]
   ENDIFF
)

set KeySequence=%@char[%TOGGLE%]
SET KeySequence*

setdos /x0
OUTPUT:
Code:
KeySequence=tHIS "IS" 'a' rANDOM text /[email protected]#$%^*()<>\ 123
KeySequence=This "is" 'A' Random TEXT /[email protected]#$%^*()<>\ 123