No args Display this message (same as -?) -i Display GUI interface, must be the first option -l Log off (cannot be used with -m option) -s Shutdown the computer -r Shutdown and restart the computer -a Abort a system shutdown -m \\computername Remote computer to shutdown/restart/abort -t xx Set timeout for shutdown to xx seconds -c "comment" Shutdown comment (maximum of 127 characters) -f Forces running applications to close without warning -d [u][p]:xx:yy The reason code for the shutdown u is the user code p is a planned shutdown code xx is the major reason code (positive integer less than 256) yy is the minor reason code (positive integer less than 65536)

Using Jeb's tips (with a few modifications) the functions now support all but the following two characters: NUL 0x00 and LF 0x0A.

Most of the added characters are embedded directly in the batch file. I reconfigured my editor to preserve Tabs (I use Context - it was configured to convert tabs into 2 spaces).

By default, only the characters that are embedded in the batch file are available. The optional /X switch adds support for CR 0x0D and SUB 0x1A by adding them programatically. I structured things this way because the /X option takes significantly longer to build the ASCII map, and variables with CR can only be accessed with delayed expansion. I don't want to penalize calls that don't need CR or SUB.

The /X option requires writing and reading a temporary file. I used the DOS Tips :Unique function plus %random% to generate a temporary file name that should prevent collisions even during concurrent use on a shared drive.

I would love to add support for LF 0x0A. I tried many variations of Jeb's for loop idea, but I could never get it to work when combined with the full ASCII map. I think if I restrict the code to :asc and :chr I could make things work with ugly code. But :hex2str really causes problems.

The problem I have is when trying to pass a string that requires delayed expansion across the function return boundry. I need a technique that will allow me to pass any combination of characters across the boundry. Usually the problem involves the for loop parsing the string into two or more strings such that the loop has multiple iterations. The technique I am currently using splits the string at each LF but works for all other characters.

Some notes on what I discovered:

1) CR passes through my variation of the function for loop return technique as long as it is not the last character in the string. If it is the last character it gets dropped. I solved this by simply appending an extra CR on the end if and only if the string ends in CR.

2) I ended up not using this, but I tested Jeb's code to generate a BS 0x08 and I found one small flaw that is easy to fix. Jeb's current DEL variable actually has length 3. It consists of <BS><Space><BS> (hex 08 20 08). A simple substring operation trims it down to the desired single character.

