870 lines
19 KiB
Perl
870 lines
19 KiB
Perl
|
#!/usr/local/bin/perl
|
|||
|
# $0 - Front end of FvwmConsole
|
|||
|
# FvwmConsole server must be running
|
|||
|
|
|||
|
# Copyright 1997, Toshi Isogai
|
|||
|
# You may use this code for any purpose, as long as the original
|
|||
|
# copyright remains in the source code and all documentation
|
|||
|
|
|||
|
require 5.002;
|
|||
|
use Socket;
|
|||
|
|
|||
|
$ESC = "\e";
|
|||
|
$HISTFILE = "$ENV{HOME}/.FvConHist0";
|
|||
|
$SOCKET_NAME = "$ENV{HOME}/.FvConSocket";
|
|||
|
$VERSION = '1.2';
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if( -c "/dev/console" ) {
|
|||
|
close STDERR;
|
|||
|
open (STDERR,">/dev/console") || die "/dev/console: ",$!;
|
|||
|
}
|
|||
|
|
|||
|
($Filename = $0) =~ s@.*/@@;
|
|||
|
($Sname = $Filename) =~ s/C(\.pl)?$//;
|
|||
|
|
|||
|
|
|||
|
$tty = `tty`;
|
|||
|
$tty =~ s/\n//;
|
|||
|
$org_stty = &stty('-g');
|
|||
|
|
|||
|
@Hist = ();
|
|||
|
@Histall = ();
|
|||
|
$HIST_SIZE = 50;
|
|||
|
$MAX_COMMAND_SIZE = 1000;
|
|||
|
|
|||
|
main();
|
|||
|
exit;
|
|||
|
|
|||
|
|
|||
|
sub main::default_key {
|
|||
|
#------------ default key bindings ----------
|
|||
|
# these can be overidden by config lines
|
|||
|
#
|
|||
|
# It may need these lines in .Xdefault to make home and end key work
|
|||
|
# FvwmConsole*VT100*Translations: #override \n \
|
|||
|
# <Key> Home: string(0x1b) string("[1~" ) \n \
|
|||
|
# <Key> Delete: string(0x1b) string("[3~" ) \n
|
|||
|
# <Key> End: string(0x1b) string("[4~" ) \n
|
|||
|
|
|||
|
package User;
|
|||
|
|
|||
|
$ESC = $main::ESC;
|
|||
|
$Key{"$ESC\[1~"} = 'bol'; #home Key
|
|||
|
$Key{"$ESC\[3~"} = 'del_char';
|
|||
|
$Key{"$ESC\[4~"} = 'eol'; #end key
|
|||
|
$Key{"$ESC\[A"}= 'prev_line'; #up
|
|||
|
$Key{"$ESC\[B"}= 'next_line'; #down
|
|||
|
$Key{"$ESC\[C"}= 'next_char'; #right
|
|||
|
$Key{"$ESC\[D"}= 'prev_char'; #left
|
|||
|
$Key{"${ESC}f"}= 'next_word';
|
|||
|
$Key{"${ESC}b"} = 'prev_word';
|
|||
|
|
|||
|
$Key{"$ESC"} = 'prefix';
|
|||
|
$Key{"\cD"} = 'del_char';
|
|||
|
$Key{"\c?"} = 'del_char';
|
|||
|
$Key{"\cH"} = 'bs';
|
|||
|
$Key{"\cq"} = 'quote';
|
|||
|
$Key{"\cU"} = 'del_line';
|
|||
|
$Key{"\cs"} = 'search';
|
|||
|
$Key{"\cR"} = 'search_rev';
|
|||
|
$Key{"\cK"} = 'del_forw_line';
|
|||
|
$Key{"\ca"} = 'bol';
|
|||
|
$Key{"\ce"} = 'eol';
|
|||
|
$Key{"\cp"} = 'prev_line';
|
|||
|
$Key{"\cn"} = 'next_line';
|
|||
|
$Key{"\cf"} = 'next_char';
|
|||
|
$Key{"\cb"} = 'prev_char';
|
|||
|
$Key{"\cx"} = 'prefix';
|
|||
|
$Key{"\cx\cb"} = 'bind';
|
|||
|
$Key{"\cx\ck"} = 'cancel';
|
|||
|
$Key{"\cw"} = 'del_back_word';
|
|||
|
$Key{"\x8d"} = 'enter_wo_subst'; # alt enter
|
|||
|
$Key{"\n"} = 'enter';
|
|||
|
$Key{"\ci"} = 'ins_char (" ")';
|
|||
|
$Key{"\xE4"} = 'del_forw_word'; # alt_d
|
|||
|
$Key{"\xE6"} = 'next_word'; # alt_f
|
|||
|
$Key{"\xEB"} = 'bind'; # alt_k
|
|||
|
$Key{"\xEC"} = 'list_func'; # alt_k
|
|||
|
$Key{"\xF3"} = 'subst'; # alt_s
|
|||
|
$Key{"\xF4"} = 'termsize'; # alt_t
|
|||
|
$Key{"\xE2"} = 'prev_word'; # alt_b
|
|||
|
$Key{"\xb1"} = 'ins_nth_word(1)';
|
|||
|
$Key{"\xb2"} = 'ins_nth_word(2)';
|
|||
|
$Key{"\xb3"} = 'ins_nth_word(3)';
|
|||
|
$Key{"\xb4"} = 'ins_nth_word(4)';
|
|||
|
$Key{"\xb5"} = 'ins_nth_word(5)';
|
|||
|
$Key{"\xb6"} = 'ins_nth_word(6)';
|
|||
|
$Key{"\xb7"} = 'ins_nth_word(7)';
|
|||
|
$Key{"\xb8"} = 'ins_nth_word(8)';
|
|||
|
$Key{"\xb9"} = 'ins_nth_word(9)';
|
|||
|
$Key{"${ESC}b"} = 'prev_word'; # esc_b
|
|||
|
$Key{"${ESC}f"} = 'next_word'; # esc_f
|
|||
|
$Key{"${ESC}>"} = 'eoh_ign_mode'; # end of history, ignore mode
|
|||
|
$Key{"${ESC}<"} = 'boh_ign_mode'; # begining of history, ignore mode
|
|||
|
$Key{"${ESC}."} = 'ins_last_word';
|
|||
|
|
|||
|
$Key{EOF} = "\cD"; #eof work only when line is empty
|
|||
|
$Subst{'^#.*'} = ''; # ignore comments
|
|||
|
#---------------- end of key binding -----------------
|
|||
|
|
|||
|
#---------------- Terminal control -------------------
|
|||
|
$TERM_EEOL = "$ESC\[K"; # erase to end of line
|
|||
|
$TERM_RIGHT = "$ESC\[C"; # move cursor right
|
|||
|
$TERM_LEFT = "$ESC\[D"; # move cursor left
|
|||
|
$TERM_DOWN = "$ESC\[B"; # move cursor up
|
|||
|
$TERM_UP = "$ESC\[A"; # move cursor up
|
|||
|
}
|
|||
|
|
|||
|
sub read_config {
|
|||
|
my( $hash,@keys,$key,@vals,$val);
|
|||
|
while(<SH>) {
|
|||
|
last if $_ eq "_C_Config_Line_End_\n";
|
|||
|
next if !s/^\*${Sname}//;
|
|||
|
($hash,@keys[0..3],@vals) =
|
|||
|
(/
|
|||
|
^(\w+)\s+ #hash name
|
|||
|
('([^\']*)'|"([^\"]*)"|(\S+)) #key quoted or bare word
|
|||
|
(\s+('([^\']*)'|"([^\"]*)"|(\S+)))? #value
|
|||
|
/x);
|
|||
|
$key = $keys[1].$keys[2].$keys[3];
|
|||
|
$val = $vals[2].$vals[3].$vals[4];
|
|||
|
|
|||
|
if( defined %{$User::{$hash}} ) {
|
|||
|
User::bind( $hash, $key, $val );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
sub main {
|
|||
|
my($sin, $cmd);
|
|||
|
my($name, $ppid, $cpid);
|
|||
|
|
|||
|
socket(SH, PF_UNIX, SOCK_STREAM, 0) || die "$! ";
|
|||
|
$sun = sockaddr_un($SOCKET_NAME);
|
|||
|
connect(SH,$sun) || die "$sun:", $!;
|
|||
|
print "$Filename $VERSION\n";
|
|||
|
default_key();
|
|||
|
read_config(); #must be done before forking
|
|||
|
|
|||
|
$ppid = $$;
|
|||
|
if( $cpid = fork() ) {
|
|||
|
&input_open($tty,$tty,$HISTFILE,1);
|
|||
|
while( $cmd = &input('','',1) ) {
|
|||
|
next if $cmd =~/^\s*$/;
|
|||
|
last if $cmd eq "\0";
|
|||
|
if( length($cmd) > $MAX_COMMMAND_SIZE ) {
|
|||
|
print User::OUT "\a";
|
|||
|
}
|
|||
|
send( SH, $cmd."\0", 0 );
|
|||
|
}
|
|||
|
dokill( $cpid );
|
|||
|
} else {
|
|||
|
#child handles output
|
|||
|
while(<SH>) {
|
|||
|
last if $_ eq '';
|
|||
|
if( $_ eq "_C_Socket_Close_\n" ) {
|
|||
|
dokill( $ppid );
|
|||
|
}
|
|||
|
print;
|
|||
|
}
|
|||
|
dokill( $ppid );
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
sub dokill {
|
|||
|
my($pid) = @_;
|
|||
|
unlink SH;
|
|||
|
kill -9,$pid if $pid;
|
|||
|
exit;
|
|||
|
}
|
|||
|
|
|||
|
sub input_open {
|
|||
|
# arg0 input device
|
|||
|
# arg1 output device
|
|||
|
# arg2 history file
|
|||
|
# arg3 key selection - bit0
|
|||
|
# bit1
|
|||
|
# bit2 return undef esc code as it is
|
|||
|
|
|||
|
($Dev_in,$Dev_out,$File,$Ksel) = @_;
|
|||
|
if( !$Dev_in ) {$Dev_in = $tty;}
|
|||
|
elsif( $Dev_in eq "not a tty" ) { $Dev_in = $ENV{'TTY'};}
|
|||
|
if( !$Dev_out ) {$Dev_out = $tty;}
|
|||
|
if( !$File ) { $File = '/tmp/input.tmp';}
|
|||
|
open(User::IN,"<$Dev_in") || die "open in at input_open '$Dev_in' $!\n";
|
|||
|
open(User::OUT,">$Dev_out") || die "can't open input at 'input_open' $!\n";
|
|||
|
select((select(User::OUT), $| = 1)[0]); # unbuffer pipe
|
|||
|
if( defined $File ) {
|
|||
|
if( open(INITF,"$File") ) {
|
|||
|
do "$File";
|
|||
|
@Histall=<INITF>; close(INITF); $#Histall--;
|
|||
|
}else{
|
|||
|
print STDERR "Can't open history file $File\n";
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
sub input_close {
|
|||
|
close(User::IN);
|
|||
|
close(User::OUT);
|
|||
|
}
|
|||
|
|
|||
|
sub getchar {
|
|||
|
# get char from input
|
|||
|
# if esc , check for more char
|
|||
|
my($c,$s,$rin,$rout);
|
|||
|
sysread(User::IN, $c, 1);
|
|||
|
if( $c ne $ESC ) {
|
|||
|
$s = $c;
|
|||
|
}else {
|
|||
|
$rin = '';
|
|||
|
vec( $rin, fileno(User::IN),1) = 1;
|
|||
|
$n= select( $rout=$rin, undef, undef, 0.1 );
|
|||
|
$s = $ESC;
|
|||
|
if($n) {
|
|||
|
while($n= select( $rout=$rin, undef, undef, 0.1 ) ) {
|
|||
|
sysread( User::IN, $c, 1 );
|
|||
|
$s .= $c;
|
|||
|
last if $c =~ /[A-Dz~]/; # end of escape seq
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
$s;
|
|||
|
}
|
|||
|
|
|||
|
sub insert_char {
|
|||
|
local($c,*len,*ix,*hist) =@_;
|
|||
|
local($clen);
|
|||
|
$clen = length $c;
|
|||
|
if( $init_in ) {
|
|||
|
$len = $ix = $clen; # new hist - clear old one
|
|||
|
$hist[$#hist] = $c;
|
|||
|
}else{
|
|||
|
substr($hist[$#hist],$ix,0) = $c; #insert char
|
|||
|
$len += $clen;
|
|||
|
$ix += $clen;
|
|||
|
}
|
|||
|
}
|
|||
|
sub stty {
|
|||
|
my($arg) = @_;
|
|||
|
`/bin/stty $arg <$tty 2>&1`;
|
|||
|
# if( -x "/usr/5bin/stty" ) {
|
|||
|
# `/usr/5bin/stty $arg <$tty`;
|
|||
|
# }elsif( -x "/usr/bin/stty" ) {
|
|||
|
# `/usr/bin/stty $arg `;
|
|||
|
# }else {
|
|||
|
# `/bin/stty $arg `;
|
|||
|
# }
|
|||
|
}
|
|||
|
|
|||
|
sub add_hist {
|
|||
|
# add input into history file
|
|||
|
local($type,*cmd) = @_; #not my
|
|||
|
my( $t )= sprintf("%s",$type);
|
|||
|
my($h) = $cmd[$#cmd];
|
|||
|
return if !defined $File;
|
|||
|
if( $#cmd ==0 || $h ne $cmd[$#cmd-1] ) {
|
|||
|
$h =~ s/([\"@\$\\])/\\$1/g;
|
|||
|
$t =~ s/^\*//;
|
|||
|
push(@Histall, "push (\@$t, \"$h\");\n" );
|
|||
|
@Histall = splice( @Histall, -$HIST_SIZE, $HIST_SIZE ); # take last HIST_SIZE commands
|
|||
|
if( open( FILE, ">$File" ) ){
|
|||
|
print FILE @Histall;
|
|||
|
print FILE "1;\n";
|
|||
|
close(FILE);
|
|||
|
}
|
|||
|
}else {
|
|||
|
$#cmd--;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#----------------
|
|||
|
# print mini help
|
|||
|
#----------------
|
|||
|
sub usage_error {
|
|||
|
open( THIS, "$0");
|
|||
|
while(<THIS>) {
|
|||
|
s/\$0/$Filename/;
|
|||
|
if( /^\#/ ) {
|
|||
|
print STDERR $_;
|
|||
|
}else{
|
|||
|
last;
|
|||
|
}
|
|||
|
}
|
|||
|
close THIS;
|
|||
|
sleep 3;
|
|||
|
exit 1;
|
|||
|
}
|
|||
|
|
|||
|
sub search_mode {
|
|||
|
local(*c, *s, *prompt, *mode, *isp, *hist ) =@_;
|
|||
|
my($p_save, $isp_cur);
|
|||
|
if($c eq "\n"){
|
|||
|
$prompt = $p_save;
|
|||
|
$mode = 'normal';
|
|||
|
last IN_STACK;
|
|||
|
}
|
|||
|
$isp_cur = $isp;
|
|||
|
if( $User::Key{$c} =~ /^search/ ) {
|
|||
|
#search furthur
|
|||
|
$mode = $User::Key{$c};
|
|||
|
while(1) {
|
|||
|
if( $mode eq 'search_rev' && --$isp<0 ||
|
|||
|
$mode eq 'search' && ++$isp>$#hist-1 ) {
|
|||
|
print User::OUT "\a"; # couldn't find one
|
|||
|
$isp = $isp_cur;
|
|||
|
last;
|
|||
|
}
|
|||
|
last if( index($hist[$isp],$s) >=0);
|
|||
|
}
|
|||
|
}elsif( $User::Key{$c} eq 'bs' ) {
|
|||
|
$s =~ s/.$//;
|
|||
|
}elsif( ord($c) < 32 ) {
|
|||
|
#non-printable char, get back to normal mode
|
|||
|
print User::OUT "\a";
|
|||
|
$prompt = $p_save;
|
|||
|
$mode = 'normal';
|
|||
|
return;
|
|||
|
}else{
|
|||
|
$s .= $c;
|
|||
|
while(1) {
|
|||
|
last if (index($hist[$isp],$s) >=0);
|
|||
|
if( $mode eq 'search_rev' && --$isp<0 ||
|
|||
|
$mode eq 'search' && ++$isp>$#hist ) {
|
|||
|
print User::OUT "\a"; #couldn't find one
|
|||
|
chop($s);
|
|||
|
$isp = $isp_cur;
|
|||
|
last;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
$prompt = "($mode)'$s':";
|
|||
|
}
|
|||
|
|
|||
|
sub calcxy {
|
|||
|
my( $mode, $prompt, $len, $ix, $off, $wd ) = @_;
|
|||
|
my($plen);
|
|||
|
my( $y_len, $y_ix, $col);
|
|||
|
my($adjust); # 1 when the last char is on right edge
|
|||
|
|
|||
|
$plen = length($prompt);
|
|||
|
$y_len = int (($plen+$len+$off) / $wd );
|
|||
|
$adjust = ( (($plen+$len+$off) % $wd == 0) && ($y_len > 0 )) ? 1:0;
|
|||
|
if( $mode =~ /^search/ ) {
|
|||
|
#move cursor to search string
|
|||
|
$y_ix = int (($plen-2+$off) / $wd );
|
|||
|
$col = ($plen-2+$off) % $wd;
|
|||
|
}else{
|
|||
|
#normal mode - move cursor back to $ix
|
|||
|
$y_ix = int (($plen+$ix+$off) / $wd );
|
|||
|
$col = ($plen+$ix+$off) % $wd;
|
|||
|
}
|
|||
|
($y_len, $y_ix, $col, $adjust);
|
|||
|
}
|
|||
|
|
|||
|
package User;
|
|||
|
|
|||
|
sub move_cursor {
|
|||
|
my($x,$y, $x_prev,$y_prev) = @_;
|
|||
|
my($termcode);
|
|||
|
|
|||
|
$termcode = '';
|
|||
|
if($y > $y_prev ) {
|
|||
|
$termcode = $TERM_DOWN x ($y-$y_prev);
|
|||
|
}elsif( $y < $y_prev ) {
|
|||
|
$termcode = $TERM_UP x ($y_prev-$y);
|
|||
|
}
|
|||
|
if( $x > $x_prev ) {
|
|||
|
$termcode .= $TERM_RIGHT x ($x-$x_prev);
|
|||
|
}elsif( $x < $x_prev ) {
|
|||
|
$termcode .= $TERM_LEFT x ($x_prev-$x);
|
|||
|
}
|
|||
|
print OUT $termcode;
|
|||
|
}
|
|||
|
|
|||
|
sub another_line {
|
|||
|
$init_in = 1-$app;
|
|||
|
($hist[$#hist] = $hist[$isp]) =~ s/\n//;
|
|||
|
$ix = length($hist[$#hist]);
|
|||
|
}
|
|||
|
|
|||
|
sub main::input {
|
|||
|
# arg0 - prompt
|
|||
|
# arg1 - input stack
|
|||
|
# arg2 - append input to command if 1
|
|||
|
# arg3 - # of column offset
|
|||
|
local($prompt,*hist,$app,$off) = @_;
|
|||
|
local($len,$ix);
|
|||
|
local($c,$isp,$s,$wisp);
|
|||
|
local($mode);
|
|||
|
local(%lastop);
|
|||
|
|
|||
|
local($init_in);
|
|||
|
local($print_line); #0-none, 1-whole, 2-from cursor
|
|||
|
my($y_ix,$y_ix0,$y_len,$wd,$ht,$col,$col0);
|
|||
|
my($term);
|
|||
|
my($init_in,$op);
|
|||
|
|
|||
|
$off = 0 if( !defined $off );
|
|||
|
*hist = *main::Hist if( ! defined @hist );
|
|||
|
$isp = ++$#hist ;
|
|||
|
$wisp = $isp;
|
|||
|
if( -f "/vmunix" ) {
|
|||
|
&main::stty("-echo -icanon min 1 time 0 stop ''");
|
|||
|
}else {
|
|||
|
&main::stty(" -echo -icanon eol \001 stop ''");
|
|||
|
}
|
|||
|
($ht,$wd) = &termsize();
|
|||
|
$y_ix = $y_len = 0;
|
|||
|
$mode = 'normal';
|
|||
|
another_line();
|
|||
|
$print_line = 1;
|
|||
|
|
|||
|
IN_STACK:while(1){
|
|||
|
|
|||
|
if( $print_line==0 ) {
|
|||
|
#just move cursor
|
|||
|
($y_len,$y_ix,$col,$adjust) =
|
|||
|
&main::calcxy($mode,$prompt,$len,$ix,$off,$wd);
|
|||
|
move_cursor( $col,$y_ix, $col0,$y_ix0);
|
|||
|
|
|||
|
}elsif($print_line==2 || $print_line==3 ) {
|
|||
|
# delete - print cursor to eol
|
|||
|
$len = length($hist[$#hist]);
|
|||
|
($y_len,$y_ix,$col,$adjust) =
|
|||
|
&main::calcxy($mode,$prompt,$len,$ix,$off,$wd);
|
|||
|
|
|||
|
if( $print_line==3 ) {
|
|||
|
# delete backward
|
|||
|
move_cursor( $col,$y_ix, $col0,$y_ix0);
|
|||
|
}
|
|||
|
|
|||
|
if( $y_len0 > $y_ix && ($adjust || $y_len0 > $y_len) ) {
|
|||
|
print( OUT "\n$TERM_EEOL" x ($y_len0-$y_ix),
|
|||
|
$TERM_UP x ($y_len0-$y_ix),
|
|||
|
"\r", $TERM_RIGHT x $col, );
|
|||
|
}
|
|||
|
print( OUT substr("$prompt$hist[$#hist]", $ix),
|
|||
|
$adjust ? '':$TERM_EEOL,
|
|||
|
"\r", $TERM_RIGHT x $col,
|
|||
|
$TERM_UP x ($y_len-$y_ix) ,
|
|||
|
($adjust && $ix!=$len)? $TERM_DOWN : '' );
|
|||
|
|
|||
|
|
|||
|
}elsif($print_line==4) {
|
|||
|
# insert
|
|||
|
$len = length($hist[$#hist]);
|
|||
|
($y_len,$y_ix,$col,$adjust) =
|
|||
|
&main::calcxy($mode,$prompt,$len,$ix,$off,$wd);
|
|||
|
|
|||
|
print( OUT substr("$prompt$hist[$#hist]", $ix),
|
|||
|
$TERM_UP x ($y_len-$y_ix) ,"\r", $TERM_RIGHT x $col,
|
|||
|
$TERM_DOWN x $adjust );
|
|||
|
|
|||
|
}else{
|
|||
|
# print whole line
|
|||
|
$len = length($hist[$#hist]);
|
|||
|
#move cursor to bol on screen, erase prev printout
|
|||
|
print (OUT $TERM_DOWN x ($y_len-$y_ix),
|
|||
|
"\r$TERM_EEOL$TERM_UP" x ($y_len),
|
|||
|
"\r$TERM_EEOL\r",
|
|||
|
$TERM_RIGHT x $off,"$prompt$hist[$#hist]");
|
|||
|
($y_len,$y_ix,$col,$adjust) =
|
|||
|
&main::calcxy($mode,$prompt,$len,$ix,$off,$wd);
|
|||
|
|
|||
|
#mv cursor to cur pos
|
|||
|
print( OUT $TERM_UP x ($y_len-$y_ix) ,"\r", $TERM_RIGHT x $col,
|
|||
|
$TERM_DOWN x $adjust);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
GETC:{
|
|||
|
($col0, $y_ix0, $y_len0) = ($col, $y_ix, $y_len);
|
|||
|
$print_line=1;
|
|||
|
|
|||
|
$c = main::getchar();
|
|||
|
while($Key{$c} eq "prefix" ) {
|
|||
|
$c .= main::getchar();
|
|||
|
}
|
|||
|
|
|||
|
($op = $Key{$c}) =~ s/(.*)\s*[\(;].*/$1/;
|
|||
|
$op =~ /(\w+)$/;
|
|||
|
$op = $1;
|
|||
|
|
|||
|
if( $Key{$c} =~ /ign_mode/ ) {
|
|||
|
# ignore mode and execute command
|
|||
|
eval "&{$Key{$c}}";
|
|||
|
}elsif( $mode =~ /^search/ ) {
|
|||
|
main::search_mode(*c,*s,*prompt,*mode,*isp, *hist);
|
|||
|
another_line();
|
|||
|
}elsif( $c eq $Key{EOF} && $len==0 ) {
|
|||
|
return ''; # eof return null
|
|||
|
}elsif( defined $Key{$c} ) {
|
|||
|
eval "&{$Key{$c}}";
|
|||
|
$lastop{op} = $op;
|
|||
|
}elsif( ord ($c) < 32 ) {
|
|||
|
#undefined control char
|
|||
|
print OUT "\a";
|
|||
|
$print_line = 0;
|
|||
|
}else {
|
|||
|
$lastop{op} = 'ins_char';
|
|||
|
&ins_char( $c );
|
|||
|
print OUT $c;
|
|||
|
}
|
|||
|
$init_in = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( $y_ix != $y_len ) {
|
|||
|
print OUT "\n" x ($y_len-$y_ix);
|
|||
|
}
|
|||
|
&main::stty($org_stty);
|
|||
|
|
|||
|
print OUT "\n";
|
|||
|
if( $hist[$#hist] eq '' ) {
|
|||
|
pop(@hist);
|
|||
|
return "\n";
|
|||
|
}
|
|||
|
if( $#hist>0 && $hist[$#hist] eq $hist[$#hist-1] ) {
|
|||
|
pop(@hist); # if it is the same, delete
|
|||
|
}else{
|
|||
|
&main::add_hist( *hist, *hist );
|
|||
|
}
|
|||
|
$hist[$#hist]."\n";
|
|||
|
}
|
|||
|
|
|||
|
#-----------------------------
|
|||
|
# editinig command functions
|
|||
|
#
|
|||
|
# functions must be below here to be listed by list_func
|
|||
|
#
|
|||
|
# the variables below are local to sub input
|
|||
|
# $prompt,$hist,$app,$off
|
|||
|
# $len,$ix
|
|||
|
# $c,$isp,$wisp,$s
|
|||
|
# $mode
|
|||
|
#-----------------------------
|
|||
|
sub prefix { } # it's only here to be listed by list_func
|
|||
|
sub boh {
|
|||
|
$isp = 0;
|
|||
|
another_line();
|
|||
|
}
|
|||
|
sub boh_ign_mode {
|
|||
|
boh();
|
|||
|
}
|
|||
|
sub bol {
|
|||
|
$ix = 0 ;
|
|||
|
$print_line=0;
|
|||
|
}
|
|||
|
sub bs {
|
|||
|
my($l) = @_;
|
|||
|
$l = 1 if $l eq '';
|
|||
|
if( $len && $ix ) {
|
|||
|
$ix-=$l; # mv left
|
|||
|
substr($hist[$#hist],$ix,$l) = ""; # del char
|
|||
|
}
|
|||
|
$print_line = 3;
|
|||
|
}
|
|||
|
sub del_back_line {
|
|||
|
substr($hist[$#hist],0,$ix) = "";
|
|||
|
$ix = 0;
|
|||
|
$print_line = 3;
|
|||
|
}
|
|||
|
sub del_forw_line {
|
|||
|
substr($hist[$#hist],$ix) = "";
|
|||
|
$print_line = 2;
|
|||
|
}
|
|||
|
sub del_char {
|
|||
|
my($l) = @_;
|
|||
|
$l = 1 if $l eq '';
|
|||
|
if( $len > $ix ) {
|
|||
|
substr($hist[$#hist],$ix,$l) = ""; # del char
|
|||
|
}
|
|||
|
$print_line = 2;
|
|||
|
}
|
|||
|
sub del_line {
|
|||
|
$ix = 0;
|
|||
|
$hist[$#hist] = "";
|
|||
|
$print_line = 3;
|
|||
|
}
|
|||
|
sub del_back_word {
|
|||
|
my($tmp);
|
|||
|
$tmp = substr($hist[$#hist],0,$ix);
|
|||
|
$tmp =~ s/(^|\S+)\s*$//;
|
|||
|
$tmp = length $tmp;
|
|||
|
substr($hist[$#hist],$tmp,$ix-$tmp) = "";
|
|||
|
$ix = $tmp;
|
|||
|
$print_line = 3;
|
|||
|
}
|
|||
|
sub del_forw_word {
|
|||
|
$hist[$#hist] =~ s/^(.{$ix})\s*\S+/$1/;
|
|||
|
$print_line = 2;
|
|||
|
}
|
|||
|
sub enter {
|
|||
|
subst();
|
|||
|
enter_wo_subst();
|
|||
|
}
|
|||
|
sub eoh {
|
|||
|
if( $isp==$#hist ) {
|
|||
|
print OUT "\a";
|
|||
|
}else{
|
|||
|
$hist[$#hist] = ''
|
|||
|
}
|
|||
|
$isp = $#hist;
|
|||
|
another_line();
|
|||
|
$print_line = 1;
|
|||
|
}
|
|||
|
sub eoh_ign_mode {
|
|||
|
eoh();
|
|||
|
$print_line = 1;
|
|||
|
}
|
|||
|
sub eol {
|
|||
|
$ix = $len;
|
|||
|
$print_line=0;
|
|||
|
}
|
|||
|
sub execute {
|
|||
|
eval "$hist[$#hist]";
|
|||
|
if( $#hist>0 && $hist[$#hist] eq $hist[$#hist-1] ) {
|
|||
|
pop(@hist); # if it is the same, delete
|
|||
|
}else{
|
|||
|
&main::add_hist( *hist, *hist );
|
|||
|
}
|
|||
|
push( @hist, ''); # deceive 'input' it is an empty line
|
|||
|
last IN_STACK;
|
|||
|
}
|
|||
|
sub ins_char {
|
|||
|
my($c) = @_;
|
|||
|
&main::insert_char($c,*len,*ix,*hist);
|
|||
|
$print_line = 4;
|
|||
|
}
|
|||
|
sub ins_last_word {
|
|||
|
if( $lastop{op} =~ /^ins_(nth|last)_word/ ) {
|
|||
|
return if $wisp < 1;
|
|||
|
#delete last last_word
|
|||
|
bs(length $lastop{word});
|
|||
|
}else {
|
|||
|
$wisp = $#hist;
|
|||
|
return if $wisp < 1;
|
|||
|
}
|
|||
|
$hist[--$wisp] =~ /(\S+)\s*$/;
|
|||
|
$lastop{word} = $1;
|
|||
|
ins_char($lastop{word});
|
|||
|
}
|
|||
|
sub ins_nth_word {
|
|||
|
my($n) = @_;
|
|||
|
if( $lastop{op} =~ /^ins_(nth|last)_word/ ) {
|
|||
|
return if $wisp < 1;
|
|||
|
#delete last last_word
|
|||
|
bs(length $lastop{word});
|
|||
|
}else {
|
|||
|
$wisp = $#hist;
|
|||
|
return if $wisp < 1;
|
|||
|
}
|
|||
|
$hist[--$wisp] =~ /((\S+)\s*){1,$n}/;
|
|||
|
$lastop{word} = $2;
|
|||
|
ins_char($lastop{word});
|
|||
|
}
|
|||
|
sub list_func {
|
|||
|
my( $s, @cmds, $cmd, $func);
|
|||
|
$func = 0;
|
|||
|
open( THIS, "$0" ) || return; #shouldn't occur
|
|||
|
while( $s = <THIS> ) {
|
|||
|
if( $s =~ /^\s*sub\s+main::input\s*\{/ ) {
|
|||
|
$func = 1;
|
|||
|
next;
|
|||
|
}
|
|||
|
next if !$func;
|
|||
|
if( $s =~ s/^\s*sub\s+// ) {
|
|||
|
$s =~ s/\s*[\{].*//;
|
|||
|
push @cmds,$s;
|
|||
|
}
|
|||
|
}
|
|||
|
close THIS;
|
|||
|
foreach $cmd (sort @cmds) {
|
|||
|
print OUT $cmd;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
sub bind {
|
|||
|
# bind Key or Subst
|
|||
|
# if there is no arguments, then list them
|
|||
|
my($hash,$key,$val) = @_;
|
|||
|
my( $mod,$chr,$v2,$k,$cnt );
|
|||
|
if( defined %{$hash} ) {
|
|||
|
$k = $key;
|
|||
|
if( $hash eq "Key" ) {
|
|||
|
($v2 = $val) =~ s/\s*[\(;].*//;
|
|||
|
if( !defined &{$v2} ) {
|
|||
|
print STDERR "Unknown function $v2\n";
|
|||
|
return;
|
|||
|
}
|
|||
|
$mod = 0; $cnt =0; $k = '';
|
|||
|
for( $i=0; $i<length $key; $i++ ) {
|
|||
|
$chr = substr($key,$i,1);
|
|||
|
if( $chr eq "\\" ) {
|
|||
|
$chr = substr($key,++$i,1);
|
|||
|
if( $chr=~/m/i ) {
|
|||
|
$mod = 0x80;
|
|||
|
}elsif( $chr=~/c/i ) {
|
|||
|
$cnt = 1;
|
|||
|
}elsif( $chr=~/e/i ) {
|
|||
|
$chr = $ESC;
|
|||
|
$chr = pack("c",ord($chr)+$mod);
|
|||
|
$mod = 0 ; $cnt = 0;
|
|||
|
$k .= $chr;
|
|||
|
}else {
|
|||
|
print "Unknown char $key\n";
|
|||
|
}
|
|||
|
}else {
|
|||
|
if( $cnt ) {
|
|||
|
eval "\$chr = \"\\c$chr\" ";
|
|||
|
}
|
|||
|
$chr = pack("c",ord($chr)+$mod);
|
|||
|
$mod = 0 ; $cnt = 0;
|
|||
|
$k .= $chr;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
if( $val eq '' ) {
|
|||
|
delete ${$hash}{$k};
|
|||
|
}else {
|
|||
|
${$hash}{$k} = $val;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}else {
|
|||
|
foreach $key (sort(keys(%Key) )){
|
|||
|
$val = $Key{$key};
|
|||
|
$mod = '';
|
|||
|
while( $key =~ s/(.|\s)// ) {
|
|||
|
$chr = $1;
|
|||
|
if( ord($chr) >= 0x80 ) {
|
|||
|
$mod .= '\M';
|
|||
|
$chr = pack("c", ord($chr)-0x80);
|
|||
|
}
|
|||
|
if( $chr eq $ESC ) {
|
|||
|
$chr = '\E';
|
|||
|
}elsif( ord($chr) < 0x20 ) {
|
|||
|
$mod .= '\C';
|
|||
|
$chr = pack("c", ord($chr)+0x40);
|
|||
|
}elsif( ord($chr) == 0x7f ) {
|
|||
|
$chr = '\C?';
|
|||
|
}
|
|||
|
$mod .= $chr;
|
|||
|
}
|
|||
|
if( ord($val) < 0x20 ) {
|
|||
|
$val = '\C'.pack("c", ord($val)+0x40);
|
|||
|
}
|
|||
|
print OUT "Key $mod $val\n";
|
|||
|
}
|
|||
|
while( ($key,$val) = each(%Subst) ) {
|
|||
|
print OUT "Subst $key $val\n";
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
sub next_char {
|
|||
|
$ix++ if ($ix<$len);
|
|||
|
$print_line=0;
|
|||
|
}
|
|||
|
|
|||
|
sub next_line {
|
|||
|
if($isp<$#hist) {
|
|||
|
$isp++;
|
|||
|
if( $isp==$#hist ) {
|
|||
|
$hist[$isp] = '';
|
|||
|
}
|
|||
|
}else {
|
|||
|
$isp = $#hist;
|
|||
|
print OUT "\a";
|
|||
|
}
|
|||
|
another_line();
|
|||
|
}
|
|||
|
|
|||
|
sub next_word {
|
|||
|
$hist[$#hist] =~ /^(.{$ix}\S*(\s+|$))/;
|
|||
|
$ix = length($1);
|
|||
|
$print_line=0;
|
|||
|
}
|
|||
|
|
|||
|
sub enter_wo_subst {
|
|||
|
last IN_STACK;
|
|||
|
}
|
|||
|
|
|||
|
sub prev_char {
|
|||
|
$ix-- if $ix>0;
|
|||
|
$print_line=0;
|
|||
|
}
|
|||
|
|
|||
|
sub prev_line {
|
|||
|
if($isp>0) {
|
|||
|
$isp--;
|
|||
|
}else {
|
|||
|
$isp = 0;
|
|||
|
print OUT "\a";
|
|||
|
}
|
|||
|
another_line();
|
|||
|
}
|
|||
|
|
|||
|
sub prev_word {
|
|||
|
my($tmp);
|
|||
|
$tmp = substr($hist[$#hist],0,$ix);
|
|||
|
$tmp =~ s/(^|\S+)\s*$//;
|
|||
|
$ix = length($tmp);
|
|||
|
$print_line=0;
|
|||
|
}
|
|||
|
|
|||
|
sub cancel {
|
|||
|
$hist[$#hist] = "";
|
|||
|
$len = 0;
|
|||
|
last IN_STACK;
|
|||
|
}
|
|||
|
sub quote {
|
|||
|
my($c);
|
|||
|
sysread(IN, $c, 1);
|
|||
|
# $c = getc(IN);
|
|||
|
ins_char($c);
|
|||
|
}
|
|||
|
|
|||
|
sub search_rev {
|
|||
|
$s = '';
|
|||
|
$mode = 'search_rev';
|
|||
|
$p_save = $prompt;
|
|||
|
$prompt = "($mode)'$s':";
|
|||
|
$hist[$#hist] = $hist[$isp];
|
|||
|
another_line();
|
|||
|
}
|
|||
|
|
|||
|
sub search {
|
|||
|
$s = '';
|
|||
|
$mode = 'search';
|
|||
|
$p_save = $prompt;
|
|||
|
$prompt = "($mode)'$s':";
|
|||
|
$hist[$#hist] = $hist[$isp];
|
|||
|
another_line();
|
|||
|
}
|
|||
|
|
|||
|
sub subst {
|
|||
|
my($key,$val);
|
|||
|
$done = 0;
|
|||
|
while( ($key,$val) = each(%Subst) ) {
|
|||
|
last if( eval "\$hist[\$#hist] =~ s\$key$val" ) ;
|
|||
|
}
|
|||
|
$ix = $len = length($hist[$#hist]);
|
|||
|
}
|
|||
|
|
|||
|
sub termsize {
|
|||
|
my($row, $col,$s);
|
|||
|
if( -f "/vmunix" ) {
|
|||
|
$s =&main::stty ("everything");
|
|||
|
($row,$col) = ($s =~ /(\d+)\s+rows[,\s]+(\d+)\s+columns/ );
|
|||
|
} else {
|
|||
|
$s =&main::stty ("-a");
|
|||
|
($row,$col) = ($s =~ /rows[=\s]+(\d+)[,;\s]+columns[=\s]+(\d+)/ );
|
|||
|
}
|
|||
|
($row,$col);
|
|||
|
}
|
|||
|
|