This command provides several operations for reading from, writing to and otherwise manipulating open channels (such as have been created with the open and socket commands, or the default named channels stdin, stdout or stderr which correspond to the process's standard input, output and error streams respectively). Option indicates what to do with the channel; any unique abbreviation for option is acceptable. Valid options are:
As part of closing the channel, all buffered output is flushed to the channel's output device (only if the channel is ceasing to be writable), any buffered input is discarded (only if the channel is ceasing to be readable), the underlying operating system resource is closed and channelId becomes unavailable for future use (both only if the channel is being completely closed).
If the channel is blocking and the channel is ceasing to be writable, the command does not return until all output is flushed. If the channel is non-blocking and there is unflushed output, the channel remains open and the command returns immediately; output will be flushed in the background and the channel will be closed when all the flushing is complete.
If channelId is a blocking channel for a command pipeline then chan close waits for the child processes to complete.
If the channel is shared between interpreters, then chan close makes channelId unavailable in the invoking interpreter but has no other effect until all of the sharing interpreters have closed the channel. When the last interpreter in which the channel is registered invokes chan close (or close), the cleanup actions described above occur. With half-closing, the half-close of the channel only applies to the current interpreter's view of the channel until all channels have closed it in that direction (or completely). See the interp command for a description of channel sharing.
Channels are automatically fully closed when an interpreter is destroyed and when the process exits. Channels are switched to blocking mode, to ensure that all output is correctly flushed before the process exits.
The command returns an empty string, and may generate an error if an error occurs while flushing output. If a command in a command pipeline created with open returns an error, chan close generates an error (similar to the exec command.)
Note that half-closes of sockets and command pipelines can have important side effects because they result in a shutdown() or close() of the underlying system resource, which can change how other processes or systems respond to the Tcl program.
If no optionName or value arguments are supplied, the command returns a list containing alternating option names and values for the channel. If optionName is supplied but no value then the command returns the current value of the given option. If one or more pairs of optionName and value are supplied, the command sets each of the named options to the corresponding value; in this case the return value is an empty string.
The options described below are supported for all channels. In addition, each channel type may add options that only it supports. See the manual entry for the command that creates each type of channel for the options supported by that specific type of channel. For example, see the manual entry for the socket command for additional options for sockets, and the open command for additional options for serial devices.
If a file contains pure binary data (for instance, a JPEG image), the encoding for the channel should be configured to be binary. Tcl will then assign no interpretation to the data in the file and simply read or write raw bytes. The Tcl binary command can be used to manipulate this byte-oriented data. It is usually better to set the -translation option to binary when you want to transfer binary data, as this turns off the other automatic interpretations of the bytes in the stream as well.
The default encoding for newly opened channels is the same platform- and locale-dependent system encoding used for interfacing with the operating system, as returned by encoding system.
The value associated with -translation is a single item for read-only and write-only channels. The value is a two-element list for read-write channels; the read translation mode is the first element of the list, and the write translation mode is the second element. As a convenience, when setting the translation mode for a read-write channel you can specify a single value that will apply to both reading and writing. When querying the translation mode of a read-write channel, a two-element list will always be returned. The following values are currently supported:
The chan copy command transfers data from inputChan until end of file or size bytes or characters have been transferred; size is in bytes if the two channels are using the same encoding, and is in characters otherwise. If no -size argument is given, then the copy goes until end of file. All the data read from inputChan is copied to outputChan. Without the -command option, chan copy blocks until the copy is complete and returns the number of bytes or characters (using the same rules as for the -size option) written to outputChan.
The -command argument makes chan copy work in the background. In this case it returns immediately and the callback is invoked later when the copy completes. The callback is called with one or two additional arguments that indicates how many bytes were written to outputChan. If an error occurred during the background copy, the second argument is the error string associated with the error. With a background copy, it is not necessary to put inputChan or outputChan into non-blocking mode; the chan copy command takes care of that automatically. However, it is necessary to enter the event loop by using the vwait command or by using Tk.
You are not allowed to do other I/O operations with inputChan or outputChan during a background chan copy. If either inputChan or outputChan get closed while the copy is in progress, the current copy is stopped and the command callback is not made. If inputChan is closed, then all data already queued for outputChan is written out.
Note that inputChan can become readable during a background copy. You should turn off any chan event or fileevent handlers during a background copy so those handlers do not interfere with the copy. Any I/O attempted by a chan event or fileevent handler will get a “channel busy” error.
Chan copy translates end-of-line sequences in inputChan and outputChan according to the -translation option for these channels (see chan configure above). The translations mean that the number of bytes read from inputChan can be different than the number of bytes written to outputChan. Only the number of bytes written to outputChan is reported, either as the return value of a synchronous chan copy or as the argument to the callback for an asynchronous chan copy.
Chan copy obeys the encodings and character translations configured for the channels. This means that the incoming characters are converted internally first UTF-8 and then into the encoding of the channel chan copy writes to (see chan configure above for details on the -encoding and -translation options). No conversion is done if both channels are set to encoding binary and have matching translations. If only the output channel is set to encoding binary the system will write the internal UTF-8 representation of the incoming characters. If only the input channel is set to encoding binary the system will assume that the incoming bytes are valid UTF-8 characters and convert them according to the output encoding. The behaviour of the system for bytes which are not valid UTF-8 characters is undefined in this case.
The argument mode specifies if the new channel is opened for reading, writing, or both. It has to be a list containing any of the strings “read” or “write” The list must have at least one element, as a channel you can neither write to nor read from makes no sense. The handler command for the new channel must support the chosen mode, or an error is thrown.
The command prefix is executed in the global namespace, at the top of call stack, following the appending of arguments as described in the refchan manual page. Command resolution happens at the time of the call. Renaming the command, or destroying it means that the next call of a handler method may fail, causing the channel command invoking the handler to fail as well. Depending on the subcommand being invoked, the error message may not be able to explain the reason for that failure.
Every channel created with this subcommand knows which interpreter it was created in, and only ever executes its handler command in that interpreter, even if the channel was shared with and/or was moved into a different interpreter. Each reflected channel also knows the thread it was created in, and executes its handler command only in that thread, even if the channel was moved into a different thread. To this end all invocations of the handler are forwarded to the original thread by posting special events to it. This means that the original thread (i.e. the thread that executed the chan create command) must have an active event loop, i.e. it must be able to process such events. Otherwise the thread sending them will block indefinitely. Deadlock may occur.
Note that this permits the creation of a channel whose two endpoints live in two different threads, providing a stream-oriented bridge between these threads. In other words, we can provide a way for regular stream communication between threads instead of having to send commands.
When a thread or interpreter is deleted, all channels created with this subcommand and using this thread/interpreter as their computing base are deleted as well, in all interpreters they have been shared with or moved into, and in whatever thread they have been transferred to. While this pulls the rug out under the other thread(s) and/or interpreter(s), this cannot be avoided. Trying to use such a channel will cause the generation of a regular error about unknown channel handles.
This subcommand is safe and made accessible to safe interpreters. While it arranges for the execution of arbitrary Tcl code the system also makes sure that the code is always executed within the safe interpreter.
A file event handler is a binding between a channel and a script, such that the script is evaluated whenever the channel becomes readable or writable. File event handlers are most commonly used to allow data to be received from another process on an event-driven basis, so that the receiver can continue to interact with the user or with other channels while waiting for the data to arrive. If an application invokes chan gets or chan read on a blocking channel when there is no input data available, the process will block; until the input data arrives, it will not be able to service other events, so it will appear to the user to “freeze up” With chan event, the process can tell when data is present and only invoke chan gets or chan read when they will not block.
A channel is considered to be readable if there is unread data available on the underlying device. A channel is also considered to be readable if there is unread data in an input buffer, except in the special case where the most recent attempt to read from the channel was a chan gets call that could not find a complete line in the input buffer. This feature allows a file to be read a line at a time in non-blocking mode using events. A channel is also considered to be readable if an end of file or error condition is present on the underlying file or device. It is important for script to check for these conditions and handle them appropriately; for example, if there is no special check for end of file, an infinite loop may occur where script reads no data, returns, and is immediately invoked again.
A channel is considered to be writable if at least one byte of data can be written to the underlying file or device without blocking, or if an error condition is present on the underlying file or device. Note that client sockets opened in asynchronous mode become writable when they become connected or if the connection fails.
Event-driven I/O works best for channels that have been placed into non-blocking mode with the chan configure command. In blocking mode, a chan puts command may block if you give it more data than the underlying file or device can accept, and a chan gets or chan read command will block if you attempt to read more data than is ready; no events will be processed while the commands block. In non-blocking mode chan puts, chan read, and chan gets never block.
The script for a file event is executed at global level (outside the context of any Tcl procedure) in the interpreter in which the chan event command was invoked. If an error occurs while executing the script then the command registered with interp bgerror is used to report the error. In addition, the file event handler is deleted if it ever returns an error; this is done in order to prevent infinite loops due to buggy handlers.
If the channel is in blocking mode the command does not return until all the buffered output has been flushed to the channel. If the channel is in non-blocking mode, the command may return before all buffered output has been flushed; the remainder will be flushed in the background as fast as the underlying file or device is able to absorb it.
If an end-of-file occurs while part way through reading a line, the partial line will be returned (or written into varName). When varName is not specified, the end-of-file case can be distinguished from an empty line using the chan eof command, and the partial-line-but-non-blocking case can be distinguished with the chan blocked command.
Note that the pipe buffering semantics can vary at the operating system level substantially; it is not safe to assume that a write performed on the output side of the pipe will appear instantly to the input side. This is a fundamental difference and Tcl cannot conceal it. The overall stream semantics are compatible, so blocking reads and writes will not see most of the differences, but the details of what exactly gets written when are not. This is most likely to show up when using pipelines for testing; care should be taken to ensure that deadlocks do not occur and that potential short reads are allowed for.
Note that this subcommand can only be used with channel handles that were created/opened by chan create. All other channels will cause this subcommand to report an error.
As only the Tcl level of a channel, i.e. its command handler, should post events to it we also restrict the usage of this command to the interpreter that created the channel. In other words, posting events to a reflected channel from an interpreter that does not contain it's implementation is not allowed. Attempting to post an event from any other interpreter will cause this subcommand to report an error.
Another restriction is that it is not possible to post events that the I/O core has not registered an interest in. Trying to do so will cause the method to throw an error. See the command handler method watch described in refchan, the document specifying the API of command handlers for reflected channels.
This command is safe and made accessible to safe interpreters. It can trigger the execution of chan event handlers, whether in the current interpreter or in other interpreters or other threads, even where the event is posted from a safe interpreter and listened for by a trusted interpreter. Chan event handlers are always executed in the interpreter that set them up.
Newline characters in the output are translated by chan puts to platform-specific end-of-line sequences according to the currently configured value of the -translation option for the channel (for example, on PCs newlines are normally replaced with carriage-return-linefeed sequences; see chan configure above for details).
Tcl buffers output internally, so characters written with chan puts may not appear immediately on the output file or device; Tcl will normally delay output until the buffer is full or the channel is closed. You can force output to appear immediately with the chan flush command.
When the output buffer fills up, the chan puts command will normally block until all the buffered data has been accepted for output by the operating system. If channelId is in non-blocking mode then the chan puts command will not block even if the operating system cannot accept the data. Instead, Tcl continues to buffer the data and writes it in the background as fast as the underlying file or device can accept it. The application must use the Tcl event loop for non-blocking output to work; otherwise Tcl never finds out that the file or device is ready for more output data. It is possible for an arbitrarily large amount of data to be buffered for a channel in non-blocking mode, which could consume a large amount of memory. To avoid wasting memory, non-blocking I/O should normally be used in an event-driven fashion with the chan event command (do not invoke chan puts unless you have recently been notified via a file event that the channel is ready for more output data).
If channelId is in non-blocking mode, chan read may not read as many characters as requested: once all available input has been read, the command will return the data that is available rather than blocking for more input. If the channel is configured to use a multi-byte encoding, then there may actually be some bytes remaining in the internal buffers that do not form a complete character. These bytes will not be returned until a complete character is available or end-of-file is reached. The -nonewline switch is ignored if the command returns before reaching the end of the file.
Chan read translates end-of-line sequences in the input into newline characters according to the -translation option for the channel (see chan configure above for a discussion on the ways in which chan configure will alter input).
When reading from a serial port, most applications should configure the serial port channel to be non-blocking, like this:
chan configure channelId -blocking 0.
Then chan read behaves much like described above. Note that most serial ports are comparatively slow; it is entirely possible to get a readable event for each character read from them. Care must be taken when using chan read on blocking serial ports:
The origin argument defaults to start.
Chan seek flushes all buffered output for the channel before the command returns, even if the channel is in non-blocking mode. It also discards any buffered and unread input. This command returns an empty string. An error occurs if this command is applied to channels whose underlying file or device does not support seeking.
Note that offset values are byte offsets, not character offsets. Both chan seek and chan tell operate in terms of bytes, not characters, unlike chan read.
This opens a file using a known encoding (CP1252, a very common encoding on Windows), searches for a string, rewrites that part, and truncates the file after a further two lines.
set f [open somefile.txt r+] chan configure $f -encoding cp1252 set offset 0 # Search for string "FOOBAR" in the file while {[chan gets $f line] >= 0} { set idx [string first FOOBAR $line] if {$idx > -1} { # Found it; rewrite line chan seek $f [expr {$offset + $idx}] chan puts -nonewline $f BARFOO # Skip to end of following line, and truncate chan gets $f chan gets $f chan truncate $f # Stop searching the file now break } # Save offset of start of next line for later set offset [chan tell $f] } chan close $f
A network server that does echoing of its input line-by-line without preventing servicing of other connections at the same time.
# This is a very simple logger... proc log {message} { chan puts stdout $message } # This is called whenever a new client connects to the server proc connect {chan host port} { set clientName [format <%s:%d> $host $port] log "connection from $clientName" chan configure $chan -blocking 0 -buffering line chan event $chan readable [list echoLine $chan $clientName] } # This is called whenever either at least one byte of input # data is available, or the channel was closed by the client. proc echoLine {chan clientName} { chan gets $chan line if {[chan eof $chan]} { log "finishing connection from $clientName" chan close $chan } elseif {![chan blocked $chan]} { # Didn't block waiting for end-of-line log "$clientName - $line" chan puts $chan $line } } # Create the server socket and enter the event-loop to wait # for incoming connections... socket -server connect 12345 vwait forever