Page 1 of 1

[Solved] Return ANY string across ENDLOCAL boundry - BUG!

Posted: 22 May 2011 20:11
by dbenham
Noooooooo! :x :cry:

I just discovered a serious bug in the "magic" code that Jeb developed here :chr, :asc, :str2hex, :hex2str

The code is intended to return any string across an ENDLOCAL boundry, whether or not delayed expansion is enabled or disabled. But it doesn't work if the string contains any of the following: %A, %B, %C, %L.

The results are weird, especially for %B. :shock: I am mystified why those strings don't work, yet %~A, %~B, %~C and %~L work perfectly.

I have been lovin' the technique and have used it in a number of functions. I hope this doesn't mean the whole house of cards is collapsing :!:

Jeb - do you see a way to fix this :?:

Here is Jeb's original test code, with the failing new test cases appended to the "zero" line.

Code: Select all

@echo off
setlocal EnableDelayedExpansion
cls
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
set LF=^


rem TWO Empty lines are neccessary
set "original=zero*? %%~A%%~B%%~C%%~L :%%A:%%B:%%C:%%L:!LF!one&line!LF!two with exclam^! !LF!three with "quotes^&"&"!LF!four with ^^^^ ^| ^< ^> ( ) ^& ^^^! ^"!LF!xxxxxwith CR!CR!five !LF!six with ^"^"Q ^"^"L still six "

setlocal DisableDelayedExpansion
call :lfTest result original

setlocal EnableDelayedExpansion
echo The result with disabled delayed expansion is:
if !original! == !result! (echo OK) ELSE echo !result!

call :lfTest result original
echo The result with enabled delayed expansion is:
if !original! == !result! (echo OK) ELSE echo !result!
echo !lf!------------------!lf!original:
echo !original!

goto :eof

