From 6b03c9f2856c144e45ea39f24ef50158b9343818 Mon Sep 17 00:00:00 2001 From: "John W. Parent" <45471568+johnwparent@users.noreply.github.com> Date: Tue, 28 Mar 2023 19:50:37 -0400 Subject: Windows: spack.bat CLI handling robustness (#36281) * Current develop spack.bat file cannot handle any reserved characters being passed via the CLI, particularly '=' and '?'. To address this, re-do the CLI parsing for loop to use custom logic to allow for more granular handling of CLI args. * We take a less-than-ideal approach to escaping local scope and handling unset variables as well as the actual parsing of CL arguments. To address this, don't quote the args and then try to parse the quotes we just added (resulting in spack flags being undefined). Instead, leverage batch script features. Since we are not unnecessarily quoting things, we don't need to think about removing them, and in the case of paths with spaces, we should _not_ be removing the quotes as we currently do. --- bin/spack.bat | 107 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/bin/spack.bat b/bin/spack.bat index 310b59506c..2c2947ac0f 100644 --- a/bin/spack.bat +++ b/bin/spack.bat @@ -50,24 +50,69 @@ setlocal enabledelayedexpansion :: flags will always start with '-', e.g. --help or -V :: subcommands will never start with '-' :: everything after the subcommand is an arg -for %%x in (%*) do ( - set t="%%~x" + +:: we cannot allow batch "for" loop to directly process CL args +:: a number of batch reserved characters are commonly passed to +:: spack and allowing batch's "for" method to process the raw inputs +:: results in a large number of formatting issues +:: instead, treat the entire CLI as one string +:: and split by space manually +:: capture cl args in variable named cl_args +set cl_args=%* +:process_cl_args +:: tokens=1* returns the first processed token produced +:: by tokenizing the input string cl_args on spaces into +:: the named variable %%g +:: While this make look like a for loop, it only +:: executes a single time for each of the cl args +:: the actual iterative loop is performed by the +:: goto process_cl_args stanza +:: we are simply leveraging the "for" method's string +:: tokenization +for /f "tokens=1*" %%g in ("%cl_args%") do ( + set t=%%~g + :: remainder of string is composed into %%h + :: these are the cl args yet to be processed + :: assign cl_args var to only the args to be processed + :: effectively discarding the current arg %%g + :: this will be nul when we have no further tokens to process + set cl_args=%%h + :: process the first space delineated cl arg + :: of this iteration if "!t:~0,1!" == "-" ( if defined _sp_subcommand ( :: We already have a subcommand, processing args now - set "_sp_args=!_sp_args! !t!" + if not defined _sp_args ( + set "_sp_args=!t!" + ) else ( + set "_sp_args=!_sp_args! !t!" + ) ) else ( - set "_sp_flags=!_sp_flags! !t!" - shift + if not defined _sp_flags ( + set "_sp_flags=!t!" + shift + ) else ( + set "_sp_flags=!_sp_flags! !t!" + shift + ) ) ) else if not defined _sp_subcommand ( set "_sp_subcommand=!t!" shift ) else ( - set "_sp_args=!_sp_args! !t!" - shift + if not defined _sp_args ( + set "_sp_args=!t!" + shift + ) else ( + set "_sp_args=!_sp_args! !t!" + shift + ) ) ) +:: if this is not nil, we have more tokens to process +:: start above process again with remaining unprocessed cl args +if defined cl_args goto :process_cl_args + :: --help, -h and -V flags don't require further output parsing. :: If we encounter, execute and exit @@ -95,31 +140,21 @@ if not defined _sp_subcommand ( :: pass parsed variables outside of local scope. Need to do :: this because delayedexpansion can only be set by setlocal -echo %_sp_flags%>flags -echo %_sp_args%>args -echo %_sp_subcommand%>subcmd -endlocal -set /p _sp_subcommand=