Tcl flush command returns error - tcl

I've gotten this error from Tcl flush command:
error flushing "stdout": I/O error
Any ideas why it can happen? Also, the man page doesn't say anything about flush returning errors. Can the same error come from puts?
And ultimately: what do do about it?
Thank you.

By default Tcl uses line buffering on stdout (more on this later). This means whatever you puts there gets buffered and is only output when the newline is seen in the buffer or when you flush the channel. So, yes, you can get the same error from puts directly, if a call to it is done on an unbuffered channel or if it managed to hit that "buffer full" condition so that the underlying medium is really accessed.
As to "I/O error", we do really need more details. Supposedly the stdout of your program has been redirected (or reopened), and the underlying media is inaccessible for some reason.
You could try to infer more details about the cause by inspecting the POSIX errno of the actual failing syscall — it's accessible via the global errorCode variable after a Tcl command involving such a syscall signalized error condition. So you could go like this:
set rc [catch {flush stdout} err]
if {$rc != 0} {
global errorCode
set fd [open error.log w]
puts $fd $err\n\n$errorCode
close $fd
}
(since Tcl 8.5 you can directly ask catch to return all the relevant info instead of inspecting magic variables, see the manual).
Providing more details on how you run your program or whether you reopen stdout is strongly suggested.
A note on buffering. Output buffering can be controlled using fconfigure (or chan configure since 8.5 onwards) on a channel by manipulating its -buffering option. Usually it's set to line on stdout but it can be set to full or none. When set to full, the -buffersize option can be used to control the buffer size explicitly.

Related

How is a channel's output-buffer content deleted without writing it to the channel?

