our
$VERSION
=
'2.10'
;
my
(
$SP
,
$RT
) = $|;
local
$SIG
{INT} =
local
$SIG
{QUIT} = \
&close
;
sub
ReadMode;
sub
ReadKey;
sub
new {
my
$class
=
shift
;
my
%param
=
@_
;
$ENV
{TERM} =
$ENV
{TERM} ||
''
;
$ENV
{TERMCAP} =
$ENV
{TERMCAP} ||
''
;
my
%dims
= get_size(
cols
=>
$param
{cols} ||80,
rows
=>
$param
{rows} ||25,
speed
=>
$param
{speed}||38400);
$dims
{rows}--;
$ENV
{TERM} =
'vt100'
if
(
$ENV
{TERM} eq
'screen'
&&
$ENV
{TERMCAP} !~ /sf/ );
if
( $^O =~ /MSWin/ ){
eval
"use Win32::Console::ANSI;"
;
if
( $@ ){
warn
"Could not load Win32::Console::ANSI, falling back to dumb mode - $@"
; }
else
{
$ENV
{TERM} =
'WINANSI'
;
$ENV
{TERMCAP} =
do
{
undef
$/;
$_
=<DATA>; y/\n//d;
$_
};
}
}
else
{
print
"\e[?1000;1006;1015h"
;
}
my
$t
= Term::Cap->Tgetent({
OSPEED
=>
$param
{speed} });
my
$dumb
=
eval
{
$t
->Trequire(
qw/cm ce cl sf sr/
) } ? 1 : 0;
my
%primitives
= (
BLD
=>
$t
->Tputs(
'md'
),
ULN
=>
$t
->Tputs(
'us'
),
REV
=>
$t
->Tputs(
'mr'
),
NOR
=>
$t
->Tputs(
'me'
),
);
my
$text
;
if
(
defined
(
$param
{text} ) ){
my
$ref
=
ref
(
$param
{text} );
if
(
$ref
eq
'ARRAY'
){
die
"Invalid text, must be string, code ref, or [string, code ref]"
unless
(
scalar
( @{
$param
{text}} ) ==2) and
ref
(
$param
{text}->[0] ) eq
''
and
ref
(
$param
{text}->[1] ) eq
'CODE'
;
$text
=
$param
{text}->[0];
$param
{text} =
$param
{text}->[1]
}
elsif
(
$ref
eq
''
){
$text
=
delete
(
$param
{text} );
}
}
$param
{visualBell} =
delete
(
$param
{visualBeep})
if
defined
(
$param
{visualBeep}) and not
defined
(
$param
{visualBell});
my
$me
=
bless
{
_cursor
=> 0,
_end
=> 0,
_left
=> 0,
_term
=>
$t
,
_dumb
=>
$dumb
,
_txtN
=> 0,
_search
=>
''
,
_statCols
=> 0,
_lineNo
=>[0],
lineNo
=> 0,
pause
=>
''
,
raw
=> 0,
statusCol
=> 0,
squeeze
=>0,
visualBell
=> 0,
fold
=> 0,
_fileN
=> 1,
_mark
=> {
1
=>0},
scrollBar
=> 0,
%dims
,
%primitives
,
MENU
=>
$primitives
{BLD}.
$primitives
{REV},
HILT
=>
$primitives
{BLD}.
$primitives
{ULN},
SRCH
=>
$primitives
{BLD}.
$primitives
{ULN},
%param
,
},
$class
;
$me
->add_text(
$text
)
if
defined
$text
;
$me
->{_I18N}={
prompt
=>
''
,
404
=>
'Not Found'
,
top
=>
'Top'
,
bottom
=>
'Bottom'
,
minihelp
=>
"<h>=help \000<space>=down <b>=back <q>=quit"
,
continue
=>
'press any key to continue'
,
searchwrap
=>
'No more matches, next search will wrap'
,
nowrap
=>
'Text::Wrap unavailable, disabling folding'
,
help
=>
<<EOH
q quit \000 h help
r C-l refresh \000 R flush buffers
/ search \000 ? search backwards
n P next match \000 p N previous match
space C-v page down \000 b M-v page up
enter down line down \000 y up line up
d half page down \000 u half page up
g < goto top \000 G > goto bottom
left scroll left 1 tab\000 right scroll right 1 tab
S-left scroll left 1/2 \000 S-right scroll right 1/2
m mark position \000 ' return to mark
# line numbering \000 \\d+\\n jump to line \\d+
:n next file \000 :p previous file
C toogle raw \000 S toggle folding
EOH
};
our
%config
;
add_keys(\
&help
,
'h'
,
'H'
);
add_keys(\
&close
,
'q'
,
'Q'
,
':q'
,
':Q'
);
add_keys(\
&refresh
,
'r'
,
"\cL"
,
"\cR"
);
add_keys(\
&next_match
,
'n'
,
'P'
);
add_keys(\
&prev_match
,
'p'
,
'N'
);
add_keys(\
&to_bott
,
'>'
,
'G'
,
'$'
,
"\e>"
,
"\e[F"
,
"\e0E"
,
"\e0W"
,
"\e[4~"
);
add_keys(\
&downpage
,
' '
,
'z'
,
"\cV"
, ,
'f'
,
"\cF"
,
"\e "
,
"\e[6~"
);
add_keys(\
&downpage
,
"\eOs"
)
if
$ENV
{TERM} eq
'WINANSI'
;
add_keys(\
&downhalf
,
'd'
,
"\cD"
);
add_keys(\
&downline
,
'e'
,
'j'
,
'J'
,
"\cE"
,
"\cN"
,
"\e[B"
);
add_keys(\
&downline_raw
,
"\n"
,
"\r"
);
add_keys(\
&upline
,
'y'
,
'k'
,
"\cY"
,
"\cK"
,
'K'
,
'Y'
,
"\cP"
,
"\e[A"
);
add_keys(\
&uphalf
,
'u'
,
"\cU"
);
add_keys(\
&uppage
,
'w'
,
'b'
,
"\cB"
,
"\ev"
,
"\e[5~"
);
add_keys(\
&uppage
,
"\eOy"
)
if
$ENV
{TERM} eq
'WINANSI'
;
add_keys(\
&to_top
,
'<'
,
'g'
,
"\e<"
,
"\e[H"
,
"\e0"
,
"\e[1~"
);
add_keys(\
&next_file
,
':n'
,
"\e[1;4C"
);
add_keys(\
&prev_file
,
':p'
,
"\e[1;4D"
);
add_keys(\
&save_mark
,
'm'
,
"\e[2~"
);
add_keys(\
&shift_left
,
"\e\[1;2D"
,
"\e("
);
add_keys(\
&shift_right
,
"\e\[1;2C"
,
"\e)"
);
$me
->add_func(
%config
,
"\e[<"
=> \
&mouse
,
'/(\d+)/'
=> 1,
"\e[D"
=> \
&tab_left
,
"\e[C"
=> \
&tab_right
,
'&'
=> \
&grep
,
'/'
=> \
&search
,
'?'
=> \
&hcraes
,
"'"
=> \
&goto_mark
,
'#'
=> \
&toggle_num
, #XXX Change toggle* to
'-'
initiated
'C'
=> \
&toggle_raw
,
'S'
=> \
&toggle_fold
,
'R'
=> \
&flush_buffer
,
':w'
=> \
&write_buffer
,
':e'
=> \
&open_file
,
);
$me
->{_raw}->{
chr
(
$_
)} =
chr
(64+
$_
)
foreach
(0..8, 11..31);
$me
->{_end} =
$me
->{rows} - 1;
$SIG
{WINCH} =
sub
{
$me
->resize() }
unless
$ENV
{TERM} eq
'WINANSI'
;
$me
->{cols}--
if
$me
->{scrollBar};
eval
"use Text::Wrap"
;
if
( $@ ){
sub
wrap{
join
''
,
@_
}
$me
->{fold} = 0;
}
$me
;
}
sub
resize {
my
$me
=
shift
;
my
%dims
= get_size();
$dims
{rows}--;
$dims
{cols}--
if
$me
->{scrollBar};
$me
->{
$_
} =
$dims
{
$_
}
foreach
keys
%dims
;
$me
->{_end} =
$me
->{rows} - 1;
if
(
$me
->{fold} ){
$me
->reflow();
}
else
{
$me
->refresh();
}
$me
->status();
$me
->{WINCH}->()
if
ref
(
$me
->{WINCH}) eq
'CODE'
;
}
sub
get_size {
my
%dims
=
@_
;
if
(
defined
(
$Term::ReadKey::VERSION
) ){
Term::ReadKey->
import
();
local
$SIG
{__WARN__} =
sub
{};
my
@Tsize
= Term::ReadKey::GetTerminalSize(
*STDOUT
);
@dims
{
'rows'
,
'cols'
} =
@Tsize
[1,0];
$dims
{speed} ||= (Term::ReadKey::GetSpeed())[1];
}
else
{
*ReadMode
=
sub
{
if
(
$_
[0] == 3 ){
system
(
'stty -icanon -echo min 1'
); }
elsif
(
$_
[0] == 0 ){
system
(
'stty icanon echo'
); }
};
*ReadKey
=
sub
{
getc
() };
if
(
$ENV
{TERM} eq
'WINANSI'
){
eval
{
@dims
{
'rows'
,
'cols'
} = Win32::Console::ANSI::Cursor() };
}
elsif
( `stty` =~ /speed/ ){
@dims
{
'rows'
,
'cols'
} = ($1-1,$2-1)
if
`stty size` =~ /^(\d+)\s+(\d+)$/;
$dims
{speed} = $1
if
`stty speed` =~ /^(\d+)$/;
}
else
{
$dims
{rows} = `tput lines` ||
$dims
{rows};
$dims
{cols} = `tput cols` ||
$dims
{cols};
}
}
return
%dims
;
}
sub
add_text {
return
unless
defined
(
$_
[1]);
my
$me
=
shift
;
local
$_
=
join
(
''
,
@_
);
my
$LF
=
do
{
chomp
(
local
$_
=
$_
) };
s/\n{2,}/\n\n/g
if
$me
->{squeeze};
my
@F
=
split
(/\n/,
$_
, -1);
if
(
$me
->{fold} ){
local
$Text::Wrap::columns
;
$Text::Wrap::columns
=
$me
->{cols} -
(
$me
->{_statCols} = (
$me
->{lineNo} ? 9 :
$me
->{statusCol} ? 1 : 0) );
my
$lines
=
scalar
(
@F
);
my
$extraSum
=0;
for
(
my
$i
=0;
$i
<
$lines
;
$i
++ ){
$me
->{_lineNo}->[
$i
+
$me
->{_txtN}] =
$me
->{_txtN}+
$i
+1-
$extraSum
;
$me
->{_mark}->{$1} =
$i
+
$me
->{_txtN}
if
defined
(
$F
[
$i
]) &&
$F
[
$i
] =~ m%\cF\c]\cL\cE \[(\d+)/%;
if
(
defined
(
$F
[
$i
]) &&
length
(
$F
[
$i
]) >
$me
->{cols} ){
my
@G
=
split
/\n/, wrap(
''
,
''
,
$F
[
$i
]);
my
$extras
=
scalar
(
@G
);
splice
(
@F
,
$i
, 1,
@G
);
$me
->{_lineNo}->[
$i
+
$me
->{_txtN}+
$_
] =
$me
->{_txtN}+
$i
+1-
$extraSum
foreach
1..
$extras
-1;
$i
+=
$extras
-1;
$lines
+=
$extras
;
$extraSum
+=
$extras
-1;
}
}
}
pop
@F
if
$LF
;
push
(
@F
,
undef
)
unless
$LF
;
if
(
$me
->{_txtN} && !
defined
(
$me
->{_text}->[-1]) ){
pop
@{
$me
->{_text}};
$me
->{_text}->[-1] .=
shift
@F
;
}
my
$shown
=
$me
->{_txtN};
push
@{
$me
->{_text}},
@F
;
$me
->{_txtN} = @{
$me
->{_text} };
$me
->refresh();
}
sub
reflow {
my
$me
=
shift
;
my
(
$prevLine
,
@text
) = 0;
while
(
scalar
@{
$me
->{_text}} ){
my
$curLine
=
shift
@{
$me
->{_lineNo}};
if
(
$curLine
==
$prevLine
){
$text
[-1] .=
' '
. (
shift
@{
$me
->{_text}}||
''
); }
else
{
push
@text
,
shift
@{
$me
->{_text}}; }
$prevLine
=
$curLine
;
}
$me
->{_lineNo}=[];
$me
->{_txtN}=0;
$me
->add_text(
join
($/,
@text
) );
}
sub
AUTOLOAD{
eval
"use Carp"
;
my
$me
=
shift
;
our
$AUTOLOAD
=~ s/.*:://;
return
if
$AUTOLOAD
eq
'DESTROY'
;
local
$Text::Wrap::columns
=
int
(.75
*$me
->{cols});
my
$msg
= wrap(
''
,
''
,
"$AUTOLOAD\n\n"
. Carp::longmess());
$me
->beep();
$me
->dialog(
"Unknown method $msg"
, 1);
}
my
$input
;
sub
more {
my
$me
=
shift
;
my
%param
=
@_
;
$RT
=
$me
->{RT} =
$param
{RT};
ReadMode 3;
$| = 1;
if
(
$me
->{_dumb} ){
$me
->dumb_mode();
}
else
{
print
$me
->{NOR};
while
( 1 ){
$me
->status();
my
$exit
=
undef
;
my
$char
= ReadKey(
$param
{RT});
return
1
unless
defined
(
$char
);
$me
->{_I18N}{prompt} =
$input
.=
$char
;
$me
->status();
unless
( (
$input
=~ /^\e/ and
index
(
$me
->{_fncRE},
$input
)>0 )
||
$input
=~ /^\d+/
||
$input
=~ /:+/
||
defined
(
$me
->{_fnc}->{
$input
}) ){
$me
->beep(
$input
);
$input
=
''
;
next
;
}
if
(
$me
->{_fnc}->{
$input
} ){
$exit
=
$me
->{_fnc}->{
$input
}->(
$me
);
$me
->{_I18N}{prompt} =
$input
=
''
;
}
elsif
(
$input
=~ /^:/ ){
if
( (
$char
eq
"\cG"
) or (
$input
eq
'::'
) ){
$me
->{_I18N}{prompt} =
$input
=
''
;
$me
->status();
return
1; }
}
elsif
(
$me
->{_fnc}->{
'/(\d+)/'
} and
$input
=~ /^\d+/ ){
if
(
$char
eq
"\cH"
or
ord
(
$char
)==127 ){
$input
=
substr
(
$input
, 0, -2,
''
); }
elsif
(
$char
eq
"\cG"
){
$input
=
''
;
$exit
= 1; }
elsif
(
$char
eq
"\n"
||
$char
eq
"\r"
){
$input
=~ y/0-9//cd;
$exit
=
$input
<
$me
->{_txtN} ?
$me
->jump(
$input
) :
$me
->to_bott();
$input
=
''
; }
$me
->{_I18N}{prompt} =
$input
;
$me
->status();
}
return
1
if
$param
{RT} &&
defined
(
$exit
);
}
}
$me
->
close
();
}
*less
= \
&more
;
*page
= \
&more
;
my
$foo
= \
&less
;
$foo
= \
&page
;
sub
I18N {
my
(
$me
,
$msg
,
$text
) =
@_
;
$me
->{_I18N}{
$msg
} =
$text
if
defined
(
$text
);
$me
->{_I18N}{
$msg
};
}
BEGIN{
no
strict
'refs'
;
foreach
my
$method
(
qw(eof lineNo pause raw statusCol visualBell)
){
*{
$method
} =
sub
{
$_
[0]->{
$method
}=
$_
[1]
if
defined
(
$_
[1]);
$_
[0]->{
$method
} }
}
foreach
my
$method
(
qw(rows cols speed fold squeeze)
){
*{
$method
} =
sub
{
$_
[0]->{
$method
}}
}
}
sub
add_keys{
our
%config
;
my
$sub
=
shift
;
$config
{
$_
} =
$sub
foreach
@_
;
}
sub
add_func{
my
$me
=
shift
;
my
%param
=
@_
;
while
(
my
(
$k
,
$v
) =
each
%param
){
$me
->{_fnc}{
$k
} =
$v
;
}
$me
->{_fncRE} =
join
'|'
,
sort
keys
%{
$me
->{_fnc} };
}
sub
beep{
print
"\a"
;
print
$_
[0]->{_term}->Tputs(
'vb'
)
if
$_
[0]->{visualBell};
if
(
defined
(
$_
[1]) ){
$_
[1] =~ s/\e/^[/;
$_
[1] =~ s/([^[:
print
:]])/
sprintf
(
"\\%03o"
,
ord
($1))/ge;
$_
[0]->dialog(
"Unrecognized command: $_[1]"
, 1);
$_
[0]->{_I18N}{prompt} =
''
;
$_
[0]->status();
}
}
sub
getln{
my
$input
;
while
(1){
my
$l
= ReadKey();
last
if
$l
eq
"\n"
||
$l
eq
"\r"
;
if
( !
defined
(
$l
)|
$l
eq
"\e"
||
$l
eq
"\cG"
){
$input
=
''
;
last
; }
elsif
(
$l
eq
"\b"
||
$l
eq
"\177"
){
print
"\b \b"
if
$input
ne
''
;
substr
(
$input
, -1, 1,
''
);
next
;
}
print
$l
;
$input
.=
$l
;
}
return
$input
;
}
sub
status{
my
$me
=
shift
;
$me
->{_txtN} ||= 0;
my
$end
=
$me
->{_cursor} +
$me
->{rows};
my
$pct
=
$me
->{_txtN} >
$end
?
$end
/(
$me
->{_txtN}) : 1;
my
$pos
=
$me
->{_cursor} ?
(
$pct
==1 ?
$me
->{_I18N}{bottom} :
'L'
.
$me
->{_cursor}) :
$me
->{_I18N}{top};
$pos
.=
'C'
.
$me
->{_left}
if
$me
->{_left};
my
$p
=
sprintf
"[tp] %d%% %s %s"
, 100
*$pct
,
$pos
,
$me
->{_I18N}{prompt};
print
$me
->{_term}->Tgoto(
'cm'
, 0,
$me
->{rows});
print
$me
->{_term}->Tputs(
'ce'
);
my
$minihelp
=
$me
->{_I18N}{minihelp};
(
my
$pSansCodes
=
$p
) =~ s/\e\[[\d;]*[a-zA-Z]//g;
my
$pN
=
$me
->{cols} -1 -
length
(
$pSansCodes
) -
length
(
$me
->{_I18N}{minihelp});
$p
.=
' '
x (
$pN
> 1 ?
$pN
: 1);
$minihelp
=
$pN
>2 ?
$minihelp
:
do
{
$minihelp
=~ s/\000.+//;
$minihelp
};
print
$me
->{REV};
print
$p
,
" "
,
$minihelp
;
print
$me
->{NOR};
}
sub
close
{
ReadMode 0;
print
"\n\e[?1000l"
;
$| =
$SP
|| 0;
$RT
?
die
:
return
\
"foo"
;
}
{
no
warnings
'once'
;
*done
= \
&close
;
}
sub
help{
my
$me
=
shift
;
my
$help
=
$me
->{_I18N}{help};
my
$cont
=
$me
->{_I18N}{
continue
};
if
(
$me
->max_width(
split
/\n/,
$help
) >
$me
->{cols} ){
my
$help2
=
$help
;
$help2
=~ s/\000.*//mg;
$help
=~ s/.*\000//mg;
my
$padding
=
$me
->max_width(
$cont
) / 2;
$me
->dialog(
$help2
.
"\n"
. (
' '
x
$padding
) .
$cont
);
}
else
{
$help
=~ y/\000//d;
}
my
$padding
=
$me
->max_width(
$cont
) / 2;
$me
->dialog(
$help
.
"\n"
. (
' '
x
$padding
) .
$cont
);
}
sub
max_width{
my
$me
=
shift
;
my
$width
= 0;
foreach
(
@_
){
$width
=
length
(
$_
)
if
length
(
$_
) >
$width
};
return
$width
;
}
sub
dialog{
my
(
$me
,
$msg
,
$timeout
) =
@_
;
my
@txt
=
defined
(
$msg
) ?
split
(/\n/,
$msg
) : ();
my
$w
=
$me
->max_width(
@txt
);
my
$h
=
'+'
.
'='
x(
$w
+2) .
'+'
;
my
$d
=
join
(
''
,
map
{
sprintf
(
"%s| %- @{[$w+4]}s |\n"
,
$me
->{_term}->Tgoto(
'RI'
,0,4),
$_
) }
$h
,
@txt
,
$h
);
print
$me
->{_term}->Tgoto(
'cm'
,0, 2),
$me
->{MENU},
$d
,
$me
->{NOR};
defined
(
$timeout
) ?
sleep
(
$timeout
) :
getc
();
local
(
$me
->{pause});
foreach
my
$n
(2 ..
scalar
(
@txt
)+3){
print
$me
->{_term}->Tgoto(
'cm'
, 0,
$n
);
print
$me
->{_term}->Tputs(
'ce'
);
$me
->line(
$n
);
}
}
sub
flush_buffer{
my
$me
=
shift
;
$me
->{_text} = [];
$me
->{_txtN} = 0;
$me
->{_lineNo}=[];
$me
->refresh();
}
sub
refresh{
my
$me
=
shift
;
print
$me
->{_term}->Tputs(
'cl'
);
for
my
$n
(0 ..
$me
->{rows} -1){
print
$me
->{_term}->Tgoto(
'cm'
, 0,
$n
);
print
$me
->{_term}->Tputs(
'ce'
);
if
(
$me
->{_grep} &&
defined
(
$me
->{_text}->[
$me
->{_cursor}+
$n
]) ){
until
(
$me
->{_text}->[
$me
->{_cursor}+
$n
] =~
m
%$me
->{_search}|\cF\c]\cL\cE \[\d+/% ){
$me
->{_cursor}++;
last
if
$me
->{_cursor}+
$me
->{rows}+
$n
>=
$me
->{_txtN};
}
}
$me
->line(
$n
+
$me
->{_cursor})
if
$me
->{_cursor}+
$me
->{rows}+
$n
<=
$me
->{_txtN}
}
$me
->scrollBar()
if
$me
->{scrollBar};
}
sub
scrollBar{
my
$me
=
shift
;
$me
->{_pages} =
$me
->{_txtN}/
$me
->{rows};
$me
->{_thumbW} =
$me
->{rows}/
$me
->{_pages};
$me
->{_thumbT} =
sprintf
(
"%i"
, (
$me
->{_cursor} /
$me
->{_pages}) )+(
$me
->{_cursor}>
$me
->{_txtN}/2);
$me
->{_thumbB} =
sprintf
(
"%i"
,
$me
->{_thumbT}+
$me
->{_thumbW});
for
my
$n
(0 ..
$me
->{rows} -1){
print
$me
->{_term}->Tgoto(
'cm'
,
$me
->{cols}+1,
$n
);
print
$n
>=
$me
->{_thumbT} &&
$n
<
$me
->{_thumbB} ?
' '
:
"$me->{REV} $me->{NOR}"
;
}
}
sub
mouse{
my
$me
=
shift
;
my
$input
=
''
;
$input
.= ReadKey(0)
until
$input
=~ /M$/i;
my
@args
=
split
/;/,
$input
;
if
(
$args
[0] == 65 ){
$me
->downhalf(); }
elsif
(
$args
[0] == 64 ){
$me
->uphalf(); }
elsif
(
$me
->{scrollBar} &&
$args
[1] ==
$me
->{cols}+1 ){
if
(
chop
$args
[2] eq
'm'
){
if
(
$me
->{_thumbDrag} ){
$me
->{_thumbDrag} = 0;
my
$pos
;
if
(
$args
[2]==1 ){
$pos
=0 }
elsif
(
$args
[2]==
$me
->{rows} ){
$pos
=
$me
->{_txtN} - 2
*$me
->{rows}-1 }
else
{
$pos
=
sprintf
(
"%i"
,
$args
[2] /
$me
->{rows} *
$me
->{_txtN}) }
$me
->jump(
$pos
);
}
$me
->uppage()
if
$args
[2] <
$me
->{_thumbT};
$me
->downpage()
if
$args
[2] >
$me
->{_thumbB};
}
elsif
(
$args
[2]>=
$me
->{_thumbT} &&
$args
[2]<=
$me
->{_thumbB} ){
$me
->{_thumbDrag}=1;
}
}
}
sub
line{
my
$me
=
shift
;
my
$n
=
shift
;
local
$_
=
$me
->{_text}[
$n
]||
''
;
$me
->{_curFile} = $1
if
m%\cF\c]\cL\cE \[(\d+)/%;
my
$pausey
= 1
if
length
(
$me
->{pause}) &&
defined
&& /
$me
->{pause}/;
my
$len
=
length
();
unless
(
$me
->{fold} ){
$_
= (
$len
-
$me
->{_statCols}) <
$me
->{_left} ?
''
:
substr
(
$_
,
$me
->{_left},
$me
->{cols}-
$me
->{_statCols});
if
(
$len
-
$me
->{_left} >
$me
->{cols} ){
substr
(
$_
, -1, 1,
"\$"
);
}
}
unless
(
$me
->{raw} ){
s/([\000-\010\013-\037])/
$me
->{REV}^
$me
->{_raw}->{$1}
$me
->{NOR}/g;
}
my
$matched
= (s/(
$me
->{_search})/
$me
->{SRCH}$1
$me
->{NOR}/g)
if
$me
->{_search} ne
''
;
my
$info
=
$me
->{statusCol} && !
$me
->{lineNo} ? (
$matched
?
'*'
:
' '
) :
''
;
$info
=
sprintf
(
"% 8s"
,
$me
->{fold} ? (
$me
->{_lineNo}->[
$n
]||-1) :
(
defined
(
$me
->{_text}[
$n
]) ?
$n
+1 :
''
)
)
if
$me
->{lineNo};
$_
= (
$me
->{Statuscol} &&
$matched
?
$me
->{REV} :
''
).
$info
.
(
$me
->{statusCol} &&
$matched
?
$me
->{NOR} :
''
).
(
$me
->{lineNo} ?
' '
:
''
).
$_
;
print
;
if
(
$pausey
){
$me
->{_end} =
$n
;
no
warnings
'exiting'
;
last
;
}
}
sub
down_lines{
my
$me
=
shift
;
my
$n
=
shift
;
my
$t
=
$me
->{_term};
LINE:
for
(1..
$n
){
if
(
$me
->{_end} >=
$me
->{_txtN}-1 ){
$me
->
close
()
if
$me
->{
eof
} &&
ref
(
$me
->{text}) ne
'CODE'
;
if
(
ref
(
$me
->{text}) eq
'CODE'
){
$me
->add_text(
$me
->{text}->() ); }
else
{
&beep
;
last
; }
}
if
(
$me
->{_end} <
$me
->{_txtN}-1 ){
if
(
length
(
$me
->{pause}) &&
$me
->{_end} <
$me
->{rows}-1 ){
print
$t
->Tgoto(
'cm'
, 0,
$me
->{_end}+1 ); }
else
{
print
$t
->Tgoto(
'cm'
, 0,
$me
->{rows} );
print
$t
->Tputs(
'sf'
);
print
$t
->Tgoto(
'cm'
, 0,
$me
->{rows} - 1);
}
print
$t
->Tputs(
'ce'
);
if
(
$me
->{_grep} &&
$me
->{_end} <
$me
->{_txtN} ){
until
(
$me
->{_text}->[
$me
->{_end}] =~
m
%$me
->{_search}|\cF\c]\cL\cE \[\d+/% ){
$me
->dialog(
'Pagination in grep mode does not work at this time.'
, 1);
last
LINE;
$me
->{_cursor}++;
if
(
$me
->{_end} >=
$me
->{_txtN} ){
$me
->{cursor} =
$me
->{_end} =
$me
->{_txtN};
last
;
}
}
}
$me
->line( ++
$me
->{_end} )
if
$me
->{_end} <=
$me
->{_txtN};
$me
->{_cursor}++;
}
}
$me
->refresh()
if
$ENV
{TERM} eq
'WINANSI'
;
$me
->scrollBar()
if
$me
->{scrollBar};
}
sub
downhalf {
$_
[0]->down_lines(
$_
[0]->{rows} / 2 ); }
sub
downpage {
$_
[0]->down_lines(
$_
[0]->{rows} );
select
(
undef
,
undef
,
undef
, .1);
}
sub
downline {
$_
[0]->down_lines( 1 ); }
sub
downline_raw {
$_
[0]->down_lines( 1 );
$_
[0]->refresh(); }
sub
up_lines{
my
$me
=
shift
;
my
$n
=
shift
;
for
(1 ..
$n
){
if
(
$me
->{_cursor} <= 0 ){
&beep
;
last
; }
else
{
print
$me
->{_term}->Tgoto(
'cm'
,0,0);
print
$me
->{_term}->Tputs(
'sr'
);
$me
->line( --
$me
->{_cursor} );
$me
->{_end}--;
}
}
$me
->refresh()
if
$ENV
{TERM} eq
'WINANSI'
;
print
$me
->{_term}->Tgoto(
'cm'
,0,
$me
->{rows});
$me
->scrollBar()
if
$me
->{scrollBar};
}
sub
uppage {
$_
[0]->up_lines(
$_
[0]->{rows} ); }
sub
upline {
$_
[0]->up_lines( 1 ); }
sub
uphalf {
$_
[0]->up_lines(
$_
[0]->{rows} / 2 ); }
sub
to_top {
$_
[0]->jump(0); }
sub
to_bott{
my
$me
=
shift
;
if
(
$me
->{rows}>
$me
->{_txtN} ){
$me
->jump( 0 ) }
else
{
$me
->jump(
$me
->{_txtN}-1 );
$me
->uppage() }
}
sub
save_mark{
my
$me
=
shift
;
$me
->I18N(
'status'
,
$me
->{BLD}.
'*Mark name?*'
.
$me
->{NOR}.
$me
->{REV});
$me
->status();
$me
->{_term}->Tgoto(
'cm'
,
length
(
'[tp] 100% Bottom Mark name?'
)+1,
$me
->{rows});
my
$mark
= ReadKey();
return
if
$mark
eq
"\cG"
;
next
if
$mark
eq
"'"
;
$me
->{_mark}->{
$mark
} =
$me
->{_cursor};
$me
->I18N(
'status'
,
''
);
$me
->status();
}
sub
goto_mark{
my
$me
=
shift
;
my
$mark
= ReadKey();
return
if
$mark
eq
"\cG"
or not
exists
(
$me
->{_mark}->{
$mark
});
my
$jump
=
$me
->{_mark}->{
$mark
};
if
(
$mark
eq
'^'
){
$jump
= 0;
}
elsif
(
$mark
eq
'$'
){
$jump
=
$me
->{_txtN} -
$me
->{rows};
}
elsif
(
$mark
eq
'"'
){
my
$marks
=
join
(
"\n"
,
map
{
"$_ = $me->{_mark}->{$_}"
}
sort
keys
%{
$me
->{_mark} } );
$me
->dialog(
$marks
);
return
;
}
$me
->{_mark}->{
"'"
} =
$me
->{_cursor};
$me
->jump(
$jump
);
}
sub
prev_file{
$_
[0]->next_file(
'anti'
) }
sub
next_file{
my
$me
=
shift
;
my
$mode
=
shift
||
''
;
my
$mark
=
$me
->{_curFile} + (
$mode
eq
'anti'
? -1 : 1 );
if
(
exists
(
$me
->{_mark}->{
$mark
}) ){
$me
->{_mark}->{
"'"
} =
$me
->{_cursor};
$me
->jump(
$me
->{_mark}->{
$mark
} ); }
else
{
$me
->beep()
}
}
sub
jump{
my
$me
=
shift
;
$me
->{_cursor} =
shift
;
$me
->{_end} =
$me
->{_cursor} +
$me
->{rows};
$me
->refresh();
}
sub
tab_right{
my
$me
=
shift
;
$me
->{_left} += 8;
$me
->refresh();
}
sub
tab_left{
my
$me
=
shift
;
$me
->{_left} = 0
if
(
$me
->{_left} -= 8) < 0;
$me
->refresh();
}
sub
shift_right{
my
$me
=
shift
;
$me
->{_left} +=
int
(
$me
->{cols}/2);
$me
->refresh();
}
sub
shift_left{
my
$me
=
shift
;
$me
->{_left} = 0
if
(
$me
->{_left} -=
int
(
$me
->{cols}/2) ) < 0;
$me
->refresh();
}
sub
grep
{
$_
[0]->search(-1); }
sub
hcraes{
$_
[0]->search(1); }
sub
search{
my
$me
=
shift
;
my
$mode
=
shift
|| 0;
$me
->{_hcraes} =
$mode
== 1;
$me
->{_grep} =
$mode
== -1;
$me
->{_searchWrap} = 0
unless
$me
->{_grep};
(
my
(
$prev
),
$me
->{_search}) = (
$me
->{_search},
''
);
print
$me
->{_term}->Tgoto(
'cm'
, 0,
$me
->{rows});
print
$me
->{_term}->Tputs(
'ce'
);
print
$me
->{HILT};
print
$mode
? (
$mode
> 0 ?
'?'
:
'&'
) :
'/'
;
$me
->{_search} =
$me
->getln() ||
''
;
print
$me
->{NOR};
print
$me
->{_term}->Tgoto(
'cm'
, 0,
$me
->{rows});
print
$me
->{_term}->Tputs(
'ce'
);
if
(
$me
->{_search} eq
''
){
$me
->refresh();
return
;
}
$me
->{_search} =
'(?i)'
.
$me
->{_search}
unless
$me
->{_search} ne
lc
(
$me
->{_search});
$me
->{_search} =
$prev
if
$me
->{_search} eq
'/'
&&
$prev
;
for
my
$n
(
$me
->{_cursor} ..
$me
->{_txtN} -1){
next
unless
$me
->{_text}[
$n
] =~ /
$me
->{_search}/i;
$me
->{_cursor} =
$n
;
$me
->{_cursor} = 0
if
$me
->{_txtN} <
$me
->{rows};
$me
->{_end} =
$me
->{_cursor} +
$me
->{rows};
if
(
$me
->{_cursor} +
$me
->{rows} >
$me
->{_txtN} - 1 &&
$me
->{_cursor} ){
my
$x
=
$me
->{_cursor} +
$me
->{rows} -
$me
->{_txtN};
$x
=
$me
->{_cursor}
if
$x
>
$me
->{_cursor};
$me
->{_cursor} -=
$x
;
$me
->{_end} -=
$x
;
}
$me
->refresh();
return
;
}
&beep
;
$me
->dialog(
$me
->{_I18N}{404}, 1);
return
;
}
sub
prev_match{
$_
[0]->next_match(
'anti'
); }
sub
next_match{
my
$me
=
shift
;
return
unless
defined
(
$me
->{_txtN}) and
defined
(
$me
->{_search});
my
$mode
=
shift
;
if
(
defined
(
$mode
) and
$mode
=
'anti'
){
$mode
= not
$me
->{_hcraes}; }
else
{
$mode
=
$me
->{_hcraes};
}
if
(
$me
->{_searchWrap} ){
$me
->{_searchWrap} = 0;
$me
->jump(
$mode
?
$me
->{_txtN} : 0 );
}
my
$i
=
$mode
? (
$me
->{_cursor}||0)-1 : (
$me
->{_cursor})+1;
my
$matched
=0;
for
( ;
$mode
?
$i
>0 :
$i
<
$me
->{_txtN};
$mode
?
$i
-- :
$i
++ ){
$matched
=
$me
->{_text}[
$i
] =~ /
$me
->{_search}/;
last
if
$matched
;
}
if
( (
$i
== (
$mode
? 0 :
$me
->{_txtN} )) && (
$me
->{_searchWrap} == 0) ){
$me
->dialog(
$me
->I18N(
'searchwrap'
), 1);
$me
->{_searchWrap} = 1;
return
;
}
$matched
?
$me
->jump(
$i
) :
&beep
;
}
sub
toggle_num{
my
$me
=
shift
;
$me
->{lineNo} = not
$me
->{lineNo};
$me
->refresh();
}
sub
toggle_raw{
my
$me
=
shift
;
$me
->{raw} = not
$me
->{raw};
$me
->reflow();
}
sub
toggle_fold{
my
$me
=
shift
;
$me
->{fold} = not
$me
->{fold};
$me
->{_lineNo} = [1 ..
$me
->{_txtN}]
if
$me
->{fold};
$me
->reflow();
}
sub
write_buffer{
my
$me
=
shift
;
print
$me
->{_term}->Tgoto(
'cm'
, 0,
$me
->{rows});
print
$me
->{_term}->Tputs(
'ce'
);
print
"Save to: "
;
my
$out
=
$me
->{_search} =
$me
->getln();
if
( ! -e
$out
&&
open
(OUT,
'>'
,
$out
) ){
print
OUT
join
($/, @{
$me
->{_text}});
CORE::
close
(OUT);
}
else
{
$me
->dialog(
"ERROR: "
. -e
$out
?
"File exists"
: $!)
}
}
sub
open_file{
my
$me
=
shift
;
print
$me
->{_term}->Tgoto(
'cm'
, 0,
$me
->{rows});
print
$me
->{_term}->Tputs(
'ce'
);
print
"Examine: "
;
my
$file
=
$me
->getln();
unless
( -e
$file
){
$me
->dialog(
sprintf
(
"%s: $file"
,
$me
->{_I18N}{404}) );
return
;
}
unless
(
open
(IN,
'<'
,
$file
) ){
$me
->dialog($!);
return
;
}
my
$N
=
$me
->get_fileN();
$me
->set_fileN(
$N
+1);
$me
->add_text(
sprintf
(
"======== \cF\c]\cL\cE [%i/..] %s ========\n"
,
$N
,
$file
), <IN>);
}
sub
get_fileN{
$_
[0]->{_fileN} }
sub
set_fileN{
$_
[0]->{_fileN} =
$_
[1] }
sub
dumb_mode{
my
$me
=
shift
;
my
$end
= 0;
while
(1){
for
my
$i
(1 ..
$me
->{rows} ){
last
if
$end
>=
$me
->{_txtN};
print
$me
->{_text}[
$end
++],
"\n"
;
}
print
"--more [dumb]-- <q> quit"
;
my
$a
=
getc
();
print
"\b \b"
x15;
return
if
$a
eq
'q'
;
return
if
$end
>=
$me
->{_txtN};
}
}
1;