I guess i will probably leave it alone after this. This does quite a few things compared to my former patches.
- totally get rid of eval, it doen't make sense anymore - declare variables before they get used, which tends to simplify things. - change quaint formatting to something more BSD like - update documentation to new style of doing OO - use defined logic on entry and such - always try to run infocmp as a last resort, even if we have a path. - run infocmp with the best options we have to get a good termcap - use \Q\E, which gets rid of termpat entirely - dedup the path along the way: for us, /etc/termcap and /usr/share/misc/termcap are the same. - redo recursion logic by just recording which term values we already saw, the max=32 value overflow was absurd, proper parsing yields roughly 10 or so tc redirections for xterm, not >32. Index: Cap.pm =================================================================== RCS file: /cvs/src/gnu/usr.bin/perl/cpan/Term-Cap/Cap.pm,v retrieving revision 1.3 diff -u -p -r1.3 Cap.pm --- Cap.pm 18 Oct 2023 01:49:26 -0000 1.3 +++ Cap.pm 20 Oct 2023 09:47:05 -0000 @@ -16,8 +16,8 @@ sub croak use strict; +use v5.16; use vars qw($VERSION $VMS_TERMCAP); -use vars qw($termpat $state $first $entry); $VERSION = '1.17'; @@ -33,7 +33,7 @@ Term::Cap - Perl termcap interface =head1 SYNOPSIS require Term::Cap; - $terminal = Tgetent Term::Cap { TERM => undef, OSPEED => $ospeed }; + $terminal = Term::Cap->Tgetent({ TERM => undef, OSPEED => $ospeed }); $terminal->Trequire(qw/ce ku kd/); $terminal->Tgoto('cm', $col, $row, $FH); $terminal->Tputs('dl', $count, $FH); @@ -75,10 +75,10 @@ if ( $^O eq 'VMS' ) sub termcap_path { ## private - my @termcap_path; + my @l; # $TERMCAP, if it's a filespec - push( @termcap_path, $ENV{TERMCAP} ) + push(@l, $ENV{TERMCAP}) if ( ( exists $ENV{TERMCAP} ) && ( @@ -87,23 +87,27 @@ sub termcap_path : $ENV{TERMCAP} =~ /^\//s ) ); - if ( ( exists $ENV{TERMPATH} ) && ( $ENV{TERMPATH} ) ) - { - + if (exists $ENV{TERMPATH} && $ENV{TERMPATH}) { # Add the users $TERMPATH - push( @termcap_path, split( /(:|\s+)/, $ENV{TERMPATH} ) ); - } - else - { - + push(@l, split( /(:|\s+)/, $ENV{TERMPATH})); + } else { # Defaults - push( @termcap_path, - exists $ENV{'HOME'} ? $ENV{'HOME'} . '/.termcap' : undef, - '/etc/termcap', '/usr/share/misc/termcap', ); + if (exists $ENV{HOME}) { + push(@l, $ENV{HOME}.'/.termcap'); + } + push(@l, '/etc/termcap', '/usr/share/misc/termcap', ); + } + my @termcap_path; + my $seen = {}; + for my $i (@l) { + next unless -f $i; + my $k = join(',', (stat _)[0,1]); + next if $seen->{$k}; + push(@termcap_path, $i); + $seen->{$k} = 1; } - # return the list of those termcaps that exist - return grep { defined $_ && -f $_ } @termcap_path; + return @termcap_path; } =over 4 @@ -164,195 +168,158 @@ It calls C<croak> on failure. sub Tgetent { ## public -- static method - my $class = shift; - my ($self) = @_; + my ($class, $self) = @_; $self = {} unless defined $self; bless $self, $class; - my ( $term, $cap, $search, $field, $max, $tmp_term, $TERMCAP ); - local ( $termpat, $state, $first, $entry ); # used inside eval + my ($cap, $field); + local $_; # Compute PADDING factor from OSPEED (to be used by Tpad) - if ( !$self->{OSPEED} ) - { - if ($^W) - { + if (!$self->{OSPEED}) { + if ($^W) { carp "OSPEED was not set, defaulting to 9600"; } $self->{OSPEED} = 9600; } - if ( $self->{OSPEED} < 16 ) - { - + if ($self->{OSPEED} < 16) { # delays for old style speeds 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 ); $self->{PADDING} = $pad[ $self->{OSPEED} ]; - } - else - { + } else { $self->{PADDING} = 10000 / $self->{OSPEED}; } - unless ( $self->{TERM} ) - { - if ( $ENV{TERM} ) - { - $self->{TERM} = $ENV{TERM} ; - } - else - { - if ( $^O eq 'MSWin32' ) - { + unless ($self->{TERM}) { + if ($ENV{TERM}) { + $self->{TERM} = $ENV{TERM} ; + } else { + if ( $^O eq 'MSWin32' ) { $self->{TERM} = 'dumb'; - } - else - { + } else { croak "TERM not set"; } } } - $term = $self->{TERM}; # $term is the term type we are looking for + my $term = $self->{TERM}; # $term is the term type we are looking for # $tmp_term is always the next term (possibly :tc=...:) we are looking for - $tmp_term = $self->{TERM}; + my $tmp_term = $term; - # protect any pattern metacharacters in $tmp_term - $termpat = $tmp_term; - $termpat =~ s/(\W)/\\$1/g; - - my $foo = ( exists $ENV{TERMCAP} ? $ENV{TERMCAP} : '' ); - - # $entry is the extracted termcap entry - if ( ( $foo !~ m:^/:s ) && ( $foo =~ m/(^|\|)${termpat}[:|]/s ) ) - { - $entry = $foo; + my $foo = (exists $ENV{TERMCAP} ? $ENV{TERMCAP} : '' ); + + my $seen = {}; + my $entry; + if (exists $ENV{TERMCAP}) { + $_ = $ENV{TERMCAP}; + if ( !m:^/:s && m/(^|\|)\Q$tmp_term\E[:|]/s) { + # $entry is the extracted termcap entry + $entry = $_; + $seen->{$tmp_term} = 1; + } } my @termcap_path = termcap_path(); + print "TEMCAP_PATH", join(' ', @termcap_path), "\n"; - if ( !@termcap_path && !$entry ) - { - - # last resort--fake up a termcap from terminfo - local $ENV{TERM} = $term; - - if ( $^O eq 'VMS' ) - { + if (!@termcap_path && !$entry) { + if ( $^O eq 'VMS' ) { $entry = $VMS_TERMCAP; - } - else - { - if ( grep { -x "$_/infocmp" } split /:/, $ENV{PATH} ) - { - eval { - my $tmp = `infocmp -C 2>/dev/null`; - $tmp =~ s/^#.*\n//gm; # remove comments - if ( ( $tmp !~ m%^/%s ) - && ( $tmp =~ /(^|\|)${termpat}[:|]/s ) ) - { - $entry = $tmp; - } - }; - warn "Can't run infocmp to get a termcap entry: $@" if $@; - } - else - { - # this is getting desperate now - if ( $self->{TERM} eq 'dumb' ) - { - $entry = 'dumb|80-column dumb tty::am::co#80::bl=^G:cr=^M:do=^J:sf=^J:'; - } - } - } + } } - croak "Can't find a valid termcap file" unless @termcap_path || $entry; - - $state = 1; # 0 == finished + my $state = 1; # 0 == finished # 1 == next file # 2 == search again + # 3 == infocmp - $first = 0; # first entry (keeps term name) - - $max = 64; # max :tc=...:'s - - if ($entry) - { + my $first = 0; # first entry (keeps term name) + if (defined $entry) { # ok, we're starting with $TERMCAP $first++; # we're the first entry # do we need to continue? - if ( $entry =~ s/:tc=([^:]+):/:/ ) - { + if ($entry =~ s/:tc=([^:]+):/:/ ) { $tmp_term = $1; - - # protect any pattern metacharacters in $tmp_term - $termpat = $tmp_term; - $termpat =~ s/(\W)/\\$1/g; - } - else - { + } else { $state = 0; # we're already finished } } - # This is eval'ed inside the while loop for each file - $search = q{ - while (<TERMCAP>) { - next if /^\\t/ || /^#/; - if ($_ =~ m/(^|\\|)${termpat}[:|]/o) { + my $TERMCAP; + while ($state != 0) { + if ($state == 1) { + # get the next TERMCAP or get ready for infocmp + $TERMCAP = shift @termcap_path or $state = 3; + } elsif ($state == 3) { + croak "failed termcap lookup on $tmp_term"; + } else { + # do the same file again + # prevent endless recursion + $state = 1; # ok, maybe do a new file next time + } + + my ($fh, $child); + if ($state == 3) { + my $child = open($fh, "-|"); + # TODO this breaks on !UNIX + # not do anything, or let it break here + croak "cannot fork: $!" if !defined $child; + if (!$child) { + open(STDERR, ">", "/dev/null"); + system('infocmp', '-CTr', $tmp_term); + exit(1); + } + } else { + open($fh, "<", $TERMCAP ) || croak "open $TERMCAP: $!"; + } + undef $_; + while (<$fh>) { + next if /^\t/ || /^#/; + if (m/(^|\|)\Q$tmp_term\E[:|]/) { chomp; s/^[^:]*:// if $first++; $state = 0; - while ($_ =~ s/\\\\$//) { - defined(my $x = <TERMCAP>) or last; + $seen->{$tmp_term} = 1; + while (s/\\$//) { + defined(my $x = <$fh>) or last; $_ .= $x; chomp; } + if (defined $entry) { + $entry .= $_; + } else { + $entry = $_; + } last; } } - defined $entry or $entry = ''; - $entry .= $_ if $_; - }; - - while ( $state != 0 ) - { - if ( $state == 1 ) - { - - # get the next TERMCAP - $TERMCAP = shift @termcap_path - || croak "failed termcap lookup on $tmp_term"; - } - else - { - - # do the same file again - # prevent endless recursion - $max-- || croak "failed termcap loop at $tmp_term"; - $state = 1; # ok, maybe do a new file next time - } - - open( TERMCAP, "< $TERMCAP\0" ) || croak "open $TERMCAP: $!"; - eval $search; - die $@ if $@; - close TERMCAP; + close($fh); + waitpid($child, 0) if defined $child; + next if !defined $entry; # If :tc=...: found then search this file again - $entry =~ s/:tc=([^:]+):/:/ && ( $tmp_term = $1, $state = 2 ); - - # protect any pattern metacharacters in $tmp_term - $termpat = $tmp_term; - $termpat =~ s/(\W)/\\$1/g; + while ($entry =~ s/:tc=([^:]+):/:/) { + $tmp_term = $1; + if ($seen->{$tmp_term}) { + next; + } + $state = 2; + last; + } } - croak "Can't find $term" if $entry eq ''; + if (!defined $entry) { + if ($self->{TERM} eq 'dumb') { + $entry = 'dumb|80-column dumb tty::am::co#80::bl=^G:cr=^M:do=^J:sf=^J:'; + } + } + croak "Can't find $term" if !defined $entry; $entry =~ s/:+\s*:+/:/g; # cleanup $entry $entry =~ s/:+/:/g; # cleanup $entry $self->{TERMCAP} = $entry; # save it