I don't know much about PHP or Tcl; but I am trying to learn both concurrently.
In PHP, I read that every script should start with ob_start and, therefore, have been using the following.
ob_start(NULL, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
echo header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_end_clean();
In Tcl channels, I see that the options of -buffering full and -buffersize take care of ob_start() and chan flush is analogous to ob_end_flush() and chan pending output returns the number of bytes written to the output buffer but not yet written out.
I've been looking at my two texts on Tcl and the Tcl manual web page for channels and I can't find a method of just clearing the channel output buffer without writing it.
If data is being written to a channel set to -buffering full and an error is caught/trapped is it possible to empty the buffer and not write it to the channel?
It though perhaps that could use chan seek to set the position back to start similar to setting a pointer back to the beginning of a segment of RAM but the pipe example doesn't appear to create a channel that supports seeking.
lassign [chan pipe] rchan wchan
chan configure $rchan -buffering line -blocking 0 -translation crlf
chan configure $wchan -buffering full -blocking 0 -translation crlf
chan puts $wchan "This is the full messsage which shall attempt to truncate."
chan puts stdout "wchan pending: [chan pending output $wchan]"
chan puts stdout "wchan tell: [chan tell $wchan]"
# => -1 Thus, channel does not support seeking.
#chan seek $wchan 5 start
# => Errors invalid seek
chan flush $wchan
chan puts stdout [chan gets $rchan]
Thank you.
Sounds like you want to only output text written to a channel if no error happens in the middle of writing?
One way is to use a variable channel from tcllib; everything written to the channel is stored in a variable, which can then be written out to the real target on successful completion of whatever you're trying to do.
Example:
#!/usr/bin/env tclsh
package require tcl::chan::variable
proc main {} {
variable output
set output ""
set outputchan [::tcl::chan::variable output]
try {
puts $outputchan "Some text"
error "This is an error"
# Won't get written if an error is raised
chan flush $outputchan
puts -nonewline $output
} on error {errMsg errOptions} {
# Report error if you want
} finally {
chan close $outputchan
}
}
main
I don't think Tcl provides the functionality you are looking for. It's assumed that if you send something to a channel then it should always be written out.

How to control reading of bits in XOR data frames?

I'm trying to learn to read the XOR data frames used in web sockets in Tcl.
I was reading the HTTP requests using:
chan configure $sock -buffering line -blocking 0 -encoding iso8859-1 -translation crlf
chan event $sock readable [list ReadLine $sock]
[catch {chan gets $sock line} len]
Now after the socket is opened, chan configure $sock -translation binary to read the component bits of the XOR frame, but I'm confused about the -buffering and -buffersize
and I changed the chan event to not get a full line but chan read numChars; but the readable event seems to fire for every character or again after each character is read.
Should the various segments of bits be read directly from the channel or should larger pieces be read from the channel into variables and then the bits separated from those pieces?
What is the proper channel configuration in order to read the bits in a controlled manner?
Also, it reads here https://www.tcl.tk/man/tcl/TclCmd/chan.html#M35 that in non-blocking mode chan read may not read all the requested characters. What is to be done? Count them and read again until get them all?
Thank you.
The -buffering and -buffersize are options used to manage the output side of the channel, i.e., when you write data to the socket with puts (or chan puts; it's an alternate name for the same thing). They're not used for input.
When you have the channel in binary mode, the characters you read and write correspond one-to-one with the bytes. You probably shouldn't use gets (chan gets) on binary data; read (chan read) is more likely to be appropriate. (For writing, the -nonewline option to puts is virtually mandatory.)
When you read a non-blocking channel with a number of characters/bytes requested, you can get up to that amount of data. If the request can be satisfied with what is in the read buffer, that is used and no request to the underlying file descriptor is done. If the request can be partially satisfied with buffered data, that's used first and only then is a request done for more data; if that request produces more data than needed, it is stored in the buffer (you can see how much with chan pending, but that's not normally important for binary channels). However, if that one non-blocking request does not deliver enough data to give you what you asked for, read returns anyway: you have a short read. Short reads don't necessarily mean that you're at the end of the channel, use chan eof and chan blocked to find out more (especially if you get the special case of a zero-length read). Being blocked might also not mean that you're at the end of a message within a higher-level protocol; more data may be coming, but it hasn't reached the OS yet (which is why you need a framing protocol on top of TCP; websockets are one such framing protocol).
Counting the data is easy: string length.
tl;dr: In non-blocking mode, the maximum amount that read of a binary channel can return is whatever is currently in the input buffers plus whatever is obtained from one non-blocking read of the file descriptor. In blocking mode, read will wait until the requested amount of data is available or definitely not available (end-of-file), performing multiple reads of the file descriptor if necessary.

Tcl - Reading characters from stdin without having to press enter in Tcl

I would like to read from stdin on a per character basis without the stdin being flushed. I could not find how to do that after tweaking for hours. Tcl always seems to wait for the channel to be flushed even in fconfigure stdin -blocking 0 -buffering none. Is that true? How would I otherwise approach this?
More explanation:
Imagine a Tcl program that makes its own prompt with some threads running code in the background. I would like this prompt to react to single keystrokes, for example: when you press 'p' (without pressing enter) the prompt reads that character and pauses the threads, when you press 'q' the prompt kills the threads and stops the program. The cleanest and closest solution is shortly demonstrated in the following code snippet.
proc readPrompt { } {
set in [ read stdin 1 ]
if { $in eq "q" } {
puts "Quitting..."
set ::x 1
} {
puts "Given unknown command $in"
}
}
fconfigure stdin -blocking 0 -buffering none
fileevent stdin readable { readPrompt }
vwait x
The result from running this is:
a
Given unknown command a
Given unknown command
After pressing the 'a', nothing happens. My guess is that the stdin is not flushed or something. Pressing enter or CTRL-d triggers the fileevent and the prompt then reads both the characters 'a' and 'enter'.
Ideally, I want the enter-press not to be needed. How could I accomplish this?
EDIT: I found this question and solution about a related use in Python: Determine the terminal cursor position with an ANSI sequence in Python 3 This is approximately the behaviour I'm looking for, but in Tcl.
If you have 8.7 (currently in alpha) then this is “trivial”:
fconfigure stdin -inputmode raw
That delivers all characters to you, without echoing them. (There's also modes normal and password, both of which preprocess the data before delivery and only one of which echoes.) You'll have to look after giving visual feedback to the user yourself, and be aware that all includes all characters usually only used for line editing purposes.
Otherwise, on Unixes (Linux, macOS) you do:
exec stty raw -echo <#stdin >#stdout
to switch the mode to the same config, and:
exec stty -raw echo <#stdin >#stdout
to switch back. (Not all Unixes need the input and output redirects, but some definitely do.)
Windows consoles have something similar in 8.7, but not in previous versions; a workaround might be possible using the TWAPI console support but that's a very low level API (and I don't know the details).

How to check if the value of file handle is not null in tcl

I have this snippet in my script:
puts "Enter Filename:"
set file_name [gets stdin]
set fh [open $file_name r]
#Read from the file ....
close $fh
Now, this snippet asks user for a file name.. which is then set as an input file and then read. But when the file with the name $file_name doesn't exists, it shows error saying
illegal file character
How do i check if fh is not null (I don't think there is a concept of NULL in tcl being "everyting is a string" language!), so that if an invalid file_name is given, i can throw a print saying file doesn't exists!
Short answer:
try {
open $file_name
} on ok f {
# do stuff with the open channel using the handle $f
} on error {} {
error {file doesn't exist!}
}
This solution attempts to open the file inside an exception handler. If open is successful, the handler runs the code you give it inside the on ok clause. If open failed, the handler deals with that error by raising a new error with the message you wanted (note that open might actually fail for other reasons as well).
The try command is Tcl 8.6+, for Tcl 8.5 or earlier see the long answer.
Long answer:
Opening a file can fail for several reasons, including missing files or insufficient privileges. Some languages lets the file opening function return a special value indicating failure. Others, including Tcl, signal failure by not letting open return at all but instead raise an exception. In the simplest case, this means that a script can be written without caring about this eventuality:
set f [open nosuchf.ile]
# do stuff with the open channel using the handle $f
# run other code
This script will simply terminate with an error message while executing the open command.
The script doesn't have to terminate because of this. The exception can be intercepted and the code using the file handle be made to execute only if the open command was successful:
if {![catch {open nosuchf.ile} f]} {
# do stuff with the open channel using the handle $f
}
# run other code
(The catch command is a less sophisticated exception handler used in Tcl 8.5 and earlier.)
This script will not terminate prematurely even if open fails, but it will not attempt to use $f either in that case. The "other code" will be run no matter what.
If one wants the "other code" to be aware of whether the open operation failed or succeeded, this construct can be used:
if {![catch {open nosuchf.ile} f]} {
# do stuff with the open channel using the handle $f
# run other code in the knowledge that open succeeded
} else {
# run other code in the knowledge that open failed
}
# run code that doesn't care whether open succeeded or failed
or the state of the variable f can be examined:
catch {open nosuchf.ile} f
if {$f in [file channels $f]} {
# do stuff with the open channel using the handle $f
# run other code in the knowledge that open succeeded
} else {
# run other code in the knowledge that open failed
}
# run code that doesn't care whether open succeeded or failed
(The in operator is in Tcl 8.5+; if you have an earlier version you will need to write the test in another manner. You shouldn't be using earlier versions anyway, since they're not supported.)
This code checks if the value of f is one of the open channels that the interpreter knows about (if it isn't, the value is probably an error message). This is not an elegant solution.
Ensuring the channel is closed
This isn't really related to the question, but a good practice.
try {
open nosuchf.ile
} on ok f {
# do stuff with the open channel using the handle $f
# run other code in the knowledge that open succeeded
} on error {} {
# run other code in the knowledge that open failed
} finally {
catch {chan close $f}
}
# run code that doesn't care whether open succeeded or failed
(The chan command was added in Tcl 8.5 to group several channel-related commands as subcommands. If you're using earlier versions of Tcl, you can just call close without the chan but you will have to roll your own replacement for try ... finally.)
The finally clause ensures that whether or not the file was opened or any error occurred during the execution of the on ok or on error clauses, the channel is guaranteed to be non-existent (destroyed or never created) when we leave the try construct (the variable f will remain with an unusable value, unless we unset it. Since we don't know for sure if it exists, we need to prevent the unset operation from raising errors by using catch {unset f} or unset -nocomplain f. I usually don't bother: if I use the name f again I just set it to a fresh value.).
Documentation: catch, chan, close, error, in operator, file, if, open, set, try, unset
Old answer:
(This answer has its heart in the right place but I'm not satified with it these months later. Since it was accepted and even marked as useful by three people I am loath to delete it, but the answer above is IMHO better.)
If you attempt to open a non-existing file and assign the channel identifier to a variable, an error is raised and the contents of the variable are unchanged. If the variable didn't exist, it won't be created by the set command. So while there is no null value, you can either 1) set the variable to a value you know isn't a channel identifier before opening the file:
set fh {} ;# no channel identifier is the empty string
set fh [open foo.bar]
if {$fh eq {}} {
puts "Nope, file wasn't opened."
}
or 2) unset the variable and test it for existence afterwards (use catch to handle the error that is raised if the variable didn't exist):
catch {unset fh}
set fh [open foo.bar]
if {![info exists fh]} {
puts "Nope, file wasn't opened."
}
If you want to test if a file exists, the easiest way is to use the file exists command:
file exists $file_name
if {![file exists $file_name]} {
puts "No such file"
}
Documentation: catch, file, if, open, puts, set, unset

how to check that file is closed

How can I check that file is closed?
For example:
set fh [open "some_test_file" "w"]
puts $fh "Something"
close $fh
Now I want to check that channel $fh is closed
The command:
file channels $fh
return nothing so I cannot use it in any condition.
You could also use something like:
proc is_open {chan} {expr {[catch {tell $chan}] == 0}}
If the close command did not return an error, then it was successful. The file channels command doesn't take an argument but just returns all the open channels so $channel in [file channels] would be a redundant test to ensure that you closed the channel. However, how about believing the non-error response of the close command?
I must correct myself - a bit of checking and it turns out the file channels command can take an optional pattern (a glob expression) of channels names to return. So the original example will work if the file is still open. You can test this in a tclsh interpreter using file channels std*. However, the close command will still return an error that can be caught if it fails to close the channel which would also allow you to potentially handle such errors (possibly retry later for some).
Why not put the channel name into a variable, make the closing code unset that variable, if [close] suceeded and in the checking code just check the variable does not exist (that is, unset)?
Note that I'm following a more general practice found in system programming: once you closed an OS file handle, it became invalid and all accesses to it are hence invalid. So you use other means to signalizing the handle is no more associated with a file.