Article 10259 of comp.lang.perl:
Xref: feenix.metronet.com comp.lang.perl:10259
Newsgroups: comp.lang.perl
Path: feenix.metronet.com!news.ecn.bgu.edu!psuvax1!uwm.edu!cs.utexas.edu!swrinde!sgiblab!munnari.oz.au!newsroom.utas.edu.au!ml.csiro.au!solaris!jstander
From: [email protected] (Jeff Stander)
Subject: Re: Interactive scripts (cbreak, flushing STDI
Message-ID: <[email protected]>
Sender: [email protected]
Reply-To: [email protected]
Organization: CSIRO Marine Laboratories
References: <[email protected]>
Date: Fri, 28 Jan 1994 06:08:55 GMT
Lines: 414

In article [email protected], [email protected] (Jeff Blaine) writes:
>The problem:
>------------
>
>I need input from an interactive user.  The user picks a number from
>a menu (1-15) and types it in.  Great.  Except that the terminal
>is doing buffering and it doesn't work properly.
>

Jeff

I have had the same problem and wrote some interactive subs
to handle it.  I'll attach them to this letter.  I don't gurantee
anything here and do not consider myself a Perl "guru".
Let me know if they are of help and if they work.

Jeff Stander

___________________________________________________________________________

[email protected]        _--_|\        Database Analyst
CSIRO Division Of Fisheries    /      \       Pelagic Fisheries Resources
GPO Box 1538, Hobart           \_.--._/       Tasmania 7001, Australia
Aus Tel: 002-325-332                 v        Intl Tel: +61-02-325-332
Aus Fax: 002-325-000                          Intl Fax: +61-02-325-000
___________________________________________________________________________

#!/bin/sh
# This is a shell archive (produced by shar 3.50)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 01/28/1994 06:05 UTC by tuna@deep
# Source directory /a/aqueous/tuna/jstander/lib/perl
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   6445 -rwxrwxr-x keypress.pl
#   3734 -rwxrwx--x selection.pl
#
# ============= keypress.pl ==============
if test -f 'keypress.pl' -a X"$1" != X"-c"; then
       echo 'x - skipping keypress.pl (File already exists)'
else
echo 'x - extracting keypress.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'keypress.pl' &&
#! /usr/local/bin/perl
# @(#) keypress : subroutine to read one character from keyboard
# @(#) SunOS deep sun4m (jstander)
# @(#) loc: /home/tuna/bin
# @(#) $Revision 1.0 $ (jstander 07.12.93): new
###############################################################################
# Subroutine:   keypress - perl function to read one charaacter from keyboard
#
# Synopsis      &keypress([options],["message"])
X
# Description   Read one character from keyboard and return value, optionally
#               suppressing echo and allow setting of default values
#
# Options
#       noecho          suppress echoing of input character
#       nobell          turn of bell
#       bell            turn on bell
#       fold            if the first argument is "fold" then select
#                       characters are all folded to lower case
#                       for testing against user entry. I.e. user
#                       entry is case-insensitive.  This is the default.
#       nofold          if the first argument is "nofold" then select
#                       characters are NOT folded to lower case.
#                       I.e. the user entry is case senstive.
#       "c"             a single character to be taken as default if <CR> is
#                       pressed.  Note that this may have to be enclosed in quotes.
#       "[pattern]"     a regular expression character set to match, e.g. "[YNQ]"
#                       If a default character was specified, <CR> is implied as
#                       a selection character.
#       t=n             Set wait time in seconds before default is taken.
#                       If n seconds elapse, the default character is
#                       returned, or if no default, undef is returned.
# Arguments
#       "message"       Message to user printed before
#
# Returns:              The value of the pressed key.  Note that if fold
#                       is enabled (the default) that pressing an upper or
#                       lowercase key still returns the matched key, e.g.
#                       pressing "y" or "Y" returns a "Y" if the pattern was
#                       "[YN]".   Pressing "y" or "Y" if the pattern was
#                       "[yn]" returns a "y".  If the pattern was "[yYnN]"
#                       then the pressed key is returned.
# Environment
#       KEYPRESS_WAITTIME       sets default waittime
#       KEYPRESS_NOBELL         if present, don't ring bell when querying user
#       KEYPRESS_BG             if present, set background mode - don't query user
#                               and take default response.
# Author
#       [email protected] CSIRO Division Of Fisheries, Hobart,
#       Tasmania 7001, Australia
###############################################################################
# Subroutine:   no/yes - perl functions to read "y" or "n" from keyboard
#
# Synopsis:     &no([options],["message"])      - default if <CR> is pressed is "n"
#               &yes([options],["message"])     - default if <CR> is pressed is "y"
#
# Description:  Read a "y" or "n" keyboard and return value, optionally
#               suppressing echo.  Only <CR>, y, Y, n, N are accepted.
#               and upper case characters are folded to lower
#
# Options:      {options}       same as for &keypress
#
# Arguments:    "message"       Message to user printed before
#
# Returns:      True if y for &es, n for &no, else false.
#
# Author
#       [email protected] CSIRO Division Of Fisheries, Hobart,
#       Tasmania 7001, Australia
###############################################################################
X
X
$keypress_defined=1;
X
{
X
#local($BSD) = -f '/vmunix';
local($fold)="i";
local($store,$keypress_echo,$keypress_defchar,$keypress_msg,$keypress_pattern,$skip_cleanup);
local($keypress_wait_time,$keypress_nobell);
X
sub keypress_cleanup {
X       undef $keypress_msg;
X       undef $keypress_defchar;
X       $keypress_wait_time = $ENV{'KEYPRESS_WAITTIME'} || 0 ;
X       $keypress_nobell    = $ENV{'KEYPRESS_NOBELL'} || 0 ;
X       $fold="i";
X       $keypress_pattern=".";
X       $keypress_echo=1;;
}
X
sub get_keypress_args {
X       local($arg) = pop(@_);
X
X       if ( defined($arg) ) {
X               if ( $arg eq "bell" ) {
X                       $keypress_nobell = 0;
X               }
X               elsif ( $arg eq "nobell" ) {
X                       $keypress_nobell = 1;
X               }
X               elsif ( $arg eq "fold" ) {
X                       $fold = "i";
X               }
X               elsif ( $arg eq "nofold" ) {
X                       $fold = 0;
X               }
X               elsif ( $arg eq "noecho" ) {
X                       $keypress_echo = 0;
X               }
X               elsif ( length($arg) == 1 ) {
X                       $keypress_defchar = unpack( "a", $arg );
X               }
X               elsif ( $arg =~ /^t=([0-9]+)$/ ) {
X                       $keypress_wait_time = $1;
X               }
X               elsif( $arg =~ /^\[/ ) {
X                       $keypress_pattern=$arg;
X               }
X               else {
X                       $keypress_msg = $arg;
X               };
X       }
X       @_;
}
X
sub keypress {
X       &keypress_cleanup if !$skip_cleanup;
X       while ( @_ ) { &get_keypress_args(@_); pop(@_); }
X       if ( $ENV{'KEYPRESS_BG'} ) { return $keypress_defchar; }
X       $store=$|; $|=1;
X
X       $keypress_pattern =~ tr/[A-Z]/[a-z]/ if $fold;
X       $keypress_pattern =~ s#^\[#\[\n# if $keypress_defchar;;
X       $keypress_pattern =~ "[\n]" if !$keypress_pattern;
X       local($key,$ok);
X
X       if ( defined($keypress_msg) ) {
X               print $keypress_msg;
X               print "$keypress_defchar" if $keypress_defchar;
X       }
X
X       while (!$ok) {
X               undef $key;
X               print "\a" if !$nobell;
X               $key = `keypress -R -t$keypress_wait_time $keypress_defchar`;
X               if ( !$key || $key eq "" ) { last };
X               last if $key =~ /\B/;
X               $key =~ tr/[A-Z]/[a-z]/ if $fold;
X               $ok = $key =~ /$keypress_pattern/;
X       }
X
X       $key=$keypress_defchar if ( $keypress_defchar && ( $key eq "\n" || !$key || $key =~ /\B/ ) );
X
X       print "$key\n" if $keypress_echo;
X
X       $|=$store; $skip_cleanup=0;
X       $key;
}
X
X
sub yes {
X       &keypress_cleanup;
X       while ( @_ ) { &get_keypress_args(@_); pop(@_); }
X       $skip_cleanup=1;
X       $keypress_msg = "Proceed? (y/n) n" if !$keypress_msg;
X       local($key) = &keypress("y","[yYnN]");
X       $key =~ /[yY]/;
}
X
X
sub no {
X       &keypress_cleanup;
X       while ( @_ ) { &get_keypress_args(@_); pop(@_); }
X       $skip_cleanup=1;
X       $keypress_msg = "Proceed? (y/n) n" if !$keypress_msg;
X       local($key) = &keypress("n","[yYnN]");
X       $key =~ /[nN]/;
}
}
1;
X
__END__
print "ECHO  : [" . &keypress("t=2","y","[yYnYabcXYZ]","Enter y or n: ") ."]\n";
$res=&yes("t=1") ; print $res ? "YES" : "NO" , "\n";
X
# test program
while ( @_=(&get_keypress_args(@_)) ) {};
#print "ECHO  : [" . &keypress ."]\n";
#print "ECHO  : [" . &keypress(Q) ."]\n";
#print "ECHO  : [" . &keypress(x) ."]\n";
#print "NOECHO: [" . &keypress(noecho,x) ."]\n";
#print "NOECHO: [" . &keypress("q",noecho,"[abcq]",nofold) ."]\n";
X
#@_=(xx,yy,zzz);
#while ( @_=(&get_keypress_args(@_)) ) {};
X
#print "NO    : [" . &no(noecho,"Read disk?") ."]\n";
#print "NO    : [" . &no(noecho) ."]\n";
#print "NO    : [" . &no(noecho,"") ."]\n";
#print "NO    : [" . &no("") ."]\n";
#print "NO    : [" . &yes("") ."]\n";
print &yes("HELLO? ") ? "YES\n" : "NO\n";
print "NO    : [" . &no(noecho,"Read disk?") ."]\n";
print "NO    : [" . &no(noecho) ."]\n";
print "NO    : [" . &no(noecho,"") ."]\n";
print "NO    : [" . &no("") ."]\n";
print "NO    : [" . &yes("") ."]\n";
SHAR_EOF
chmod 0775 keypress.pl ||
echo 'restore of keypress.pl failed'
Wc_c="`wc -c < 'keypress.pl'`"
test 6445 -eq "$Wc_c" ||
       echo 'keypress.pl: original size 6445, current size' "$Wc_c"
fi
# ============= selection.pl ==============
if test -f 'selection.pl' -a X"$1" != X"-c"; then
       echo 'x - skipping selection.pl (File already exists)'
else
echo 'x - extracting selection.pl (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'selection.pl' &&
#! /usr/local/bin/perl
###############################################################################
# Subroutine:   selection - sub to select from a list
# Synopsis:     &selection([nofold],$title,$default,@itemlist);
# Description:  Displays an itemized list on /dev/tty and prompts user to
#               enter a choice (read from STDIN).
#               User's entries are screened for validity.
# Arguments:    title           - scalar containing title to be displayed
#               default         - scalar which is default selection on <RETURN>
#                                 keypress.  If undefined or null, no default
#                                 return is allowed.  The default character
#                                 should be one of the select-characters.
#               itemlist        - 1) list of select-character/select-item pairs
#                                               or
#                                 2) list of select items.  These will be given
#                                    selection numbers automatically, any
#                                    single characater list element is assumed
#                                    to be a select-characater and the following
#                                    element its select-item (see example).
#               fold            - if the first argument is "fold" then select
#                                 characters are all folded to lower case
#                                 for testing against user entry. I.e. user
#                                 entry is case-insensitive.  This is the default.
#               nofold          - if the first argument is "nofold" then select
#                                 characters are NOT folded to lower case.
#                                 I.e. the user entry is case senstive.
# Example 1:
#               @items = ("H","HAPPY","S","SAD","B","BORED","Q","QUIT");
#               ($result,$answer) = &selection("How are you feeling?","H",@items);
#               exit if $answer =~ /qQ/;
#               print "Oh, so you are feeling $result today\n";
#
#               would print the following menu for the user..
#                       How are you feeling?
#                       H.  HAPPY
#                       S.  SAD
#                       B.  BORED
#                       Q.  QUIT
#                       Select one:
#
# Example 2:
#               @items = ("HAPPY","SAD","BORED","Q","QUIT);
#               ($result,$answer) = &selection("How are you feeling?","H",@items);
#               exit if $answer =~ /qQ/;
#               print "Oh, so you are feeling $result today\n";
#
#               would print the following menu for the user..
#                       How are you feeling?
#                       1.  HAPPY
#                       2.  SAD
#                       3.  BORED
#                       Q.  QUIT
#                       Select one:
#
# Returns:      2-element list containing the item selected and the
#               select-character.
# On Error:     Returns undef
# Host:         SunOS deep sun4m
# Author:       Jeff Stander ([email protected])
# Revision:     1.0 (jstander 04.01.94): new
# Author:       [email protected]
# (c) 1994      CSIRO Div. of Fisheries, Hobart Tasmania, Australia
###############################################################################
X
require "keypress.pl" if !$keypress_defined;
X
sub selection {
X       local($ans,$fmt,$nofold,$key,$item,$list,$cnt,$ndx);
X
X       open(TTY,"> /dev/tty");
X       local($stdin) = select(TTY);
X
X       if ($_[0] eq nofold) {
X               $nofold = "nofold";
X               shift(@_);
X       }
X       elsif ($_[0] eq fold) {
X               shift(@_);
X               $nofold = "fold";
X       }
X       else {
X               $nofold = "fold";
X       }
X
X       local($title,$default) = @_;
X       shift @_;
X       shift @_;
X
X       $ndx=0;
X       if ( @_[0] =~ /^.{2,}$/ ) {
X               for $item (@_) {
X                       if ( $key ) {
X                               $list[$ndx++] = $key;
X                               $list[$ndx++] = $item;
X                               undef $key;
X                               $len    = length($key);
X                               $maxlen = $len>$maxlen ? $len : $maxlen;
X                               next;
X                       }
X                       elsif ( $item =~ /^.$/ ) {
X                               $key=$item;
X                               next;
X                       }
X                       else {
X                               $list[$ndx++] = ++$cnt;
X                               $list[$ndx++] = $item;
X                               $len    = length($key);
X                               $maxlen = $len>$maxlen ? $len : $maxlen;
X                       }
X               }
X       }
X       else {
X               @list = @_;
X       }
X       $default=substr($default,0,1);
X       print "$title\n";
X
X       $fmt="\%" . $maxlen . "s.  \%s\n";
X
X       %list = @list;
X       while (($key,$item) = splice(@list,0,2)) {
X               printf ($fmt, $key, $item);
X               $keys .= $key;
X       }
X       local($pat) = "[$keys]";
X
X       print "\n" if $title =~ /\n$/;
X       print "Select one: ";
X       $ans = &keypress($nofold,$default,undef,$pat);
X
X       select($stdin);
X
X       ($list{$ans},$ans);
}
1;
SHAR_EOF
chmod 0771 selection.pl ||
echo 'restore of selection.pl failed'
Wc_c="`wc -c < 'selection.pl'`"
test 3734 -eq "$Wc_c" ||
       echo 'selection.pl: original size 3734, current size' "$Wc_c"
fi
exit 0