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

Reply via email to