An interactive tclsh with an event loop (but no Tk)

For a project I (cjl) am working on I wanted a tclsh to be both interactive (so I can poke around inside) and running the event loop (so that it can deal with activity on various file handles and sockets). There didn't seem to be an existing way of doing that (unless I'm missing something very obvious), so I came up with the following, which I'm posting here so that the real gurus can either put me on the right track or make this solution more correct and robust. It uses fileevent on stdin to get the user's input and accumulates the command a line at a time until info complete likes it, at which point it is eval uplevel'd. It does accept multi-line input (so you can write procedure definitions). Ctrl-d signals the end of your input and allows a gracious exit.

#!/usr/bin/env tclsh8.5

proc input { buffer ch } {
    if { [gets $ch line] != -1 } {
        append buffer "$line\n"
        if { [info complete $buffer] } {
            if { $buffer ne "\n" } {
                catch {eval uplevel #0 [list $buffer]} res
                if { [string length $res] } {
                    puts $res
                }
            }
            puts -nonewline "% "
            flush stdout
            fileevent stdin readable [list input "" stdin]
        } else {
            fileevent stdin readable [list input $buffer stdin]
        }
    } else {
        set ::forever 1
    }
    
}

puts -nonewline "% "
flush stdout
fileevent stdin readable [list input "" stdin]

vwait forever

exit

Improvements and suggestions welcomed!

APN See the commandloop page (the discussion section) for several alternatives.


Rfoxmich - 2019-07-23 21:01:22

So this event driven loop is a bit naive and is vulnerable to recursive invocations of the event loop (e.g vwait).

Consider the following script which we'll put in the file bad.tcl

after 10000 incr a
vwait a
puts oops

Now, don't type, but source this file into the script shown. When vwait a is hit, the event loop recurses. Since, however this is a sourced script, stdin is always readable which gets the interpreter to continue reading the script which outputs oops prior to the vwait actually completing.