Command line parsing differences between cmd.exe and TCC

Nov 6, 2014
15
0
#1
When using TCC (v23.00.25 x64 on Win7 SP1) I find that the Subversion (svn) diff command doesn't get parsed the same way as cmd.exe does it.

In order for svn to integrate with a GUI diff utility, you write a script that parses a bunch of command line options then have the script turn around and pass the options in the order and with the switches appropriate for the GUI diff program (Beyond Compare in my case).

I found that the script that was working in cmd.exe broke when I tried it in TCC. Here's a version of the script that simply dumps the arguments passed to it:

=============
@echo off
echo all args: :%*:
echo arg1: :%1:
echo arg2: :%2:
echo arg3: :%3:
echo arg4: :%4:
echo arg5: :%5:
echo arg6: :%6:
echo arg7: :%7:
echo arg8: :%8:
=============

Here's what cmd.exe displays when svn passes the diff arguments to it:

=============
all args: :-u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0:
arg1: :-u:
arg2: :-L:
arg3: :"CalcsMgr/CalcsMgr.c (revision 38085)":
arg4: :-L:
arg5: :"CalcsMgr/CalcsMgr.c (working copy)":
arg6: :D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base:
arg7: :C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0:
arg8: ::
=============

And here's what I get when the script is run in TCC:

