Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Can Tcl do isatty() on windows?

148 views
Skip to first unread message

Dave

unread,
Mar 15, 2017, 1:11:50 AM3/15/17
to
I found this info: http://wiki.tcl.tk/11510

Towards the bottom:

DKF: Going through the set to produce a smaller one:
6. A channel refers to a tty if (and only if) it has the serial options.

I see options on 'open' for serial I/O
How can I query those options? fconfigure throws an error

File t.tcl:
puts stderr [fconfigure stdout -ttystatus]

Using a cmd prompt (win7 x64 tcl 8.6.6):

C:\Users\imdave\>tclsh t.tcl
bad option "-ttystatus": should be one of -blocking, -buffering,
-buffersize, -e
ncoding, -eofchar, or -translation
while executing
"fconfigure stdout -ttystatus"
invoked from within
"puts stderr [fconfigure stdout -ttystatus]"
(file "t.tcl" line 1)

I also tried 'chan configure stdout -ttystatus' and got the same result.

How can one tell if a channelID has "serial" options?
Is that info outdated? If so, is there a work-around?

(I looked thru TclX and did not find isatty there either... but maybe I
did not look long enough?)

--
computerjock AT mail DOT com

Dave

unread,
Mar 15, 2017, 2:07:35 AM3/15/17
to
After thinking about this, I guess I see what's happening.

On unix, ones terminal is either a real tty or a ptty such as when
running xterm under X11. On windows, I don't think one can login over a
serial port. Therefore whatever interface is attached to stdin/out/err
in a cmd window is not going to be a tty.

So, my question boils down to this: is there a way for Tcl to determine
if stdout is a pipe or the windows terminal?

Rich

unread,
Mar 15, 2017, 5:47:55 AM3/15/17
to
Dave <nor...@nohost.com> wrote:
> How can one tell if a channelID has "serial" options?

Using the 'catch' command.

> Is that info outdated? If so, is there a work-around?

No, worked just fine here on Linux.

stefan

unread,
Mar 15, 2017, 8:34:04 AM3/15/17
to
> How can one tell if a channelID has "serial" options?

Why not simply:

set opts [dict create {*}[chan configure $channelID]]
if {"-ttystatus" in [dict keys $opts]} {
# channelID is all serial ...
} else {
# ... or not
}

Stefan

Andreas Leitgeb

