OK, I've finally got a significant update to this project.
Sorry this is so long, but there is a lot to cover.
1) New DevelopmentsNew strategy for "calling" a macro within a macro:I have abandoned the
${calledMacroName} syntax and the relatively slow
drefMacro function. I am now using delayed expansion to very quickly include a macro within a macro at definition time. I now realize it is not so difficult to escape ! and ^ as needed. (Ed - Sorry I was so pig headed before. I think you have been on the right path in this regard all along). My concern about having the macro definition persist after the batch file terminates is handled by the next developement.
New macros to return any string across the ENDLOCAL border: See the
macroLib_Return.bat file later in this post. These macros are useful for both macro and function development. They not only allow macros to return any value across the ENDLOCAL border, they also allow us to preserve the macro definitions themselves across the ENDLOCAL border!
For examples on how to use this library for function development, see
New Function Template - return ANY string safely and easily. Note - the macros have changed some since my original post there.
For examples on how to use this library for macro development, see in this post:
- macroLib_Strings.bat : macro.ToUpper and macro.AnyToUpper for examples of using macro.Rtn1 and macro.AnyRtn1
- macroLib_Swap.bat : macro.Swap and macro.AnySwap for examples of using macro.RtnN and macro.AnyRtnN
Modified callMacro.bat - this "command" allows macros to be called under almost any circumstance at a cost of reduced speed, (but still faster than calling a function with the same functionality). See the code posted later in this post for more information on its usage. The file now includes a call to %macro_EndAnyRtn% to support macros that use either macro.AnyRtn1 or macro.AnyRtnN. It also now has embedded help.
Formalized naming convention and help system:macro.MacroName - Specifies a macro that requires arguments.
macro\args.MacroName - Contains calling syntax (argument list) for macro.MacroName
macro\help.MacroName - Contains detailed help on usage of macro.MacroName
macro_SimpleMacro - Specifies a simple macro that does not require arguments.
macro\args_SimpleMacro - Displays as empty but contains a space: Used to inform user of availability of a simple macro that does not require arguments.
macro\help_SimpleMacro - Contains detailed help on usage of a simple macro named macro_SimpleMacro.
macro\load.LibraryName - Allways set to 1: Used to indicate that the library has been loaded into memory.
Help commands using DOSKEY macros - See
macroLib_Base.bat for full documentation
margs - Convenient access to a list of available macros and their arguments.
mhelp - Convenient access to detailed macro usage information.
mload - Convenient access to the list of macro libraries that have been loaded.
Adopted use of ECHO( instead of ECHO:- defined
echo=echo( in macroLib_base.bat.
- replaced all instances of
echo: with
%echo% in all macro libraries.
2) General Macro Development Design considerationsNeed for local argument naming convention when passing multiple variables by reference.Macros (and functions) typically have local variables that are supposed to have no impact on the caller. But when passing multiple variables by reference we can get in trouble if the variable names passed in happen to match the names of local variables.
I have a convention that all reference argument values are read into local variables that are prefixed with the macro name like so:
macro.Name.LocalVar. Once all of the argument values have been transferred, the macro is free to define any local variable using any name. This strategy eliminates the risk of name collision problems as long as the caller never passes in a variable that is prefixed with the called macro name.
Macros make heavy use of delayed expansion: We need the ability to set a value within a macro and then subsequently read the value in the same macro. Since the macro is all within one statement block we cannot use
%var% notation. We don't want to use
CALL %%var%% notation because it is slow and a major purpose of defining a macro in the first place is for speed. This means we almost always reference variables using delayed expansion as
!var!. This leads to two constructs that are not normally used for function development but are often critical to macro development.
Parametized substring (or parametized search and replace):- in a function we use
SET variable=!variable:~%start%,%length%!- in a macro we use
FOR /F "tokens=1,2" %%A IN ("!start! !length!") DO set variable=!variable:~%%A,%%B!Passing Numbers and simple strings across the ENDLOCAL border:- in a function we use
(ENDLOCAL & SET "var=%val%")- in a macro we use
FOR /F %macro_ForEntireLine% %%v ("!val!") DO (ENDLOCAL & SET "var=%%v")3) Open IssuesDealing with multiple optional arguments - In functions we can shift varying numbers of leading option arguments so that required arguments are always in standard positions after options have been processed. We don't have any correlary to the SHIFT command for macro options.
I am toying with the idea of treating the macro as an object and storing options as macro attributes - a limited adoption of what Ed Dyreen has been doing. The options would be set prior to the call using syntax like
SET macro.MacroName.OptionName=value. The options could possibly reset automatically to default values upon macro completion.
Passing string literals as arguments - There is no simple way to quote or escape argument delimiters so that they may be included in an argument value. We can define different macro_Call variants that use different delimiters, but there is not a generic solution that can receive any combination of characters. For this reason all my macros have been restricted to passing unconstrained string values by reference.
We could use the object attribute strategy here as well, but I don't really like it. I have a vague idea to pass all arguments as one string and then process the arguments with a dedicated "load arguments" macro. This macro would use a simple FOR loop to parse the arguments, but it would need special code to preserve * and ? characters. I'm not sure when (or if) I will get around to testing this.
4) The Code (finally
)
I've broken up the code into multiple libraries based on functionality:
macroLib_Base.bat - basic definitions needed by every library, plus new help macros
macroLib_Return.bat - new macros for returning values across ENDLOCAL barrier
macroLib_String.bat - macros for basic string operations, including new macros to demo macro.Rtn1 and macro.AnyRtn1
macroLib_Swap.bat - new macros of limited functionality to demo usage of macro.RtnN and macro.AnyRtnN
macroLib_Num.bat - macros dealing with numbers (Random and Num2Hex)
macroLib_Time.bat - macros for dealing with time, useful for timing code
callMacro.bat - command used to call macros when %macro_call% cannot be used
macroLib_Base.batCode: Select all
@echo off
:: File = macroLib_Base.bat
:: Dependencies: <none>
:: This batch file will fail if called while delayed expansion is enabled.
::
:: This library defines basic macros and variables that are used by all other
:: macro libraries.
::
:: The following DOSKEY macros are installed to provide help on all currently
:: loaded macro libraries. These DOSKEY macros are only available from the
:: command line. They cannot be used within a batch file.
::
:: margs [MacroNameFilter]
::
:: List all macros along with their arguments.
::
:: If the MacroNameFilter is specified then only list macros whose name
:: begins with macro.MacroNameFilter or macro_MacroNameFilter.
::
:: mhelp [MacroNameFilter]
::
:: Display help for all macros.
::
:: If the MacroNameFilter is specified then only display help for macros
:: whose name begins with macro.MacroNameFilter or macro_MacroNameFilter.
::
:: mload [LibraryNameFilter]
::
:: Displays a list of all macro libraries that are currently loaded.
::
:: If the LibraryNameFilter is specified then only display loaded libraries
:: whose name begins with LibraryNameFilter.
::
:: The library is designed to be installed in a directory in your PATH.
:: Any batch file that requires it can include it by simply placing the
:: following line of code at the top before any SETLOCAL:
::
:: IF NOT DEFINED macro\load.macroLib_Base CALL macroLib_Base
::
:: In this way the library becomes resident in your command shell environment
:: where it is available to any batch file that may need it. The IF condition
:: prevents unneccessary reloads of the same library.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::define a Carriage Return string, only useable as !CR!
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
::define a Line Feed (newline) string (normally only used as !LF!)
set LF=^
::Above 2 blank lines are required - do not remove
::define a Line Feed string that can be used as %xLF%
set ^"xLF=^^^%LF%%LF%^%LF%%LF%"
::define a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
::define a version of ECHO that supports displaying a blank line
set "echo=echo("
set "esc1=^"
set "esc2=^^^"
set "esc3=^^^^^^^"
set "esc4=^^^^^^^^^^^^^^^"
set "macro\args_ForEntireLine= "
set macro\help_ForEntireLine=%\n%
%\n%
A simple macro to be used with the FOR /F command to disable the EOL and%\n%
DELIMS options, thus enabling preservation of the entire line regardless of%\n%
what the leading character is.%\n%
%\n%
There are two versions of this macro:%\n%
%\n%
ForEntireLine - For normal use within a batch file or on the command line.%\n%
If this macro is included in a macro definition then it must be referenced%\n%
as !ForEntireLine! and %%macro_BeginDef%% and %%macro.EndDef%% must be used%\n%
before and after the macro definition.%\n%
%\n%
macro_ForEntireLine - This version is used exclusively within a macro%\n%
definition as %%macro_ForEntireLine%% when macro_BeginDef and macro.EndDef%\n%
are not needed.%\n%
%\n%
Sample use from the command line:%\n%
%\n%
for /f %%ForEntireLine%% %%a in (";Preserve this entire line") do @echo %%a%xLF%
set macro_ForEntireLine=^^^^^^^"eol^^^^=^^^^^^^%LF%%LF%^%LF%%LF%^^^%LF%%LF%^%LF%%LF%^^^^ delims^^^^=^^^^^^^"
set ForEntireLine=%macro_ForEntireLine%
set "macro\args_Call= "
set macro\help_Call=%\n%
%\n%
A simple macro used to call macros with arguments from within a batch file.%\n%
This is the fastest way to call a macro from within a batch file.%\n%
%\n%
Usage:%\n%
%\n%
%%macro_Call%% ("arg1 arg2 arg3...") %%macro.macroName%%%\n%
%\n%
To call a macro from the command line use the callMacro.bat command instead.%\n%
Note that the macro. prefix is excluded from the macroName when using the%\n%
callMacro.bat command.%\n%
%\n%
callMacro macroName arg1 arg2 arg3...%\n%
%\n%
The callMacro.bat command can also be used within a batch as follows:%\n%
%\n%
call callMacro macroName arg1 arg2 arg3...%\n%
%\n%
There is a small performance penalty when calling a macro this way, but it is%\n%
usually still faster than calling a function with the same functionality.%\n%
The callMacro.bat command is especially useful within a batch file under any%\n%
of the following circumstances:%\n%
%\n%
- Performance is not overly important and the callMacro command is more%\n%
natural than the %%macro_Call%% syntax.%\n%
%\n%
- The macro must be called within a code block and the code block is%\n%
exceeding the ~8K limit.%\n%
%\n%
- An "Any" macro must be called within a code block. This is possible%\n%
because the callMacro.bat command implicitly calls macro_EndAnyRtn%\n%
outside of the calling code block.%\n%
%\n%
- The macro must be called within a macro definition and the macro%\n%
definition is exceeding the ~8K limit.%xLF%
set macro_Call=for /f "tokens=1-26" %%a in
::-----------------------------------------------------
:: DEFINE HELP MACROS (true DOSKEY macros)
doskey margs=echo off$T%echo%$T2$Gnul set macro\args.$1$T2$Gnul set macro\args_$1$Techo on
doskey mhelp=echo off$T%echo%$T2$Gnul set macro\help.$1$T2$Gnul set macro\help_$1$Techo on
doskey mload=echo off$T%echo%$Tset macro\load.$1$Techo on
::----------------------------------------------------
:: Mark that this library has been loaded.
set macro\load.%~n0=1
macroLib_Return.batCode: Select all
@echo off
:: File = macroLib_Return.bat
:: Dependencies: macroLib_Base.bat
:: This batch file will fail if called while delayed expansion is enabled.
::
:: This library defines macros that enable the return of any string value(s)
:: across the ENDLOCAL border. These macros are very useful for creating
:: other macros and functions. Routines built with these macros are safe to
:: call even when delayed expansion is enabled.
::
:: The library is designed to be installed in a directory in your PATH.
:: Any batch file that requires it can include it by simply placing the
:: following line of code at the top before any SETLOCAL:
::
:: IF NOT DEFINED macro\load.MacroLib_Return CALL macroLib_Return
::
:: In this way the library becomes resident in your command shell environment
:: where it is available to any batch file that may need it. The IF condition
:: prevents unneccessary reloads of the same library.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Conditionally load dependencies (normally residing somewhere in the PATH)
if not defined macro\load.macroLib_Base call macroLib_Base
set "macro\args_InitRtn= "
set macro\help_InitRtn=%\n%
%\n%
A simple macro used at the top of a macro definition to prepare%\n%
for a return by any of the Rtn macros.%\n%
%\n%
See any of the following for more information:%\n%
macro.Rtn1%\n%
macro.RtnN%\n%
macro.AnyRtn1%\n%
macro.AnyRtnN%xLF%
set macro_InitRtn=setlocal^&set "macro_NotDelayed=!"^&set "macro_inFcn="
set "macro\args_InitFcnRtn= "
set macro\help_InitFcnRtn=%\n%
%\n%
A simple macro used at the top of a function definition to prepare%\n%
for a return by any of the Rtn macros.%\n%
%\n%
See any of the following for more information:%\n%
macro.Rtn1%\n%
macro.RtnN%\n%
macro.AnyRtn1%\n%
macro.AnyRtnN%xLF%
set macro_InitFcnRtn=setlocal^&set "macro_NotDelayed=!"^&set "macro_inFcn=true"
set "macro\args_EndAnyRtn= "
set macro\help_EndAnyRtn=%\n%
%\n%
A temporary simple macro that must immediately follow calls to any%\n%
of the following (or any macro that uses the following):%\n%
macro.AnyRtn1%\n%
macro.AnyRtnN%\n%
macro.EndDef%\n%
%\n%
The macro is not defined until one of the above macros is called,%\n%
and macro_EndAnyRtn undefines itself once it is called.%\n%
See any of the above macros for more information.%xLF%
:: A simple macro for Rtn macro internal use
set "macro_EndAnyRtnPrefix=for /f "tokens=1-3" %%1 in ("!replace!") do for %%4 in ("!LF!") do "
set macro\args.AnyRtn1= ErrLvl EndLocalCnt ValueVar [RtnVar]
set macro\help.AnyRtn1= ErrLvl EndLocalCnt ValueVar [RtnVar]%\n%
%\n%
Returns the contents of variable ValueVar across the ENDLOCAL border at the%\n%
end of a function or macro. The number of ENDLOCAL executions is controlled%\n%
by the EndLocalCnt which should match the number of times SETLOCAL was used%\n%
within the calling function/macro.%\n%
%\n%
The return value is stored in RtnVar%\n%
or the value is echoed if RtnVar is not specified.%\n%
The ERRORLEVEL is set to ErrLvl%\n%
%\n%
Numeric values ErrLvl and EndLocalCnt may be passed using any expression%\n%
supported by SET /A.%\n%
%\n%
This macro can return a string containing any combination of characters%\n%
supported by DOS, and the macro works regardless whether the target return%\n%
environment has enabled or disabled delayed expansion.%\n%
%\n%
In order for a function to use AnyRtn1, the calling function must start with%\n%
%%macro_InitFcnRtn%% at the top, and %%macro_EndAnyRtn%% must immediately follow%\n%
the call to %%macro.AnyRtn1%%.%\n%
%\n%
:func inputVar [RtnVar]%\n%
%%macro_InitFcnRtn%%%\n%
setlocal%\n%
{do whatever you need to do}%\n%
set rtnValue={your return value}%\n%
%%macro_Call%% ("errorlevel 1 rtnValue %%~2") %%macro.AnyRtn1%%%\n%
%%macro_EndAnyRtn%%%\n%
exit /b%\n%
%\n%
In order for a macro to use AnyRtn1, the calling macro must start with%\n%
%%macro_InitRtn%% at the top. In the macro template below, assume the %%%%c%\n%
argument holds the name of the variable to receive the result.%\n%
%\n%
set macro.Name= do(%%\n%%%\n%
!macro_InitRtn!%%\n%%%\n%
setlocal%%\n%%%\n%
{do whatever you need to do}%\n%
set rtnValue={your return value}%%\n%%%\n%
!macro_Call! ("errorlevel 1 rtnValue %%%%~c") !macro.AnyRtn1!%%\n%%%\n%
)%\n%
%\n%
Since this is a case of a macro "calling" a macro, the macro definition must%\n%
be preceded by %%macro_BeginDef%% and followed by a call to %%macro.EndDef%%%\n%
and %%macro_EndAnyRtn%%. See macro.EndDef for more information.%\n%
%\n%
Any call to a macro that uses AnyRtn1 must be followed by %%macro_EndAnyRtn%%.%\n%
The macro call and %%macro_EndAnyRtn%% must not share a common statement block.%\n%
This limitation can be overcome by calling the macro through the callMacro%\n%
function which implicitly calls %%macro_EndAnyRtn%%%xLF%
set macro.AnyRtn1=do (%\n%
setlocal enableDelayedExpansion%\n%
set /a "macro.AnyRtn1.ErrLvl=(%%~a), macro.AnyRtn1.EndLocalCnt=(%%~b)"%\n%
set "rtn=!%%~c!"%\n%
set ^"replace=%% ^"^"^" !CR!!CR!^"%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtnPrefix!endlocal&endlocal"%\n%
for /l %%N in (1,1,!macro.AnyRtn1.EndLocalCnt!) do set "macro_EndAnyRtn=!macro_EndAnyRtn!&endlocal"%\n%
if "%%~d" equ "" (%echo%!rtn!) else (%\n%
if defined rtn (%\n%
set "rtn=!rtn:%%=%%~1!"%\n%
set ^"rtn=!rtn:^"=%%~2!^"%\n%
if defined CR for %%A in ("!CR!") do set "rtn=!rtn:%%~A=%%~3!"%\n%
for %%A in ("!LF!") do set "rtn=!rtn:%%~A=%%~4!"%\n%
if not defined macro_NotDelayed (%\n%
set "rtn=!rtn:^=^^!"%\n%
call set "rtn=%%^rtn:^!=""^!%%" ! %\n%
set "rtn=!rtn:""=^!"%\n%
)%\n%
)%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&set "%%~d=!rtn!" ^!"%\n%
)%\n%
if defined macro_inFcn (%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&exit /b !macro.AnyRtn1.ErrLvl!"%\n%
) else if "!macro.AnyRtn1.ErrLvl!" == "1" (%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&(2>nul set =)"%\n%
) else if "!macro.AnyRtn1.ErrLvl!" neq "0" (%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&cmd /d /c exit !macro.AnyRtn1.ErrLvl!"%\n%
)%\n%
)
set macro\args.AnyRtnN= ErrLvl EndLocalCnt ValVar1:RtnVar1[,ValVar2:RtnVar2]...
set macro\help.AnyRtnN= ErrLvl EndLocalCnt ValVar1:RtnVar1[,ValVar2:RtnVar2]...%\n%
%\n%
Returns the contents of multiple variables across the ENDLOCAL border at%\n%
the end of a function or macro. The number of ENDLOCAL executions is%\n%
controlled by the EndLocalCnt which should match the number of times%\n%
SETLOCAL was used within the calling function/macro. The errorlevel is%\n%
set to ErrLvl.%\n%
%\n%
A pair of variable names must be specified for each output value - the%\n%
name of the variable containing the value followed by a colon followed%\n%
by the name of the variable that is to receive the value. Multiple pairs%\n%
are delimited by commas. The list of outputs cannot contain any spaces,%\n%
and the variable names cannot contain asterisk ^(*^), question mark ^(?^),%\n%
colon ^(:^) or comma ^(,^).%\n%
%\n%
Numeric values ErrLvl and EndLocalCnt may be passed using any expression%\n%
supported by SET /A.%\n%
%\n%
This macro can return strings containing any combination of characters%\n%
supported by DOS, and the macro works regardless whether the target return%\n%
environment has enabled or disabled delayed expansion.%\n%
%\n%
In order for a function to use AnyRtnN, the calling function must start with%\n%
%%macro_InitFcnRtn%% at the top, and %%macro_EndAnyRtn%% must immediately follow%\n%
the call to %%macro.AnyRtn1%%.%\n%
%\n%
:func inputVar RtnVar1 RtnVar2%\n%
%%macro_InitFcnRtn%%%\n%
setlocal%\n%
{do whatever you need to do}%\n%
set rtnVal1={your first return value}%\n%
set rtnVal2={your second return value}%\n%
%%macro_Call%% ("errorlevel 1 rtnVal1:%%~2,rtnVal2:%%~3") %%macro.AnyRtnN%%%\n%
%%macro_EndAnyRtn%%%\n%
exit /b%\n%
%\n%
In order for a macro to use AnyRtnN, the calling macro must start with%\n%
%%macro_InitRtn%% at the top. In the macro template below, assume the %%%%c%\n%
and %%%%~d arguments hold the name of the variables to receive the results.%\n%
%\n%
set macro.Name= do(%%\n%%%\n%
!macro_InitRtn!%%\n%%%\n%
setlocal%%\n%%%\n%
{do whatever you need to do}%\n%
set rtnVal1={your first return value}%%\n%%%\n%
set rtnVal2={your second return value}%%\n%%%\n%
!macro_Call! ("errorlevel 1 rtnVal1:%%%%~c,rtnVal2:%%%%~d") !macro.AnyRtnN!%%\n%%%\n%
)%\n%
%\n%
Since this is a case of a macro "calling" a macro, the macro definition must%\n%
be preceded by %%macro_BeginDef%% and followed by a call to %%macro.EndDef%%%\n%
and %%macro_EndAnyRtn%%. See macro.EndDef for more information.%\n%
%\n%
Any call to a macro that uses AnyRtnN must be followed by %%macro_EndAnyRtn%%.%\n%
The macro call and %%macro_EndAnyRtn%% must not share a common statement block.%\n%
This limitation can be overcome by calling the macro through the callMacro%\n%
function which implicitly calls %%macro_EndAnyRtn%%%xLF%
set macro.AnyRtnN=do (%\n%
setlocal enableDelayedExpansion%\n%
set /a "macro.AnyRtn1.ErrLvl=(%%~a), macro.AnyRtn1.EndLocalCnt=(%%~b)"%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtnPrefix!endlocal&endlocal"%\n%
for /l %%N in (1,1,!macro.AnyRtn1.EndLocalCnt!) do set "macro_EndAnyRtn=!macro_EndAnyRtn!&endlocal"%\n%
for %%c in (%%~c) do for /f "tokens=1,2 eol=: delims=:" %%c in ("%%c") do (%\n%
set "macro.AnyRtnN.rtn=!%%~c!"%\n%
if defined macro.AnyRtnN.rtn (%\n%
set "macro.AnyRtnN.rtn=!macro.AnyRtnN.rtn:%%=%%~1!"%\n%
set ^"macro.AnyRtnN.rtn=!macro.AnyRtnN.rtn:^"=%%~2!^"%\n%
if defined CR for %%A in ("!CR!") do set "macro.AnyRtnN.rtn=!macro.AnyRtnN.rtn:%%~A=%%~3!"%\n%
for %%A in ("!LF!") do set "macro.AnyRtnN.rtn=!macro.AnyRtnN.rtn:%%~A=%%~4!"%\n%
if not defined macro_NotDelayed (%\n%
set "macro.AnyRtnN.rtn=!macro.AnyRtnN.rtn:^=^^!"%\n%
call set "macro.AnyRtnN.rtn=%%^macro.AnyRtnN.rtn:^!=""^!%%" ! %\n%
set "macro.AnyRtnN.rtn=!macro.AnyRtnN.rtn:""=^!"%\n%
)%\n%
)%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&set "%%~d=!macro.AnyRtnN.rtn!" ^!"%\n%
)%\n%
if defined macro_inFcn (%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&exit /b !macro.AnyRtn1.ErrLvl!"%\n%
) else if "!macro.AnyRtn1.ErrLvl!" == "1" (%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&(2>nul set =)"%\n%
) else if "!macro.AnyRtn1.ErrLvl!" neq "0" (%\n%
set "macro_EndAnyRtn=!macro_EndAnyRtn!&cmd /d /c exit !macro.AnyRtn1.ErrLvl!"%\n%
)%\n%
set ^"replace=%% ^"^"^" !CR!!CR!^"%\n%
)
set macro\args.Rtn1= ErrLvl EndLocalCnt ValueVar [RtnVar]
set macro\help.Rtn1= ErrLvl EndLocalCnt ValueVar [RtnVar]%\n%
%\n%
Returns the contents of variable ValueVar across the ENDLOCAL border at the%\n%
end of a function or macro. The number of ENDLOCAL executions is controlled%\n%
by the EndLocalCnt which should match the number of times SETLOCAL was used%\n%
within the calling function/macro.%\n%
%\n%
The return value is stored in RtnVar%\n%
or the value is echoed if RtnVar is not specified.%\n%
The ERRORLEVEL is set to ErrLvl%\n%
%\n%
Numeric values ErrLvl and EndLocalCnt may be passed using any expression%\n%
supported by SET /A.%\n%
%\n%
This macro can return a string containing any combination of characters%\n%
supported by DOS, except for 0x0A ^<Line Feed^> or 0x0D ^<Carriage Return^>.%\n%
The macro works regardless whether the target return environment has%\n%
enabled or disabled delayed expansion.%\n%
%\n%
In order for a function to use Rtn1, the calling function must start with%\n%
%%macro_InitFcnRtn%% at the top.%\n%
%\n%
:func inputVar [RtnVar]%\n%
%%macro_InitFcnRtn%%%\n%
setlocal%\n%
{do whatever you need to do}%\n%
set rtnValue={your return value}%\n%
%%macro_Call%% ("errorlevel 1 rtnValue %%~2") %%macro.Rtn1%%%\n%
exit /b%\n%
%\n%
In order for a macro to use Rtn1, the calling macro must start with%\n%
%%macro_InitRtn%% at the top. In the macro template below, assume the %%%%c%\n%
argument holds the name of the variable to receive the result.%\n%
%\n%
set macro.Name=do (%%\n%%%\n%
!macro_InitRtn!%%\n%%%\n%
setlocal%%\n%%%\n%
{do whatever you need to do}%\n%
set rtnValue={your return value}%%\n%%%\n%
!macro_Call! ("errorlevel 1 rtnValue %%%%~c") !macro.Rtn1!%%\n%%%\n%
)%\n%
%\n%
Since this is a case of a macro "calling" a macro, the macro definition%\n%
must be preceded by %%macro_BeginDef%% and followed by a call to %%macro.EndDef%%%\n%
and %%macro_EndAnyRtn%%. See macro.EndDef for more information.%xLF%
set macro.Rtn1=do (%\n%
setlocal enableDelayedExpansion%\n%
set /a "macro.Rtn1.ErrLvl=(%%~a), macro.Rtn1.EndLocalCnt=(%%~b)+2"%\n%
set "rtn=!%%~c!"%\n%
if defined macro_inFcn (%\n%
set "errCmd=exit /b !macro.Rtn1.ErrLvl!"%\n%
) else if "!macro.Rtn1.ErrLvl!" == "0" (%\n%
set "errCmd=rem"%\n%
) else if "!macro.Rtn1.ErrLvl!" == "1" (%\n%
set "errCmd=set ="%\n%
) else (%\n%
set "errCmd=cmd /d /c exit !macro.Rtn1.ErrLvl!"%\n%
)%\n%
if "%%~d" equ "" (%echo%!rtn!) else if defined rtn if not defined macro_NotDelayed (%\n%
set "rtn=!rtn:^=^^!"%\n%
set "rtn=!rtn:"=""Q!^"%\n%
call set "rtn=%%^rtn:^!=""E^!%%" ! %\n%
set "rtn=!rtn:""E=^!"%\n%
set "rtn=!rtn:""Q="!^"%\n%
)%\n%
for /f "delims=" %%e in ("!errCmd!") do for /f %macro_ForEntireLine% %%v in (""!rtn!"") do (%\n%
for /l %%n in (1,1,!macro.Rtn1.EndLocalCnt!) do endlocal%\n%
if "%%~d" neq "" set "%%~d=%%~v" !%\n%
%%e 2^>nul%\n%
)%\n%
)
set macro\args.RtnN= ErrLvl EndLocalCnt ValVar1:RtnVar1[,ValVar2:RtnVar2]...
set macro\help.RtnN= ErrLvl EndLocalCnt ValVar1:RtnVar1[,ValVar2:RtnVar2]...%\n%
%\n%
Returns the contents of multiple variables across the ENDLOCAL border at%\n%
the end of a function or macro. The number of ENDLOCAL executions is%\n%
controlled by the EndLocalCnt which should match the number of times%\n%
SETLOCAL was used within the calling function/macro. The errorlevel is%\n%
set to ErrLvl.%\n%
%\n%
A pair of variable names must be specified for each output value - the%\n%
name of the variable containing the value followed by a colon followed%\n%
by the name of the variable that is to receive the value. Multiple pairs%\n%
are delimited by commas. The list of outputs cannot contain any spaces,%\n%
and the variable names cannot contain asterisk ^(*^), question mark ^(?^),%\n%
colon ^(:^) or comma ^(,^).%\n%
%\n%
Numeric values ErrLvl and EndLocalCnt may be passed using any expression%\n%
supported by SET /A.%\n%
%\n%
This macro can return strings containing any combination of characters%\n%
supported by DOS, except for 0x0A ^<Line Feed^> or 0x0D ^<Carriage Return^>.%\n%
The macro works regardless whether the target return environment has%\n%
enabled or disabled delayed expansion.%\n%
%\n%
In order for a function to use RtnN, the calling function must start with%\n%
%%macro_InitFcnRtn%% at the top.%\n%
%\n%
:func inputVar RtnVar1 RtnVar2%\n%
%%macro_InitFcnRtn%%%\n%
setlocal%\n%
{do whatever you need to do}%\n%
set rtnVal1={your first return value}%\n%
set rtnVal2={your second return value}%\n%
%%macro_Call%% ("errorlevel 1 rtnVal1:%%~2,rtnVal2:%%~3") %%macro.RtnN%%%\n%
exit /b%\n%
%\n%
In order for a macro to use RtnN, the calling macro must start with%\n%
%%macro_InitRtn%% at the top. In the macro template below, assume the %%%%c%\n%
and %%%%~d arguments hold the name of the variables to receive the results.%\n%
%\n%
set macro.Name=do (%%\n%%%\n%
!macro_InitRtn!%%\n%%%\n%
setlocal%%\n%%%\n%
{do whatever you need to do}%\n%
set rtnVal1={your first return value}%%\n%%%\n%
set rtnVal2={your second return value}%%\n%%%\n%
!macro_Call! ("errorlevel 1 rtnVal1:%%%%~c,rtnVal2:%%%%~d") !macro.RtnN!%%\n%%%\n%
)%\n%
%\n%
Since this is a case of a macro "calling" a macro, the macro definition%\n%
must be preceded by %%macro_BeginDef%% and followed by a call to %%macro.EndDef%%%\n%
and %%macro_EndAnyRtn%%. See macro.EndDef for more information.%xLF%
set macro.RtnN=do (%\n%
setlocal enableDelayedExpansion%\n%
set /a "macro.RtnN.ErrLvl=(%%~a), macro.RtnN.EndLocalCnt=(%%~b)+2"%\n%
set "macro.RtnN.cmd="%\n%
for /l %%n in (1,1,!macro.RtnN.EndLocalCnt!) do set "macro.RtnN.cmd=!macro.RtnN.cmd!endlocal!lf!"%\n%
for %%c in (%%~c) do for /f "tokens=1,2 eol=: delims=:" %%c in ("%%c") do (%\n%
set "macro.RtnN.rtn=!%%~c!"%\n%
if defined macro.RtnN.rtn if not defined macro_NotDelayed (%\n%
set "macro.RtnN.rtn=!macro.RtnN.rtn:^=^^!"%\n%
set "macro.RtnN.rtn=!macro.RtnN.rtn:"=""Q!^"%\n%
call set "macro.RtnN.rtn=%%^macro.RtnN.rtn:^!=""E^!%%" ! %\n%
set "macro.RtnN.rtn=!macro.RtnN.rtn:""E=^!"%\n%
set "macro.RtnN.rtn=!macro.RtnN.rtn:""Q="!^"%\n%
)%\n%
set "macro.RtnN.cmd=!macro.RtnN.cmd!set "%%~d=!macro.RtnN.rtn!"^!!lf!"%\n%
)%\n%
if defined macro_inFcn (%\n%
set "macro.RtnN.cmd=!macro.RtnN.cmd!exit /b !macro.RtnN.ErrLvl!!lf!"%\n%
) else if "!macro.RtnN.ErrLvl!" == "0" (%\n%
rem errorlevel set to 0 by default%\n%
) else (%\n%
set "macro.RtnN.cmd=!macro.RtnN.cmd!cmd /d /c exit !macro.RtnN.ErrLvl!!lf!"%\n%
)%\n%
for /f "delims=" %%v in ("!macro.RtnN.cmd!") do %%v%\n%
)
set "macro\args_BeginDef= "
set macro\help_BeginDef=%\n%
%\n%
Define a simple macro to be used before defining a macro that includes%\n%
another macro in its definition. See macro.EndDef for more information%xLF%
set macro_BeginDef=(%\n%
setlocal enableDelayedExpansion%\n%
set "macro_NotDelayed=1"%\n%
set "macro_inFcn="%\n%
)
set macro\args.EndDef= MacroName
set macro\help.EndDef= MacroName%\n%
%\n%
One of three macros needed to define a macro that "calls" another macro%\n%
in its definition. %%macro_BeginDef%% is used before the macro definition%\n%
and %%macro.EndDef%% and %%macro_EndAnyRtn%% are used after the macro definition.%\n%
%\n%
Macro calling macro template:%\n%
%\n%
%%macro_BeginDef%%%\n%
set macro.Name=do (%%\n%%%\n%
{macro definition goes here}%\n%
)%\n%
%%macro_Call%% ("macro.Name") %%macro.EndDef%%%\n%
%%macro_EndAnyRtn%%%\n%
%\n%
Calls to a macro within the definition should use delayed expansion:%\n%
%\n%
!macro_SimpleMacroName!%%\n%%%\n%
!macro_Call! ("{macro aruments}") !macro.MacroName!%%\n%%%\n%
%\n%
Carets and exclamation points not involved with a macro call must be%\n%
escaped. Escape once if within quotes, twice if not within quotes:%\n%
%\n%
set "caret=^^"%%\n%%%\n%
set "exclamation=^!"%%\n%%%\n%
set caret=^^^^^^^^%%\n%%%\n%
set exclamation=^^^^^^!%%\n%%%xLF%
%macro_BeginDef%
set macro.EndDef=do !macro_call! ("errorlevel 0 %%a %%a") !macro.AnyRtn1!
%macro_Call% ("errorlevel 0 macro.EndDef macro.EndDef") %macro.AnyRtn1%
%macro_EndAnyRtn%
set macro\args.SetErr= ErrLvl
set macro\help.SetErr= ErrLvl%\n%
%\n%
Sets ERRORLEVEL to the integral value ErrLvl%xLF%
set macro.SetErr=do (%\n%
if "%%~a" equ "0" (%\n%
verify ^>nul%\n%
) else if "%%~a" equ "1" (%\n%
set = 2^>nul%\n%
) else (%\n%
cmd /d /c exit %%~a%\n%
)%\n%
)
::----------------------------------------------------
:: Mark that this library has been loaded.
set macro\load.%~n0=1
macroLib_String.batCode: Select all
@echo off
:: File = macroLib_String.bat
:: Dependencies: macroLib_Base.bat, macroLib_Return.bat
:: This batch file will fail if called while delayed expansion is enabled.
::
:: This library defines macros that are useful for working with strings.
::
:: The library is designed to be installed in a directory in your PATH.
:: Any batch file that requires it can include it by simply placing the
:: following line of code at the top before any SETLOCAL:
::
:: IF NOT DEFINED macro\load.MacroLib_String CALL macroLib_String
::
:: In this way the library becomes resident in your command shell environment
:: where it is available to any batch file that may need it. The IF condition
:: prevents unneccessary reloads of the same library.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Conditionally load dependencies (normally residing somewhere in the PATH)
if not defined macro\load.macroLib_Return call macroLib_Return
set macro\args.StrLen= StrVar [RtnVar]
set macro\help.StrLen= StrVar [RtnVar]%\n%
%\n%
Computes the length of the string within variable StrVar%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified%xLF%
set macro.StrLen=do (%\n%
setlocal enableDelayedExpansion%\n%
set "str=A!%%~a!"%\n%
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~b" neq "" (set "%%~b=%%v") else %echo%%%v%\n%
)
set macro\args.AnyToLower= StrVar [RtnVar]
set macro\help.AnyToLower= StrVar [RtnVar]%\n%
%\n%
Converts the string within variable StrVar to lower case%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified%\n%
%\n%
Like all "Any" macros, this version of ToLower supports all characters%\n%
supported by DOS, including 0x0A ^<LF^> and 0x0D ^<CR^>.%\n%
%\n%
%%macro_EndAnyRtn%% must follow a call to this macro, and it cannot share%\n%
a code block with the call.%xLF%
%macro_BeginDef%
set macro.AnyToLower=do (%\n%
!macro_InitRtn!%\n%
setlocal enableDelayedExpansion%\n%
set "str=^!%%~a^!"%\n%
for %%A in (%\n%
"A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i"%\n%
"J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r"%\n%
"S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z" "Ä=ä"%\n%
"Ö=ö" "Ü=ü"%\n%
) do set "str=^!str:%%~A^!"%\n%
!macro_Call! ("^!errorlevel^! 1 str %%~b") !macro.AnyRtn1!%\n%
)
%macro_Call% ("macro.AnyToLower") %macro.EndDef%
%macro_EndAnyRtn%
set macro\args.AnyToUpper= StrVar [RtnVar]
set macro\help.AnyToUpper= StrVar [RtnVar]%\n%
%\n%
Converts the string within variable StrVar to upper case%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified%\n%
%\n%
Like all "Any" macros, this version of ToUpper supports all characters%\n%
supported by DOS, including 0x0A ^<LF^> and 0x0D ^<CR^>.%\n%
%\n%
%%macro_EndAnyRtn%% must follow a call to this macro, and it cannot share%\n%
a code block with the call.%xLF%
%macro_BeginDef%
set macro.AnyToUpper=do (%\n%
!macro_InitRtn!%\n%
setlocal enableDelayedExpansion%\n%
set "str=^!%%~a^!"%\n%
for %%A in (%\n%
"a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I"%\n%
"j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R"%\n%
"s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z" "ä=Ä"%\n%
"ö=Ö" "ü=Ü"%\n%
) do set "str=^!str:%%~A^!"%\n%
!macro_Call! ("^!errorlevel^! 1 str %%~b") !macro.AnyRtn1!%\n%
)
%macro_call% ("macro.AnyToUpper") %macro.EndDef%
%macro_EndAnyRtn%
set macro\args.ToLower= StrVar [RtnVar]
set macro\help.ToLower= StrVar [RtnVar]%\n%
%\n%
Converts the string within variable StrVar to lower case%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified%xLF%
%macro_BeginDef%
set macro.ToLower=do (%\n%
!macro_InitRtn!%\n%
setlocal enableDelayedExpansion%\n%
set "str=^!%%~a^!"%\n%
for %%A in (%\n%
"A=a" "B=b" "C=c" "D=d" "E=e" "F=f" "G=g" "H=h" "I=i"%\n%
"J=j" "K=k" "L=l" "M=m" "N=n" "O=o" "P=p" "Q=q" "R=r"%\n%
"S=s" "T=t" "U=u" "V=v" "W=w" "X=x" "Y=y" "Z=z" "Ä=ä"%\n%
"Ö=ö" "Ü=ü"%\n%
) do set "str=^!str:%%~A^!"%\n%
!macro_Call! ("^!errorlevel^! 1 str %%~b") !macro.Rtn1!%\n%
)
%macro_Call% ("macro.ToLower") %macro.EndDef%
%macro_EndAnyRtn%
set macro\args.ToUpper= StrVar [RtnVar]
set macro\help.ToUpper= StrVar [RtnVar]%\n%
%\n%
Converts the string within variable StrVar to upper case%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified%xLF%
%macro_BeginDef%
set macro.ToUpper=do (%\n%
!macro_InitRtn!%\n%
setlocal enableDelayedExpansion%\n%
set "str=^!%%~a^!"%\n%
for %%A in (%\n%
"a=A" "b=B" "c=C" "d=D" "e=E" "f=F" "g=G" "h=H" "i=I"%\n%
"j=J" "k=K" "l=L" "m=M" "n=N" "o=O" "p=P" "q=Q" "r=R"%\n%
"s=S" "t=T" "u=U" "v=V" "w=W" "x=X" "y=Y" "z=Z" "ä=Ä"%\n%
"ö=Ö" "ü=Ü"%\n%
) do set "str=^!str:%%~A^!"%\n%
!macro_Call! ("^!errorlevel^! 1 str %%~b") !macro.Rtn1!%\n%
)
%macro_Call% ("macro.ToUpper") %macro.EndDef%
%macro_EndAnyRtn%
::------------------------------------------
:: MARK THAT THIS LIBRARY HAS BEEN LOADED
set macro\load.%~n0=1
macroLib_Swap.batCode: Select all
@echo off
:: File = macroLib_Swap.bat
:: Dependencies: macroLib_Base.bat, macroLib_Return.bat
:: This batch file will fail if called while delayed expansion is enabled.
::
:: This library defines macros that swap values between two variables.
::
:: The library is designed to be installed in a directory in your PATH.
:: Any batch file that requires it can include it by simply placing the
:: following line of code at the top before any SETLOCAL:
::
:: IF NOT DEFINED macro\load.MacroLib_Swap CALL macroLib_Swap
::
:: In this way the library becomes resident in your command shell environment
:: where it is available to any batch file that may need it. The IF condition
:: prevents unneccessary reloads of the same library.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Conditionally load dependencies (normally residing somewhere in the PATH)
if not defined macro\load.macroLib_Return call macroLib_Return
set macro\args.Swap= Var1 Var2
set macro\help.Swap= Var1 Var2%\n%
%\n%
Swap the contents of variable Var1 with variable Var2.%\n%
%\n%
The variables may contain any combination of characters supported by DOS%\n%
except for 0x0A ^<Line Feed^> or 0x0D ^<Carriage Return^>.%xLF%
%macro_BeginDef%
set macro.Swap=do (%\n%
!macro_InitRtn!%\n%
setlocal enableDelayedExpansion%\n%
set "macro.swap.b=^!%%~a^!"%\n%
set "macro.swap.a=^!%%~b^!"%\n%
!macro_Call! ("errorlevel 1 macro.swap.a:%%~a,macro.swap.b:%%~b") !macro.RtnN!%\n%
)
%macro_Call% ("macro.Swap") %macro.EndDef%
%macro_EndAnyRtn%
set macro\args.AnySwap= Var1 Var2
set macro\help.AnySwap= Var1 Var2%\n%
%\n%
Swap the contents of variable Var1 with variable Var2.%\n%
%\n%
The variables may contain any combination of characters supported by DOS%\n%
including 0x0A ^<Line Feed^> and 0x0D ^<Carriage Return^>.%\n%
%\n%
%%macro_RtnAny%% must follow a call to this macro, and it cannot share a code%\n%
block with the call.%xLF%
%macro_BeginDef%
set macro.AnySwap=do (%\n%
!macro_InitRtn!%\n%
setlocal enableDelayedExpansion%\n%
set "macro.anyswap.b=^!%%~a^!"%\n%
set "macro.anyswap.a=^!%%~b^!"%\n%
!macro_Call! ("errorlevel 1 macro.anyswap.a:%%~a,macro.anyswap.b:%%~b") !macro.AnyRtnN!%\n%
)
%macro_Call% ("macro.AnySwap") %macro.EndDef%
%macro_EndAnyRtn%
::----------------------------------------------------
:: Mark that this library has been loaded.
set macro\load.%~n0=1
macroLib_Num.batCode: Select all
@echo off
:: File = macroLib_Num.bat
:: Dependencies: macroLib_Base.bat
:: This batch file will fail if called while delayed expansion is enabled.
::
:: This library defines macros that are useful for working with numbers.
::
:: The library is designed to be installed in a directory in your PATH.
:: Any batch file that requires it can include it by simply placing the
:: following line of code at the top before any SETLOCAL:
::
:: IF NOT DEFINED macro\load.MacroLib_Num CALL macroLib_Num
::
:: In this way the library becomes resident in your command shell environment
:: where it is available to any batch file that may need it. The IF condition
:: prevents unneccessary reloads of the same library.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Conditionally load dependencies (normally residing somewhere in the PATH)
if not defined macro\load.macroLib_Base call macroLib_Base
set macro\args.Num2Hex= NumVal [RtnVar]
set macro\help.Num2Hex= NumVal [RtnVar]%\n%
%\n%
Converts the integer value NumVal to the hexadecimal representation%\n%
of a signed 32 bit integer.%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified%\n%
%\n%
NumVal may be passed as any expression supported by SET /A%xLF%
set macro.Num2Hex=do (%\n%
setlocal enableDelayedExpansion%\n%
set /a "dec=(%%~a)"%\n%
if defined hex set "hex="%\n%
set "map=0123456789ABCDEF"%\n%
for /l %%n in (1,1,8) do (%\n%
set /a "d=dec&15,dec>>=4"%\n%
for %%d in (!d!) do set "hex=!map:~%%d,1!!hex!"%\n%
)%\n%
for %%v in (!hex!) do endlocal^&if "%%~b" neq "" (set "%%~b=%%v") else %echo%%%v%\n%
)
set macro\args.Random= MinVal MaxVal [RtnVar]
set macro\help.Random= MinVal MaxVal [RtnVar]%\n%
%\n%
Compute a pseudo random integral value between numeric values%\n%
MinVal and MaxVal.%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified%\n%
%\n%
MinVal and MaxVal may be specified using any expression supported%\n%
by SET /A%xLF%
set macro.Random=do (%\n%
setlocal enableDelayedExpansion%\n%
set /a "macro.Random.rtn=!random! %% ((%%~b)-(%%~a)+1) + (%%~a)"%\n%
for /f %%v in ("!macro.Random.rtn!") do endlocal^&if "%%~c" neq "" (set %%~c=%%v) else %echo%%%v%\n%
)
::------------------------------------------
:: MARK THAT THIS LIBRARY HAS BEEN LOADED
set macro\load.%~n0=1
macroLib_Time.batCode: Select all
@echo off
:: File = macroLib_Time.bat
:: Dependencies: macroLib_Base.bat
:: This batch file will fail if called while delayed expansion is enabled.
::
:: This library defines macros that are useful for working with Time.
::
:: The library is designed to be installed in a directory in your PATH.
:: Any batch file that requires it can include it by simply placing the
:: following line of code at the top before any SETLOCAL:
::
:: IF NOT DEFINED macro\load.MacroLib_Time CALL macroLib_Time
::
:: In this way the library becomes resident in your command shell environment
:: where it is available to any batch file that may need it. The IF condition
:: prevents unneccessary reloads of the same library.
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Conditionally load dependencies (normally residing somewhere in the PATH)
if not defined macro\load.macroLib_Base call macroLib_Base
set macro\args.GetTime= [RtnVar]
set macro\help.GetTime= [RtnVar]%\n%
%\n%
Computes the current time of day measured as 1/100th seconds past midnight%\n%
%\n%
Sets RtnVar = result%\n%
or displays result if RtnVar not specified ("""")%xLF%
set macro.GetTime=do (%\n%
setlocal enableDelayedExpansion%\n%
set "t=0"%\n%
for /f "tokens=1-4 delims=:." %%A in ("!time: =0!") do set /a "t=(((1%%A*60)+1%%B)*60+1%%C)*100+1%%D-36610100"%\n%
for %%v in (!t!) do endlocal^&if "%%~a" neq "" (set "%%~a=%%v") else %echo%%%v%\n%
)
set macro\args.DiffTime= StartTime StopTime [RtnVar]
set macro\help.DiffTime= StartTime StopTime [RtnVar]%\n%
%\n%
Computes the elapsed time between StartTime and%\n%
StopTime and formats the result as HH:MM:SS.DD%\n%
%\n%
StartTime and StopTime must be integral values%\n%
representing 1/100th seconds past midnight. These%\n%
values are typically gotten via calls to GetTime.%\n%
%\n%
Sets RtnVar=result%\n%
or displays result if RtnVar not specified%\n%
%\n%
DiffTime will properly handle elapsed times that span%\n%
midnight. However it cannot handle times that%\n%
reach 24 hours or more.%\n%
%\n%
Note that StartTime and StopTime may be passed using%\n%
any numeric expression supported by SET /A%xLF%
set macro.DiffTime=do (%\n%
setlocal enableDelayedExpansion%\n%
set /a "DD=(%%~b)-(%%~a)"%\n%
if !DD! lss 0 set /a "DD+=24*60*60*100"%\n%
set /a "HH=DD/360000, DD-=HH*360000, MM=DD/6000, DD-=MM*6000, SS=DD/100, DD-=SS*100"%\n%
if "!HH:~1!"=="" set "HH=0!HH!"%\n%
if "!MM:~1!"=="" set "MM=0!MM!"%\n%
if "!SS:~1!"=="" set "SS=0!SS!"%\n%
if "!DD:~1!"=="" set "DD=0!DD!"%\n%
for %%v in (!HH!:!MM!:!SS!.!DD!) do endlocal^&if "%%~c" neq "" (set "%%~c=%%v") else %echo%%%v%\n%
)
::------------------------------------------
:: MARK THAT THIS LIBRARY HAS BEEN LOADED
set macro\load.%~n0=1
callMacro.batCode: Select all
@echo off
if "%~1"=="" goto :help
setlocal enableDelayedExpansion
set "macro.temp=!macro.%~1!"
endlocal & for /f "tokens=2-27" %%a in ("%*") %macro.temp%
%macro_EndAnyRtn%
exit /b
:help
echo(
for /f "tokens=1* eol=: delims=:" %%a in ('findstr /bn : %~f0') do echo(%%b
exit /b
:callMacro MacroName Arg1 [ArgN]...
::
:: Calls macro MacroName via a function.
:: The MacroName should not include the macro. prefix.
::
:: Macros have restrictions on where they may be used. Calling a macro
:: through this function eliminates most of the restrictions at a cost of
:: reduced speed due to the function call overhead.
::
:: Macros are normally called inside a batch file using the macro_Call macro.
::
:: Use the following macro help command to get more information on options
:: for calling a macro:
::
:: mhelp call
::
:: For a full list of available macros to call, use:
::
:: margs
::
:: For detailed help on a particular macro, use:
::
:: mhelp macroName
2011-Jun-26: Fixed major bug in macroLib_Return.bat - macro.Rtn1Enjoy! (maybe)
Dave Benham