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