:BL.String.CreateDEL_ESC:: Creates two variables with one character DEL=Ascii-08 and ESC=Ascii-27:: DEL and ESC can be used with and without DelayedExpansionsetlocalfor /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do ( ENDLOCAL set "DEL=%%a" set "DEL=%DEL:~0,1% set "ESC=%%b" goto :EOF)goto :eof

The prompt $H works the way it does because there is no guarantee there will be a subsequent character after the $H. The first <BS> moves the screen cursor back one position, but does not erase the character there. The <Space> "erases" the character but moves the cursor forward, and the final <BS> moves the cursor back again.

-----

And now the revised code. As before, the test cases are at the top. There are two tests. First loop through all valid ASCII codes and convert to char and back to code using :chr and :asc. The end code should match the original code. The second test builds the ASCII map, converts the entire ASCII map to a hex string and then back to a map string using :asciiMap, :str2hex and :hex2str. The starting map should match the ending map. The tests are run both with and without the /X option. I won't include the output because it is so long, but it does give the expected results on my Vista machine.

The /X option would be much faster if the ASCII map were preserved as a global variable, but I want each function to stand on its own without depending on global variables (in other words no side effects).

WARNING - I believe the web site is corrupting the ASCII map in the code block. The 10th character after the = should be a TAB but I think it is getting converted into 4 spaces. Below is the actual line (hopefully intact)

:str2hex [/X] StrVar [RtnVar]:::: Converts the string contained within variable StrVar into a string of:: ASCII codes, with each code represented as a pair of hexadecimal digits.:: The length of the result will always be exactly twice the length of:: the original string.:::: Sets RtnVar=result:: or displays result if RtnVar not specified:::: If one of the following problematic characters appears within StrVar:: then the corresponding hex pair is set to 00 and errorlevel is set to 1.:::: C H A R A C T E R S U P P O R T E D ?:: Dec Hex Oct Char Normal /X Option:: --- ---- ---- ---- ------ ---------:: 0 0x00 00 NUL (null) No No:: 10 0x0A 012 LF (line feed) No No:: 13 0x0D 015 CR (carriage return) No Yes:: 26 0x1A 032 SUB (substitute) No Yes:::: If the case insensitive /X option is specified then CR (0x0D) and:: SUB (0x1A) are supported properly as input.:::: Note: The /X option requires writing and reading a small temporary file.:::::: Dependencies - :asciiMap, :strLen, :Unique::: setlocal disableDelayedExpansion if /i "%~1"=="/X" ( set option=%1 shift /1 ) else set option= call :asciiMap %option% asciiMap set "hexMap=0123456789ABCDEF" call :strLen %~1 len set /a len-=1 set rtn= set err= setlocal enableDelayedExpansion for /l %%n in (0,1,%len%) do ( set c=!%~1:~%%n,1! set d=0 for /l %%n in (0,1,255) do if "!asciiMap:~%%n,1!"=="!c!" set d=%%n set /a h1=d/16, h2=d%%16 for %%a in (!h1!) do for %%b in (!h2!) do set rtn=!rtn!!hexMap:~%%a,1!!hexMap:~%%b,1! ) ( endlocal endlocal if "%~2" neq "" (set %~2=%rtn%) else echo:%rtn% exit /b %err% )exit /b

:hex2str [/X] HexVar RtnVar:::: Converts a string of hexadecimal digits contained within variable HexVar:: into a string, where each pair of hex digits in the input represents the:: ASCII code of a character in the result.:::: Stores the result in the variable RtnVar.:::: If one of the following problematic characters is specified within the Hex:: string then a space is substituted and errorlevel is set to 1::::: C H A R A C T E R S U P P O R T E D ?:: Dec Hex Oct Char Normal /X Option:: --- ---- ---- ---- ------ ---------:: 0 0x00 00 NUL (null) No No:: 10 0x0A 012 LF (line feed) No No:: 13 0x0D 015 CR (carriage return) No Yes:: 26 0x1A 032 SUB (substitute) No Yes:::: If the case insensitive /X option is specified then CR (0x0D) and:: SUB (0x1A) may appear in the output. If the result contains CR (0x0D):: then RtnVar should only be accessed via delayed expansion.:::: Note: The /X option requires writing and reading a small temporary file.:::: If an invalid hexadecimal digit is detected within the Hex string then the:: corresponding result character is set to a space and errorlevel is set:: to 2.:::: Aborts with an error message to stderr and errorlevel 3 if the Hex string:: length is not divisible by 2.:::::: Dependencies - :asciiMap, :strLen, :Unique::: setlocal disableDelayedExpansion if /i "%~1"=="/X" ( set option=%1 shift /1 ) else set option= call :asciiMap %option% map call :strLen %1 len set /a mod=len%%2 if %mod%==1 1>&2 echo "ERROR: Hex string length not a multiple of 2" & exit /b 3 set rtn= set /a len-=1 setlocal enableDelayedExpansion set err=0 for /l %%n in (0,2,%len%) do ( 2>nul set /a d=0x!%~1:~%%n,2! || (set d=32&set err=2) for %%d in (!d!) do set c=^!map:~%%d,1! if "!c!"==" " if not !d!==32 if !err!==0 set err=1 set "rtn=!rtn!!c!" ) if defined option if "!rtn:~-1!"=="!map:~13,1!" set "rtn=!rtn!!map:~13,1!" for /f "delims=" %%s in ("!rtn!") do ( endlocal endlocal if "%~2" neq "" set %~2=%%s exit /b %err% )exit /b

:asc [/X] StrVar IntVal [RtnVar]:::: Computes the ASCII code for a specified character within the string:: contained by variable StrVar. The position within the string is specified:: by the IntVal argument. A non-negative value is relative to the beginning:: of the string, with 0 specifiying the first character. A negative value is:: relative to the end of the string, with -1 specifying the last character.:::: Sets RtnVar=result:: or displays result if RtnVar not specified:::: IntVal may be passed as a variable without enclosing the name in percent:: symbols.:::: If one of the following problematic characters is specified then RtnVar:: will be undefined and errorlevel will be set to 1.:::: C H A R A C T E R S U P P O R T E D ?:: Dec Hex Oct Char Normal /X Option:: --- ---- ---- ---- ------ ---------:: 0 0x00 00 NUL (null) No No:: 10 0x0A 012 LF (line feed) No No:: 13 0x0D 015 CR (carriage return) No Yes:: 26 0x1A 032 SUB (substitute) No Yes:::: If the case insensitive /X option is specified then CR (0x0D) and:: SUB (0x1A) may successfully be specified as input.:::: Note: The /X option requires writing and reading a small temporary file.:::: If StrVar is not defined then aborts with an error message to stderr and:: errorlevel 2.:::: If IntVal is greater than or equal to the length of the string then aborts:: with an error message to stderr and errorlevel 3.:::: Negative IntVal values will never result in errorlevel 2: Positions earlier:: than the 1st character are treated as the 1st character.:::::: Dependencies - :asciiMap, :Unique::: setlocal disableDelayedExpansion if /i "%~1"=="/X" ( set option=%1 shift /1 ) else set option= set /a n=%~2 2>nul if errorlevel 1 1>&2 echo "ERROR: Invalid numeric value"&exit /b 11 if not defined %~1 1>&2 echo "ERROR: Variable not defined"&exit /b 2 call :asciiMap %option% ascii setlocal enableDelayedExpansion set "chr=!%~1:~%n%,1!" if not defined chr 1>&2 echo "ERROR: String position not found"&exit /b 3 if "!chr!"==" " (set /a rtn=32) else ( if defined rtn set rtn= for /l %%n in (0,1,255) do if "!ascii:~%%n,1!"=="!chr!" set rtn=%%n ) if defined rtn (set err=0) else set err=1 (endlocal & rem -- return values endlocal if "%~3" neq "" (set %~3=%rtn%) else (echo:%rtn%) exit /b %err% )exit /b

::-----------------------------------------------------------------------------:: The following are existing dostips functions::-----------------------------------------------------------------------------

:strLen string len -- returns the length of a string:: -- string [in] - variable name containing the string being measured for length:: -- len [out] - variable to be used to return the string length:: Many thanks to 'sowgtsoi', but also 'jeb' and 'amel27' dostips forum users helped making this short and efficient:$created 20081122 :$changed 20101116 :$categories StringOperation:$source http://www.dostips.com( SETLOCAL ENABLEDELAYEDEXPANSION set "str=A!%~1!"&rem keep the A up front to ensure we get the length and not the upper bound rem it also avoids trouble in case of empty string set "len=0" for /L %%A in (12,-1,0) do ( set /a "len|=1<<%%A" for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A" ))( ENDLOCAL & REM RETURN VALUES IF "%~2" NEQ "" SET /a %~2=%len%)EXIT /b

Correction - the code block is converting the TAB into 3 spaces. The TAB in the map outside the code block is correct. But the 161st character after the = is supposed to be ASCII decimal 160, but it has been corrupted into a space in both places.

rem ** Prepare for returnif not defined NotDelayedFlag ( for %%a in ("!LF!") do set "var=!var:%%~a=""L!" set "var=!var:^=^^^^!" set "var=!var:"=""Q!")if not defined NotDelayedFlag ( set "var=%var:!=^^^^^!%" ! set "var=!var:""Q="!" for %%a in ("!LF!") do set "var=!var:""L=%%~a!")set ^"var=!var:"=^"!"set "var=!var:&=^&!"for %%a in ("!LF!") do set "var=!var:%%~a=^%%~a%%~a!"( ENDLOCAL ENDLOCAL set ^"%~1=%var%" ! goto :eof)

build my original map length 256set rtn=0For each 1 bit in a byte going from high to low ( XOR the bit with rtn if CHR < %map:~rtn,1% then remove the bit (XOR the bit with rtn again))

Upon completion of the loop, rtn should be the ASCII code for CHR. The loop has only 8 iterations, so it would be very fast. But the algorithm FAILS because the characters do not sort in ASCII code sequence.

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

Regarding the 2nd part of your reply:

Close, but I still see problems. I added support for ^ | < > ( ) that was missing in your original. But there are still problems if input contains CR. Also, there is a problem with calling while using delayed expansion if input contains ""L.

lfTest was called with Delayed Expansion DISABLEDthe input is:one&linetwo with exclam!three with "quotes&"&"four with ^ | < > ( ) & ! "five with CRsix with ""Q ""L still six

The result is:one&linetwo with exclam!three with "quotes&"&"four with ^ | < > ( ) & ! "xxxxxwith CRfivesix with ""Q ""L still six

lfTest was called with Delayed Expansion ENABLEDthe input is:one&linetwo with exclam!three with "quotes&"&"four with ^ | < > ( ) & ! "five with CRsix with ""Q ""L still six

The result is:one&linetwo with exclam!three with "quotes&"&"four with ^ | < > ( ) & ! "xxxxxwith CRfivesix with ""Q still six

So it looks like the functions can support CR or LF, but not both. Unless you have any other ideas?

I had already given up on calling functions while delayed expansion is enabled. But it looks like you have discovered a "universal" technique that works as long as returned value does not contain CR or LF! Awsome!

I have fixed two bugs in my last posted code- :chr and :hex2str both failed with ; (0x59) due to the default FOR /F "eol=;" option. Unfortunately eol= option cannot be completely disabled like "delims=", there is always an eol character. (The description of eol within "help for" or "for /?" is terrible!)

I'm still working on Jeb's idea for speeding up :str2hex and :asc. I'm close, but still some things to overcome. So far there is some improved performance, but I'm not sure yet if it's worth the effort.

dbenham wrote:Close, but I still see problems. I added support for ^ | < > ( ) that was missing in your original. But there are still problems if input contains CR. Also, there is a problem with calling while using delayed expansion if input contains ""L.

Ok, I was a bit imprecise. The ""L problem can be solved by the correct order of the commands.And the support for | < > ( ) is equal to &.But I didn't look at this way anymore, as I think it comes to a dead end.

dbenham wrote:So it looks like the functions can support CR or LF, but not both. Unless you have any other ideas?

I had already given up on calling functions while delayed expansion is enabled. But it looks like you have discovered a "universal" technique that works as long as returned value does not contain CR or LF! Awsome!

It was a bit tricky, but I created a slightly other solution. It can handle CR and LF and all the other characters And it's shorter and doesn't need to handle the & | < > characters.

It replaces % with %~A" with %~B<CR> with %~C<LF> with %~LAnd in the return statement it conterts them back to the original values.I need two FOR statements, as the FOR /F can't assign <LF> to a parameter, but a simple FOR %%a.The % substituition to %~A is neccessary, else there could be content with something like "100%C" which would expand the wrong way.

And in the enabled delayed expansion mode, there are two more substituitions^ with ^^! with ^!The caret doubling is neccessary, as in a line is one or more exclamation marks the ^ works a second time as an escape character, but now in and outside of quotes.To assure that the doubled carets will always reduced to a single caret, I append an exlamation mark after the last quote in the set statements.

For each character in the line do:- If it is a caret (^) the next character has no special meaning, the caret itself is removed- If it is an exclamation mark, search for the next exclamation mark (carets are not observed here), then expands to the content of the variable

- If no exclamation mark is found in this phase, the result is discarded, the result of the phase before is used instead (important for the carets)

So, at this point the difference should be clear, the carets are removed even if the exclamation mark have no other effect in a line.

Oh my goodness Jeb! Your solution is insanely clever. I'm able to follow the substitutions, but I'll have to spend more time looking at your explanation to fully understand the games you play with !Thankfully I don't have to fully understand it to use it.

I've finished incorporating your improved map lookup for asc and str2hex. It doesn't make much difference for :asc, but str2hex is now nearly 7 times faster! I solved the case issue by doubling each character after # and testing the 1st remaining character after substitution against the target character. If no match then I just perform one more substitution to get the correct case.

I've eliminated the clunky /X option and now create the special characters only as needed.

For :hex2str I added options to perform substitutions for NUL, CR, LF, and Errors. By default NUL is represented as <NUL>, LF as <LF>, invalid hex as <ERR>, and CR as itself. But the options allow setting each representation to any string you want.

The only thing that was missing was support for a true LF in :hex2str. I was all ready to post what I had but now you've provided the last missing piece.

I'll incorporate your solution and post the final result in the next few days.

I think this project is now complete! (with the exception of bugs which are bound to crop up ).

The library now supports 255 ASCII characters (only 00 NUL not supported). I've fully incorporated Jeb's new found technique allowing a function to return any string value, regardless whether the function was called with delayed expansion enabled or disabled.

I've turned the library into a complete, self-contained, callable library, complete with built-in help functionality. There is no need to copy the routines into your own batch file - you can simply call the library directly. Of course there is nothing stopping you from copying the routines if that is your preference.

To avoid corruption of the code that I experienced earlier, I've made the file available on a free Google web site. Download the CharLib_bat.txt file found here: https://sites.google.com/site/dbenhamfiles/home/CharLib_bat.txtand rename it to CharLib.bat. Ideally the file should be in a directory that is in your path.

Run CharLib without any options to get basic syntax and other general info.

CharLib help will provide a list of available functions.

CharLib test will run a test suite that exercises most of the functionality.

One more time I have to thank Jeb for all his work and advice that enabled me to make these routines fully functional.

I hope others find this library useful. Please post a bug report here if you find problems. However I am not interested in adding additional functionality (except for maybe some form of versioning info in the case of bug fix updates)

Hi plp626. I'm assuming the functions failed miserably for you. There is an interesting discussion at "universal" %DATE% parser concerning multi-national byte representation of DOS strings. I think I have a handle on how to work with single byte character sets. But I have no idea if functions like :asc, :chr, :parseDate etc. can be made to work with multibyte character sets like GBK.

I would like to see if the functions can be made to work for you, but I will need your help. I don't have a machine on which I can test GBK. One quick question - how does DOS substring work with Chinese characters like your test string? In other words, if variable str contains your string, would %str:~0,1% return the 1st Chinese character consisting of two bytes?