Some will say that it deserves it. Since the DOS times, Microsoft added various extensions to what was known as "batch files" to gives us improved loops, conditionnals, and gosubs. But the language is still far from any Unix shell as escaping special chars is often difficult, sometimes impossible.

So, implementing Reverse for Cmd.exe was a challenge. But it was also for me a look in the past. I started to learn to write batch files even before having a PC at home. My first batch files were written on paper, while reading the MS-DOS 5.0 manual that the seller of the PC gave me one month before the PC itself. Batch files on MS-DOS has been my first experience at builing tool for system administration at a time where most of my friends were just using their PCs only for games. You can still find some sample of those old scripts on my old web site. My biggest achievement (in lines of code at least) is probably "Dolmen's config", a set of boot scripts (config.sys, autoexec.bat) optimized for launching about 100 games. The lack of power of batch files is one of the reasons that pushed me to learn and practice x86 assembly language first with Microsoft's debug.exe (that you also still have on your Windows PC).

Here is the code implemented in pure cmd.exe (no external program called). You can test it on any Microsoft Windows 2000/XP (probably Vista too) without having to install any virtual machine or interpreter. Just save it as reverse.cmd and run it from the command prompt. I had to use many tricks I learned during the past years. String operations can be very hard to implement and are not perfect due to the lack of character escaping.

@echo off

verify other 2>nul
setlocal enabledelayedexpansion
if errorlevel 1 echo Windows 2000/XP required.& goto :EOF

set sorted=%2
set sorted=%sorted:"=%
if "%sorted%"=="" (
    set sorted=123456789
    rem set sorted=ABCDE
)


set current=%1
set current=%current:"=%
if "%current%"=="" (
    set current=%sorted%
    call :Shuffle current
)

set steps=0

:Play
echo.%current%
if "%current%"=="%sorted%" goto Done
set /P flipcount=Reverse how many?
if /I "%flipcount:~0,1%"=="q" goto :EOF

set left=!current:~0,%flipcount%!
set right=!current:~%flipcount%!
call :Reverse left
set current=%left%%right%

set /A steps+=1
goto Play


:Done
:: The only way to print '!' is to disable DelayedExpansion.
:: Yes, cmd.exe is really flawed: there is no escape char for '!' in
:: DelayedExpansion mode.
setlocal disabledelayedexpansion
echo Done! That took you %steps% steps.
endlocal
goto :EOF


:: ======= FUNCTIONS =======

:: Compute number of characters of variable named %1
:: Result is returned in both %errorlevel% and %Length%
:Length
setlocal enabledelayedexpansion
set l=0
:LengthLoop
set _=!%1:~%l%,1!
if not "%_%"=="" set /A l+=1& goto LengthLoop
endlocal & set Length=%l%
:: Set errorlevel and return
exit /B %Length%


:: Reverse characters of variable named %1
:Reverse
setlocal enabledelayedexpansion
call :Length %1
set /A p=Length/2
set r=!%1:~%p%,-%p%!
:ReverseLoop
set /A q=p-1
set r=!%1:~-%p%,1!%r%!%1:~%q%,1!
set p=%q%
if not %p%==0 goto ReverseLoop
endlocal & set %1=%r%
goto :EOF


:: Shuffle characters in variable named %1
:Shuffle
setlocal enabledelayedexpansion
call :Length %1
set /A count=5*Length
set d=!%1!
for /L %%i in (1,1,%count%) do call :ShuffleSub d
endlocal & set %1=%d%
goto :EOF
:ShuffleSub
set /A n=^(Length * %RANDOM%^) / 32768
set right=!%1:~%n%!
set %1=!right:~1!!%1:~%n%,1!!%1:~0,%n%!
::echo.!%1!
goto :EOF

Update 2007-02-22: added syntax highlighting.