xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/Term/Cap.pm (revision 0:68f95e015346)
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