Home     |     .Net Programming    |     cSharp Home    |     Sql Server Home    |     Javascript / Client Side Development     |     Ajax Programming

Ruby on Rails Development     |     Perl Programming     |     C Programming Language     |     C++ Programming     |     IT Jobs

Python Programming Language     |     Laptop Suggestions?    |     TCL Scripting     |     Fortran Programming     |     Scheme Programming Language


 
 
Cervo Technologies
The Right Source to Outsource

MS Dynamics CRM 3.0

TCL(Tool Command Language) Scripting

How to read one character at a time without enter pressing


I need an interactive shell programm. It must read one character as
command
and process it immediately without enter pressing.

I try to do it in this way:

fconfigure stdin -buffering none
set var [read stdin 1]

but its not working. In TCL FAQ I found this link on solution:
ftp://ftp.neosoft.com/pub/tcl/alcatel/code/binary-io-hack.shar.gz
but it doesn't work.

Maybe somebody have this file or know better solution?

On 5 Jun., 17:35, Cris <c@univ.kiev.ua> wrote:

> I need an interactive shell programm. It must read one character as
> command
> and process it immediately without enter pressing.

See http://wiki.tcl.tk/14693 for solutions - Linux/Cygwin can use
stty, for Windows without Cygwin things are a bit more complicated.

Cris wrote:
> I need an interactive shell programm. It must read one character as
> command
> and process it immediately without enter pressing.

> I try to do it in this way:

> fconfigure stdin -buffering none
> set var [read stdin 1]

> but its not working. In TCL FAQ I found this link on solution:
> ftp://ftp.neosoft.com/pub/tcl/alcatel/code/binary-io-hack.shar.gz
> but it doesn't work.

> Maybe somebody have this file or know better solution?

If you are on a unixy platform:
#!/usr/bin/tclsh

exec stty raw <@ stdin

while {![eof stdin]} {
         set char [ read stdin 1 ]
         puts stderr char:$char
         if { "$char" == "\004" } break
         if { "$char" == "\003" } break

}

exec stty -raw <@ stdin

uwe

On Jun 6, 12:08 am, Uwe Klein <uwe_klein_habertw@t-online.de>
wrote:

Though it may work, the correct way (IMHO) to use [eof] is:

  while 1 {
    set char [ read stdin 1 ]
    if {[eof stdin]} break

    puts stderr char:$char
    if { "$char" == "\004" } break
    if { "$char" == "\003" } break
  }

otherwise you'll process an extra character on the final read.
Unimportant and ignorable for some code but can possibly lead to
subtle bugs.

On Jun 6, 2:32 am, "slebet@yahoo.com" <slebet@gmail.com> wrote:

AIUI, eof will only return true _after_ the last succesful read. So
the code you posted above will miss the last character:
1) read will get the last character and set eof
2) eof will break from the loop
3) the last character will not be processed.

I would probably write this as:

while {![eof stdin] {
     set char [ read stdin 1 ]

     puts stderr char:$char
     if { "$char" == "\004" } break
     if { "$char" == "\003" } break
   }

This will terminate the loop after the last character has been read
and handled.

Mark

On Jun 6, 10:21 am, Mark Janssen <mpc.jans@gmail.com> wrote:

It pays to read the whole thread before replying. This was of course
exactly what Uwe Klein also wrote.

Mark

> Seehttp://wiki.tcl.tk/14693for solutions - Linux/Cygwin can use
> stty, for Windows without Cygwin things are a bit more complicated.

Thanks a lot :). It works.
* Mark Janssen <mpc.jans@gmail.com>
| AIUI, eof will only return true _after_ the last succesful read. So
| the code you posted above will miss the last character:
--<snip-snip>--
| 1) read will get the last character and set eof

*BZZT* wrong.  'read' will never set eof if it was successful (i.e.:
has read the requested length, 1 here).  eof' will return true only if
'read' has failed due to EOF.  If the read was successful, how would
'eof' know that there is nothing more to read?

Try your code and you will see an extra character processed at the
end:

    % cat t.tcl
    while {![eof stdin]} {
        set char [ read stdin 1 ]

        puts stderr "char:{$char}"
        if { "$char" == "\004" } break
        if { "$char" == "\003" } break
    }
    % cat x
    123
    % tclsh t.tcl < x
    char:{1}
    char:{2}
    char:{3}
    char:{
    }
    char:{}
         ^ This last one is wrong.

As slebetman wrote, the correct approach is to 'read', and then check
for 'eof'.

R'

On Jun 6, 4:22 pm, Mark Janssen <mpc.jans@gmail.com> wrote:

It pays to actually test your code before replying. My code is
correct, yours will process an extra character. It also pays to
actually read documentation:

Example (from man eof):

set f [open somefile.txt]
while {1} {
    set line [gets $f]
    if {[eof $f]} {
        close $f
        break
    }
    puts "Read line: $line"

On Jun 7, 12:44 am, "slebet@yahoo.com" <slebet@gmail.com> wrote:

Yup you guys got me with the hand in the cookie jar. Thanks for the
correction.

Mark 'I should have tested that' Janssen

* Mark Janssen <mpc.jans@gmail.com>
| Yup you guys got me with the hand in the cookie jar. Thanks for the
| correction.

After playing around a bit it shows that we were both right/wrong to
some degree.  While in the specific case (read $fd 1) the read will
either succeed completely OR fail completely, there is also the case
when you read more than 1 byte: then 'read' may read less than the
requested number of bytes, but still read _some_.  Nevertheless EOF
will be set in this case:

  # assume input has left $N-1 bytes to read
  set bytes [read $fd $N]
  # now [string length $bytes] will be $N-1,
  # _and_ EOF will be set on $fd.

If you simply 'break' on EOF here, the last partial read will be
lost.  Whether this is a problem depends on context, of course.

R'

On Jun 5, 10:35 am, Cris <c@univ.kiev.ua> wrote:

> I need an interactive shell programm. It must read one character as
> command
> and process it immediately without enter pressing.

> I try to do it in this way:

> fconfigure stdin -buffering none
> set var [read stdin 1]

> but its not working. In TCL FAQ I found this link on solution:ftp://ftp.neosoft.com/pub/tcl/alcatel/code/binary-io-hack.shar.gz
> but it doesn't work.

> Maybe somebody have this file or know better solution?

Something to allow multiple inputs again for unix  like uwe solution
the terminal is what is fouling up the stdin so put it in raw
mode the if user hits q puts it back in cooked mode.  Expect could be
used and I believe the new Tcllib terminal package has
a key input loop you can use as well.

Carl

#!/opt/usr/bin/tclsh8.5
exec stty raw
fconfigure stdin -blocking 0 -buffering none

proc myRead {fd  } {
   global myCharQueue
   set myCharQueue [read $fd 1 ]

}

proc handleInput { name name2 op } {
   puts " trace activated"
   global $name
   global forever
   switch [ set $name ] {
          h {
              puts "help"
              flush stdout
             }
          q {
              incr forever
              exec /bin/stty -raw
            }
          default {
              puts "You hit the [set $name] key"
          }
   }
   eval set $name \"\"

}

set myCharQueue ""
fileevent stdin readable [list myRead stdin ];
trace variable  myCharQueue  "w" handleInput
global forever
set forever 0
vwait forever
On Jun 7, 4:56 pm, Ralf Fassel <ralf@gmx.de> wrote:

Hmm.. It appears that [eof] after a read is correct for [read $f 1]
and [gets] but [eof] before read is correct for reads of blocks of
more than 1 byte.

In the case of [read] it's easy to tell that there's no data read
since you'll never get an empty string otherwise. But for [gets] an
empty string is simply an empty line. So I guess the correct way to
use [eof] is:

  while {![eof $f]} {
    set data [read $f $bytes]
    if {$data == ""} break
    # ...
  }

and

  while 1 {
    set line [gets $f]
    if {[eof $f]} break
    # ...
  }

respectively.

Add to del.icio.us | Digg this | Stumble it | Powered by Megasolutions Inc