1b39c5158Smillertpackage Term::Cap; 2b39c5158Smillert 3b39c5158Smillert# Since the debugger uses Term::ReadLine which uses Term::Cap, we want 4b39c5158Smillert# to load as few modules as possible. This includes Carp.pm. 5b39c5158Smillertsub carp 6b39c5158Smillert{ 7b39c5158Smillert require Carp; 8b39c5158Smillert goto &Carp::carp; 9b39c5158Smillert} 10b39c5158Smillert 11b39c5158Smillertsub croak 12b39c5158Smillert{ 13b39c5158Smillert require Carp; 14b39c5158Smillert goto &Carp::croak; 15b39c5158Smillert} 16b39c5158Smillert 17b39c5158Smillertuse strict; 18b39c5158Smillert 19b39c5158Smillertuse vars qw($VERSION $VMS_TERMCAP); 20b39c5158Smillert 21ab0ef1e6Sespie$VERSION = '1.18'; 22b39c5158Smillert 23b39c5158Smillert# TODO: 24b39c5158Smillert# support Berkeley DB termcaps 25b39c5158Smillert# force $FH into callers package? 26b39c5158Smillert# keep $FH in object at Tgetent time? 27b39c5158Smillert 28b39c5158Smillert=head1 NAME 29b39c5158Smillert 30b39c5158SmillertTerm::Cap - Perl termcap interface 31b39c5158Smillert 32b39c5158Smillert=head1 SYNOPSIS 33b39c5158Smillert 34b39c5158Smillert require Term::Cap; 35ab0ef1e6Sespie $terminal = Term::Cap->Tgetent({ TERM => undef, OSPEED => $ospeed }); 36b39c5158Smillert $terminal->Trequire(qw/ce ku kd/); 37b39c5158Smillert $terminal->Tgoto('cm', $col, $row, $FH); 38b39c5158Smillert $terminal->Tputs('dl', $count, $FH); 39b39c5158Smillert $terminal->Tpad($string, $count, $FH); 40b39c5158Smillert 41b39c5158Smillert=head1 DESCRIPTION 42b39c5158Smillert 43b39c5158SmillertThese are low-level functions to extract and use capabilities from 44b39c5158Smillerta terminal capability (termcap) database. 45b39c5158Smillert 46b39c5158SmillertMore information on the terminal capabilities will be found in the 47b39c5158Smillerttermcap manpage on most Unix-like systems. 48b39c5158Smillert 49b39c5158Smillert=head2 METHODS 50b39c5158Smillert 51b39c5158SmillertThe output strings for B<Tputs> are cached for counts of 1 for performance. 52b39c5158SmillertB<Tgoto> and B<Tpad> do not cache. C<$self-E<gt>{_xx}> is the raw termcap 53b39c5158Smillertdata and C<$self-E<gt>{xx}> is the cached version. 54b39c5158Smillert 55b39c5158Smillert print $terminal->Tpad($self->{_xx}, 1); 56b39c5158Smillert 57b39c5158SmillertB<Tgoto>, B<Tputs>, and B<Tpad> return the string and will also 58b39c5158Smillertoutput the string to $FH if specified. 59b39c5158Smillert 60b39c5158Smillert 61b39c5158Smillert=cut 62b39c5158Smillert 63b39c5158Smillert# Preload the default VMS termcap. 64b39c5158Smillert# If a different termcap is required then the text of one can be supplied 65b39c5158Smillert# in $Term::Cap::VMS_TERMCAP before Tgetent is called. 66b39c5158Smillert 67b39c5158Smillertif ( $^O eq 'VMS' ) 68b39c5158Smillert{ 69b39c5158Smillert chomp( my @entry = <DATA> ); 70b39c5158Smillert $VMS_TERMCAP = join '', @entry; 71b39c5158Smillert} 72b39c5158Smillert 73b39c5158Smillert# Returns a list of termcap files to check. 74b39c5158Smillert 75b39c5158Smillertsub termcap_path 76b39c5158Smillert{ ## private 77b39c5158Smillert my @termcap_path; 78b39c5158Smillert 79b39c5158Smillert # $TERMCAP, if it's a filespec 80b39c5158Smillert push( @termcap_path, $ENV{TERMCAP} ) 81b39c5158Smillert if ( 82b39c5158Smillert ( exists $ENV{TERMCAP} ) 83b39c5158Smillert && ( 84b39c5158Smillert ( $^O eq 'os2' || $^O eq 'MSWin32' || $^O eq 'dos' ) 85b39c5158Smillert ? $ENV{TERMCAP} =~ /^[a-z]:[\\\/]/is 86b39c5158Smillert : $ENV{TERMCAP} =~ /^\//s 87b39c5158Smillert ) 88b39c5158Smillert ); 89b39c5158Smillert if ( ( exists $ENV{TERMPATH} ) && ( $ENV{TERMPATH} ) ) 90b39c5158Smillert { 91b39c5158Smillert 92b39c5158Smillert # Add the users $TERMPATH 93ab0ef1e6Sespie push( @termcap_path, split( /:|\s+/, $ENV{TERMPATH} ) ); 94b39c5158Smillert } 95b39c5158Smillert else 96b39c5158Smillert { 97b39c5158Smillert 98b39c5158Smillert # Defaults 99b39c5158Smillert push( @termcap_path, 100b39c5158Smillert exists $ENV{'HOME'} ? $ENV{'HOME'} . '/.termcap' : undef, 101b39c5158Smillert '/etc/termcap', '/usr/share/misc/termcap', ); 102b39c5158Smillert } 103b39c5158Smillert 104b39c5158Smillert # return the list of those termcaps that exist 105b39c5158Smillert return grep { defined $_ && -f $_ } @termcap_path; 106b39c5158Smillert} 107b39c5158Smillert 108898184e3Ssthen=over 4 109898184e3Ssthen 110b39c5158Smillert=item B<Tgetent> 111b39c5158Smillert 112b39c5158SmillertReturns a blessed object reference which the user can 113b39c5158Smillertthen use to send the control strings to the terminal using B<Tputs> 114b39c5158Smillertand B<Tgoto>. 115b39c5158Smillert 116b39c5158SmillertThe function extracts the entry of the specified terminal 117b39c5158Smillerttype I<TERM> (defaults to the environment variable I<TERM>) from the 118b39c5158Smillertdatabase. 119b39c5158Smillert 120b39c5158SmillertIt will look in the environment for a I<TERMCAP> variable. If 121b39c5158Smillertfound, and the value does not begin with a slash, and the terminal 122b39c5158Smillerttype name is the same as the environment string I<TERM>, the 123b39c5158SmillertI<TERMCAP> string is used instead of reading a termcap file. If 124b39c5158Smillertit does begin with a slash, the string is used as a path name of 125b39c5158Smillertthe termcap file to search. If I<TERMCAP> does not begin with a 126b39c5158Smillertslash and name is different from I<TERM>, B<Tgetent> searches the 127b39c5158Smillertfiles F<$HOME/.termcap>, F</etc/termcap>, and F</usr/share/misc/termcap>, 128b39c5158Smillertin that order, unless the environment variable I<TERMPATH> exists, 129b39c5158Smillertin which case it specifies a list of file pathnames (separated by 130b39c5158Smillertspaces or colons) to be searched B<instead>. Whenever multiple 131b39c5158Smillertfiles are searched and a tc field occurs in the requested entry, 132b39c5158Smillertthe entry it names must be found in the same file or one of the 133b39c5158Smillertsucceeding files. If there is a C<:tc=...:> in the I<TERMCAP> 134b39c5158Smillertenvironment variable string it will continue the search in the 135b39c5158Smillertfiles as above. 136b39c5158Smillert 137b39c5158SmillertThe extracted termcap entry is available in the object 138b39c5158Smillertas C<$self-E<gt>{TERMCAP}>. 139b39c5158Smillert 140b39c5158SmillertIt takes a hash reference as an argument with two optional keys: 141b39c5158Smillert 142b39c5158Smillert=over 2 143b39c5158Smillert 144b39c5158Smillert=item OSPEED 145b39c5158Smillert 146b39c5158SmillertThe terminal output bit rate (often mistakenly called the baud rate) 147b39c5158Smillertfor this terminal - if not set a warning will be generated 1486fb12b70Safresh1and it will be defaulted to 9600. I<OSPEED> can be specified as 149b39c5158Smillerteither a POSIX termios/SYSV termio speeds (where 9600 equals 9600) or 150b39c5158Smillertan old DSD-style speed ( where 13 equals 9600). 151b39c5158Smillert 152b39c5158Smillert 153b39c5158Smillert=item TERM 154b39c5158Smillert 155b39c5158SmillertThe terminal type whose termcap entry will be used - if not supplied it will 156b39c5158Smillertdefault to $ENV{TERM}: if that is not set then B<Tgetent> will croak. 157b39c5158Smillert 158b39c5158Smillert=back 159b39c5158Smillert 160b39c5158SmillertIt calls C<croak> on failure. 161b39c5158Smillert 162b39c5158Smillert=cut 163b39c5158Smillert 164b39c5158Smillertsub Tgetent 165b39c5158Smillert{ ## public -- static method 166b39c5158Smillert my $class = shift; 167b39c5158Smillert my ($self) = @_; 168b39c5158Smillert 169b39c5158Smillert $self = {} unless defined $self; 170b39c5158Smillert bless $self, $class; 171b39c5158Smillert 17223376045Sespie my ( $term, $cap, $search, $field, $tmp_term, $TERMCAP ); 173fa4ae7feSespie my ( $state, $first, $entry ); 174b39c5158Smillert local $_; 175b39c5158Smillert 176b39c5158Smillert # Compute PADDING factor from OSPEED (to be used by Tpad) 177b39c5158Smillert if ( !$self->{OSPEED} ) 178b39c5158Smillert { 179b39c5158Smillert if ($^W) 180b39c5158Smillert { 181b39c5158Smillert carp "OSPEED was not set, defaulting to 9600"; 182b39c5158Smillert } 183b39c5158Smillert $self->{OSPEED} = 9600; 184b39c5158Smillert } 185b39c5158Smillert if ( $self->{OSPEED} < 16 ) 186b39c5158Smillert { 187b39c5158Smillert 188b39c5158Smillert # delays for old style speeds 189b39c5158Smillert my @pad = ( 190b39c5158Smillert 0, 200, 133.3, 90.9, 74.3, 66.7, 50, 33.3, 191b39c5158Smillert 16.7, 8.3, 5.5, 4.1, 2, 1, .5, .2 192b39c5158Smillert ); 193b39c5158Smillert $self->{PADDING} = $pad[ $self->{OSPEED} ]; 194b39c5158Smillert } 195b39c5158Smillert else 196b39c5158Smillert { 197b39c5158Smillert $self->{PADDING} = 10000 / $self->{OSPEED}; 198b39c5158Smillert } 199b39c5158Smillert 200b39c5158Smillert unless ( $self->{TERM} ) 201b39c5158Smillert { 202b39c5158Smillert if ( $ENV{TERM} ) 203b39c5158Smillert { 204b39c5158Smillert $self->{TERM} = $ENV{TERM} ; 205b39c5158Smillert } 206b39c5158Smillert else 207b39c5158Smillert { 2086fb12b70Safresh1 if ( $^O eq 'MSWin32' ) 209b39c5158Smillert { 210b39c5158Smillert $self->{TERM} = 'dumb'; 211b39c5158Smillert } 212b39c5158Smillert else 213b39c5158Smillert { 214b39c5158Smillert croak "TERM not set"; 215b39c5158Smillert } 216b39c5158Smillert } 217b39c5158Smillert } 218b39c5158Smillert 219b39c5158Smillert $term = $self->{TERM}; # $term is the term type we are looking for 220b39c5158Smillert 221b39c5158Smillert # $tmp_term is always the next term (possibly :tc=...:) we are looking for 222b39c5158Smillert $tmp_term = $self->{TERM}; 22323376045Sespie my $seen = {}; 224b39c5158Smillert 225962e816aSespie if (exists $ENV{TERMCAP}) { 226962e816aSespie local $_ = $ENV{TERMCAP}; 227962e816aSespie if ( !m:^/:s && m/(^|\|)\Q$tmp_term\E[:|]/s ) { 228962e816aSespie $entry = $_; 22923376045Sespie $seen->{$tmp_term} = 1; 230b39c5158Smillert } 231962e816aSespie } 232b39c5158Smillert 233b39c5158Smillert my @termcap_path = termcap_path(); 234b39c5158Smillert 235b39c5158Smillert $state = 1; # 0 == finished 236b39c5158Smillert # 1 == next file 237b39c5158Smillert # 2 == search again 238000d6ed8Sespie # 3 == try infocmp 239b39c5158Smillert 240b39c5158Smillert $first = 0; # first entry (keeps term name) 241b39c5158Smillert 242b39c5158Smillert if ($entry) 243b39c5158Smillert { 244b39c5158Smillert 245b39c5158Smillert # ok, we're starting with $TERMCAP 246b39c5158Smillert $first++; # we're the first entry 247b39c5158Smillert # do we need to continue? 248b39c5158Smillert if ( $entry =~ s/:tc=([^:]+):/:/ ) 249b39c5158Smillert { 250b39c5158Smillert $tmp_term = $1; 251b39c5158Smillert } 252b39c5158Smillert else 253b39c5158Smillert { 254b39c5158Smillert $state = 0; # we're already finished 255b39c5158Smillert } 256b39c5158Smillert } 257b39c5158Smillert 258b39c5158Smillert 259b39c5158Smillert while ( $state != 0 ) 260b39c5158Smillert { 261000d6ed8Sespie if ( $state == 1 ) { 262b39c5158Smillert # get the next TERMCAP 263000d6ed8Sespie $TERMCAP = shift @termcap_path or $state = 3; 264000d6ed8Sespie } elsif ($state == 3) { 265000d6ed8Sespie croak "failed termcap lookup on $tmp_term"; 266000d6ed8Sespie } else { 267b39c5158Smillert # do the same file again 268b39c5158Smillert $state = 1; # ok, maybe do a new file next time 269b39c5158Smillert } 270b39c5158Smillert 271000d6ed8Sespie my ($fh, $child); 272000d6ed8Sespie if ($state == 3) { 273000d6ed8Sespie # need to do a proper fork, so that we can pass tmp_term 274000d6ed8Sespie # without having to quote it. 275000d6ed8Sespie $child = open($fh, "-|"); 276000d6ed8Sespie warn "cannot run infocmp: $!" if !defined $child; 277000d6ed8Sespie if (!$child) { 278000d6ed8Sespie open(STDERR, ">", "/dev/null"); 279*6835ac24Sespie exec('infocmp', '-CTrx', '--', $tmp_term); 280fea8967dSespie exit(1); 281000d6ed8Sespie } 282000d6ed8Sespie } else { 283000d6ed8Sespie open($fh, '<', $TERMCAP) || croak "open $TERMCAP: $!"; 284000d6ed8Sespie } 2852da37aacSespie while (<$fh>) { 286fa4ae7feSespie next if /^\t/ || /^#/; 287fa4ae7feSespie if (m/(^|\|)\Q$tmp_term\E[:|]/) { 288fa4ae7feSespie chomp; 289fa4ae7feSespie s/^[^:]*:// if $first++; 290fa4ae7feSespie $state = 0; 29123376045Sespie $seen->{$tmp_term} = 1; 292fa4ae7feSespie while (s/\\$//) { 2932da37aacSespie defined(my $x = <$fh>) or last; 294fa4ae7feSespie $_ .= $x; chomp; 295fa4ae7feSespie } 296460dc485Sespie if (defined $entry) { 297460dc485Sespie $entry .= $_; 298460dc485Sespie } else { 299460dc485Sespie $entry = $_; 300460dc485Sespie } 301fa4ae7feSespie last; 302fa4ae7feSespie } 303fa4ae7feSespie } 3042da37aacSespie close $fh; 305000d6ed8Sespie waitpid($child, 0) if defined $child; 306b39c5158Smillert 307035d1660Sespie next if $state != 0; 308035d1660Sespie 309b39c5158Smillert # If :tc=...: found then search this file again 31023376045Sespie while ($entry =~ s/:tc=([^:]+):/:/) { 31123376045Sespie $tmp_term = $1; 31223376045Sespie next if $seen->{$tmp_term}; 31323376045Sespie $state = 2; 31423376045Sespie last; 31523376045Sespie } 316b39c5158Smillert } 317b39c5158Smillert 318035d1660Sespie if ( !defined $entry ) { 319035d1660Sespie if ( $^O eq 'VMS' ) { 320035d1660Sespie $entry = $VMS_TERMCAP; 321035d1660Sespie # this is getting desperate now 322035d1660Sespie } elsif ( $self->{TERM} eq 'dumb' ){ 323035d1660Sespie $entry = 'dumb|80-column dumb tty::am::co#80::bl=^G:cr=^M:do=^J:sf=^J:'; 324035d1660Sespie } 325035d1660Sespie } 326035d1660Sespie 327035d1660Sespie croak "Can't find $term" if !defined $entry; 328b39c5158Smillert $entry =~ s/:+\s*:+/:/g; # cleanup $entry 329b39c5158Smillert $entry =~ s/:+/:/g; # cleanup $entry 330b39c5158Smillert $self->{TERMCAP} = $entry; # save it 331b39c5158Smillert # print STDERR "DEBUG: $entry = ", $entry, "\n"; 332b39c5158Smillert 333b39c5158Smillert # Precompile $entry into the object 334b39c5158Smillert $entry =~ s/^[^:]*://; 335b39c5158Smillert foreach $field ( split( /:[\s:\\]*/, $entry ) ) 336b39c5158Smillert { 337b8851fccSafresh1 if ( defined $field && $field =~ /^(\w{2,})$/ ) 338b39c5158Smillert { 339b39c5158Smillert $self->{ '_' . $field } = 1 unless defined $self->{ '_' . $1 }; 340b39c5158Smillert 341b39c5158Smillert # print STDERR "DEBUG: flag $1\n"; 342b39c5158Smillert } 343b8851fccSafresh1 elsif ( defined $field && $field =~ /^(\w{2,})\@/ ) 344b39c5158Smillert { 345b39c5158Smillert $self->{ '_' . $1 } = ""; 346b39c5158Smillert 347b39c5158Smillert # print STDERR "DEBUG: unset $1\n"; 348b39c5158Smillert } 349b8851fccSafresh1 elsif ( defined $field && $field =~ /^(\w{2,})#(.*)/ ) 350b39c5158Smillert { 351b39c5158Smillert $self->{ '_' . $1 } = $2 unless defined $self->{ '_' . $1 }; 352b39c5158Smillert 353b39c5158Smillert # print STDERR "DEBUG: numeric $1 = $2\n"; 354b39c5158Smillert } 355b8851fccSafresh1 elsif ( defined $field && $field =~ /^(\w{2,})=(.*)/ ) 356b39c5158Smillert { 357b39c5158Smillert 358b39c5158Smillert # print STDERR "DEBUG: string $1 = $2\n"; 359b39c5158Smillert next if defined $self->{ '_' . ( $cap = $1 ) }; 360b39c5158Smillert $_ = $2; 361b39c5158Smillert if ( ord('A') == 193 ) 362b39c5158Smillert { 363b39c5158Smillert s/\\E/\047/g; 364b39c5158Smillert s/\\(\d\d\d)/pack('c',oct($1) & 0177)/eg; 365b39c5158Smillert s/\\n/\n/g; 366b39c5158Smillert s/\\r/\r/g; 367b39c5158Smillert s/\\t/\t/g; 368b39c5158Smillert s/\\b/\b/g; 369b39c5158Smillert s/\\f/\f/g; 370b39c5158Smillert s/\\\^/\337/g; 371b39c5158Smillert s/\^\?/\007/g; 372b39c5158Smillert s/\^(.)/pack('c',ord($1) & 31)/eg; 373b39c5158Smillert s/\\(.)/$1/g; 374b39c5158Smillert s/\337/^/g; 375b39c5158Smillert } 376b39c5158Smillert else 377b39c5158Smillert { 378b39c5158Smillert s/\\E/\033/g; 379b39c5158Smillert s/\\(\d\d\d)/pack('c',oct($1) & 0177)/eg; 380b39c5158Smillert s/\\n/\n/g; 381b39c5158Smillert s/\\r/\r/g; 382b39c5158Smillert s/\\t/\t/g; 383b39c5158Smillert s/\\b/\b/g; 384b39c5158Smillert s/\\f/\f/g; 385b39c5158Smillert s/\\\^/\377/g; 386b39c5158Smillert s/\^\?/\177/g; 387b39c5158Smillert s/\^(.)/pack('c',ord($1) & 31)/eg; 388b39c5158Smillert s/\\(.)/$1/g; 389b39c5158Smillert s/\377/^/g; 390b39c5158Smillert } 391b39c5158Smillert $self->{ '_' . $cap } = $_; 392b39c5158Smillert } 393b39c5158Smillert 394b39c5158Smillert # else { carp "junk in $term ignored: $field"; } 395b39c5158Smillert } 396b39c5158Smillert $self->{'_pc'} = "\0" unless defined $self->{'_pc'}; 397b39c5158Smillert $self->{'_bc'} = "\b" unless defined $self->{'_bc'}; 398b39c5158Smillert $self; 399b39c5158Smillert} 400b39c5158Smillert 401b39c5158Smillert# $terminal->Tpad($string, $cnt, $FH); 402b39c5158Smillert 403b39c5158Smillert=item B<Tpad> 404b39c5158Smillert 405b39c5158SmillertOutputs a literal string with appropriate padding for the current terminal. 406b39c5158Smillert 407b39c5158SmillertIt takes three arguments: 408b39c5158Smillert 409b39c5158Smillert=over 2 410b39c5158Smillert 411b39c5158Smillert=item B<$string> 412b39c5158Smillert 413b39c5158SmillertThe literal string to be output. If it starts with a number and an optional 414b39c5158Smillert'*' then the padding will be increased by an amount relative to this number, 4156fb12b70Safresh1if the '*' is present then this amount will be multiplied by $cnt. This part 416b39c5158Smillertof $string is removed before output/ 417b39c5158Smillert 418b39c5158Smillert=item B<$cnt> 419b39c5158Smillert 420b39c5158SmillertWill be used to modify the padding applied to string as described above. 421b39c5158Smillert 422b39c5158Smillert=item B<$FH> 423b39c5158Smillert 424b39c5158SmillertAn optional filehandle (or IO::Handle ) that output will be printed to. 425b39c5158Smillert 426b39c5158Smillert=back 427b39c5158Smillert 428b39c5158SmillertThe padded $string is returned. 429b39c5158Smillert 430b39c5158Smillert=cut 431b39c5158Smillert 432b39c5158Smillertsub Tpad 433b39c5158Smillert{ ## public 434b39c5158Smillert my $self = shift; 435b39c5158Smillert my ( $string, $cnt, $FH ) = @_; 436b39c5158Smillert my ( $decr, $ms ); 437b39c5158Smillert 438b39c5158Smillert if ( defined $string && $string =~ /(^[\d.]+)(\*?)(.*)$/ ) 439b39c5158Smillert { 440b39c5158Smillert $ms = $1; 441b39c5158Smillert $ms *= $cnt if $2; 442b39c5158Smillert $string = $3; 443b39c5158Smillert $decr = $self->{PADDING}; 444b39c5158Smillert if ( $decr > .1 ) 445b39c5158Smillert { 446b39c5158Smillert $ms += $decr / 2; 447b39c5158Smillert $string .= $self->{'_pc'} x ( $ms / $decr ); 448b39c5158Smillert } 449b39c5158Smillert } 450b39c5158Smillert print $FH $string if $FH; 451b39c5158Smillert $string; 452b39c5158Smillert} 453b39c5158Smillert 454b39c5158Smillert# $terminal->Tputs($cap, $cnt, $FH); 455b39c5158Smillert 456b39c5158Smillert=item B<Tputs> 457b39c5158Smillert 458b39c5158SmillertOutput the string for the given capability padded as appropriate without 459b39c5158Smillertany parameter substitution. 460b39c5158Smillert 461b39c5158SmillertIt takes three arguments: 462b39c5158Smillert 463b39c5158Smillert=over 2 464b39c5158Smillert 465b39c5158Smillert=item B<$cap> 466b39c5158Smillert 467b39c5158SmillertThe capability whose string is to be output. 468b39c5158Smillert 469b39c5158Smillert=item B<$cnt> 470b39c5158Smillert 471b39c5158SmillertA count passed to Tpad to modify the padding applied to the output string. 472b39c5158SmillertIf $cnt is zero or one then the resulting string will be cached. 473b39c5158Smillert 474b39c5158Smillert=item B<$FH> 475b39c5158Smillert 476b39c5158SmillertAn optional filehandle (or IO::Handle ) that output will be printed to. 477b39c5158Smillert 478b39c5158Smillert=back 479b39c5158Smillert 480b39c5158SmillertThe appropriate string for the capability will be returned. 481b39c5158Smillert 482b39c5158Smillert=cut 483b39c5158Smillert 484b39c5158Smillertsub Tputs 485b39c5158Smillert{ ## public 486b39c5158Smillert my $self = shift; 487b39c5158Smillert my ( $cap, $cnt, $FH ) = @_; 488b39c5158Smillert my $string; 489b39c5158Smillert 490b39c5158Smillert $cnt = 0 unless $cnt; 491b39c5158Smillert 492b39c5158Smillert if ( $cnt > 1 ) 493b39c5158Smillert { 494b39c5158Smillert $string = Tpad( $self, $self->{ '_' . $cap }, $cnt ); 495b39c5158Smillert } 496b39c5158Smillert else 497b39c5158Smillert { 498b39c5158Smillert 499b39c5158Smillert # cache result because Tpad can be slow 500b39c5158Smillert unless ( exists $self->{$cap} ) 501b39c5158Smillert { 502b39c5158Smillert $self->{$cap} = 503b39c5158Smillert exists $self->{"_$cap"} 504b39c5158Smillert ? Tpad( $self, $self->{"_$cap"}, 1 ) 505b39c5158Smillert : undef; 506b39c5158Smillert } 507b39c5158Smillert $string = $self->{$cap}; 508b39c5158Smillert } 509b39c5158Smillert print $FH $string if $FH; 510b39c5158Smillert $string; 511b39c5158Smillert} 512b39c5158Smillert 513b39c5158Smillert# $terminal->Tgoto($cap, $col, $row, $FH); 514b39c5158Smillert 515b39c5158Smillert=item B<Tgoto> 516b39c5158Smillert 517b39c5158SmillertB<Tgoto> decodes a cursor addressing string with the given parameters. 518b39c5158Smillert 519b39c5158SmillertThere are four arguments: 520b39c5158Smillert 521b39c5158Smillert=over 2 522b39c5158Smillert 523b39c5158Smillert=item B<$cap> 524b39c5158Smillert 525b39c5158SmillertThe name of the capability to be output. 526b39c5158Smillert 527b39c5158Smillert=item B<$col> 528b39c5158Smillert 529b39c5158SmillertThe first value to be substituted in the output string ( usually the column 530b39c5158Smillertin a cursor addressing capability ) 531b39c5158Smillert 532b39c5158Smillert=item B<$row> 533b39c5158Smillert 534b39c5158SmillertThe second value to be substituted in the output string (usually the row 535b39c5158Smillertin cursor addressing capabilities) 536b39c5158Smillert 537b39c5158Smillert=item B<$FH> 538b39c5158Smillert 539b39c5158SmillertAn optional filehandle (or IO::Handle ) to which the output string will be 540b39c5158Smillertprinted. 541b39c5158Smillert 542b39c5158Smillert=back 543b39c5158Smillert 544b39c5158SmillertSubstitutions are made with $col and $row in the output string with the 545b39c5158Smillertfollowing sprintf() line formats: 546b39c5158Smillert 547b39c5158Smillert %% output `%' 548b39c5158Smillert %d output value as in printf %d 549b39c5158Smillert %2 output value as in printf %2d 550b39c5158Smillert %3 output value as in printf %3d 551b39c5158Smillert %. output value as in printf %c 552b39c5158Smillert %+x add x to value, then do %. 553b39c5158Smillert 554b39c5158Smillert %>xy if value > x then add y, no output 555b39c5158Smillert %r reverse order of two parameters, no output 556b39c5158Smillert %i increment by one, no output 557b39c5158Smillert %B BCD (16*(value/10)) + (value%10), no output 558b39c5158Smillert 559b39c5158Smillert %n exclusive-or all parameters with 0140 (Datamedia 2500) 560b39c5158Smillert %D Reverse coding (value - 2*(value%16)), no output (Delta Data) 561b39c5158Smillert 562b39c5158SmillertThe output string will be returned. 563b39c5158Smillert 564b39c5158Smillert=cut 565b39c5158Smillert 566b39c5158Smillertsub Tgoto 567b39c5158Smillert{ ## public 568b39c5158Smillert my $self = shift; 569b39c5158Smillert my ( $cap, $code, $tmp, $FH ) = @_; 570b39c5158Smillert my $string = $self->{ '_' . $cap }; 571b39c5158Smillert my $result = ''; 572b39c5158Smillert my $after = ''; 573b39c5158Smillert my $online = 0; 574b39c5158Smillert my @tmp = ( $tmp, $code ); 575b39c5158Smillert my $cnt = $code; 576b39c5158Smillert 577b39c5158Smillert while ( $string =~ /^([^%]*)%(.)(.*)/ ) 578b39c5158Smillert { 579b39c5158Smillert $result .= $1; 580b39c5158Smillert $code = $2; 581b39c5158Smillert $string = $3; 582b39c5158Smillert if ( $code eq 'd' ) 583b39c5158Smillert { 584b39c5158Smillert $result .= sprintf( "%d", shift(@tmp) ); 585b39c5158Smillert } 586b39c5158Smillert elsif ( $code eq '.' ) 587b39c5158Smillert { 588b39c5158Smillert $tmp = shift(@tmp); 589b39c5158Smillert if ( $tmp == 0 || $tmp == 4 || $tmp == 10 ) 590b39c5158Smillert { 591b39c5158Smillert if ($online) 592b39c5158Smillert { 593b39c5158Smillert ++$tmp, $after .= $self->{'_up'} if $self->{'_up'}; 594b39c5158Smillert } 595b39c5158Smillert else 596b39c5158Smillert { 597b39c5158Smillert ++$tmp, $after .= $self->{'_bc'}; 598b39c5158Smillert } 599b39c5158Smillert } 600b39c5158Smillert $result .= sprintf( "%c", $tmp ); 601b39c5158Smillert $online = !$online; 602b39c5158Smillert } 603b39c5158Smillert elsif ( $code eq '+' ) 604b39c5158Smillert { 605b39c5158Smillert $result .= sprintf( "%c", shift(@tmp) + ord($string) ); 606b39c5158Smillert $string = substr( $string, 1, 99 ); 607b39c5158Smillert $online = !$online; 608b39c5158Smillert } 609b39c5158Smillert elsif ( $code eq 'r' ) 610b39c5158Smillert { 611b39c5158Smillert ( $code, $tmp ) = @tmp; 612b39c5158Smillert @tmp = ( $tmp, $code ); 613b39c5158Smillert $online = !$online; 614b39c5158Smillert } 615b39c5158Smillert elsif ( $code eq '>' ) 616b39c5158Smillert { 617b39c5158Smillert ( $code, $tmp, $string ) = unpack( "CCa99", $string ); 6186fb12b70Safresh1 if ( $tmp[0] > $code ) 619b39c5158Smillert { 6206fb12b70Safresh1 $tmp[0] += $tmp; 621b39c5158Smillert } 622b39c5158Smillert } 623b39c5158Smillert elsif ( $code eq '2' ) 624b39c5158Smillert { 625b39c5158Smillert $result .= sprintf( "%02d", shift(@tmp) ); 626b39c5158Smillert $online = !$online; 627b39c5158Smillert } 628b39c5158Smillert elsif ( $code eq '3' ) 629b39c5158Smillert { 630b39c5158Smillert $result .= sprintf( "%03d", shift(@tmp) ); 631b39c5158Smillert $online = !$online; 632b39c5158Smillert } 633b39c5158Smillert elsif ( $code eq 'i' ) 634b39c5158Smillert { 635b39c5158Smillert ( $code, $tmp ) = @tmp; 636b39c5158Smillert @tmp = ( $code + 1, $tmp + 1 ); 637b39c5158Smillert } 638b39c5158Smillert else 639b39c5158Smillert { 640b39c5158Smillert return "OOPS"; 641b39c5158Smillert } 642b39c5158Smillert } 643b39c5158Smillert $string = Tpad( $self, $result . $string . $after, $cnt ); 644b39c5158Smillert print $FH $string if $FH; 645b39c5158Smillert $string; 646b39c5158Smillert} 647b39c5158Smillert 648b39c5158Smillert# $terminal->Trequire(qw/ce ku kd/); 649b39c5158Smillert 650b39c5158Smillert=item B<Trequire> 651b39c5158Smillert 652b39c5158SmillertTakes a list of capabilities as an argument and will croak if one is not 653b39c5158Smillertfound. 654b39c5158Smillert 655b39c5158Smillert=cut 656b39c5158Smillert 657b39c5158Smillertsub Trequire 658b39c5158Smillert{ ## public 659b39c5158Smillert my $self = shift; 660b39c5158Smillert my ( $cap, @undefined ); 661b39c5158Smillert foreach $cap (@_) 662b39c5158Smillert { 663b39c5158Smillert push( @undefined, $cap ) 664b39c5158Smillert unless defined $self->{ '_' . $cap } && $self->{ '_' . $cap }; 665b39c5158Smillert } 666b39c5158Smillert croak "Terminal does not support: (@undefined)" if @undefined; 667b39c5158Smillert} 668b39c5158Smillert 669b39c5158Smillert=back 670b39c5158Smillert 671b39c5158Smillert=head1 EXAMPLES 672b39c5158Smillert 673b39c5158Smillert use Term::Cap; 674b39c5158Smillert 675b39c5158Smillert # Get terminal output speed 676b39c5158Smillert require POSIX; 677ab0ef1e6Sespie my $termios = POSIX::Termios->new; 678b39c5158Smillert $termios->getattr; 679b39c5158Smillert my $ospeed = $termios->getospeed; 680b39c5158Smillert 681b39c5158Smillert # Old-style ioctl code to get ospeed: 682b39c5158Smillert # require 'ioctl.pl'; 683b39c5158Smillert # ioctl(TTY,$TIOCGETP,$sgtty); 684b39c5158Smillert # ($ispeed,$ospeed) = unpack('cc',$sgtty); 685b39c5158Smillert 686b39c5158Smillert # allocate and initialize a terminal structure 687ab0ef1e6Sespie my $terminal = Term::Cap->Tgetent({ TERM => undef, OSPEED => $ospeed }); 688b39c5158Smillert 689b39c5158Smillert # require certain capabilities to be available 690b39c5158Smillert $terminal->Trequire(qw/ce ku kd/); 691b39c5158Smillert 692b39c5158Smillert # Output Routines, if $FH is undefined these just return the string 693b39c5158Smillert 694b39c5158Smillert # Tgoto does the % expansion stuff with the given args 695b39c5158Smillert $terminal->Tgoto('cm', $col, $row, $FH); 696b39c5158Smillert 697b39c5158Smillert # Tputs doesn't do any % expansion. 698b39c5158Smillert $terminal->Tputs('dl', $count = 1, $FH); 699b39c5158Smillert 700b39c5158Smillert=head1 COPYRIGHT AND LICENSE 701b39c5158Smillert 702b8851fccSafresh1Copyright 1995-2015 (c) perl5 porters. 703b8851fccSafresh1 704b8851fccSafresh1This software is free software and can be modified and distributed under 705b8851fccSafresh1the same terms as Perl itself. 706b8851fccSafresh1 707b8851fccSafresh1Please see the file README in the Perl source distribution for details of 708b8851fccSafresh1the Perl license. 709b39c5158Smillert 710b39c5158Smillert=head1 AUTHOR 711b39c5158Smillert 712b39c5158SmillertThis module is part of the core Perl distribution and is also maintained 7136fb12b70Safresh1for CPAN by Jonathan Stowe <jns@gellyfish.co.uk>. 7146fb12b70Safresh1 7156fb12b70Safresh1The code is hosted on Github: https://github.com/jonathanstowe/Term-Cap 7166fb12b70Safresh1please feel free to fork, submit patches etc, etc there. 717b39c5158Smillert 718b39c5158Smillert=head1 SEE ALSO 719b39c5158Smillert 720b39c5158Smillerttermcap(5) 721b39c5158Smillert 722b39c5158Smillert=cut 723b39c5158Smillert 724b39c5158Smillert# Below is a default entry for systems where there are terminals but no 725b39c5158Smillert# termcap 726b39c5158Smillert1; 727b39c5158Smillert__DATA__ 728b39c5158Smillertvt220|vt200|DEC VT220 in vt100 emulation mode: 729b39c5158Smillertam:mi:xn:xo: 730b39c5158Smillertco#80:li#24: 731b39c5158SmillertRA=\E[?7l:SA=\E[?7h: 732b39c5158Smillertac=kkllmmjjnnwwqquuttvvxx:ae=\E(B:al=\E[L:as=\E(0: 733b39c5158Smillertbl=^G:cd=\E[J:ce=\E[K:cl=\E[H\E[2J:cm=\E[%i%d;%dH: 734b39c5158Smillertcr=^M:cs=\E[%i%d;%dr:dc=\E[P:dl=\E[M:do=\E[B: 735b39c5158Smillertei=\E[4l:ho=\E[H:im=\E[4h: 736b39c5158Smillertis=\E[1;24r\E[24;1H: 737b39c5158Smillertnd=\E[C: 738b39c5158Smillertkd=\E[B::kl=\E[D:kr=\E[C:ku=\E[A:le=^H: 739b39c5158Smillertmb=\E[5m:md=\E[1m:me=\E[m:mr=\E[7m: 740b39c5158Smillertkb=\0177: 741b39c5158Smillertr2=\E>\E[24;1H\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E=:rc=\E8: 742b39c5158Smillertsc=\E7:se=\E[27m:sf=\ED:so=\E[7m:sr=\EM:ta=^I: 743b39c5158Smillertue=\E[24m:up=\E[A:us=\E[4m:ve=\E[?25h:vi=\E[?25l: 744b39c5158Smillert 745