2 # $0 - Front end of FvwmConsole
3 # FvwmConsole server must be running
5 # Copyright 1997, Toshi Isogai
6 # You may use this code for any purpose, as long as the original
7 # copyright remains in the source code and all documentation
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 # If you change this, be sure to change the test in configure.in
29 $HISTFILE = "$ENV{FVWM_USERDIR}/.FvwmConsole-History0";
30 $SOCKET_NAME = "$ENV{FVWM_USERDIR}/.FvwmConsole-Socket";
32 $VERSION = '@VERSION@';
36 if (-c
"/dev/console" && -w
"/dev/console") {
38 if (!open (STDERR
, ">/dev/console")) {
39 print "Can't redirect STDERR to /dev/console: $!\n";
45 ($Filename = $0) =~ s@
.*/@@
;
46 ($Sname = $Filename) =~ s/C(\.pl)?$//;
51 $org_stty = &stty
('-g');
56 $MAX_COMMAND_SIZE = 1000;
62 sub main
::default_key
{
63 #------------ default key bindings ----------
64 # these can be overidden by config lines
66 # It may need these lines in .Xdefault to make home and end key work
67 # FvwmConsole*VT100*Translations: #override \n \
68 # <Key> Home: string(0x1b) string("[1~" ) \n \
69 # <Key> Delete: string(0x1b) string("[3~" ) \n
70 # <Key> End: string(0x1b) string("[4~" ) \n
75 $Key{"$ESC\[1~"} = 'bol'; #home Key
76 $Key{"$ESC\[3~"} = 'del_char';
77 $Key{"$ESC\[4~"} = 'eol'; #end key
78 $Key{"$ESC\[A"}= 'prev_line'; #up
79 $Key{"$ESC\[B"}= 'next_line'; #down
80 $Key{"$ESC\[C"}= 'next_char'; #right
81 $Key{"$ESC\[D"}= 'prev_char'; #left
82 $Key{"${ESC}f"}= 'next_word';
83 $Key{"${ESC}b"} = 'prev_word';
85 $Key{"$ESC"} = 'prefix';
86 $Key{"\cD"} = 'del_char';
87 $Key{"\c?"} = 'del_char';
89 $Key{"\cq"} = 'quote';
90 $Key{"\cU"} = 'del_line';
91 $Key{"\cs"} = 'search';
92 $Key{"\cR"} = 'search_rev';
93 $Key{"\cK"} = 'del_forw_line';
96 $Key{"\cp"} = 'prev_line';
97 $Key{"\cn"} = 'next_line';
98 $Key{"\cf"} = 'next_char';
99 $Key{"\cb"} = 'prev_char';
100 $Key{"\cx"} = 'prefix';
101 $Key{"\cx\cb"} = 'bind';
102 $Key{"\cx\ck"} = 'cancel';
103 $Key{"\cw"} = 'del_back_word';
104 $Key{"\x8d"} = 'enter_wo_subst'; # alt enter
105 $Key{"\n"} = 'enter';
106 $Key{"\ci"} = 'ins_char (" ")';
107 $Key{"\xE4"} = 'del_forw_word'; # alt_d
108 $Key{"\xE6"} = 'next_word'; # alt_f
109 $Key{"\xEB"} = 'bind'; # alt_k
110 $Key{"\xEC"} = 'list_func'; # alt_k
111 $Key{"\xF3"} = 'subst'; # alt_s
112 $Key{"\xF4"} = 'termsize'; # alt_t
113 $Key{"\xE2"} = 'prev_word'; # alt_b
114 $Key{"\xb1"} = 'ins_nth_word(1)';
115 $Key{"\xb2"} = 'ins_nth_word(2)';
116 $Key{"\xb3"} = 'ins_nth_word(3)';
117 $Key{"\xb4"} = 'ins_nth_word(4)';
118 $Key{"\xb5"} = 'ins_nth_word(5)';
119 $Key{"\xb6"} = 'ins_nth_word(6)';
120 $Key{"\xb7"} = 'ins_nth_word(7)';
121 $Key{"\xb8"} = 'ins_nth_word(8)';
122 $Key{"\xb9"} = 'ins_nth_word(9)';
123 $Key{"${ESC}b"} = 'prev_word'; # esc_b
124 $Key{"${ESC}f"} = 'next_word'; # esc_f
125 $Key{"${ESC}>"} = 'eoh_ign_mode'; # end of history, ignore mode
126 $Key{"${ESC}<"} = 'boh_ign_mode'; # begining of history, ignore mode
127 $Key{"${ESC}."} = 'ins_last_word';
129 $Key{EOF
} = "\cD"; #eof work only when line is empty
130 $Subst{'^#.*'} = ''; # ignore comments
131 #---------------- end of key binding -----------------
133 #---------------- Terminal control -------------------
134 $TERM_EEOL = "$ESC\[K"; # erase to end of line
135 $TERM_RIGHT = "$ESC\[C"; # move cursor right
136 $TERM_LEFT = "$ESC\[D"; # move cursor left
137 $TERM_DOWN = "$ESC\[B"; # move cursor up
138 $TERM_UP = "$ESC\[A"; # move cursor up
142 my( $hash,@keys,$key,@vals,$val);
144 last if $_ eq "_C_Config_Line_End_\n";
145 next if !s/^\*${Sname}//;
146 ($hash,@keys[0..3],@vals) =
149 ('([^\']*)'|"([^\"]*)"|(\S
+)) #key quoted or bare word
150 (\s
+('([^\']*)'|"([^\"]*)"|(\S
+)))?
#value
152 $key = $keys[1].$keys[2].$keys[3];
153 $val = $vals[2].$vals[3].$vals[4];
155 if( defined %{$User::{$hash}} ) {
156 User
::bind( $hash, $key, $val );
163 my($name, $ppid, $cpid);
165 socket(SH
, PF_UNIX
, SOCK_STREAM
, 0) || die "$!\n";
166 $sun = sockaddr_un
($SOCKET_NAME);
167 connect(SH
, $sun) || die "$sun: $!\n";
168 print "$Filename $VERSION\n";
170 read_config
(); #must be done before forking
173 if( $cpid = fork() ) {
174 &input_open
($tty,$tty,$HISTFILE,1);
175 while( $cmd = &input
('','',1) ) {
176 next if $cmd =~/^\s*$/;
177 last if $cmd eq "\0";
178 if( length($cmd) > $MAX_COMMMAND_SIZE ) {
179 print User
::OUT
"\a";
181 send( SH
, $cmd."\0", 0 );
185 #child handles output
188 if( $_ eq "_C_Socket_Close_\n" ) {
201 kill -9,$pid if $pid;
209 # arg3 key selection - bit0
211 # bit2 return undef esc code as it is
213 ($Dev_in,$Dev_out,$File,$Ksel) = @_;
214 if( !$Dev_in ) {$Dev_in = $tty;}
215 elsif( $Dev_in eq "not a tty" ) { $Dev_in = $ENV{'TTY'};}
216 if( !$Dev_out ) {$Dev_out = $tty;}
217 if( !$File ) { $File = '/tmp/input.tmp';}
218 open(User
::IN
,"<$Dev_in") || die "open in at input_open '$Dev_in' $!\n";
219 open(User
::OUT
,">$Dev_out") || die "can't open input at 'input_open' $!\n";
220 select((select(User
::OUT
), $| = 1)[0]); # unbuffer pipe
221 if( defined $File ) {
222 if( open(INITF
,"$File") ) {
224 @Histall=<INITF
>; close(INITF
); $#Histall--;
226 print STDERR
"Can't open history file $File\n";
237 # get char from input
238 # if esc , check for more char
239 my($c,$s,$rin,$rout);
240 sysread(User
::IN
, $c, 1);
245 vec( $rin, fileno(User
::IN
),1) = 1;
246 $n= select( $rout=$rin, undef, undef, 0.1 );
249 while($n= select( $rout=$rin, undef, undef, 0.1 ) ) {
250 sysread( User
::IN
, $c, 1 );
252 last if $c =~ /[A-Dz~]/; # end of escape seq
260 local($c,*len
,*ix
,*hist
) =@_;
264 $len = $ix = $clen; # new hist - clear old one
267 substr($hist[$#hist],$ix,0) = $c; #insert char
274 `/bin/stty $arg <$tty 2>&1`;
275 # if( -x "/usr/5bin/stty" ) {
276 # `/usr/5bin/stty $arg <$tty`;
277 # }elsif( -x "/usr/bin/stty" ) {
278 # `/usr/bin/stty $arg `;
285 # add input into history file
286 local($type,*cmd
) = @_; #not my
287 my( $t )= sprintf("%s",$type);
288 my($h) = $cmd[$#cmd];
289 return if !defined $File;
290 if( $#cmd ==0 || $h ne $cmd[$#cmd-1] ) {
291 $h =~ s/([\"@\$\\])/\\$1/g;
293 push(@Histall, "push (\@$t, \"$h\");\n" );
294 @Histall = splice( @Histall, -$HIST_SIZE, $HIST_SIZE ); # take last HIST_SIZE commands
295 if( open( FILE
, ">$File" ) ){
324 local(*c
, *s
, *prompt
, *mode
, *isp
, *hist
) =@_;
325 my($p_save, $isp_cur);
332 if( $User::Key
{$c} =~ /^search/ ) {
334 $mode = $User::Key
{$c};
336 if( $mode eq 'search_rev' && --$isp<0 ||
337 $mode eq 'search' && ++$isp>$#hist-1 ) {
338 print User
::OUT
"\a"; # couldn't find one
342 last if( index($hist[$isp],$s) >=0);
344 }elsif( $User::Key
{$c} eq 'bs' ) {
346 }elsif( ord($c) < 32 ) {
347 #non-printable char, get back to normal mode
348 print User
::OUT
"\a";
355 last if (index($hist[$isp],$s) >=0);
356 if( $mode eq 'search_rev' && --$isp<0 ||
357 $mode eq 'search' && ++$isp>$#hist ) {
358 print User
::OUT
"\a"; #couldn't find one
365 $prompt = "($mode)'$s':";
369 my( $mode, $prompt, $len, $ix, $off, $wd ) = @_;
371 my( $y_len, $y_ix, $col);
372 my($adjust); # 1 when the last char is on right edge
374 $plen = length($prompt);
375 $y_len = int (($plen+$len+$off) / $wd );
376 $adjust = ( (($plen+$len+$off) % $wd == 0) && ($y_len > 0 )) ?
1:0;
377 if( $mode =~ /^search/ ) {
378 #move cursor to search string
379 $y_ix = int (($plen-2+$off) / $wd );
380 $col = ($plen-2+$off) % $wd;
382 #normal mode - move cursor back to $ix
383 $y_ix = int (($plen+$ix+$off) / $wd );
384 $col = ($plen+$ix+$off) % $wd;
386 ($y_len, $y_ix, $col, $adjust);
392 my($x,$y, $x_prev,$y_prev) = @_;
397 $termcode = $TERM_DOWN x
($y-$y_prev);
398 }elsif( $y < $y_prev ) {
399 $termcode = $TERM_UP x
($y_prev-$y);
402 $termcode .= $TERM_RIGHT x
($x-$x_prev);
403 }elsif( $x < $x_prev ) {
404 $termcode .= $TERM_LEFT x
($x_prev-$x);
411 ($hist[$#hist] = $hist[$isp]) =~ s/\n//;
412 $ix = length($hist[$#hist]);
418 # arg2 - append input to command if 1
419 # arg3 - # of column offset
420 local($prompt,*hist
,$app,$off) = @_;
422 local($c,$isp,$s,$wisp);
427 local($print_line); #0-none, 1-whole, 2-from cursor
428 my($y_ix,$y_ix0,$y_len,$wd,$ht,$col,$col0);
432 $off = 0 if( !defined $off );
433 *hist
= *main
::Hist
if( ! defined @hist );
437 &main
::stty
("-echo -icanon min 1 time 0 stop ''");
439 &main
::stty
(" -echo -icanon eol \001 stop ''");
441 ($ht,$wd) = &termsize
();
449 if( $print_line==0 ) {
451 ($y_len,$y_ix,$col,$adjust) =
452 &main
::calcxy
($mode,$prompt,$len,$ix,$off,$wd);
453 move_cursor
( $col,$y_ix, $col0,$y_ix0);
455 }elsif($print_line==2 || $print_line==3 ) {
456 # delete - print cursor to eol
457 $len = length($hist[$#hist]);
458 ($y_len,$y_ix,$col,$adjust) =
459 &main
::calcxy
($mode,$prompt,$len,$ix,$off,$wd);
461 if( $print_line==3 ) {
463 move_cursor
( $col,$y_ix, $col0,$y_ix0);
466 if( $y_len0 > $y_ix && ($adjust || $y_len0 > $y_len) ) {
467 print( OUT
"\n$TERM_EEOL" x
($y_len0-$y_ix),
468 $TERM_UP x
($y_len0-$y_ix),
469 "\r", $TERM_RIGHT x
$col, );
471 print( OUT
substr("$prompt$hist[$#hist]", $ix),
472 $adjust ?
'':$TERM_EEOL,
473 "\r", $TERM_RIGHT x
$col,
474 $TERM_UP x
($y_len-$y_ix) ,
475 ($adjust && $ix!=$len)?
$TERM_DOWN : '' );
478 }elsif($print_line==4) {
480 $len = length($hist[$#hist]);
481 ($y_len,$y_ix,$col,$adjust) =
482 &main
::calcxy
($mode,$prompt,$len,$ix,$off,$wd);
484 print( OUT
substr("$prompt$hist[$#hist]", $ix),
485 $TERM_UP x
($y_len-$y_ix) ,"\r", $TERM_RIGHT x
$col,
486 $TERM_DOWN x
$adjust );
490 $len = length($hist[$#hist]);
491 #move cursor to bol on screen, erase prev printout
492 print (OUT
$TERM_DOWN x
($y_len-$y_ix),
493 "\r$TERM_EEOL$TERM_UP" x
($y_len),
495 $TERM_RIGHT x
$off,"$prompt$hist[$#hist]");
496 ($y_len,$y_ix,$col,$adjust) =
497 &main
::calcxy
($mode,$prompt,$len,$ix,$off,$wd);
499 #mv cursor to cur pos
500 print( OUT
$TERM_UP x
($y_len-$y_ix) ,"\r", $TERM_RIGHT x
$col,
501 $TERM_DOWN x
$adjust);
506 ($col0, $y_ix0, $y_len0) = ($col, $y_ix, $y_len);
509 $c = main
::getchar
();
510 while($Key{$c} eq "prefix" ) {
511 $c .= main
::getchar
();
514 ($op = $Key{$c}) =~ s/(.*)\s*[\(;].*/$1/;
518 if( $Key{$c} =~ /ign_mode/ ) {
519 # ignore mode and execute command
521 }elsif( $mode =~ /^search/ ) {
522 main
::search_mode
(*c
,*s
,*prompt
,*mode
,*isp
, *hist
);
524 }elsif( $c eq $Key{EOF
} && $len==0 ) {
525 return ''; # eof return null
526 }elsif( defined $Key{$c} ) {
529 }elsif( ord ($c) < 32 ) {
530 #undefined control char
534 $lastop{op
} = 'ins_char';
542 if( $y_ix != $y_len ) {
543 print OUT
"\n" x
($y_len-$y_ix);
545 &main
::stty
($org_stty);
548 if( $hist[$#hist] eq '' ) {
552 if( $#hist>0 && $hist[$#hist] eq $hist[$#hist-1] ) {
553 pop(@hist); # if it is the same, delete
555 &main
::add_hist
( *hist
, *hist
);
560 #-----------------------------
561 # editinig command functions
563 # functions must be below here to be listed by list_func
565 # the variables below are local to sub input
566 # $prompt,$hist,$app,$off
570 #-----------------------------
571 sub prefix
{ } # it's only here to be listed by list_func
588 substr($hist[$#hist],$ix,$l) = ""; # del char
593 substr($hist[$#hist],0,$ix) = "";
598 substr($hist[$#hist],$ix) = "";
605 substr($hist[$#hist],$ix,$l) = ""; # del char
616 $tmp = substr($hist[$#hist],0,$ix);
617 $tmp =~ s/(^|\S+)\s*$//;
619 substr($hist[$#hist],$tmp,$ix-$tmp) = "";
624 $hist[$#hist] =~ s/^(.{$ix})\s*\S+/$1/;
650 eval "$hist[$#hist]";
651 if( $#hist>0 && $hist[$#hist] eq $hist[$#hist-1] ) {
652 pop(@hist); # if it is the same, delete
654 &main
::add_hist
( *hist
, *hist
);
656 push( @hist, ''); # deceive 'input' it is an empty line
661 &main
::insert_char
($c,*len
,*ix
,*hist
);
665 if( $lastop{op
} =~ /^ins_(nth|last)_word/ ) {
667 #delete last last_word
668 bs
(length $lastop{word
});
673 $hist[--$wisp] =~ /(\S+)\s*$/;
675 ins_char
($lastop{word
});
679 if( $lastop{op
} =~ /^ins_(nth|last)_word/ ) {
681 #delete last last_word
682 bs
(length $lastop{word
});
687 $hist[--$wisp] =~ /((\S+)\s*){1,$n}/;
689 ins_char
($lastop{word
});
692 my( $s, @cmds, $cmd, $func);
694 open( THIS
, "$0" ) || return; #shouldn't occur
695 while( $s = <THIS
> ) {
696 if( $s =~ /^\s*sub\s+main::input\s*\{/ ) {
701 if( $s =~ s/^\s*sub\s+// ) {
707 foreach $cmd (sort @cmds) {
714 # if there is no arguments, then list them
715 my($hash,$key,$val) = @_;
716 my( $mod,$chr,$v2,$k,$cnt );
717 if( defined %{$hash} ) {
719 if( $hash eq "Key" ) {
720 ($v2 = $val) =~ s/\s*[\(;].*//;
721 if( !defined &{$v2} ) {
722 print STDERR
"Unknown function $v2\n";
725 $mod = 0; $cnt =0; $k = '';
726 for( $i=0; $i<length $key; $i++ ) {
727 $chr = substr($key,$i,1);
729 $chr = substr($key,++$i,1);
732 }elsif( $chr=~/c/i ) {
734 }elsif( $chr=~/e/i ) {
736 $chr = pack("c",ord($chr)+$mod);
740 print "Unknown char $key\n";
744 eval "\$chr = \"\\c$chr\" ";
746 $chr = pack("c",ord($chr)+$mod);
760 foreach $key (sort(keys(%Key) )){
763 while( $key =~ s/(.|\s)// ) {
765 if( ord($chr) >= 0x80 ) {
767 $chr = pack("c", ord($chr)-0x80);
771 }elsif( ord($chr) < 0x20 ) {
773 $chr = pack("c", ord($chr)+0x40);
774 }elsif( ord($chr) == 0x7f ) {
779 if( ord($val) < 0x20 ) {
780 $val = '\C'.pack("c", ord($val)+0x40);
782 print OUT
"Key $mod $val\n";
784 while( ($key,$val) = each(%Subst) ) {
785 print OUT
"Subst $key $val\n";
808 $hist[$#hist] =~ /^(.{$ix}\S*(\s+|$))/;
834 $tmp = substr($hist[$#hist],0,$ix);
835 $tmp =~ s/(^|\S+)\s*$//;
854 $mode = 'search_rev';
856 $prompt = "($mode)'$s':";
857 $hist[$#hist] = $hist[$isp];
865 $prompt = "($mode)'$s':";
866 $hist[$#hist] = $hist[$isp];
873 while( ($key,$val) = each(%Subst) ) {
874 last if( eval "\$hist[\$#hist] =~ s\x05\$key\x05$val\x05" ) ;
876 $ix = $len = length($hist[$#hist]);
882 $s =&main
::stty
("everything");
883 ($row,$col) = ($s =~ /(\d+)\s+rows[,\s]+(\d+)\s+columns/ );
885 $s =&main
::stty
("-a");
886 ($row,$col) = ($s =~ /rows[=\s]+(\d+)[,;\s]+columns[=\s]+(\d+)/ );