7 use vars qw($termpat $state $first $entry);
11 # Version undef: Thu Dec 14 20:02:42 CST 1995 by sanders@bsdi.com
12 # Version 1.00: Thu Nov 30 23:34:29 EST 2000 by schwern@pobox.com
13 # [PATCH] $VERSION crusade, strict, tests, etc... all over lib/
14 # Version 1.01: Wed May 23 00:00:00 CST 2001 by d-lewart@uiuc.edu
15 # Avoid warnings in Tgetent and Tputs
16 # Version 1.02: Sat Nov 17 13:50:39 GMT 2001 by jns@gellyfish.com
17 # Altered layout of the POD
18 # Added Test::More to PREREQ_PM in Makefile.PL
19 # Fixed no argument Tgetent()
20 # Version 1.03: Wed Nov 28 10:09:38 GMT 2001
21 # VMS Support from Charles Lane <lane@DUPHY4.Physics.Drexel.Edu>
22 # Version 1.04: Thu Nov 29 16:22:03 GMT 2001
23 # Fixed warnings in test
24 # Version 1.05: Mon Dec 3 15:33:49 GMT 2001
25 # Don't try to fall back on infocmp if it's not there. From chromatic.
29 # support Berkeley DB termcaps
30 # should probably be a .xs module
31 # force $FH into callers package?
32 # keep $FH in object at Tgetent time?
36 Term::Cap - Perl termcap interface
41 $terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed };
42 $terminal->Trequire(qw/ce ku kd/);
43 $terminal->Tgoto('cm', $col, $row, $FH);
44 $terminal->Tputs('dl', $count, $FH);
45 $terminal->Tpad($string, $count, $FH);
49 These are low-level functions to extract and use capabilities from
50 a terminal capability (termcap) database.
52 More information on the terminal capabilities will be found in the
53 termcap manpage on most Unix-like systems.
59 The output strings for B<Tputs> are cached for counts of 1 for performance.
60 B<Tgoto> and B<Tpad> do not cache. C<$self-E<gt>{_xx}> is the raw termcap
61 data and C<$self-E<gt>{xx}> is the cached version.
63 print $terminal->Tpad($self->{_xx}, 1);
65 B<Tgoto>, B<Tputs>, and B<Tpad> return the string and will also
66 output the string to $FH if specified.
71 # Returns a list of termcap files to check.
72 sub termcap_path { ## private
74 # $TERMCAP, if it's a filespec
75 push(@termcap_path, $ENV{TERMCAP})
76 if ((exists $ENV{TERMCAP}) &&
77 (($^O eq 'os2' || $^O eq 'MSWin32' || $^O eq 'dos')
78 ? $ENV{TERMCAP} =~ /^[a-z]:[\\\/]/is
79 : $ENV{TERMCAP} =~ /^\//s));
80 if ((exists $ENV{TERMPATH}) && ($ENV{TERMPATH})) {
81 # Add the users $TERMPATH
82 push(@termcap_path, split(/(:|\s+)/, $ENV{TERMPATH}))
87 $ENV{'HOME'} . '/.termcap',
89 '/usr/share/misc/termcap',
92 # return the list of those termcaps that exist
93 return grep(-f, @termcap_path);
98 Returns a blessed object reference which the user can
99 then use to send the control strings to the terminal using B<Tputs>
102 The function extracts the entry of the specified terminal
103 type I<TERM> (defaults to the environment variable I<TERM>) from the
106 It will look in the environment for a I<TERMCAP> variable. If
107 found, and the value does not begin with a slash, and the terminal
108 type name is the same as the environment string I<TERM>, the
109 I<TERMCAP> string is used instead of reading a termcap file. If
110 it does begin with a slash, the string is used as a path name of
111 the termcap file to search. If I<TERMCAP> does not begin with a
112 slash and name is different from I<TERM>, B<Tgetent> searches the
113 files F<$HOME/.termcap>, F</etc/termcap>, and F</usr/share/misc/termcap>,
114 in that order, unless the environment variable I<TERMPATH> exists,
115 in which case it specifies a list of file pathnames (separated by
116 spaces or colons) to be searched B<instead>. Whenever multiple
117 files are searched and a tc field occurs in the requested entry,
118 the entry it names must be found in the same file or one of the
119 succeeding files. If there is a C<:tc=...:> in the I<TERMCAP>
120 environment variable string it will continue the search in the
123 The extracted termcap entry is available in the object
124 as C<$self-E<gt>{TERMCAP}>.
126 It takes a hash reference as an argument with two optional keys:
132 The terminal output bit rate (often mistakenly called the baud rate)
133 for this terminal - if not set a warning will be generated
134 and it will be defaulted to 9600. I<OSPEED> can be be specified as
135 either a POSIX termios/SYSV termio speeds (where 9600 equals 9600) or
136 an old DSD-style speed ( where 13 equals 9600).
141 The terminal type whose termcap entry will be used - if not supplied it will
142 default to $ENV{TERM}: if that is not set then B<Tgetent> will croak.
146 It calls C<croak> on failure.
150 sub Tgetent { ## public -- static method
154 $self = {} unless defined $self;
157 my($term,$cap,$search,$field,$max,$tmp_term,$TERMCAP);
158 local($termpat,$state,$first,$entry); # used inside eval
161 # Compute PADDING factor from OSPEED (to be used by Tpad)
162 if (! $self->{OSPEED}) {
163 carp "OSPEED was not set, defaulting to 9600";
164 $self->{OSPEED} = 9600;
166 if ($self->{OSPEED} < 16) {
167 # delays for old style speeds
168 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);
169 $self->{PADDING} = $pad[$self->{OSPEED}];
172 $self->{PADDING} = 10000 / $self->{OSPEED};
175 $self->{TERM} = ($self->{TERM} || $ENV{TERM} || croak "TERM not set");
176 $term = $self->{TERM}; # $term is the term type we are looking for
178 # $tmp_term is always the next term (possibly :tc=...:) we are looking for
179 $tmp_term = $self->{TERM};
180 # protect any pattern metacharacters in $tmp_term
181 $termpat = $tmp_term; $termpat =~ s/(\W)/\\$1/g;
183 my $foo = (exists $ENV{TERMCAP} ? $ENV{TERMCAP} : '');
185 # $entry is the extracted termcap entry
186 if (($foo !~ m:^/:s) && ($foo =~ m/(^|\|)${termpat}[:|]/s)) {
190 my @termcap_path = termcap_path();
192 unless (@termcap_path || $entry)
194 # last resort--fake up a termcap from terminfo
195 local $ENV{TERM} = $term;
197 if ( $^O eq 'VMS' ) {
198 chomp(my @entry = <DATA>);
199 $entry = join '', @entry;
204 $entry = `infocmp -C 2>/dev/null`
205 if grep { -x "$_/infocmp" } split /:/, $ENV{PATH};
210 croak "Can't find a valid termcap file" unless @termcap_path || $entry;
212 $state = 1; # 0 == finished
216 $first = 0; # first entry (keeps term name)
218 $max = 32; # max :tc=...:'s
221 # ok, we're starting with $TERMCAP
222 $first++; # we're the first entry
223 # do we need to continue?
224 if ($entry =~ s/:tc=([^:]+):/:/) {
226 # protect any pattern metacharacters in $tmp_term
227 $termpat = $tmp_term; $termpat =~ s/(\W)/\\$1/g;
230 $state = 0; # we're already finished
234 # This is eval'ed inside the while loop for each file
237 next if /^\\t/ || /^#/;
238 if ($_ =~ m/(^|\\|)${termpat}[:|]/o) {
240 s/^[^:]*:// if $first++;
242 while ($_ =~ s/\\\\$//) {
243 defined(my $x = <TERMCAP>) or last;
249 defined $entry or $entry = '';
253 while ($state != 0) {
255 # get the next TERMCAP
256 $TERMCAP = shift @termcap_path
257 || croak "failed termcap lookup on $tmp_term";
260 # do the same file again
261 # prevent endless recursion
262 $max-- || croak "failed termcap loop at $tmp_term";
263 $state = 1; # ok, maybe do a new file next time
266 open(TERMCAP,"< $TERMCAP\0") || croak "open $TERMCAP: $!";
271 # If :tc=...: found then search this file again
272 $entry =~ s/:tc=([^:]+):/:/ && ($tmp_term = $1, $state = 2);
273 # protect any pattern metacharacters in $tmp_term
274 $termpat = $tmp_term; $termpat =~ s/(\W)/\\$1/g;
277 croak "Can't find $term" if $entry eq '';
278 $entry =~ s/:+\s*:+/:/g; # cleanup $entry
279 $entry =~ s/:+/:/g; # cleanup $entry
280 $self->{TERMCAP} = $entry; # save it
281 # print STDERR "DEBUG: $entry = ", $entry, "\n";
283 # Precompile $entry into the object
284 $entry =~ s/^[^:]*://;
285 foreach $field (split(/:[\s:\\]*/,$entry)) {
286 if (defined $field && $field =~ /^(\w\w)$/) {
287 $self->{'_' . $field} = 1 unless defined $self->{'_' . $1};
288 # print STDERR "DEBUG: flag $1\n";
290 elsif (defined $field && $field =~ /^(\w\w)\@/) {
291 $self->{'_' . $1} = "";
292 # print STDERR "DEBUG: unset $1\n";
294 elsif (defined $field && $field =~ /^(\w\w)#(.*)/) {
295 $self->{'_' . $1} = $2 unless defined $self->{'_' . $1};
296 # print STDERR "DEBUG: numeric $1 = $2\n";
298 elsif (defined $field && $field =~ /^(\w\w)=(.*)/) {
299 # print STDERR "DEBUG: string $1 = $2\n";
300 next if defined $self->{'_' . ($cap = $1)};
303 s/\\(\d\d\d)/pack('c',oct($1) & 0177)/eg;
311 s/\^(.)/pack('c',ord($1) & 31)/eg;
314 $self->{'_' . $cap} = $_;
316 # else { carp "junk in $term ignored: $field"; }
318 $self->{'_pc'} = "\0" unless defined $self->{'_pc'};
319 $self->{'_bc'} = "\b" unless defined $self->{'_bc'};
323 # $terminal->Tpad($string, $cnt, $FH);
327 Outputs a literal string with appropriate padding for the current terminal.
329 It takes three arguments:
335 The literal string to be output. If it starts with a number and an optional
336 '*' then the padding will be increased by an amount relative to this number,
337 if the '*' is present then this amount will me multiplied by $cnt. This part
338 of $string is removed before output/
342 Will be used to modify the padding applied to string as described above.
346 An optional filehandle (or IO::Handle ) that output will be printed to.
350 The padded $string is returned.
356 my($string, $cnt, $FH) = @_;
359 if (defined $string && $string =~ /(^[\d.]+)(\*?)(.*)$/) {
363 $decr = $self->{PADDING};
366 $string .= $self->{'_pc'} x ($ms / $decr);
369 print $FH $string if $FH;
373 # $terminal->Tputs($cap, $cnt, $FH);
377 Output the string for the given capability padded as appropriate without
378 any parameter substitution.
380 It takes three arguments:
386 The capability whose string is to be output.
390 A count passed to Tpad to modify the padding applied to the output string.
391 If $cnt is zero or one then the resulting string will be cached.
395 An optional filehandle (or IO::Handle ) that output will be printed to.
399 The appropriate string for the capability will be returned.
403 sub Tputs { ## public
405 my($cap, $cnt, $FH) = @_;
408 $cnt = 0 unless $cnt;
411 $string = Tpad($self, $self->{'_' . $cap}, $cnt);
413 # cache result because Tpad can be slow
414 unless (exists $self->{$cap}) {
415 $self->{$cap} = exists $self->{"_$cap"} ?
416 Tpad($self, $self->{"_$cap"}, 1) : undef;
418 $string = $self->{$cap};
420 print $FH $string if $FH;
424 # $terminal->Tgoto($cap, $col, $row, $FH);
428 B<Tgoto> decodes a cursor addressing string with the given parameters.
430 There are four arguments:
436 The name of the capability to be output.
440 The first value to be substituted in the output string ( usually the column
441 in a cursor addressing capability )
445 The second value to be substituted in the output string (usually the row
446 in cursor addressing capabilities)
450 An optional filehandle (or IO::Handle ) to which the output string will be
455 Substitutions are made with $col and $row in the output string with the
456 following sprintf() line formats:
459 %d output value as in printf %d
460 %2 output value as in printf %2d
461 %3 output value as in printf %3d
462 %. output value as in printf %c
463 %+x add x to value, then do %.
465 %>xy if value > x then add y, no output
466 %r reverse order of two parameters, no output
467 %i increment by one, no output
468 %B BCD (16*(value/10)) + (value%10), no output
470 %n exclusive-or all parameters with 0140 (Datamedia 2500)
471 %D Reverse coding (value - 2*(value%16)), no output (Delta Data)
473 The output string will be returned.
477 sub Tgoto { ## public
479 my($cap, $code, $tmp, $FH) = @_;
480 my $string = $self->{'_' . $cap};
484 my @tmp = ($tmp,$code);
487 while ($string =~ /^([^%]*)%(.)(.*)/) {
492 $result .= sprintf("%d",shift(@tmp));
494 elsif ($code eq '.') {
496 if ($tmp == 0 || $tmp == 4 || $tmp == 10) {
498 ++$tmp, $after .= $self->{'_up'} if $self->{'_up'};
501 ++$tmp, $after .= $self->{'_bc'};
504 $result .= sprintf("%c",$tmp);
507 elsif ($code eq '+') {
508 $result .= sprintf("%c",shift(@tmp)+ord($string));
509 $string = substr($string,1,99);
512 elsif ($code eq 'r') {
517 elsif ($code eq '>') {
518 ($code,$tmp,$string) = unpack("CCa99",$string);
519 if ($tmp[$[] > $code) {
523 elsif ($code eq '2') {
524 $result .= sprintf("%02d",shift(@tmp));
527 elsif ($code eq '3') {
528 $result .= sprintf("%03d",shift(@tmp));
531 elsif ($code eq 'i') {
533 @tmp = ($code+1,$tmp+1);
539 $string = Tpad($self, $result . $string . $after, $cnt);
540 print $FH $string if $FH;
544 # $terminal->Trequire(qw/ce ku kd/);
548 Takes a list of capabilities as an argument and will croak if one is not
553 sub Trequire { ## public
557 push(@undefined, $cap)
558 unless defined $self->{'_' . $cap} && $self->{'_' . $cap};
560 croak "Terminal does not support: (@undefined)" if @undefined;
569 # Get terminal output speed
571 my $termios = new POSIX::Termios;
573 my $ospeed = $termios->getospeed;
575 # Old-style ioctl code to get ospeed:
576 # require 'ioctl.pl';
577 # ioctl(TTY,$TIOCGETP,$sgtty);
578 # ($ispeed,$ospeed) = unpack('cc',$sgtty);
580 # allocate and initialize a terminal structure
581 $terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed };
583 # require certain capabilities to be available
584 $terminal->Trequire(qw/ce ku kd/);
586 # Output Routines, if $FH is undefined these just return the string
588 # Tgoto does the % expansion stuff with the given args
589 $terminal->Tgoto('cm', $col, $row, $FH);
591 # Tputs doesn't do any % expansion.
592 $terminal->Tputs('dl', $count = 1, $FH);
594 =head1 COPYRIGHT AND LICENSE
596 Please see the README file in distribution.
600 This module is part of the core Perl distribution and is also maintained
601 for CPAN by Jonathan Stowe <jns@gellyfish.com>.
609 # Below is a default entry for systems where there are terminals but no
613 vt220|vt200|DEC VT220 in vt100 emulation mode:
617 ac=kkllmmjjnnwwqquuttvvxx:ae=\E(B:al=\E[L:as=\E(0:
618 bl=^G:cd=\E[J:ce=\E[K:cl=\E[H\E[2J:cm=\E[%i%d;%dH:
619 cr=^M:cs=\E[%i%d;%dr:dc=\E[P:dl=\E[M:do=\E[B:
620 ei=\E[4l:ho=\E[H:im=\E[4h:
623 kd=\E[B::kl=\E[D:kr=\E[C:ku=\E[A:le=^H:
624 mb=\E[5m:md=\E[1m:me=\E[m:mr=\E[7m:
626 r2=\E>\E[24;1H\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E=:rc=\E8:
627 sc=\E7:se=\E[27m:sf=\ED:so=\E[7m:sr=\EM:ta=^I:
628 ue=\E[24m:up=\E[A:us=\E[4m:ve=\E[?25h:vi=\E[?25l: