1*0Sstevel@tonic-gatepackage Term::Cap; 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gateuse Carp; 4*0Sstevel@tonic-gateuse strict; 5*0Sstevel@tonic-gate 6*0Sstevel@tonic-gateuse vars qw($VERSION $VMS_TERMCAP); 7*0Sstevel@tonic-gateuse vars qw($termpat $state $first $entry); 8*0Sstevel@tonic-gate 9*0Sstevel@tonic-gate$VERSION = '1.08'; 10*0Sstevel@tonic-gate 11*0Sstevel@tonic-gate# Version undef: Thu Dec 14 20:02:42 CST 1995 by sanders@bsdi.com 12*0Sstevel@tonic-gate# Version 1.00: Thu Nov 30 23:34:29 EST 2000 by schwern@pobox.com 13*0Sstevel@tonic-gate# [PATCH] $VERSION crusade, strict, tests, etc... all over lib/ 14*0Sstevel@tonic-gate# Version 1.01: Wed May 23 00:00:00 CST 2001 by d-lewart@uiuc.edu 15*0Sstevel@tonic-gate# Avoid warnings in Tgetent and Tputs 16*0Sstevel@tonic-gate# Version 1.02: Sat Nov 17 13:50:39 GMT 2001 by jns@gellyfish.com 17*0Sstevel@tonic-gate# Altered layout of the POD 18*0Sstevel@tonic-gate# Added Test::More to PREREQ_PM in Makefile.PL 19*0Sstevel@tonic-gate# Fixed no argument Tgetent() 20*0Sstevel@tonic-gate# Version 1.03: Wed Nov 28 10:09:38 GMT 2001 21*0Sstevel@tonic-gate# VMS Support from Charles Lane <lane@DUPHY4.Physics.Drexel.Edu> 22*0Sstevel@tonic-gate# Version 1.04: Thu Nov 29 16:22:03 GMT 2001 23*0Sstevel@tonic-gate# Fixed warnings in test 24*0Sstevel@tonic-gate# Version 1.05: Mon Dec 3 15:33:49 GMT 2001 25*0Sstevel@tonic-gate# Don't try to fall back on infocmp if it's not there. From chromatic. 26*0Sstevel@tonic-gate# Version 1.06: Thu Dec 6 18:43:22 GMT 2001 27*0Sstevel@tonic-gate# Preload the default VMS termcap from Charles Lane 28*0Sstevel@tonic-gate# Don't carp at setting OSPEED unless warnings are on. 29*0Sstevel@tonic-gate# Version 1.07: Wed Jan 2 21:35:09 GMT 2002 30*0Sstevel@tonic-gate# Sanity check on infocmp output from Norton Allen 31*0Sstevel@tonic-gate# Repaired INSTALLDIRS thanks to Michael Schwern 32*0Sstevel@tonic-gate# Version 1.08: Fri Aug 30 14:15:55 CEST 2002 33*0Sstevel@tonic-gate# Cope with comments lines from 'infocmp' from Brendan O'Dea 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate# TODO: 36*0Sstevel@tonic-gate# support Berkeley DB termcaps 37*0Sstevel@tonic-gate# should probably be a .xs module 38*0Sstevel@tonic-gate# force $FH into callers package? 39*0Sstevel@tonic-gate# keep $FH in object at Tgetent time? 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate=head1 NAME 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gateTerm::Cap - Perl termcap interface 44*0Sstevel@tonic-gate 45*0Sstevel@tonic-gate=head1 SYNOPSIS 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate require Term::Cap; 48*0Sstevel@tonic-gate $terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed }; 49*0Sstevel@tonic-gate $terminal->Trequire(qw/ce ku kd/); 50*0Sstevel@tonic-gate $terminal->Tgoto('cm', $col, $row, $FH); 51*0Sstevel@tonic-gate $terminal->Tputs('dl', $count, $FH); 52*0Sstevel@tonic-gate $terminal->Tpad($string, $count, $FH); 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gate=head1 DESCRIPTION 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gateThese are low-level functions to extract and use capabilities from 57*0Sstevel@tonic-gatea terminal capability (termcap) database. 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gateMore information on the terminal capabilities will be found in the 60*0Sstevel@tonic-gatetermcap manpage on most Unix-like systems. 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate=head2 METHODS 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate=over 4 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gateThe output strings for B<Tputs> are cached for counts of 1 for performance. 67*0Sstevel@tonic-gateB<Tgoto> and B<Tpad> do not cache. C<$self-E<gt>{_xx}> is the raw termcap 68*0Sstevel@tonic-gatedata and C<$self-E<gt>{xx}> is the cached version. 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate print $terminal->Tpad($self->{_xx}, 1); 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gateB<Tgoto>, B<Tputs>, and B<Tpad> return the string and will also 73*0Sstevel@tonic-gateoutput the string to $FH if specified. 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate=cut 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate# Preload the default VMS termcap. 79*0Sstevel@tonic-gate# If a different termcap is required then the text of one can be supplied 80*0Sstevel@tonic-gate# in $Term::Cap::VMS_TERMCAP before Tgetent is called. 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gateif ( $^O eq 'VMS') { 83*0Sstevel@tonic-gate chomp (my @entry = <DATA>); 84*0Sstevel@tonic-gate $VMS_TERMCAP = join '', @entry; 85*0Sstevel@tonic-gate} 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate# Returns a list of termcap files to check. 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gatesub termcap_path { ## private 90*0Sstevel@tonic-gate my @termcap_path; 91*0Sstevel@tonic-gate # $TERMCAP, if it's a filespec 92*0Sstevel@tonic-gate push(@termcap_path, $ENV{TERMCAP}) 93*0Sstevel@tonic-gate if ((exists $ENV{TERMCAP}) && 94*0Sstevel@tonic-gate (($^O eq 'os2' || $^O eq 'MSWin32' || $^O eq 'dos') 95*0Sstevel@tonic-gate ? $ENV{TERMCAP} =~ /^[a-z]:[\\\/]/is 96*0Sstevel@tonic-gate : $ENV{TERMCAP} =~ /^\//s)); 97*0Sstevel@tonic-gate if ((exists $ENV{TERMPATH}) && ($ENV{TERMPATH})) { 98*0Sstevel@tonic-gate # Add the users $TERMPATH 99*0Sstevel@tonic-gate push(@termcap_path, split(/(:|\s+)/, $ENV{TERMPATH})) 100*0Sstevel@tonic-gate } 101*0Sstevel@tonic-gate else { 102*0Sstevel@tonic-gate # Defaults 103*0Sstevel@tonic-gate push(@termcap_path, 104*0Sstevel@tonic-gate $ENV{'HOME'} . '/.termcap', 105*0Sstevel@tonic-gate '/etc/termcap', 106*0Sstevel@tonic-gate '/usr/share/misc/termcap', 107*0Sstevel@tonic-gate ); 108*0Sstevel@tonic-gate } 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate # return the list of those termcaps that exist 111*0Sstevel@tonic-gate return grep(-f, @termcap_path); 112*0Sstevel@tonic-gate} 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate=item B<Tgetent> 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gateReturns a blessed object reference which the user can 117*0Sstevel@tonic-gatethen use to send the control strings to the terminal using B<Tputs> 118*0Sstevel@tonic-gateand B<Tgoto>. 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gateThe function extracts the entry of the specified terminal 121*0Sstevel@tonic-gatetype I<TERM> (defaults to the environment variable I<TERM>) from the 122*0Sstevel@tonic-gatedatabase. 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gateIt will look in the environment for a I<TERMCAP> variable. If 125*0Sstevel@tonic-gatefound, and the value does not begin with a slash, and the terminal 126*0Sstevel@tonic-gatetype name is the same as the environment string I<TERM>, the 127*0Sstevel@tonic-gateI<TERMCAP> string is used instead of reading a termcap file. If 128*0Sstevel@tonic-gateit does begin with a slash, the string is used as a path name of 129*0Sstevel@tonic-gatethe termcap file to search. If I<TERMCAP> does not begin with a 130*0Sstevel@tonic-gateslash and name is different from I<TERM>, B<Tgetent> searches the 131*0Sstevel@tonic-gatefiles F<$HOME/.termcap>, F</etc/termcap>, and F</usr/share/misc/termcap>, 132*0Sstevel@tonic-gatein that order, unless the environment variable I<TERMPATH> exists, 133*0Sstevel@tonic-gatein which case it specifies a list of file pathnames (separated by 134*0Sstevel@tonic-gatespaces or colons) to be searched B<instead>. Whenever multiple 135*0Sstevel@tonic-gatefiles are searched and a tc field occurs in the requested entry, 136*0Sstevel@tonic-gatethe entry it names must be found in the same file or one of the 137*0Sstevel@tonic-gatesucceeding files. If there is a C<:tc=...:> in the I<TERMCAP> 138*0Sstevel@tonic-gateenvironment variable string it will continue the search in the 139*0Sstevel@tonic-gatefiles as above. 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gateThe extracted termcap entry is available in the object 142*0Sstevel@tonic-gateas C<$self-E<gt>{TERMCAP}>. 143*0Sstevel@tonic-gate 144*0Sstevel@tonic-gateIt takes a hash reference as an argument with two optional keys: 145*0Sstevel@tonic-gate 146*0Sstevel@tonic-gate=over 2 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gate=item OSPEED 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gateThe terminal output bit rate (often mistakenly called the baud rate) 151*0Sstevel@tonic-gatefor this terminal - if not set a warning will be generated 152*0Sstevel@tonic-gateand it will be defaulted to 9600. I<OSPEED> can be be specified as 153*0Sstevel@tonic-gateeither a POSIX termios/SYSV termio speeds (where 9600 equals 9600) or 154*0Sstevel@tonic-gatean old DSD-style speed ( where 13 equals 9600). 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gate=item TERM 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gateThe terminal type whose termcap entry will be used - if not supplied it will 160*0Sstevel@tonic-gatedefault to $ENV{TERM}: if that is not set then B<Tgetent> will croak. 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate=back 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gateIt calls C<croak> on failure. 165*0Sstevel@tonic-gate 166*0Sstevel@tonic-gate=cut 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gatesub Tgetent { ## public -- static method 169*0Sstevel@tonic-gate my $class = shift; 170*0Sstevel@tonic-gate my ($self) = @_; 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate $self = {} unless defined $self; 173*0Sstevel@tonic-gate bless $self, $class; 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate my($term,$cap,$search,$field,$max,$tmp_term,$TERMCAP); 176*0Sstevel@tonic-gate local($termpat,$state,$first,$entry); # used inside eval 177*0Sstevel@tonic-gate local $_; 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate # Compute PADDING factor from OSPEED (to be used by Tpad) 180*0Sstevel@tonic-gate if (! $self->{OSPEED}) { 181*0Sstevel@tonic-gate if ( $^W ) { 182*0Sstevel@tonic-gate carp "OSPEED was not set, defaulting to 9600"; 183*0Sstevel@tonic-gate } 184*0Sstevel@tonic-gate $self->{OSPEED} = 9600; 185*0Sstevel@tonic-gate } 186*0Sstevel@tonic-gate if ($self->{OSPEED} < 16) { 187*0Sstevel@tonic-gate # delays for old style speeds 188*0Sstevel@tonic-gate my @pad = (0,200,133.3,90.9,74.3,66.7,50,33.3,16.7,8.3,5.5,4.1,2,1,.5,.2); 189*0Sstevel@tonic-gate $self->{PADDING} = $pad[$self->{OSPEED}]; 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate else { 192*0Sstevel@tonic-gate $self->{PADDING} = 10000 / $self->{OSPEED}; 193*0Sstevel@tonic-gate } 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate $self->{TERM} = ($self->{TERM} || $ENV{TERM} || croak "TERM not set"); 196*0Sstevel@tonic-gate $term = $self->{TERM}; # $term is the term type we are looking for 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate # $tmp_term is always the next term (possibly :tc=...:) we are looking for 199*0Sstevel@tonic-gate $tmp_term = $self->{TERM}; 200*0Sstevel@tonic-gate # protect any pattern metacharacters in $tmp_term 201*0Sstevel@tonic-gate $termpat = $tmp_term; $termpat =~ s/(\W)/\\$1/g; 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gate my $foo = (exists $ENV{TERMCAP} ? $ENV{TERMCAP} : ''); 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate # $entry is the extracted termcap entry 206*0Sstevel@tonic-gate if (($foo !~ m:^/:s) && ($foo =~ m/(^|\|)${termpat}[:|]/s)) { 207*0Sstevel@tonic-gate $entry = $foo; 208*0Sstevel@tonic-gate } 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate my @termcap_path = termcap_path(); 211*0Sstevel@tonic-gate 212*0Sstevel@tonic-gate unless (@termcap_path || $entry) 213*0Sstevel@tonic-gate { 214*0Sstevel@tonic-gate # last resort--fake up a termcap from terminfo 215*0Sstevel@tonic-gate local $ENV{TERM} = $term; 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate if ( $^O eq 'VMS' ) { 218*0Sstevel@tonic-gate $entry = $VMS_TERMCAP; 219*0Sstevel@tonic-gate } 220*0Sstevel@tonic-gate else { 221*0Sstevel@tonic-gate if ( grep { -x "$_/infocmp" } split /:/, $ENV{PATH} ) { 222*0Sstevel@tonic-gate eval { 223*0Sstevel@tonic-gate my $tmp = `infocmp -C 2>/dev/null`; 224*0Sstevel@tonic-gate $tmp =~ s/^#.*\n//gm; # remove comments 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate if (( $tmp !~ m%^/%s ) && ( $tmp =~ /(^|\|)${termpat}[:|]/s)) { 227*0Sstevel@tonic-gate $entry = $tmp; 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate }; 230*0Sstevel@tonic-gate } 231*0Sstevel@tonic-gate } 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate croak "Can't find a valid termcap file" unless @termcap_path || $entry; 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate $state = 1; # 0 == finished 237*0Sstevel@tonic-gate # 1 == next file 238*0Sstevel@tonic-gate # 2 == search again 239*0Sstevel@tonic-gate 240*0Sstevel@tonic-gate $first = 0; # first entry (keeps term name) 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate $max = 32; # max :tc=...:'s 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate if ($entry) { 245*0Sstevel@tonic-gate # ok, we're starting with $TERMCAP 246*0Sstevel@tonic-gate $first++; # we're the first entry 247*0Sstevel@tonic-gate # do we need to continue? 248*0Sstevel@tonic-gate if ($entry =~ s/:tc=([^:]+):/:/) { 249*0Sstevel@tonic-gate $tmp_term = $1; 250*0Sstevel@tonic-gate # protect any pattern metacharacters in $tmp_term 251*0Sstevel@tonic-gate $termpat = $tmp_term; $termpat =~ s/(\W)/\\$1/g; 252*0Sstevel@tonic-gate } 253*0Sstevel@tonic-gate else { 254*0Sstevel@tonic-gate $state = 0; # we're already finished 255*0Sstevel@tonic-gate } 256*0Sstevel@tonic-gate } 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gate # This is eval'ed inside the while loop for each file 259*0Sstevel@tonic-gate $search = q{ 260*0Sstevel@tonic-gate while (<TERMCAP>) { 261*0Sstevel@tonic-gate next if /^\\t/ || /^#/; 262*0Sstevel@tonic-gate if ($_ =~ m/(^|\\|)${termpat}[:|]/o) { 263*0Sstevel@tonic-gate chomp; 264*0Sstevel@tonic-gate s/^[^:]*:// if $first++; 265*0Sstevel@tonic-gate $state = 0; 266*0Sstevel@tonic-gate while ($_ =~ s/\\\\$//) { 267*0Sstevel@tonic-gate defined(my $x = <TERMCAP>) or last; 268*0Sstevel@tonic-gate $_ .= $x; chomp; 269*0Sstevel@tonic-gate } 270*0Sstevel@tonic-gate last; 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate defined $entry or $entry = ''; 274*0Sstevel@tonic-gate $entry .= $_ if $_; 275*0Sstevel@tonic-gate }; 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate while ($state != 0) { 278*0Sstevel@tonic-gate if ($state == 1) { 279*0Sstevel@tonic-gate # get the next TERMCAP 280*0Sstevel@tonic-gate $TERMCAP = shift @termcap_path 281*0Sstevel@tonic-gate || croak "failed termcap lookup on $tmp_term"; 282*0Sstevel@tonic-gate } 283*0Sstevel@tonic-gate else { 284*0Sstevel@tonic-gate # do the same file again 285*0Sstevel@tonic-gate # prevent endless recursion 286*0Sstevel@tonic-gate $max-- || croak "failed termcap loop at $tmp_term"; 287*0Sstevel@tonic-gate $state = 1; # ok, maybe do a new file next time 288*0Sstevel@tonic-gate } 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate open(TERMCAP,"< $TERMCAP\0") || croak "open $TERMCAP: $!"; 291*0Sstevel@tonic-gate eval $search; 292*0Sstevel@tonic-gate die $@ if $@; 293*0Sstevel@tonic-gate close TERMCAP; 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate # If :tc=...: found then search this file again 296*0Sstevel@tonic-gate $entry =~ s/:tc=([^:]+):/:/ && ($tmp_term = $1, $state = 2); 297*0Sstevel@tonic-gate # protect any pattern metacharacters in $tmp_term 298*0Sstevel@tonic-gate $termpat = $tmp_term; $termpat =~ s/(\W)/\\$1/g; 299*0Sstevel@tonic-gate } 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate croak "Can't find $term" if $entry eq ''; 302*0Sstevel@tonic-gate $entry =~ s/:+\s*:+/:/g; # cleanup $entry 303*0Sstevel@tonic-gate $entry =~ s/:+/:/g; # cleanup $entry 304*0Sstevel@tonic-gate $self->{TERMCAP} = $entry; # save it 305*0Sstevel@tonic-gate # print STDERR "DEBUG: $entry = ", $entry, "\n"; 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gate # Precompile $entry into the object 308*0Sstevel@tonic-gate $entry =~ s/^[^:]*://; 309*0Sstevel@tonic-gate foreach $field (split(/:[\s:\\]*/,$entry)) { 310*0Sstevel@tonic-gate if (defined $field && $field =~ /^(\w\w)$/) { 311*0Sstevel@tonic-gate $self->{'_' . $field} = 1 unless defined $self->{'_' . $1}; 312*0Sstevel@tonic-gate # print STDERR "DEBUG: flag $1\n"; 313*0Sstevel@tonic-gate } 314*0Sstevel@tonic-gate elsif (defined $field && $field =~ /^(\w\w)\@/) { 315*0Sstevel@tonic-gate $self->{'_' . $1} = ""; 316*0Sstevel@tonic-gate # print STDERR "DEBUG: unset $1\n"; 317*0Sstevel@tonic-gate } 318*0Sstevel@tonic-gate elsif (defined $field && $field =~ /^(\w\w)#(.*)/) { 319*0Sstevel@tonic-gate $self->{'_' . $1} = $2 unless defined $self->{'_' . $1}; 320*0Sstevel@tonic-gate # print STDERR "DEBUG: numeric $1 = $2\n"; 321*0Sstevel@tonic-gate } 322*0Sstevel@tonic-gate elsif (defined $field && $field =~ /^(\w\w)=(.*)/) { 323*0Sstevel@tonic-gate # print STDERR "DEBUG: string $1 = $2\n"; 324*0Sstevel@tonic-gate next if defined $self->{'_' . ($cap = $1)}; 325*0Sstevel@tonic-gate $_ = $2; 326*0Sstevel@tonic-gate s/\\E/\033/g; 327*0Sstevel@tonic-gate s/\\(\d\d\d)/pack('c',oct($1) & 0177)/eg; 328*0Sstevel@tonic-gate s/\\n/\n/g; 329*0Sstevel@tonic-gate s/\\r/\r/g; 330*0Sstevel@tonic-gate s/\\t/\t/g; 331*0Sstevel@tonic-gate s/\\b/\b/g; 332*0Sstevel@tonic-gate s/\\f/\f/g; 333*0Sstevel@tonic-gate s/\\\^/\377/g; 334*0Sstevel@tonic-gate s/\^\?/\177/g; 335*0Sstevel@tonic-gate s/\^(.)/pack('c',ord($1) & 31)/eg; 336*0Sstevel@tonic-gate s/\\(.)/$1/g; 337*0Sstevel@tonic-gate s/\377/^/g; 338*0Sstevel@tonic-gate $self->{'_' . $cap} = $_; 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate # else { carp "junk in $term ignored: $field"; } 341*0Sstevel@tonic-gate } 342*0Sstevel@tonic-gate $self->{'_pc'} = "\0" unless defined $self->{'_pc'}; 343*0Sstevel@tonic-gate $self->{'_bc'} = "\b" unless defined $self->{'_bc'}; 344*0Sstevel@tonic-gate $self; 345*0Sstevel@tonic-gate} 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate# $terminal->Tpad($string, $cnt, $FH); 348*0Sstevel@tonic-gate 349*0Sstevel@tonic-gate=item B<Tpad> 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gateOutputs a literal string with appropriate padding for the current terminal. 352*0Sstevel@tonic-gate 353*0Sstevel@tonic-gateIt takes three arguments: 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate=over 2 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gate=item B<$string> 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gateThe literal string to be output. If it starts with a number and an optional 360*0Sstevel@tonic-gate'*' then the padding will be increased by an amount relative to this number, 361*0Sstevel@tonic-gateif the '*' is present then this amount will me multiplied by $cnt. This part 362*0Sstevel@tonic-gateof $string is removed before output/ 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate=item B<$cnt> 365*0Sstevel@tonic-gate 366*0Sstevel@tonic-gateWill be used to modify the padding applied to string as described above. 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate=item B<$FH> 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gateAn optional filehandle (or IO::Handle ) that output will be printed to. 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate=back 373*0Sstevel@tonic-gate 374*0Sstevel@tonic-gateThe padded $string is returned. 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate=cut 377*0Sstevel@tonic-gate 378*0Sstevel@tonic-gatesub Tpad { ## public 379*0Sstevel@tonic-gate my $self = shift; 380*0Sstevel@tonic-gate my($string, $cnt, $FH) = @_; 381*0Sstevel@tonic-gate my($decr, $ms); 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate if (defined $string && $string =~ /(^[\d.]+)(\*?)(.*)$/) { 384*0Sstevel@tonic-gate $ms = $1; 385*0Sstevel@tonic-gate $ms *= $cnt if $2; 386*0Sstevel@tonic-gate $string = $3; 387*0Sstevel@tonic-gate $decr = $self->{PADDING}; 388*0Sstevel@tonic-gate if ($decr > .1) { 389*0Sstevel@tonic-gate $ms += $decr / 2; 390*0Sstevel@tonic-gate $string .= $self->{'_pc'} x ($ms / $decr); 391*0Sstevel@tonic-gate } 392*0Sstevel@tonic-gate } 393*0Sstevel@tonic-gate print $FH $string if $FH; 394*0Sstevel@tonic-gate $string; 395*0Sstevel@tonic-gate} 396*0Sstevel@tonic-gate 397*0Sstevel@tonic-gate# $terminal->Tputs($cap, $cnt, $FH); 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate=item B<Tputs> 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gateOutput the string for the given capability padded as appropriate without 402*0Sstevel@tonic-gateany parameter substitution. 403*0Sstevel@tonic-gate 404*0Sstevel@tonic-gateIt takes three arguments: 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate=over 2 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate=item B<$cap> 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gateThe capability whose string is to be output. 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate=item B<$cnt> 413*0Sstevel@tonic-gate 414*0Sstevel@tonic-gateA count passed to Tpad to modify the padding applied to the output string. 415*0Sstevel@tonic-gateIf $cnt is zero or one then the resulting string will be cached. 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate=item B<$FH> 418*0Sstevel@tonic-gate 419*0Sstevel@tonic-gateAn optional filehandle (or IO::Handle ) that output will be printed to. 420*0Sstevel@tonic-gate 421*0Sstevel@tonic-gate=back 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gateThe appropriate string for the capability will be returned. 424*0Sstevel@tonic-gate 425*0Sstevel@tonic-gate=cut 426*0Sstevel@tonic-gate 427*0Sstevel@tonic-gatesub Tputs { ## public 428*0Sstevel@tonic-gate my $self = shift; 429*0Sstevel@tonic-gate my($cap, $cnt, $FH) = @_; 430*0Sstevel@tonic-gate my $string; 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate $cnt = 0 unless $cnt; 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate if ($cnt > 1) { 435*0Sstevel@tonic-gate $string = Tpad($self, $self->{'_' . $cap}, $cnt); 436*0Sstevel@tonic-gate } else { 437*0Sstevel@tonic-gate # cache result because Tpad can be slow 438*0Sstevel@tonic-gate unless (exists $self->{$cap}) { 439*0Sstevel@tonic-gate $self->{$cap} = exists $self->{"_$cap"} ? 440*0Sstevel@tonic-gate Tpad($self, $self->{"_$cap"}, 1) : undef; 441*0Sstevel@tonic-gate } 442*0Sstevel@tonic-gate $string = $self->{$cap}; 443*0Sstevel@tonic-gate } 444*0Sstevel@tonic-gate print $FH $string if $FH; 445*0Sstevel@tonic-gate $string; 446*0Sstevel@tonic-gate} 447*0Sstevel@tonic-gate 448*0Sstevel@tonic-gate# $terminal->Tgoto($cap, $col, $row, $FH); 449*0Sstevel@tonic-gate 450*0Sstevel@tonic-gate=item B<Tgoto> 451*0Sstevel@tonic-gate 452*0Sstevel@tonic-gateB<Tgoto> decodes a cursor addressing string with the given parameters. 453*0Sstevel@tonic-gate 454*0Sstevel@tonic-gateThere are four arguments: 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate=over 2 457*0Sstevel@tonic-gate 458*0Sstevel@tonic-gate=item B<$cap> 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gateThe name of the capability to be output. 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate=item B<$col> 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gateThe first value to be substituted in the output string ( usually the column 465*0Sstevel@tonic-gatein a cursor addressing capability ) 466*0Sstevel@tonic-gate 467*0Sstevel@tonic-gate=item B<$row> 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gateThe second value to be substituted in the output string (usually the row 470*0Sstevel@tonic-gatein cursor addressing capabilities) 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate=item B<$FH> 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gateAn optional filehandle (or IO::Handle ) to which the output string will be 475*0Sstevel@tonic-gateprinted. 476*0Sstevel@tonic-gate 477*0Sstevel@tonic-gate=back 478*0Sstevel@tonic-gate 479*0Sstevel@tonic-gateSubstitutions are made with $col and $row in the output string with the 480*0Sstevel@tonic-gatefollowing sprintf() line formats: 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate %% output `%' 483*0Sstevel@tonic-gate %d output value as in printf %d 484*0Sstevel@tonic-gate %2 output value as in printf %2d 485*0Sstevel@tonic-gate %3 output value as in printf %3d 486*0Sstevel@tonic-gate %. output value as in printf %c 487*0Sstevel@tonic-gate %+x add x to value, then do %. 488*0Sstevel@tonic-gate 489*0Sstevel@tonic-gate %>xy if value > x then add y, no output 490*0Sstevel@tonic-gate %r reverse order of two parameters, no output 491*0Sstevel@tonic-gate %i increment by one, no output 492*0Sstevel@tonic-gate %B BCD (16*(value/10)) + (value%10), no output 493*0Sstevel@tonic-gate 494*0Sstevel@tonic-gate %n exclusive-or all parameters with 0140 (Datamedia 2500) 495*0Sstevel@tonic-gate %D Reverse coding (value - 2*(value%16)), no output (Delta Data) 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gateThe output string will be returned. 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate=cut 500*0Sstevel@tonic-gate 501*0Sstevel@tonic-gatesub Tgoto { ## public 502*0Sstevel@tonic-gate my $self = shift; 503*0Sstevel@tonic-gate my($cap, $code, $tmp, $FH) = @_; 504*0Sstevel@tonic-gate my $string = $self->{'_' . $cap}; 505*0Sstevel@tonic-gate my $result = ''; 506*0Sstevel@tonic-gate my $after = ''; 507*0Sstevel@tonic-gate my $online = 0; 508*0Sstevel@tonic-gate my @tmp = ($tmp,$code); 509*0Sstevel@tonic-gate my $cnt = $code; 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate while ($string =~ /^([^%]*)%(.)(.*)/) { 512*0Sstevel@tonic-gate $result .= $1; 513*0Sstevel@tonic-gate $code = $2; 514*0Sstevel@tonic-gate $string = $3; 515*0Sstevel@tonic-gate if ($code eq 'd') { 516*0Sstevel@tonic-gate $result .= sprintf("%d",shift(@tmp)); 517*0Sstevel@tonic-gate } 518*0Sstevel@tonic-gate elsif ($code eq '.') { 519*0Sstevel@tonic-gate $tmp = shift(@tmp); 520*0Sstevel@tonic-gate if ($tmp == 0 || $tmp == 4 || $tmp == 10) { 521*0Sstevel@tonic-gate if ($online) { 522*0Sstevel@tonic-gate ++$tmp, $after .= $self->{'_up'} if $self->{'_up'}; 523*0Sstevel@tonic-gate } 524*0Sstevel@tonic-gate else { 525*0Sstevel@tonic-gate ++$tmp, $after .= $self->{'_bc'}; 526*0Sstevel@tonic-gate } 527*0Sstevel@tonic-gate } 528*0Sstevel@tonic-gate $result .= sprintf("%c",$tmp); 529*0Sstevel@tonic-gate $online = !$online; 530*0Sstevel@tonic-gate } 531*0Sstevel@tonic-gate elsif ($code eq '+') { 532*0Sstevel@tonic-gate $result .= sprintf("%c",shift(@tmp)+ord($string)); 533*0Sstevel@tonic-gate $string = substr($string,1,99); 534*0Sstevel@tonic-gate $online = !$online; 535*0Sstevel@tonic-gate } 536*0Sstevel@tonic-gate elsif ($code eq 'r') { 537*0Sstevel@tonic-gate ($code,$tmp) = @tmp; 538*0Sstevel@tonic-gate @tmp = ($tmp,$code); 539*0Sstevel@tonic-gate $online = !$online; 540*0Sstevel@tonic-gate } 541*0Sstevel@tonic-gate elsif ($code eq '>') { 542*0Sstevel@tonic-gate ($code,$tmp,$string) = unpack("CCa99",$string); 543*0Sstevel@tonic-gate if ($tmp[$[] > $code) { 544*0Sstevel@tonic-gate $tmp[$[] += $tmp; 545*0Sstevel@tonic-gate } 546*0Sstevel@tonic-gate } 547*0Sstevel@tonic-gate elsif ($code eq '2') { 548*0Sstevel@tonic-gate $result .= sprintf("%02d",shift(@tmp)); 549*0Sstevel@tonic-gate $online = !$online; 550*0Sstevel@tonic-gate } 551*0Sstevel@tonic-gate elsif ($code eq '3') { 552*0Sstevel@tonic-gate $result .= sprintf("%03d",shift(@tmp)); 553*0Sstevel@tonic-gate $online = !$online; 554*0Sstevel@tonic-gate } 555*0Sstevel@tonic-gate elsif ($code eq 'i') { 556*0Sstevel@tonic-gate ($code,$tmp) = @tmp; 557*0Sstevel@tonic-gate @tmp = ($code+1,$tmp+1); 558*0Sstevel@tonic-gate } 559*0Sstevel@tonic-gate else { 560*0Sstevel@tonic-gate return "OOPS"; 561*0Sstevel@tonic-gate } 562*0Sstevel@tonic-gate } 563*0Sstevel@tonic-gate $string = Tpad($self, $result . $string . $after, $cnt); 564*0Sstevel@tonic-gate print $FH $string if $FH; 565*0Sstevel@tonic-gate $string; 566*0Sstevel@tonic-gate} 567*0Sstevel@tonic-gate 568*0Sstevel@tonic-gate# $terminal->Trequire(qw/ce ku kd/); 569*0Sstevel@tonic-gate 570*0Sstevel@tonic-gate=item B<Trequire> 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gateTakes a list of capabilities as an argument and will croak if one is not 573*0Sstevel@tonic-gatefound. 574*0Sstevel@tonic-gate 575*0Sstevel@tonic-gate=cut 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gatesub Trequire { ## public 578*0Sstevel@tonic-gate my $self = shift; 579*0Sstevel@tonic-gate my($cap,@undefined); 580*0Sstevel@tonic-gate foreach $cap (@_) { 581*0Sstevel@tonic-gate push(@undefined, $cap) 582*0Sstevel@tonic-gate unless defined $self->{'_' . $cap} && $self->{'_' . $cap}; 583*0Sstevel@tonic-gate } 584*0Sstevel@tonic-gate croak "Terminal does not support: (@undefined)" if @undefined; 585*0Sstevel@tonic-gate} 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate=back 588*0Sstevel@tonic-gate 589*0Sstevel@tonic-gate=head1 EXAMPLES 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate use Term::Cap; 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate # Get terminal output speed 594*0Sstevel@tonic-gate require POSIX; 595*0Sstevel@tonic-gate my $termios = new POSIX::Termios; 596*0Sstevel@tonic-gate $termios->getattr; 597*0Sstevel@tonic-gate my $ospeed = $termios->getospeed; 598*0Sstevel@tonic-gate 599*0Sstevel@tonic-gate # Old-style ioctl code to get ospeed: 600*0Sstevel@tonic-gate # require 'ioctl.pl'; 601*0Sstevel@tonic-gate # ioctl(TTY,$TIOCGETP,$sgtty); 602*0Sstevel@tonic-gate # ($ispeed,$ospeed) = unpack('cc',$sgtty); 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate # allocate and initialize a terminal structure 605*0Sstevel@tonic-gate $terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed }; 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate # require certain capabilities to be available 608*0Sstevel@tonic-gate $terminal->Trequire(qw/ce ku kd/); 609*0Sstevel@tonic-gate 610*0Sstevel@tonic-gate # Output Routines, if $FH is undefined these just return the string 611*0Sstevel@tonic-gate 612*0Sstevel@tonic-gate # Tgoto does the % expansion stuff with the given args 613*0Sstevel@tonic-gate $terminal->Tgoto('cm', $col, $row, $FH); 614*0Sstevel@tonic-gate 615*0Sstevel@tonic-gate # Tputs doesn't do any % expansion. 616*0Sstevel@tonic-gate $terminal->Tputs('dl', $count = 1, $FH); 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate=head1 COPYRIGHT AND LICENSE 619*0Sstevel@tonic-gate 620*0Sstevel@tonic-gatePlease see the README file in distribution. 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate=head1 AUTHOR 623*0Sstevel@tonic-gate 624*0Sstevel@tonic-gateThis module is part of the core Perl distribution and is also maintained 625*0Sstevel@tonic-gatefor CPAN by Jonathan Stowe <jns@gellyfish.com>. 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate=head1 SEE ALSO 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gatetermcap(5) 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate=cut 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate# Below is a default entry for systems where there are terminals but no 634*0Sstevel@tonic-gate# termcap 635*0Sstevel@tonic-gate1; 636*0Sstevel@tonic-gate__DATA__ 637*0Sstevel@tonic-gatevt220|vt200|DEC VT220 in vt100 emulation mode: 638*0Sstevel@tonic-gateam:mi:xn:xo: 639*0Sstevel@tonic-gateco#80:li#24: 640*0Sstevel@tonic-gateRA=\E[?7l:SA=\E[?7h: 641*0Sstevel@tonic-gateac=kkllmmjjnnwwqquuttvvxx:ae=\E(B:al=\E[L:as=\E(0: 642*0Sstevel@tonic-gatebl=^G:cd=\E[J:ce=\E[K:cl=\E[H\E[2J:cm=\E[%i%d;%dH: 643*0Sstevel@tonic-gatecr=^M:cs=\E[%i%d;%dr:dc=\E[P:dl=\E[M:do=\E[B: 644*0Sstevel@tonic-gateei=\E[4l:ho=\E[H:im=\E[4h: 645*0Sstevel@tonic-gateis=\E[1;24r\E[24;1H: 646*0Sstevel@tonic-gatend=\E[C: 647*0Sstevel@tonic-gatekd=\E[B::kl=\E[D:kr=\E[C:ku=\E[A:le=^H: 648*0Sstevel@tonic-gatemb=\E[5m:md=\E[1m:me=\E[m:mr=\E[7m: 649*0Sstevel@tonic-gatekb=\0177: 650*0Sstevel@tonic-gater2=\E>\E[24;1H\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E=:rc=\E8: 651*0Sstevel@tonic-gatesc=\E7:se=\E[27m:sf=\ED:so=\E[7m:sr=\EM:ta=^I: 652*0Sstevel@tonic-gateue=\E[24m:up=\E[A:us=\E[4m:ve=\E[?25h:vi=\E[?25l: 653*0Sstevel@tonic-gate 654