gets workaround

Richard Suchenwirth 2004-01-25 - It is very standard I/O to read a line from standard input with gets - but older wishes on Windows, or the Keuchel port on Windows/CE don't allow this: either stdin reports EOF, or the channel name is not even known. Here is a workaround that creates a toplevel window with an entry, which is returned to the caller (and the window destroyed) once <Return> is hit. It solved the gets inability both on my W95 box and my PocketPC.

First we have to save the original gets, as it may still be needed for file I/O, by renaming it into the tcl namespace:}

 if {[info command tcl::gets] eq ""} {rename gets tcl::gets}
# This is the substitute (workaround) which discriminates use cases:
 proc gets {chan {varN ""}} {
    if {$varN ne ""} {upvar 1 $varN var}
    if {$chan ne "stdin"} {
        if {$varN ne ""} {
            tcl::gets $chan $var ;# return character count
        } else {
            tcl::gets $chan ;# return the string
        }
    } else {
        set var [gets_window] ;# always return string
    }
 }
# And now for the minimal dialog window:
 proc gets_window {{echo 1}} {
    set w [toplevel .[clock clicks]]
    wm title $w gets:
    pack [entry $w.e -textvar gets_w -width 42]
    set ::gets_w ""
    raise $w
    focus $w.e
    bind $w <Return> {set getsw_done 1}
    vwait ::getsw_done
    destroy $w
    if $echo {puts $::gets_w}
    set ::gets_w
 }

Here is a simpler rewrite, all in one proc (and this returns string length if called with a variable name):

 proc gets {channel {var {}}} {
    if {$var ne ""} {upvar 1 $var v}
    if {$channel eq "stdin"} {
       toplevel .gets
       pack [entry .gets.e -width 45]
       bind .gets.e <Return> {set gets 1}
       set f [focus]
       focus .gets.e
       vwait ::gets
       set res [.gets.e get]
       destroy .gets
       focus $f
       if {$var ne ""} {
          set v $res; string length $v
       } else {set res}
    } else {
       uplevel 1 tcl::[info level 0]
    }
 }

RS 2005-02-09: As today's breakfast fun project, here's a variant that uses the Windows/Mac console, tested to work on Win 95 and XP:

 if {[info command tcl::gets] eq ""} {rename gets tcl::gets}
 proc gets {chan {varN ""}} {
    if {$varN ne ""} {upvar 1 $varN var}
    if {$chan ne "stdin"} {
        if {$varN ne ""} {
            tcl::gets $chan $var ;# return character count
        } else {
            tcl::gets $chan ;# return the string
        }
    } else {
        gets_window
        set var [console eval {set ::consolegets}] ;# always return string
    }
 }
 proc gets_window {} {
        console show
        console eval {
           .console insert end \n
           .console see end
           .console mark set insert end-1c
           .console mark set begin end-2c
           focus .console
           bind .console <Return> {
              set consolegets [.console get begin+1c end-1c];break
           }
           vwait ::consolegets
           consoleinterp eval {raise .}
       }
 
 }