Tcl Source Code

View Ticket
Login
2024-02-28
08:01 Ticket [f91ab723f3] exec+wish fails to handle windows batch file with arguments and spaces status still Open with 4 other changes artifact: 5b793fcc9a user: oehhar
00:41 Ticket [f91ab723f3]: 3 changes artifact: 787360d42f user: anonymous
2024-02-27
20:23
Bug [f91ab723f3] : remove Windows 16 bit dead code check-in: ce2bd7a315 user: oehhar tags: core-8-6-branch
2024-02-26
14:09 Ticket [f91ab723f3] exec+wish fails to handle windows batch file with arguments and spaces status still Open with 4 other changes artifact: 7b8a65fc15 user: oehhar
13:56
Bug [f91ab723]: MS-WIN: remove dead code as TclWinGetPlatformId() constantly returns "VER_PLATFORM_W... Closed-Leaf check-in: 69a34921d1 user: oehhar tags: bug-f91ab723-dead-code-removal
13:34
bug [f91ab723f3]: possible solution to remove cmd.exe handling. Leaf check-in: bdca8a3971 user: oehhar tags: bug-f91ab723f3-no-cmd
13:29
Bug: [f91ab723f3]: possible solution to add quoting "" for cmd.exe /c argument Leaf check-in: 0530fbcf2d user: oehhar tags: bug-f91ab723f3-quoting
11:53 Ticket [f91ab723f3] exec+wish fails to handle windows batch file with arguments and spaces status still Open with 3 other changes artifact: 80835f0825 user: ralfixx
2024-02-23
10:01 Ticket [f91ab723f3]: 4 changes artifact: c025afff3d user: oehhar
09:57 Ticket [f91ab723f3]: 4 changes artifact: 18b701e85d user: oehhar
07:34 Ticket [f91ab723f3]: 3 changes artifact: 0b3d335c7f user: anonymous
2024-02-22
21:36 New ticket [f91ab723f3]. artifact: 59bc7fa31a user: anonymous

Ticket UUID: f91ab723f3dfbc0d2b720a70a1fa09eebfa1cde5
Title: exec+wish fails to handle windows batch file with arguments and spaces
Type: Bug Version: 8.6.13
Submitter: anonymous Created on: 2024-02-22 21:36:06
Subsystem: 16. Commands A-H Assigned To: nobody
Priority: 5 Medium Severity: Minor
Status: Open Last Modified: 2024-02-28 08:01:33
Resolution: None Closed By: nobody
    Closed on:
Description:
Reproduce the error with the following code run in wish console on Windows. Note that this works fine in tclsh:

#Test4:
set batFile {C:\batTest\bat test\test.bat}
set arg1 {arg with spaces}
set arg2 8
set command [list $batFile $arg1 $arg2]
exec {*}$command

Output:
'C:\batTest\bat' is not recognized as an internal or external command,
operable program or batch file. 


Where the bat file just outputs all arguments given:
@echo off
(for %%a in (%*) do (
    echo %%a
)) 


Proper output can be reproduced if the space if removed from the bat file path or the argument:

(bin) 54 % #Test1:
set batFile {C:\batTest\bat test\test.bat}
set arg1 {argNoSpaces}
set arg2 8
set command [list $batFile $arg1 $arg2]
exec {*}$command

Output:
argNoSpaces
8 


(bin) 64 % #Test3:
set batFile {C:\batTest\test.bat}
set arg1 {arg with spaces}
set arg2 8
set command [list $batFile $arg1 $arg2]
exec {*}$command

Output:
"arg with spaces"
8 

I have tested this in an older version of tcl (8.5.9) and it works fine.
User Comments: oehhar added on 2024-02-28 08:01:33:

Great. It is now removed in 8.6(.15) too. It would be great, if someone could test or comment the two bugfix branches.

Take care, Harald


anonymous added on 2024-02-28 00:41:10:
Note that the code line,