unread,
Mar 15, 2017, 11:37:36 AM3/15/17
to
stefan <stefan....@wu.ac.at> wrote:
>> How can one tell if a channelID has "serial" options?
> set opts [dict create {*}[chan configure $channelID]]
> if {"-ttystatus" in [dict keys $opts]} {

As a random by-reader, am I now supposed to thank you for
the daily cringe?

if {[dict exists $opts "-ttystatus"]} {

Dave

unread,
Mar 15, 2017, 12:47:15 PM3/15/17
to
Thanks for the replies.

As I mentioned, I believe I understand why there are no Tcl serial
options for stdout on >windows<.

However, does anyone know if it is possible on >windows< for Tcl to
determine if stdout is a pipe/file or the cmd.exe|mintty|tcc "terminal"
window?

I've even tried compiling the example from the microsoft C library docs:
_isatty( _fileno(stdout) )
but it always returns 0 -- although I'm using msys2 & gcc and not the
microsoft sdk

stefan

unread,
Mar 15, 2017, 4:47:22 PM3/15/17
to

> if {[dict exists $opts "-ttystatus"]} {

touché, you owe me one cringe now :)

stefan

unread,
Mar 15, 2017, 6:03:47 PM3/15/17
to

> However, does anyone know if it is possible on >windows< for Tcl to
> determine if stdout is a pipe/file or the cmd.exe|mintty|tcc "terminal"
> window?

internally, Tcl seems to use GetFileType for this purpose: FILE_TYPE_PIPE vs. FILE_TYPE_CHAR.

Stefan

Dave

unread,
Mar 15, 2017, 6:48:03 PM3/15/17
to
A grep for GetFileType found only two files where that is used:
win/tclWinChan.c
win/tclWinFile.c

In the first case, it appears to be used for 'open', in the second for
'file stat'. Both of those Tcl commands take a file path as argument,
not an opened channelID.

Therefore I guess the answer to my question is no, a Tcl script cannot
tell if stdout is a pipe/file or the windows console.

stefan

unread,
Mar 16, 2017, 4:27:40 AM3/16/17
to
> In the first case, it appears to be used for 'open', in the second for
> 'file stat'. Both of those Tcl commands take a file path as argument,
> not an opened channelID.

correct.

>
> Therefore I guess the answer to my question is no, a Tcl script cannot
> tell if stdout is a pipe/file or the windows console.
>

I am not proficient enough in this area of Tcl, but I wonder why there is no "chan type|stat" or similar to capture and to expose these details at the script level. there is also "file type" revealing character (serial) devices ("characterSpecial"), so why not for chans in general?

stefan

unread,
Mar 16, 2017, 11:39:10 AM3/16/17
to
Dave,

twapi may close this gap for you:

http://twapi.sourceforge.net/v4.1/console.html#get_console_handle

if {[get_console_handle stdout] ne [get_standard_handle stdout]} {
# redirected
} else {
# goes to console
}

(this is just from skimming the docs, untested, and I might have got it completely wrong.)

Stefan

Dave

unread,
Mar 16, 2017, 12:46:03 PM3/16/17
to
Unfortunately not; running the script in all "terminal" programs
(tcc,cmd,mintty) that I have produces the same result:

c:\users\imdave>tclsh isatty.tcl
redirected: 27 HANDLE ne 11 HANDLE

c:\users\imdave>tclsh isatty.tcl | cat
console: 27 HANDLE ne 11 HANDLE

Andreas Leitgeb

unread,
Mar 16, 2017, 1:12:12 PM3/16/17
to
stefan <stefan....@wu.ac.at> wrote:
>> if {[dict exists $opts "-ttystatus"]} {
> touché, you owe me one cringe now :)

May I return a "3rd party" one?

% tcl::unsupported::representation [chan conf stdin]
value is a pure string [...]

Btw., the "dict create {*}..." was redundant, too.

Brad Lanam

unread,
Mar 16, 2017, 1:19:16 PM3/16/17
to
I think that the code outlined in the answer here:
http://stackoverflow.com/questions/3648711/detect-nul-file-descriptor-isatty-is-bogus

will solve the problem. You will need twapi.

Note that the answer's first if statement is for input and the second if statement is for output.

Brad Lanam

unread,
Mar 16, 2017, 1:29:28 PM3/16/17
to
See what this returns, though I'm wondering if it will need a catch around it. I don't have twapi installed anywhere right now, so can't test it. I don't know which of the options to pass would be best.

puts "[get_console_screen_buffer_info [get_standard_handle stdout] -all]"

Dave

unread,
Mar 16, 2017, 2:21:14 PM3/16/17
to
On 3/16/2017 12:29 PM, Brad Lanam wrote:
> On Thursday, March 16, 2017 at 10:19:16 AM UTC-7, Brad Lanam wrote:
>>
>> I think that the code outlined in the answer here:
>> http://stackoverflow.com/questions/3648711/detect-nul-file-descriptor-isatty-is-bogus
>>
>> will solve the problem. You will need twapi.
>>
>> Note that the answer's first if statement is for input and the second if statement is for output.
>
I had run across that exact discussion (GetConsoleMode and
GetConsoleScreenBufferInfo) and compiled a quick test. It always returns
"not console" when running under either of tcc|cmd|mintty

> See what this returns, though I'm wondering if it will need a catch around it. I don't have twapi installed anywhere right now, so can't test it. I don't know which of the options to pass would be best.
>
> puts "[get_console_screen_buffer_info [get_standard_handle stdout] -all]"
>
tcc/cmd give the following:

C:\Users\imdave>tclsh isatty.tcl
Q:-size {80 300} -cursorpos {0 24} -maxwindowsize {80 85}
-windowlocation {0 0 7
9 24} -windowpos {0 0} -windowsize {80 25} -textattr {-fggray 1}
pipe/file: 15 HANDLE ne 11 HANDLE

mintty (msys2) throws an error:

$ /c/Program\ Files/Tcl/bin/tclsh isatty.tcl
The handle is invalid.
while executing
"GetConsoleScreenBufferInfo $conh"
(procedure "twapi::_get_console_screen_buffer_info" line 13)
invoked from within
"twapi::_get_console_screen_buffer_info {528 HANDLE} -all"
("uplevel" body line 1)
invoked from within
"uplevel 1 [list $proc] $args"

-----

This is starting to become a windows API question...

I cobbled together (using win/tclWinFile.c as an example):

hnd = GetStdHandle ( STD_OUTPUT_HANDLE );
type = GetFileType( hnd );
switch( type ) {
case FILE_TYPE_CHAR:
fprintf( stderr, "FILE_TYPE_CHAR\n" );
:

That >does< work for tcc/cmd but fails for mintty (which always returns
FILE_TYPE_PIPE

I did run across bug reports/discussions from late 2016 that were
talking about problems with _istty() in mingw/msys/cygwin(maybe). The
discussions got too deep and/or detailed for me to extract a correct method.

I also think I read that mintty uses pipes for console output -- which
is exactly what the above code reports.

So this boils down to the internals of the "terminal" program which I
doubt tcl/twapi/etc. will be able to really distinguish.

Interestingly, msys2's bash's test -t >does< work properly with
mintty|cmd|tcc. I guess I'll have to try to find the source for either
bash or test to see how it's done.

Brad Lanam

unread,
Mar 16, 2017, 2:57:53 PM3/16/17
to
On Thursday, March 16, 2017 at 11:21:14 AM UTC-7, Dave wrote:
> I also think I read that mintty uses pipes for console output -- which
> is exactly what the above code reports.

That's a problem. I was sort of hoping that the get_console_buffer_info would return an error for any FILE_TYPE_PIPE, but if mintty always returns a pipe type, I don't know what to do.

> Interestingly, msys2's bash's test -t >does< work properly with
> mintty|cmd|tcc. I guess I'll have to try to find the source for either
> bash or test to see how it's done.

Sounds like a good plan.
We'll have to put the solution up on the wiki since as it seems a bit obscure.

Göran Hanke

unread,
Mar 21, 2017, 11:05:06 AM3/21/17
to
What about "seek stdout 0"?

echo seek stdout 0 | tclsh
error during seek on "stdout": invalid argument

echo seek stdout 0 | tclsh > x
(no error)

Greetings
Göran


Dave

unread,
Mar 21, 2017, 12:59:24 PM3/21/17
to
It turns out that msys2's bash is linked with msys-2.0.dll which is
similar to cygwin's cygwin1.dll. That is why bash -t worked.

I had pretty much given up on this since it was not looking probable to
find a way without knowing details of the particular terminal program
what one is using. Then I tested Mr. Henke's suggestion.

Dave

unread,
Mar 21, 2017, 1:03:45 PM3/21/17
to
Call me a monkey's uncle! This works perfectly with each of the terminal
programs I have installed (tcc, cmd and mintty for msys2).

Thank you very much for the solution.

Dave

unread,
Mar 21, 2017, 1:19:01 PM3/21/17
to
Phoey! I spoke too soon.

$ echo seek stdout 0 | /c/Progra~1/Tcl/bin/tclsh
error during seek on "stdout": invalid argument

$ echo seek stdout 0 | /c/Progra~1/Tcl/bin/tclsh >xx

$ echo seek stdout 0 | /c/Progra~1/Tcl/bin/tclsh | cat >xx
error during seek on "stdout": invalid argument

It does not catch the case when stdout is a real pipe.

That is my real goal:
$ tclsh cmd.tcl
Print informative messages

$ tclsh cmd.tcl | bash
(Informative messages are now bash commands to actually do something).

matthe...@gmail.com

unread,
Mar 21, 2017, 2:32:31 PM3/21/17
to
You can use tclx?

How about this?

fstat stdout tty

returns 0 if you're redirecting.

Robert Heller

unread,
Mar 21, 2017, 2:57:56 PM3/21/17
to
seek fails when stdout is not a *file* (eg tty OR pipe)

>
> That is my real goal:
> $ tclsh cmd.tcl
> Print informative messages
>
> $ tclsh cmd.tcl | bash
> (Informative messages are now bash commands to actually do something).
>

The *standard* solution in the UNIX world:

send the informative messages to stderr and the bash commands stdout.

Optionally, don't bother with the informative messages at all or don't print
them if the -q option is specificed or only print them if the -v option is
specified. If the user runs the program without piping stdout it to bash, the
user will get bash commands displayed on the screen. Too bad if the user does
not know what they are. One other *option* is to prefix the informative
messages with a hash sign (#). That would make them comments as far as bash
is concerned (bash will ignore them).

This *might* be a little user unfriendly, but maybe it is the best you can do.

>
>
>

--
Robert Heller -- 978-544-6933
Deepwoods Software -- Custom Software Services
http://www.deepsoft.com/ -- Linux Administration Services
hel...@deepsoft.com -- Webhosting Services

Dave

unread,
Mar 21, 2017, 3:01:43 PM3/21/17
to
On 3/21/2017 1:32 PM, matthe...@gmail.com wrote:
> You can use tclx?
>
> How about this?
>
> fstat stdout tty
>
> returns 0 if you're redirecting.

No dice, running under msys2's mintty

$ echo "package require Tclx; puts rc=[fstat stdout tty]" |
/c/Progra~1/Tcl/bin/tclsh
rc=0

$ echo "package require Tclx; puts rc=[fstat stdout tty]" |
/c/Progra~1/Tcl/bin/tclsh >xx

$ cat xx
rc=0

$ echo "package require Tclx; puts rc=[fstat stdout tty]" |
/c/Progra~1/Tcl/bin/tclsh | cat >xx

$ cat xx
rc=0

Note that this is a problem because cygwin/msys use a windows named pipe
in the mintty terminal program. This is different than tcc/cmd which use
whatever is the native windows "terminal" or "console" interface.

I think the problem I've been attempting so solve is inherently
impossible. I found a reference to a proposed patch to isatty for msys2
that addressed this issue. However, the code involved looking for a
specific named pipe in some sort of secret windows \\device directory --
which, by the way, had to be changed from a speific cygwin name to a
specific msys name. If one has to embed checks for a specific "terminal"
program, then it will never be possible to support >all< possible
terminal programs in a generic manner.

Dave

unread,
Mar 21, 2017, 4:10:24 PM3/21/17
to
Boy, this whole thread is, in retrospect, rather amusing. I know I
started it -- the reason being that I had a little bash script that was
taking a couple of minutes to run and since I had a bit of time I
thought I'd convert it to Tcl. It's for my own use so it's not that big
a deal, but I was curious if it was possible to duplicate bash.

The bash script had this fragment:

if tty -s <&1
then
isatty=true
else
isatty=false
fi

the purpose of which is to check of stdout is a pipe or the mintty
terminal. It works for tty so I thought there might be a way to
determine the same with Tcl. As it turns out, tty is linked with
msys-2.0.dll which is why tty "works".

I've spent an inordinate amount of time on it but at least I did learn
something I hope. It certainly generated a lot of interest it seems.

Thanks for all the suggestions. I've incorporated a variation and my Tcl
script runs 10 times faster than my bash script.

Donal K. Fellows

unread,
Mar 25, 2017, 2:52:22 PM3/25/17
to
On 16/03/2017 08:27, stefan wrote:
> I wonder why there is no "chan type|stat"

The biggest impediment is possibly that the channel types that Tcl
really knows about aren't necessarily the ones that you're thinking
about. Yes, you could get the contents of the typeName field of the
relevant Tcl_ChannelType structure for the channel (which would be good
on one level, as it would let you know about scripted channels, and
could probably be extended to the stack of transformations on top too)
but that wouldn't necessarily help; there are distinctions that Tcl
ignores (e.g., named pipes are thought to be just files).

Do people think this is something that ought to be improved upon?

Donal.
--
Donal Fellows — Tcl user, Tcl maintainer, TIP editor.
0 new messages