=============
all args: :*:
arg1: :-u:
arg2: :-L:
arg3: :"CalcsMgr/CalcsMgr.c:
arg4: :(revision:
arg5: :38085)":
arg6: :-L:
arg7: :"CalcsMgr/CalcsMgr.c:
arg8: :(working:
=============

So my questions are:

- is this a TCC bug?
- if not, is there a way I can have one batch file that parses the command line the same way in both cmd.exe and in TCC?
- why doesn't TCC expand the %* construct when it is immediately followed by a ':'? It only does this when it is immediately followed by a colon - and I often use a colon as a delimiter when debugging command line arguments because I find that using quotes makes it difficult to spot how quotes are actually being used.


Thanks
 
Nov 6, 2014
15
0
#2
One more bit of information I just found: if I run the script from the TCC command line the parsting works:

============
[D:\devtrees\CPA_ExtRAM\trunk]bc4svn -u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_ExtRAM\trunk
\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0
all args: :*:
arg1: :-u:
arg2: :-L:
arg3: :"CalcsMgr/CalcsMgr.c (revision 38085)":
arg4: :-L:
arg5: :"CalcsMgr/CalcsMgr.c (working copy)":
arg6: :D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base:
arg7: :C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0:
arg8: ::
============

Update (again): I added a "pause" command to the end of the script so I could see exactly how svn.exe is invoking the batch file with the "Process Explorer" tool. It turns out that svn is wrapping the whole command line in double-quotes (and escaping parens and backslashes).

Invoked from cmd.exe:
===============
C:\Windows\system32\cmd.exe /C ""C:\util\bc4svn.bat" -u -L ^"CalcsMgr/CalcsMgr.c ^(revision 38085^)^" -L ^"CalcsMgr/CalcsMgr.c ^(working copy^)^" D:^\devtrees^\CPA_ExtRAM^\trunk^\.svn^\pristine^\19^\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:^\Users^\burrm^\AppData^\Local^\Temp^\svn-51900A6C"
===============


Invoked from TCC:
============
"C:\Program Files\JPSoft\TCMD23\TCC.EXE" /C ""C:\util\bc4svn.bat" -u -L ^"CalcsMgr/CalcsMgr.c ^(revision 38085^)^" -L ^"CalcsMgr/CalcsMgr.c ^(working copy^)^" D:^\devtrees^\CPA_ExtRAM^\trunk^\.svn^\pristine^\19^\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:^\Users^\burrm^\AppData^\Local^\Temp^\svn-48439300"
============



TCC does what I'd like it to do if the 'outer' double quotes are removed:

============
[D:\devtrees\CPA_ExtRAM\trunk]"C:\Program Files\JPSoft\TCMD23\TCC.EXE" /C "C:\util\bc4svn.bat" -u -L ^"CalcsMgr/CalcsMgr.c ^(revision 38085^)^" -L ^"CalcsMgr/CalcsMgr.c ^(working copy^)^" D:^\devtrees^\CPA_ExtRAM^\trunk^\.svn^\pristine^\19^\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:^\Users^\burrm^\AppData^\Local^\Temp^\svn-48439300
all args: :*:
arg1: :-u:
arg2: :-L:
arg3: :"CalcsMgr/CalcsMgr.c (revision 38085)":
arg4: :-L:
arg5: :"CalcsMgr/CalcsMgr.c (working copy)":
arg6: :D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base:
arg7: :C:\Users\burrm\AppData\Local\Temp\svn-48439300:
arg8: ::
============

I think that wrapping an entire command in double-quotes even when arguments are double-quoted is not an uncommon behavior, taking advantage of on of the hundreds of quirks/bugs in cmd.exe (but I don't really know that for sure - I don't know how rconn stays sane trying to keep TCC backwards compatible with cmd.exe).

Is there some reasonable way I can get TCC to handle this in a cmd.exe compatible way?
 
Last edited:
Nov 6, 2014
15
0
#4
1. Use code blocks.
Are you talking about the Code::Blocks IDE? I'm not using an IDE at all, so I'm not sure what that would have to do with anything.

2. Finally explain, what you invoke and how.
I have posted the simple batch file that parses the command line. I have also posted information about how Subversion invokes the script which seems to be the root of the problem: the whole command line is enclosed in double-quotes while it contains items that are also quoted using double quotes.
 
#5
the whole command line is enclosed in double-quotes while it contains items that are also quoted using double quotes.
That's not a good idea. Without :: TCC's %* works better. Besides that, I see that TCC and CMD work the same if there are or are not an outer set of quotes. (What appears next is a code block.)
Code:
v:\> type allargs.bat
@echo off
 echo all args: %*
 echo arg1: :%1:
 echo arg2: :%2:
 echo arg3: :%3:
 echo arg4: :%4:
 echo arg5: :%5:
 echo arg6: :%6:
 echo arg7: :%7:
 echo arg8: :%8:
v:\> allargs.bat -u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_Ex
tRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6
D8A0
all args: -u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0
arg1: :-u:
arg2: :-L:
arg3: :"CalcsMgr/CalcsMgr.c (revision 38085)":
arg4: :-L:
arg5: :"CalcsMgr/CalcsMgr.c (working copy)":
arg6: :D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base:
arg7: :C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0:
arg8: ::

v:\> cmd /c allargs.bat :-u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtree
s\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\
svn-4DC6D8A0
all args: :-u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0
arg1: ::-u:
arg2: :-L:
arg3: :"CalcsMgr/CalcsMgr.c (revision 38085)":
arg4: :-L:
arg5: :"CalcsMgr/CalcsMgr.c (working copy)":
arg6: :D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base:
arg7: :C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0:
arg8: ::
Here's another code block showing what happens when outer quotes are added.
Code:
v:\> allargs.bat "-u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_E
xtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC
6D8A0"
all args: "-u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0"
arg1: :"-u -L "CalcsMgr/CalcsMgr.c:
arg2: :(revision:
arg3: :38085)" -L "CalcsMgr/CalcsMgr.c:
arg4: :(working:
arg5: :copy)" D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0":
arg6: ::
arg7: ::
arg8: ::

v:\> cmd /c allargs.bat "-u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtree
s\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\
svn-4DC6D8A0"
all args: "-u -L "CalcsMgr/CalcsMgr.c (revision 38085)" -L "CalcsMgr/CalcsMgr.c (working copy)" D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0"
arg1: :"-u -L "CalcsMgr/CalcsMgr.c:
arg2: :(revision:
arg3: :38085)" -L "CalcsMgr/CalcsMgr.c:
arg4: :(working:
arg5: :copy)" D:\devtrees\CPA_ExtRAM\trunk\.svn\pristine\19\19ec3da79b2b6ceb9eced5aee49da6d1efe261cb.svn-base C:\Users\burrm\AppData\Local\Temp\svn-4DC6D8A0":
arg6: ::
arg7: ::
arg8: ::
 
Nov 6, 2014
15
0
#6
The problem seems to boil down to the '^' quoting escaping of the double-quotes around arguments. The following examples use Vincent's "allargs.bat" script:

TCC:
Code:
[C:\temp]ver

TCC  23.00.25 x64   Windows 7 [Version 6.1.7601]

[C:\temp]allargs.bat ^"1 2^" 3
all args: "1 2" 3
arg1: :"1:
arg2: :2":
arg3: :3:
arg4: ::
arg5: ::
arg6: ::
arg7: ::
arg8: ::
cmd.exe:
Code:
C:\temp>ver

Microsoft Windows [Version 6.1.7601]

C:\temp>allargs.bat ^"1 2^" 3
all args: "1 2" 3
arg1: :"1 2":
arg2: :3:
arg3: ::
arg4: ::
arg5: ::
arg6: ::
arg7: ::
arg8: ::
 
Last edited:
Nov 6, 2014
15
0
#7
I think I have a reasonable workaround:

Code:
@echo off
set ALLARGS=%*
call :doit %ALLARGS%
goto :eof
 
:doit
echo arg1: :%1:
echo arg2: :%2:
echo arg3: :%3:
echo arg4: :%4:
echo arg5: :%5:
echo arg6: :%6:
echo arg7: :%7:
echo arg8: :%8:
goto :eof


TCC (I get same results with cmd.exe):

Code:
[C:\temp]allargs.bat ^"1 2^" 3
arg1: :"1 2":
arg2: :3:
arg3: ::
arg4: ::
arg5: ::
arg6: ::
arg7: ::
arg8: ::

Note that if instead of passing %ALLARGS% to :doit I pass %*, TCC still parses the arguments to :doit in a way that splits the "1 and the 2" tokens. I don't know why that would be, but I'm not sure I care all that much.

To be clear, I think that TCC is behaving entirely reasonably - escaping the quotes should be an indication that the quotes be treated as normal characters not as quotes (that's what bash would do). However, cmd.exe doesn't do that and Subversion seems to rely on that cmd.exe bug. And that puts TCC in a bad position.

Anyway, I'm perfectly happy with the workaround. So rconn can do whatever he likes with this information (including ignoring it).

Thanks for helping!