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