Hi lazna.
lazna wrote: ↑24 Dec 2023 05:33
Seems even multiline prompt break scripts too :-/ So it looks this concept is wrong from scratch.
The obvious way: Don't break scripts by not outputting anything. Output your banner only when you are not in a script.
Sounds simple, but then you have to solve the problem of the decision who called the autorun script.
But this can be solved (partly) by check the cmdcmdline variable.
The opening lines show how this can be coded, the rest of the code solves the drag&drop problems of windows and implements an argument parser.
Code: Select all
@echo off
REM *** To enable this script, call it by <scriptName> --install
REM *** To see the current values
REM *** reg query "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun
REM *** reg query "HKEY_LOCAL_MACHINE\\Software\Microsoft\Command Processor" /v AutoRun
REM *** To delete the keys
REM *** reg DELETE "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun /f
REM *** reg DELETE "HKEY_LOCAL_MACHINE\\Software\Microsoft\Command Processor" /v AutoRun /f
setlocal EnableDelayedExpansion
REM *** ALWAYS make a copy of the complete CMDCMDLINE, else you destroy the originial!!!
set "_ccl_=!cmdcmdline!"
echo(>CON
REM *** %1 contains only data, when the script itself was called from the command line
if "%~1" NEQ "" (
goto :direct_call
)
REM echo #0 '!_ccl_!' > CON
REM *** The check is necessary to distinguish between a new cmd.exe instance for a user or for a "FOR /F" sub-command
if "!_ccl_:~1,-2!" == "!comspec!" (
REM ***** INTERACTIVE ****
echo %DATE% %TIME% comspec >> "%~dp0\started.log"
FOR %%M in ("%~dp0\cmdMacros.mac") DO (
endlocal
echo ********************************************************************
echo * AutoRun executed from "%~f0"
if exist "%%~M" (
doskey /macrofile="%%~M"
echo * Macros loaded from "%%~M"
) ELSE (
echo * Macrofile missing at "%%~M"
)
echo ********************************************************************
)
REM set "path=%path%;%~dp0"
SET "BATLIB=C:\Project\BatchLibrary"
call cd /D "%%BATLIB%%"
) ELSE (
REM *** This is a FOR command, a call by an explorer click or a drag & drop operation
REM *** Try to detect a PROBLEMATIC Drag&Drop operation, if it has the format `%comspec% /c ""`
REM *** It's only problematic when there is one & in the data
REM *** But it's not bullet proof, FOR /F in ('""C:\temp\myfile.bat" C:\temp\file&second.bat"') can't be distinguished
REM *** First check, that the cmdline starts with `%comspec% /c ""....`
if "!_ccl_!" NEQ "!_ccl_:*/c ""=#!" if "!comspec! /c ""!_ccl_:*/c ""=!" == "!_ccl_!" (
REM *** Only handle it, if there is at least one & and no pipe |
if "!_ccl_!" NEQ "!_ccl_:&=!" if "!_ccl_!" EQU "!_ccl_:|=!" (
call :DRAG_and_drop
%DEBUG_DRAG% echo # BACK from DRAG_and_drop
)
)
REM *** Leaving now
REM *** This is a "FOR /F" sub-command
REM *** or a "harmless" drag&drop
REM *** or the second run of a problematic drag&drop
REM echo %DATE% %TIME% internal "!_ccl_!" >> "%~dp0\started.log"
endlocal
)
exit /b
:DRAG_and_drop
REM *** This results into recursive execution of this script
REM *** Be sure, that it's not an endless recursion
REM *** Remove the front <%comspec% /c ">
REM set "_ccl_=!_ccl_:&=+!"
set "_ccl_=!_ccl_:~0,-1!"
set "_ccl_=!_ccl_:*/c "=!"
set "DEBUG_DRAG=REM "
%DEBUG_DRAG% echo #A '!_ccl_!' > CON
call :arg_parser argv _ccl_
REM *** Check if there is at least one unquoted, problematic &
REM *** This arg must not have spaces
set "isDragAndDrop="
if defined argv.onlySingleSpace (
set "isDragAndDrop=1"
set "problematic="
set "args="
FOR /L %%# in (0 1 !argv#-1!) DO (
set "arg=!argv[%%#]!"
REM *** Check if it's an absolute path
FOR /F "tokens=1,*" %%1 in (": !arg!") DO (
if "%%~f2" NEQ "%%~2" (
set "isDragAndDrop="
%DEBUG_DRAG% echo *** 'NO PATH' !arg! NEQ %%~f2 > CON
exit /b
)
)
if defined argv[%%#].quoted (
set "arg="!arg!""
) ELSE (
REM *** If not quoted but contains a &
if "!arg!" NEQ "!arg:&=!" (
if "!arg:~1,2!" EQU ":\" (
set "arg="!arg!""
set "problematic=1"
) ELSE (
REM *** This can't be drap&drop, because arg doesn't begin with "<drive>:\"
set "isDragAndDrop="
%DEBUG_DRAG% echo *** NO DRIVE '!arg!' > CON
exit /b
)
)
)
set "args=!args!!arg! "
)
set "args=!args:~,-1!"
)
if not defined problematic (
set "isDragAndDrop="
REM echo *** 'problematic = 0' > CON
)
if defined isDragAndDrop (
REM ECHO *** HANDLE isDragAndDrop *** > CON
REM echo *** '!args!' > CON
REM *** The TWO SPACES between comspec and /c are essential!
REM *** The AutoRun script detects the recursion call by this difference
(
endlocal
%comspec% /c ^"%args%"
)
REM *** Back from the dragDrop script
REM *** EXIT to avoid a second call
exit
)
REM *** This is the path for unproblematic content
REM *** Or it was detected, that this isn't a drap&drop operation
exit /b
:direct_call
if "%~1" == "--install" (
reg add "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v "AutoRun" /t REG_SZ /d "%~f0"
exit /b
)
if "%~1" == "--show" (
reg query "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun
exit /b
)
if "%~1" == "--remove" (
reg DELETE "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun /f
)
exit /b
:arg_parser <return_array> arg_line
REM *** @TODO: ENDLOCAL all arg-... variables
::: Output arg0, ..., arg<n> and arg#
::: arg0 ... arg<n> contains unquoted arguments
::: an argument that contains only one quote (last character in line) is treated as quoted arg=" -> arg=<empty>, arg.quoted=1
::: arg0.quoted contains 1 if the argument was quoted
::: \ escapes the next character (also inside quotes), can escape delimiters outside quotes, can escape quotes
::: Only \" is replaced to a single quote ", all other \<char> are unchanged
set "var=%1"
(set^ arg_line=!%2!)
call :strlen arg_len arg_line
set /a %var%_len-=1
set "quoted="
rem set "escapeChar=" --- "Say: \"Hello\" to frank\n"
set /a %var%#=0
set "%var%[0]="
set "%var%.onlySingleSpace=1"
for /L %%I in (0 1 !arg_len!) DO (
for %%# in (!%var%#!) DO (
set "char=!arg_line:~%%I,1!"
set "isDelim="
if "!escapeChar!!char!" == ^""" set "quoted=!quoted:~,0!"
REM echo %%I: "!char!" quote: !quote!
%= only outside quotes =%
%= check if char is a delim =%
if not defined quoted (
FOR /F "tokens=1,2 delims= " %%D in ("isDelim!char!NO") DO (
if "%%D" == "isDelim" (
set isDelim=1
if "!arg_line:~%%I,2!" == " " (
%DEBUG_DRAG% echo ##### NOT onlySingleSpace > CON
set "%var%.onlySingleSpace="
)
)
)
)
REM *** Split arguments by not escaped delimiter
if "!isDelim!,!quoted!,!escapeChar!" == "1,," (
REM *** Finalize arg, test if arg is quoted
if defined %var%[%%#] (
set %var%[%%#].quoted=
REM echo #in %%# "!%var%[%%#]:~,1!!%var%[%%#]:~-1!"
if "!%var%[%%#]:~,1!!%var%[%%#]:~-1!" == """" (
if "!%var%[%%#]:~-2,-1!" NEQ "\" (
set %var%[%%#].quoted=1
set "%var%[%%#]=!%var%[%%#]:~1,-1!"
)
)
REM * NOT USED HERE * if defined %var%[%%#] (
REM * NOT USED HERE * set ^"%var%[%%#]=!%var%[%%#]:"\"="!"
REM * NOT USED HERE * )
set /a %var%#+=1
)
)
REM * NOT USED HERE * if defined escapeChar (
REM * NOT USED HERE * set "escapeChar="
REM * NOT USED HERE * if "!char!" EQU ^""" (
REM * NOT USED HERE * REM *** Replace escaped quotes with "\" that can be safely detected and replaced later
REM * NOT USED HERE * set "char="\""
REM * NOT USED HERE * ) ELSE IF defined isDelim (
REM * NOT USED HERE * set "isDelim="
REM * NOT USED HERE * ) ELSE (
REM * NOT USED HERE * set "char=\!char!"
REM * NOT USED HERE * )
REM * NOT USED HERE * ) ELSE if "!char!" == "\" set "escapeChar=\"
if defined quoted set "isDelim="
REM *** Append char to arg
if "!isDelim!!escapeChar!" == "" (
set "%var%[%%#]=!%var%[%%#]!!char!"
)
)
)
for %%# in (!%var%#!) DO (
REM *** Last character is \
if defined escapeChar (
set "%var%[%%#]=!%var%[%%#]!\"
)
REM *** Duplicated code
REM *** Finalize arg, test if arg is quoted
if defined %var%[%%#] (
set %var%[%%#].quoted=
if "!%var%[%%#]:~,1!!%var%[%%#]:~-1!" == """" (
if "!%var%[%%#]:~-2,-1!" NEQ "\" (
set %var%[%%#].quoted=1
set "%var%[%%#]=!%var%[%%#]:~1,-1!"
)
)
REM * NOT USED HERE * if defined %var%[%%#] (
REM * NOT USED HERE * set ^"%var%[%%#]=!%var%[%%#]:"\"="!"
REM * NOT USED HERE * )
set /a %var%#+=1
)
)
REM *** Create a helper variable with name "arg#-1"
set /a tmp=!%var%#!-1
set "%var%#-1=!tmp!"
exit /b
::: detector line %~* *** FATAL ERROR: missing parenthesis or exit /b
:strlen <resultVar> <stringVar>
(
setlocal EnableDelayedExpansion
(set^ tmp=!%~2!)
if defined tmp (
set "len=1"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!tmp:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "tmp=!tmp:~%%P!"
)
)
) ELSE (
set len=0
)
)
(
endlocal
set "%~1=%len%"
exit /b
)