::::::::::::::::::::
:lfTest
setlocal
set "NotDelayedFlag=!"
echo(
if defined NotDelayedFlag (echo lfTest was called with Delayed Expansion DISABLED) else echo lfTest was called with Delayed Expansion ENABLED
setlocal EnableDelayedExpansion
set "var=!%~2!"

rem echo the input is:
rem echo !var!
echo(

rem ** Prepare for return
set "var=!var:%%=%%~A!"
set "var=!var:"=%%~B!"
for %%a in ("!LF!") do set "var=!var:%%~a=%%~L!"
for %%a in ("!CR!") do set "var=!var:%%~a=%%~C!"

rem ** It is neccessary to use two IF's else the %var% expansion doesn't work as expected
if not defined NotDelayedFlag set "var=!var:^=^^^^!"
if not defined NotDelayedFlag set "var=%var:!=^^^!%" !

set "replace=%% """ !CR!!CR!"
for %%L in ("!LF!") do (
   for /F "tokens=1,2,3" %%A in ("!replace!") DO (
     ENDLOCAL
     ENDLOCAL
     set "%~1=%var%" !
     @echo off
      goto :eof
   )
)

and the unfortunate results are:

Code: Select all


lfTest was called with Delayed Expansion DISABLED

The result with disabled delayed expansion is:
zero*? %~A%~B%~C%~L ::d--------:::
one&line
two with exclam!
three with "quotes&"&"
four with ^ | < > ( ) & ! "
five with CR
six with ""Q ""L still six

lfTest was called with Delayed Expansion ENABLED

The result with enabled delayed expansion is:
zero*? %~A%~B%~C%~L ::d--------:::
one&line
two with exclam!
three with "quotes&"&"
four with ^ | < > ( ) & ! "
five with CR
six with ""Q ""L still six

------------------
original:
zero*? %~A%~B%~C%~L :%A:%B:%C:%L:
one&line
two with exclam!
three with "quotes&"&"
four with ^ | < > ( ) & ! "
five with CR
six with ""Q ""L still six


Dave Benham

Re: Return ANY string across ENDLOCAL boundry - BUG!

Posted: 22 May 2011 20:29
by Ed Dyreen
I can't follow this bit of code :

Code: Select all

set "var=!var:%%=%%~A!"
set "var=!var:"=%%~B!"
for %%a in ("!LF!") do set "var=!var:%%~a=%%~L!"
for %%a in ("!CR!") do set "var=!var:%%~a=%%~C!"

Would you explain it for me :shock: I'm not at your level :?

Re: Return ANY string across ENDLOCAL boundry - BUG!

Posted: 23 May 2011 03:53
by jeb
Hi Ed,

Ed Dyreen wrote:I can't follow this bit of code :
Code:
set "var=!var:%%=%%~A!"
set "var=!var:"=%%~B!"
for %%a in ("!LF!") do set "var=!var:%%~a=%%~L!"
for %%a in ("!CR!") do set "var=!var:%%~a=%%~C!"

Would you explain it for me :shock: I'm not at your level :?


First, all (single) percents are replaces with %~A
All quotes are replaced with %~B
All <LF> are replaced with %~L
All <CR> are replaced with %~C
The <LF> and <CR> need a FOR-Loop-Var, to replace the character, as you can't place it directly between !..!

But why :?:
Because at the end, all these replacements are replaced to their original values,
this is neccessary, as they can't stay int the return statement without this trick.

Code: Select all

set "%~1=%var%" !


@dbenham
dbenham wrote:Noooooooo! :x :cry:

I just discovered a serious bug in the "magic" code that Jeb developed here


Ok there is a bug, but it's not a problem :wink:
Simply replace the %~A, %~B %~C (and perhaps %~L) with
%~2, %~3, %~4, %~9 and change the For-Loop to %%2 (%%1 could collide with the set %~1)

Code: Select all

rem ** Prepare for return
set "var=!var:%%=%%~2!"
set "var=!var:"=%%~3!"
for %%a in ("!LF!") do set "var=!var:%%~a=%%~9!"
for %%a in ("!CR!") do set "var=!var:%%~a=%%~4!"

rem ** It is neccessary to use two IF's else the %var% expansion doesn't work as expected
if not defined NotDelayedFlag set "var=!var:^=^^^^!"
if not defined NotDelayedFlag set "var=%var:!=^^^!%" !

set "replace=%% """ !CR!!CR!"
for %%9 in ("!LF!") do (
   for /F "tokens=1,2,3" %%2 in ("!replace!") DO (
     ENDLOCAL
     ENDLOCAL
     set "%~1=%var%" !
     @echo off
      goto :eof
   )
)


The problem seems to be the characters, they are interpreted as flags A=a=file attributes...

jeb

Re: Return ANY string across ENDLOCAL boundry - BUG!

Posted: 23 May 2011 05:36
by dbenham
:D :D :D Thank you Jeb :!:

I replaced the zero line characters ABCL with 2349 in the original test string, and everything works perfectly!

Jeb wrote:The problem seems to be the characters, they are interpreted as flags A=a=file attributes...

:shock: Where does that come from? Can you explain that a bit more?

Dave Benham

Re: Return ANY string across ENDLOCAL boundry - BUG!

Posted: 23 May 2011 05:49
by jeb
From the For /? help

Code: Select all

    %~I
    %~fI
    %~dI
    %~pI
    %~nI
    %~xI
    %~sI
    %~aI  - file attributes
    %~tI
    %~zI
    %~$PATH:I


The flags aren't case sensitive, and the flags can be combined.

So the %%~AAAAAB are all interpreted as file attributes for %%B.

jeb

Re: Return ANY string across ENDLOCAL boundry - BUG!

Posted: 23 May 2011 06:47
by dbenham
Hi Jeb

That doesn't make sense to me. Why would each of the following strings fail with the original code?
%%B
%%C
%%L

They don't have an A, so where do the attributes come in to the picture?

or why would %%A imply file attributes when there is no ~?

It seems like there must be some other mechanism.

Dave

Re: Return ANY string across ENDLOCAL boundry - BUG!

Posted: 23 May 2011 07:36
by jeb
dbenham wrote:That doesn't make sense to me. Why would each of the following strings fail with the original code?
%%B
%%C
%%L

They don't have an A, so where do the attributes come in to the picture?


Because "%B,%C,%L" are replaced to "%~AB,%~AC,%~AL" :wink:
Therefore they fail.

But "%~A,%~B" doesn't fail, as they are replaced to "%~A~A,%~A~B", and then it parsed only the %~A~ and this seems to work (perhaps the second ~ disables the flag mechanism).

jeb

Re: Return ANY string across ENDLOCAL boundry - BUG!

Posted: 23 May 2011 07:59
by dbenham
Ahhh, it all makes sense now. Armed with your explanation, I was able to change the algorithm to use J, K, L, M (M for <LF>), and that works as well. I'll probably stick with the numbers, but it helps to understand what is going on.

Thanks Jeb for making everything clear.

Dave