if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {

is no longer in 9.0b1, which was where I was getting the problem with cmd.exe /c and the quoting.

For 8.6.13, I couldn't tell exactly what command line was being generated, and so having just built 9.0b1 from source, I used that to instrument the code. I did see it take the path and build up the cmd.exe /c so that should be fixed regardless.

oehhar added on 2024-02-26 14:09:08:

Please find two possible branches:

Adding quotes

See branch bug-f91ab723f3-quoting starting with commit [0530fbcf2d].

A verbatim " is added around the argument.

This causes the following test failure:

==== winFCmd-3.10 TclpDeleteFile: path is readonly FAILED
==== Contents of test case:

    createfile tf1
    testchmod 0 tf1
    testfile rm tf1
    file exists tf1

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: EACCES
    while executing
"testfile rm tf1"
    ("uplevel" body line 4)
    invoked from within
"uplevel 1 $script"
---- errorCode: NONE
==== winFCmd-3.10 FAILED

Removing cmd.exe

See branch bug-f91ab723f3-no-cmd starting with commit [bdca8a3971].

The special handling with "cmd.exe /c" of batch files is totally removed.

Dead Code

While looking into this, I remarked, that the following stubs-function returns a constant value:

int
TclWinGetPlatformId(void)
{
    return VER_PLATFORM_WIN32_NT;
}

Thus, the else branch in win/tclWinPipe.c line 1092

    if (TclWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
        ....
   } else {
       if (HasConsole()) {
           createFlags = 0;
       } else {
           createFlags = DETACHED_PROCESS;
       }

       if (applType == APPL_DOS) {
           Tcl_SetObjResult(interp, Tcl_NewStringObj(
                   "DOS application process not supported on this platform",
                   -1));
           Tcl_SetErrorCode(interp, "TCL", "OPERATION", "EXEC", "DOS_APP",
                   NULL);
           goto end;
       }
   }
can never be reached.

This part is removed with commit [69a34921d1] in branch bug-f91ab723-dead-code-removal. Unfortunately, I always get a lot of test failures I am not able to explain (see below).

Test failures

I ran tests in all 3 branches plus core-8-6-branch. I always get the following test failures.

This is quite annoying, as the current 8.6.14rc1 has no test failures.

==== fCmd-9.3 file rename: comprehensive: file to new name FAILED
==== Contents of test case:

    createfile tf1
    createfile tf2
    testchmod 0o444 tf2
    file rename tf1 tf3
    file rename tf2 tf4
    list [lsort [glob tf*]] [file writable tf3] [file writable tf4]

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: error renaming "tf2" to "tf4": permission denied
    while executing
"file rename tf2 tf4"
    ("uplevel" body line 6)
    invoked from within
"uplevel 1 $script"
---- errorCode: POSIX EACCES {permission denied}
==== fCmd-9.3 FAILED



==== fCmd-9.5 file rename: comprehensive: file to self FAILED
==== Contents of test case:

    createfile tf1 tf1
    createfile tf2 tf2
    testchmod 0o444 tf2
    file rename -force tf1 tf1
    file rename -force tf2 tf2
    list [contents tf1] [contents tf2] [file writable tf1] [file writable tf2]

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: error renaming "tf2": permission denied
    while executing
"file rename -force tf2 tf2"
    ("uplevel" body line 6)
    invoked from within
"uplevel 1 $script"
---- errorCode: POSIX EACCES {permission denied}
==== fCmd-9.5 FAILED



==== fCmd-9.7 file rename: comprehensive: file to existing file FAILED
==== Contents of test case:

    createfile tf1
    createfile tf2
    createfile tfs1
    createfile tfs2
    createfile tfs3
    createfile tfs4
    createfile tfd1
    createfile tfd2
    createfile tfd3
    createfile tfd4
    testchmod 0o444 tfs3
    testchmod 0o444 tfs4
    testchmod 0o444 tfd2
    testchmod 0o444 tfd4
    set msg [list [catch {file rename tf1 tf2} msg] $msg]
    file rename -force tfs1 tfd1
    file rename -force tfs2 tfd2
    file rename -force tfs3 tfd3
    file rename -force tfs4 tfd4
    list [lsort [glob tf*]] $msg [file writable tfd1] [file writable tfd2] [file writable tfd3] [file writable tfd4]

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: error renaming "tfs2" to "tfd2": permission denied
    while executing
"file rename -force tfs2 tfd2"
    ("uplevel" body line 18)
    invoked from within
"uplevel 1 $script"
---- errorCode: POSIX EACCES {permission denied}
==== fCmd-9.7 FAILED

==== fCmd-9.10 file rename: comprehensive: file to new name and dir FAILED
==== Contents of test case:

    createfile tf1
    createfile tf2
    file mkdir td1
    testchmod 0o444 tf2
    file rename tf1 [file join td1 tf3]
    file rename tf2 [file join td1 tf4]
    list [catch {glob tf*}] [lsort [glob -directory td1 t*]]  [file writable [file join td1 tf3]] [file writable [file join td1 tf4]]

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: error renaming "tf2" to "td1/tf4": permission denied
    while executing
"file rename tf2 [file join td1 tf4]"
    ("uplevel" body line 7)
    invoked from within
"uplevel 1 $script"
---- errorCode: POSIX EACCES {permission denied}
==== fCmd-9.10 FAILED

==== winFCmd-3.10 TclpDeleteFile: path is readonly FAILED
==== Contents of test case:

    createfile tf1
    testchmod 0 tf1
    testfile rm tf1
    file exists tf1

---- Test generated error; Return code was: 1
---- Return code should have been one of: 0 2
---- errorInfo: EACCES
    while executing
"testfile rm tf1"
    ("uplevel" body line 4)
    invoked from within
"uplevel 1 $script"
---- errorCode: NONE
==== winFCmd-3.10 FAILED

Questions

May I ask for an opinion what is wise? Those are the actions of a person with no experience here. May someone double-check the removal of the dead code?

Thank you all and take care, Harald


ralfixx added on 2024-02-26 11:53:12:

This describes the quoting required in the "Remarks" section:

https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/cmd


oehhar added on 2024-02-23 10:01:52:

I have not traced it. But it may happen, that the name of the batch file is replaced by the 8 character short name in ApplicationType().

So much work to do, renovate this very old code may be a challenge...

Take care, Harald


oehhar added on 2024-02-23 09:57:40:

The upper code is in file win\tclWinPipe.c line 1126.

The comment above the code

	} else if (applType == APPL_DOS) {
	    /*
	     * Under NT, 16-bit DOS applications will not run unless they can
	     * be attached to a console. If we are running without a console,
	     * run the 16-bit program as an normal process inside of a hidden
	     * console application, and then run that hidden console as a
	     * detached process.
	     */
only speaks about 16 bit DOS applications.

So, we have two possible solutions:

  • Add double quotes around the argument cmdLine
  • Totally remove this special handling for "APPL_DOS" and use "createFlags = DETACHED_PROCESS;" for this type too.

We probably need tests in tk for this, so it is a double-project bug.

Any comment appreciated, Harald


anonymous added on 2024-02-23 07:34:22:
When it's a batch job, i.e. something .bat, wish generates:

   cmd.exe /c "pa th\to.bat" "pa th/to.txt" 8

and that fails. If you enter that into a windows cmd window, it also fails *because* it needs to have another set of quotes around the whole string following the /c.


Changing that in a cmd window to:

   cmd.exe /c ""pa th\to.bat" "pa th/to.txt" 8"

and it works.

When the path to the batch file has no spaces, it does not quote that, *and* will fail if you do add quotes anyway. Now that's really weird, but in fact I saw something about that in the documentation. However, adding quotes around the entire string will also make that case work.



Now, if you use tclsh and exec, it ends up being just this:

   "pa th\to.bat" "pa th/to.txt" 8

and that works from tclsh and also in a cmd window.

It's because the call to HasConsole() returns true in tclsh so that the command line is not prefixed with cmd.exe /c



Here's the key code:

    if (HasConsole()) {
        createFlags = 0;
    } else if (applType == APPL_DOS) {
    /*
     * Under NT, 16-bit DOS applications will not run unless they can
     * be attached to a console. If we are running without a console,
     * run the 16-bit program as an normal process inside of a hidden
     * console application, and then run that hidden console as a
     * detached process.
     */

      startInfo.wShowWindow = SW_HIDE;
      startInfo.dwFlags |= STARTF_USESHOWWINDOW;
      createFlags = CREATE_NEW_CONSOLE;
      TclDStringAppendLiteral(&cmdLine, "cmd.exe /c");
    } else {
       createFlags = DETACHED_PROCESS;
    }