File Coverage

blib/lib/MarpaX/Database/Terminfo/Interface.pm
Criterion Covered Total %
statement 461 636 72.4
branch 187 374 50.0
condition 66 149 44.3
subroutine 43 48 89.5
pod 18 18 100.0
total 775 1225 63.2


line stmt bran cond sub pod time code
1 16     16   44651606 use strict;
  16         51  
  16         794  
2 16     16   98 use warnings FATAL => 'all';
  16         36  
  16         1027  
3              
4             package MarpaX::Database::Terminfo::Interface;
5 16     16   10510 use MarpaX::Database::Terminfo;
  16         60  
  16         532  
6 16     16   13359 use MarpaX::Database::Terminfo::String;
  16         66  
  16         641  
7 16     16   130 use MarpaX::Database::Terminfo::Constants qw/:all/;
  16         33  
  16         4132  
8 16     16   107 use File::ShareDir qw/:ALL/;
  16         34  
  16         2675  
9 16     16   106 use Carp qw/carp croak/;
  16         43  
  16         902  
10 16     16   26532 use Storable qw/fd_retrieve/;
  16         1077995  
  16         1739  
11 16     16   20657 use Time::HiRes qw/usleep/;
  16         39664  
  16         95  
12 16     16   3983 use Log::Any qw/$log/;
  16         44  
  16         233  
13 16     16   1400 use constant BAUDBYTE => 9; # From GNU Ncurses: 9 = 7 bits + 1 parity + 1 stop
  16         40  
  16         51295  
14 16     16   39335 our $HAVE_POSIX = eval "use POSIX; 1;" || 0;
  16         174543  
  16         363  
15              
16             # ABSTRACT: Terminfo interface
17              
18             our $VERSION = '0.010'; # VERSION
19              
20              
21             sub new {
22 16     16 1 331 my ($class, $optp) = @_;
23              
24 16   100     143 $optp //= {};
25              
26 16 50       183 if (ref($optp) ne 'HASH') {
27 0         0 croak 'Options must be a reference to a HASH';
28             }
29              
30 16   33     232 my $file = $optp->{file} // $ENV{MARPAX_DATABASE_TERMINFO_FILE} // '';
      50        
31 16   33     193 my $txt = $optp->{txt} // $ENV{MARPAX_DATABASE_TERMINFO_TXT} // '';
      50        
32 16   33     257 my $bin = $optp->{bin} // $ENV{MARPAX_DATABASE_TERMINFO_BIN} // dist_file('MarpaX-Database-Terminfo', 'share/ncurses-terminfo.storable');
      33        
33 16 50 33     4684 my $caps = $optp->{caps} // $ENV{MARPAX_DATABASE_TERMINFO_CAPS} // (
    50 33        
    50          
34             $^O eq 'aix' ? dist_file('MarpaX-Database-Terminfo', 'share/ncurses-Caps.aix4') :
35             $^O eq 'hpux' ? dist_file('MarpaX-Database-Terminfo', 'share/ncurses-Caps.hpux11') :
36             $^O eq 'dec_osf' ? dist_file('MarpaX-Database-Terminfo', 'share/ncurses-Caps.osf1r5') :
37             dist_file('MarpaX-Database-Terminfo', 'share/ncurses-Caps'));
38              
39 16   33     2814 my $cache_stubs_as_txt = $optp->{cache_stubs_as_txt} // $ENV{MARPAX_DATABASE_TERMINFO_CACHE_STUBS_AS_TXT} // 1;
      50        
40 16   33     181 my $cache_stubs = $optp->{cache_stubs} // $ENV{MARPAX_DATABASE_TERMINFO_CACHE_STUBS} // 1;
      50        
41 16         41 my $stubs_txt;
42             my $stubs_bin;
43 16 50       64 if ($cache_stubs) {
44 16   66     259 $stubs_txt = $optp->{stubs_txt} // $ENV{MARPAX_DATABASE_TERMINFO_STUBS_TXT} // '';
      100        
45 16   33     375 $stubs_bin = $optp->{stubs_bin} // $ENV{MARPAX_DATABASE_TERMINFO_STUBS_BIN} // dist_file('MarpaX-Database-Terminfo', 'share/ncurses-terminfo-stubs.storable');
      33        
46             } else {
47 0         0 $stubs_txt = '';
48 0         0 $stubs_bin = '';
49             }
50 16   33     2963 my $bsd_tputs = $optp->{bsd_tputs} // $ENV{MARPAX_DATABASE_TERMINFO_BSD_TPUTS} // 0;
      50        
51 16   66     169 my $use_env = $optp->{use_env } // $ENV{MARPAX_DATABASE_TERMINFO_USE_ENV} // 1;
      100        
52              
53             # -------------
54             # Load Database
55             # -------------
56 16         47 my $db = undef;
57 16         41 my $db_ok = 0;
58 16 50       77 if ($file) {
59 0         0 my $fh;
60 0 0       0 if ($log->is_debug) {
61 0         0 $log->debugf('Loading %s', $file);
62             }
63 0 0       0 if (! open($fh, '<', $file)) {
64 0         0 carp "Cannot open $file, $!";
65             } else {
66 0         0 my $content = do {local $/; <$fh>;};
  0         0  
  0         0  
67 0 0       0 close($fh) || carp "Cannot close $file, $!";
68 0 0       0 if ($log->is_debug) {
69 0         0 $log->debugf('Parsing %s', $file);
70             }
71 0         0 eval {$db = MarpaX::Database::Terminfo->new()->parse(\$content)->value()};
  0         0  
72 0 0       0 if ($@) {
73 0         0 carp $@;
74             } else {
75 0         0 $db_ok = 1;
76             }
77             }
78             }
79 16 50 33     135 if (! $db_ok && $txt) {
80 0 0       0 if ($log->is_debug) {
81 0         0 $log->debugf('Parsing txt');
82             }
83 0         0 eval {$db = MarpaX::Database::Terminfo->new()->parse(\$txt)->value()};
  0         0  
84 0 0       0 if ($@) {
85 0         0 carp $@;
86             } else {
87 0         0 $db_ok = 1;
88             }
89             }
90 16 50       73 if (! $db_ok) {
91 16         42 my $fh;
92 16 50       403 if ($log->is_debug) {
93 0         0 $log->debugf('Loading %s', $bin);
94             }
95 16 50       1251 if (! open($fh, '<', $bin)) {
96 0         0 carp "Cannot open $bin, $!";
97             } else {
98 16         48 eval {$db = fd_retrieve($fh)};
  16         123  
99 16 50       21731499 if ($@) {
100 0         0 carp "$bin: $@";
101             } else {
102 16         103 $db_ok = 1;
103             }
104 16 50       1044 close($fh) || carp "Cannot close $bin, $!";
105             }
106             }
107 16 50       97 if (! $db_ok) {
108 0         0 croak 'Cannot get a valid terminfo database';
109             }
110             # -----------------------
111             # Load terminfo<->termcap
112             # -----------------------
113 16         80 my %t2other = ();
114 16         47 my %c2other = ();
115 16         46 my %capalias = ();
116 16         45 my %infoalias = ();
117             {
118 16 50       68 if ($log->is_debug) {
  16         203  
119 0         0 $log->debugf('Loading %s', $caps);
120             }
121 16         113 my $fh;
122 16 50       1810 if (! open($fh, '<', $caps)) {
123 0         0 carp "Cannot open $caps, $!";
124             } else {
125             #
126             # Get translations
127             #
128 16         51 my $line = 0;
129 16         758 while (defined($_ = <$fh>)) {
130 20160         25753 ++$line;
131 20160 100       73074 if (/^\s*#/) {
132 11408         44346 next;
133             }
134 8752         109817 s/\s*$//;
135 8752 100       52684 if (/^\s*capalias\b/) {
    100          
136 704         4525 my ($capalias, $alias, $name, $set, $description) = split(/\s+/, $_, 5);
137 704         7872 $capalias{$alias} = {name => $name, set => $set, description => $description};
138             } elsif (/^\s*infoalias\b/) {
139 96         709 my ($infoalias, $alias, $name, $set, $description) = split(/\s+/, $_, 5);
140 96         1113 $infoalias{$alias} = {name => $name, set => $set, description => $description};
141             } else {
142 7952         112745 my ($variable, $feature, $type, $termcap, $keyname, $keyvalue, $translation, $description) = split(/\s+/, $_, 8);
143 7952 100       42894 if ($type eq 'bool') {
    100          
    50          
144 704         1289 $type = TERMINFO_BOOLEAN;
145             } elsif ($type eq 'num') {
146 624         1068 $type = TERMINFO_NUMERIC;
147             } elsif ($type eq 'str') {
148 6624         11989 $type = TERMINFO_STRING;
149             } else {
150 0         0 $log->warnf('%s(%d): wrong type \'%s\'', $caps, $line, $type); exit;
  0         0  
151 0         0 next;
152             }
153 7952         44657 $t2other{$feature} = {type => $type, termcap => $termcap, variable => $variable};
154 7952         574456 $c2other{$termcap} = {type => $type, feature => $feature, variable => $variable};
155             }
156             }
157 16 50       17123 close($fh) || carp "Cannot close $caps, $!";
158             }
159             }
160             # -----------------
161             # Load stubs as txt
162             # -----------------
163 16         54 my $cached_stubs_as_txt = {};
164 16         55 my $cached_stubs_as_txt_ok = 0;
165 16 50       89 if ($cache_stubs) {
166 16 100       78 if ($stubs_txt) {
167 1         3 my $fh;
168 1 50       16 if ($log->is_debug) {
169 0         0 $log->debugf('Loading %s', $stubs_txt);
170             }
171 1 50       105 if (! open($fh, '<', $stubs_txt)) {
172 0         0 carp "Cannot open $stubs_txt, $!";
173             } else {
174 1         3 my $content = do {local $/; <$fh>;};
  1         7  
  1         2866894  
175 1 50       70 close($fh) || carp "Cannot close $stubs_txt, $!";
176 1 50       21 if ($log->is_debug) {
177 0         0 $log->debugf('Evaluating %s', $stubs_txt);
178             }
179             {
180             #
181             # Because Data::Dumper have $VARxxx
182             #
183 16     16   175 no strict 'vars';
  16         40  
  16         147046  
  1         15  
184             #
185             # Untaint data
186             #
187 1         5983 my ($untainted) = $content =~ m/(.*)/s;
188 1         3093320 $cached_stubs_as_txt = eval $untainted; ## no critic
189 1 50       22 if ($@) {
190 0         0 carp "$stubs_txt: $@";
191             } else {
192 1         17 $cached_stubs_as_txt_ok = 1;
193             }
194             }
195             }
196             }
197 16 100 66     210 if (! $cached_stubs_as_txt_ok && $stubs_bin) {
198 15         38 my $fh;
199 15 50       177 if ($log->is_debug) {
200 0         0 $log->debugf('Loading %s', $stubs_bin);
201             }
202 15 50       1600 if (! open($fh, '<', $stubs_bin)) {
203 0         0 carp "Cannot open $stubs_bin, $!";
204             } else {
205 15         53 eval {$cached_stubs_as_txt = fd_retrieve($fh)};
  15         121  
206 15 50       246656 if ($@) {
207 0         0 carp "$stubs_bin: $@";
208             } else {
209 15         60 $cached_stubs_as_txt_ok = 1;
210             }
211 15 50       691 close($fh) || carp "Cannot close $stubs_bin, $!";
212             }
213             }
214             }
215              
216             my $self = {
217             _terminfo_db => $db,
218             _terminfo_current => undef,
219             _t2other => \%t2other,
220             _c2other => \%c2other,
221             _capalias => \%capalias,
222             _infoalias => \%infoalias,
223             _stubs => {},
224             _cache_stubs => $cache_stubs,
225             _cached_stubs => {},
226             _cache_stubs_as_txt => $cache_stubs_as_txt,
227             _cached_stubs_as_txt => $cached_stubs_as_txt,
228 16     1   430 _flush => [ sub {} ],
  1         5  
229             _bsd_tputs => $bsd_tputs,
230             _term => undef, # Current terminal
231             _use_env => $use_env,
232             };
233              
234 16         156 bless($self, $class);
235              
236             #
237             # Initialize
238             #
239 16         125 $self->_terminfo_init();
240              
241 16         208 return $self;
242             }
243              
244              
245             sub _terminfo_db {
246 7817     7817   16481 my ($self) = (@_);
247 7817 50 33     32101 if ($log->is_warn && ! defined($self->{_terminfo_db})) {
248 0         0 $log->warnf('Undefined database');
249             }
250 7817         61410 return $self->{_terminfo_db};
251             }
252              
253              
254             sub _terminfo_current {
255 5549     5549   14159 my $self = shift;
256 5549 100       22581 if (@_) {
257 2675         7167 $self->{_terminfo_current} = shift;
258             }
259 5549 50 33     18631 if ($log->is_warn && ! defined($self->{_terminfo_current})) {
260 0         0 $log->warnf('Undefined current terminfo entry');
261             }
262 5549         62308 return $self->{_terminfo_current};
263             }
264              
265              
266             sub _t2other {
267 819324     819324   1072824 my ($self) = (@_);
268 819324 50 33     2242919 if ($log->is_warn && ! defined($self->{_t2other})) {
269 0         0 $log->warnf('Undefined terminfo->termcap translation hash');
270             }
271 819324         6350646 return $self->{_t2other};
272             }
273              
274              
275             sub _c2other {
276 0     0   0 my ($self) = (@_);
277 0 0 0     0 if ($log->is_warn && ! defined($self->{_c2other})) {
278 0         0 $log->warnf('Undefined terminfo->termcap translation hash');
279             }
280 0         0 return $self->{_c2other};
281             }
282              
283              
284             sub _capalias {
285 0     0   0 my ($self) = (@_);
286 0 0 0     0 if ($log->is_warn && ! defined($self->{_capalias})) {
287 0         0 $log->warnf('Undefined terminfo->termcap translation hash');
288             }
289 0         0 return $self->{_capalias};
290             }
291              
292              
293             sub _infoalias {
294 0     0   0 my ($self) = (@_);
295 0 0 0     0 if ($log->is_warn && ! defined($self->{_infoalias})) {
296 0         0 $log->warnf('Undefined terminfo->termcap translation hash');
297             }
298 0         0 return $self->{_infoalias};
299             }
300              
301              
302             sub _terminfo_init {
303 61     61   133 my ($self) = (@_);
304 61 100       428 if (! defined($self->{_terminfo_current})) {
305 16   50     199 $self->tgetent($ENV{TERM} || 'unknown');
306             }
307 61         276 return defined($self->_terminfo_current);
308             }
309              
310              
311             sub flush {
312 1     1 1 4 my ($self, $cb, @args) = @_;
313 1 50       6 if (defined($cb)) {
314 0         0 $self->{_flush} = [ $cb, @args ];
315             }
316 1         4 return $self->{_flush};
317             }
318              
319              
320             sub _find {
321 5141     5141   25927 my ($self, $name, $from) = @_;
322              
323 5141         12147 my $rc = undef;
324 5141   100     26259 $from //= '';
325              
326 5141 50       24008 if ($log->is_debug) {
327 0 0       0 if ($from) {
328 0         0 $log->debugf('Loading %s -> %s', $from, $name);
329             } else {
330 0         0 $log->debugf('Loading %s', $name);
331             }
332             }
333              
334 5141         33903 my $terminfo_db = $self->_terminfo_db;
335 5141 50       14628 if (defined($terminfo_db)) {
336 5141         10728 foreach (@{$terminfo_db}) {
  5141         13527  
337 3542482         4384563 my $terminfo = $_;
338              
339 3542482 100       3753330 if (grep {$_ eq $name} @{$terminfo->{alias}}) {
  5459044         16628498  
  3542482         8698999  
340 5141 50       46462 if ($log->is_trace) {
341 0         0 $log->tracef('Found alias \'%s\' in terminfo with aliases %s longname \'%s\'', $name, $terminfo->{alias}, $terminfo->{longname});
342             }
343 5141         42307 $rc = $terminfo;
344 5141         17914 last;
345             }
346             }
347             }
348 5141         22852 return $rc;
349             }
350              
351             sub tgetent {
352 2675     2675 1 18742717 my ($self, $name, $fh) = (@_);
353              
354 2675 50       12892 if (! defined($self->_terminfo_db)) {
355 0         0 return -1;
356             }
357 2675         18062 my $found = $self->_find($name);
358 2675 50       10572 if (! defined($found)) {
359 0         0 return 0;
360             }
361             #
362             # Process cancellations and use=
363             #
364 2675         10323 my %cancelled = ();
365             {
366 2675         4733 my %featured = ();
  2675         7731  
367 2675         6487 my $i = 0;
368 2675         5511 while ($i <= $#{$found->{feature}}) {
  214469         1348680  
369 211794         410836 my $feature = $found->{feature}->[$i];
370 211794 100 100     1431719 if ($feature->{type} == TERMINFO_BOOLEAN && substr($feature->{name}, -1, 1) eq '@') {
    100 100        
371 1051         2341 my $cancelled = $feature->{name};
372 1051         2116 substr($cancelled, -1, 1, '');
373 1051         3651 $cancelled{$cancelled} = 1;
374 1051 50       3872 if ($log->is_trace) {
375 0         0 $log->tracef('[Loading %s] New cancellation %s', $name, $cancelled);
376             }
377 1051         6026 ++$i;
378             } elsif ($feature->{type} == TERMINFO_STRING && $feature->{name} eq 'use') {
379 2466 50       14903 if ($log->is_trace) {
380 0         0 $log->tracef('[Loading %s] use=\'%s\' with cancellations %s', $name, $feature->{value}, [ keys %cancelled ]);
381             }
382 2466         22285 my $insert = $self->_find($feature->{value}, $name);
383 2466 50       10519 if (! defined($insert)) {
384 0         0 return 0;
385             }
386 2466         6657 my @keep = ();
387 2466         5525 foreach (@{$insert->{feature}}) {
  2466         9997  
388 107761 100       331707 if (exists($cancelled{$_->{name}})) {
389 910 50       2857 if ($log->is_trace) {
390 0         0 $log->tracef('[Loading %s] Skipping cancelled feature \'%s\' from terminfo with aliases %s longname \'%s\'', $name, $_->{name}, $insert->{alias}, $insert->{longname});
391             }
392 910         4745 next;
393             }
394 106851 100       252433 if (exists($featured{$_->{name}})) {
395 6713 50       19476 if ($log->is_trace) {
396 0         0 $log->tracef('[Loading %s] Skipping overwriting feature \'%s\' from terminfo with aliases %s longname \'%s\'', $name, $_->{name}, $insert->{alias}, $insert->{longname});
397             }
398 6713         28943 next;
399             }
400 100138 50       246936 if ($log->is_trace) {
401 0         0 $log->tracef('[Loading %s] Pushing feature %s from terminfo with aliases %s longname \'%s\'', $name, $_, $insert->{alias}, $insert->{longname});
402             }
403 100138         8346767 push(@keep, $_);
404             }
405 2466         7455 splice(@{$found->{feature}}, $i, 1, @keep);
  2466         223630  
406             } else {
407 208277 50       677076 if ($log->is_trace) {
408 0         0 $log->tracef('[Loading %s] New feature %s', $name, $feature);
409             }
410 208277         8024316 $featured{$feature->{name}} = 1;
411 208277         364119 ++$i;
412             }
413             }
414             }
415             #
416             # Remember cancelled things
417             #
418 2675         13372 $found->{cancelled} = \%cancelled;
419             #
420             # Drop needless cancellations
421             #
422             {
423 2675         7306 my $i = $#{$found->{feature}};
  2675         5499  
  2675         9105  
424 2675         10245 foreach (reverse @{$found->{feature}}) {
  2675         10234  
425 209328 100 100     598331 if ($_->{type} == TERMINFO_BOOLEAN && substr($_->{name}, -1, 1) eq '@') {
426 1051 50       3783 if ($log->is_trace) {
427 0         0 $log->tracef('[Loading %s] Dropping cancellation \'%s\' from terminfo', $name, $found->{feature}->[$i]->{name});
428             }
429 1051         5288 splice(@{$found->{feature}}, $i, 1);
  1051         2939  
430             }
431 209328         311794 --$i;
432             }
433             }
434             #
435             # Drop commented features
436             #
437             {
438 2675         6226 my $i = $#{$found->{feature}};
  2675         7776  
  2675         8788  
439 2675         9013 foreach (reverse @{$found->{feature}}) {
  2675         7838  
440 208277 100       496691 if (substr($_->{name}, 0, 1) eq '.') {
441 2 50       23 if ($log->is_trace) {
442 0         0 $log->tracef('[Loading %s] Dropping commented \'%s\' from terminfo', $name, $found->{feature}->[$i]->{name});
443             }
444 2         11 splice(@{$found->{feature}}, $i, 1);
  2         11  
445             }
446 208277         293528 --$i;
447             }
448             }
449             #
450             # The raw terminfo is is the features referenced array.
451             # For faster lookup we fill the terminfo, termcap and variable hashes.
452             # These are used in the subroutine _tget().
453             #
454 2675         19314 $found->{terminfo} = {};
455 2675         29300 $found->{termcap} = {};
456 2675         25377 $found->{variable} = {};
457 2675         28967 my $pad_char = undef;
458 2675         5320 my $cursor_up = undef;
459 2675         7432 my $backspace_if_not_bs = undef;
460             {
461 2675         4645 foreach (@{$found->{feature}}) {
  2675         7298  
  2675         9789  
462 208275         364620 my $feature = $_;
463 208275         406429 my $key = $feature->{name};
464             #
465             # For terminfo lookup
466             #
467 208275 50       563080 if (! exists($found->{terminfo}->{$key})) {
468 208275         7945618 $found->{terminfo}->{$key} = $feature;
469             } else {
470 0 0       0 if ($log->is_warn) {
471 0         0 $log->warnf('[Loading %s] Multiple occurence of feature \'%s\'', $name, $key);
472             }
473             }
474             #
475             # Translation exist ?
476             #
477 208275 100       502587 if (! exists($self->_t2other->{$key})) {
478 4592 50       13232 if ($log->is_trace) {
479 0         0 $log->tracef('[Loading %s] Untranslated feature \'%s\'', $name, $key);
480             }
481 4592         23336 next;
482             }
483             #
484             # Yes, check consistency
485             #
486 203683         508607 my $type = $self->_t2other->{$key}->{type};
487 203683 50       603268 if ($feature->{type} != $type) {
488 0 0       0 if ($log->is_warn) {
489 0         0 $log->warnf('[Loading %s] Wrong type when translating feature \'%s\': %d instead of %d', $name, $key, $type, $feature->{type});
490             }
491 0         0 next;
492             }
493             #
494             # Convert to termcap
495             #
496 203683         438814 my $termcap = $self->_t2other->{$key}->{termcap};
497 203683 50       574530 if (! defined($termcap)) {
498 0 0       0 if ($log->is_trace) {
499 0         0 $log->tracef('[Loading %s] Feature \'%s\' has no termcap equivalent', $name, $key);
500             }
501             } else {
502 203683 50       627271 if ($log->is_trace) {
503 0         0 $log->tracef('[Loading %s] Pushing termcap feature \'%s\'', $name, $termcap);
504             }
505 203683 50       1184230 if (! exists($found->{termcap}->{$termcap})) {
506 203683         1086973 $found->{termcap}->{$termcap} = $feature;
507             } else {
508 0 0       0 if ($log->is_warn) {
509 0         0 $log->warnf('[Loading %s] Multiple occurence of termcap \'%s\'', $name, $termcap);
510             }
511             }
512             }
513             #
514             # Convert to variable
515             #
516 203683         491218 my $variable = $self->_t2other->{$key}->{variable};
517 203683 50       548240 if (! defined($variable)) {
518 0 0       0 if ($log->is_trace) {
519 0         0 $log->tracef('[Loading %s] Feature \'%s\' has no variable equivalent', $name, $key);
520             }
521             } else {
522 203683 50       594888 if ($log->is_trace) {
523 0         0 $log->tracef('[Loading %s] Pushing variable feature \'%s\'', $name, $variable);
524             }
525 203683 50       1254661 if (! exists($found->{variable}->{$variable})) {
526 203683         599004 $found->{variable}->{$variable} = $feature;
527             #
528             # Keep track of pad_char, cursor_up and backspace_if_not_bs
529 203683 100       582577 if ($type == TERMINFO_STRING) {
530 180968 100       1279409 if ($variable eq 'pad_char') {
    100          
    100          
531 15         47 $pad_char = $feature;
532 15 50       66 if ($log->is_trace) {
533 0         0 $log->tracef('[Loading %s] pad_char is \'%s\'', $name, $pad_char->{value});
534             }
535             } elsif ($variable eq 'cursor_up') {
536 2452         5242 $cursor_up = $feature;
537 2452 50       10260 if ($log->is_trace) {
538 0         0 $log->tracef('[Loading %s] cursor_up is \'%s\'', $name, $cursor_up->{value});
539             }
540             } elsif ($variable eq 'backspace_if_not_bs') {
541 20         76 $backspace_if_not_bs = $feature;
542 20 50       83 if ($log->is_trace) {
543 0         0 $log->tracef('[Loading %s] backspace_if_not_bs is \'%s\'', $name, $backspace_if_not_bs->{value});
544             }
545             }
546             }
547             } else {
548 0 0       0 if ($log->is_warn) {
549 0         0 $log->warnf('[Loading %s] Multiple occurence of variable \'%s\'', $name, $key);
550             }
551             }
552             }
553             }
554              
555             # The variables PC, UP and BC are set by tgetent to the terminfo entry's data for pad_char, cursor_up and backspace_if_not_bs, respectively.
556             #
557             # PC is used in the delay function.
558             #
559 2675 100       12910 if (defined($pad_char)) {
560 15 50       73 if ($log->is_trace) {
561 0         0 $log->tracef('[Loading %s] Initialized PC to \'%s\'', $name, $pad_char->{value});
562             }
563 15         115 $found->{variable}->{PC} = $pad_char;
564             }
565             #
566             # UP is not used by ncurses.
567             #
568 2675 100       10897 if (defined($cursor_up)) {
569 2452 50       12919 if ($log->is_trace) {
570 0         0 $log->tracef('[Loading %s] Initialized UP to \'%s\'', $name, $cursor_up->{value});
571             }
572 2452         19069 $found->{variable}->{UP} = $cursor_up;
573             }
574             #
575             # BC is used in the tgoto emulation.
576             #
577 2675 100       10102 if (defined($backspace_if_not_bs)) {
578 20 50       96 if ($log->is_trace) {
579 0         0 $log->tracef('[Loading %s] Initialized BC to \'%s\'', $name, $backspace_if_not_bs->{value});
580             }
581 20         178 $found->{variable}->{BC} = $backspace_if_not_bs;
582             }
583             #
584             # The variable ospeed is set in a system-specific coding to reflect the terminal speed.
585             #
586 2675         13533 my ($baudrate, $ospeed) = $self->_get_ospeed_and_baudrate($fh);
587 2675         19049 my $OSPEED = {name => 'ospeed', type => TERMINFO_NUMERIC, value => $ospeed};
588 2675 50       10210 if ($log->is_trace) {
589 0         0 $log->tracef('[Loading %s] Initialized ospeed to %d', $name, $OSPEED->{value});
590             }
591 2675         22415 $found->{variable}->{ospeed} = $OSPEED;
592             #
593             # The variable baudrate is used eventually in delay
594             #
595 2675         14566 my $BAUDRATE = {name => 'baudrate', type => TERMINFO_NUMERIC, value => $baudrate};
596 2675 50       10613 if ($log->is_trace) {
597 0         0 $log->tracef('[Loading %s] Initialized baudrate to %d', $name, $BAUDRATE->{value});
598             }
599 2675         19217 $found->{variable}->{baudrate} = $BAUDRATE;
600             #
601             # ospeed and baudrate are add-ons, not in the terminfo database.
602             # If you look to the terminfo<->Caps translation files, you will see that none of ospeed
603             # nor baudrate variables exist. Nevertheless, we check if they these entries WOULD exist
604             # and warn about it, because we would overwrite them.
605             #
606 2675 50       11636 if (exists($found->{terminfo}->{ospeed})) {
607 0 0       0 if ($log->is_warn) {
608 0         0 $log->tracef('[Loading %s] Overwriting ospeed to \'%s\'', $name, $OSPEED->{value});
609             }
610             }
611 2675         8849 $found->{terminfo}->{ospeed} = $found->{variable}->{ospeed};
612 2675 50       13642 if (exists($found->{terminfo}->{baudrate})) {
613 0 0       0 if ($log->is_warn) {
614 0         0 $log->tracef('[Loading %s] Overwriting baudrate to \'%s\'', $name, $BAUDRATE->{value});
615             }
616             }
617 2675         13128 $found->{terminfo}->{baudrate} = $found->{variable}->{baudrate};
618             }
619              
620             #
621             # Remove any static/dynamic var
622             #
623 2675         14596 $found->{_static_vars} = [];
624 2675         9239 $found->{_dynamic_vars} = [];
625              
626 2675         15102 $self->_terminfo_current($found);
627              
628             #
629             # Create stubs for every string
630             #
631 2675         32875 $self->_stubs($name);
632              
633 2675         51367 return 1;
634             }
635              
636             sub _stub {
637 185265     185265   292136 my ($self, $featurevalue) = @_;
638              
639 185265 50       425605 if ($self->{_cache_stubs}) {
640 185265 100       600997 if (exists($self->{_cached_stubs}->{$featurevalue})) {
641 176711 50       523514 if ($log->is_trace) {
642 0         0 $log->tracef('Getting \'%s\' compiled stub from cache', $featurevalue);
643             }
644 176711         1203729 $self->{_stubs}->{$featurevalue} = $self->{_cached_stubs}->{$featurevalue};
645             }
646             }
647 185265 100       548625 if (! exists($self->{_stubs}->{$featurevalue})) {
648 8554         12723 my $stub_as_txt = undef;
649 8554 50       21527 if ($self->{_cache_stubs_as_txt}) {
650 8554 100       28690 if (exists($self->{_cached_stubs_as_txt}->{$featurevalue})) {
651 8552 50       27142 if ($log->is_trace) {
652 0         0 $log->tracef('Getting \'%s\' stub as txt from cache', $featurevalue);
653             }
654 8552         66778 $stub_as_txt = $self->{_cached_stubs_as_txt}->{$featurevalue};
655             }
656             }
657 8554 100       20116 if (! defined($stub_as_txt)) {
658             #
659             # Very important: we restore the ',': it is parsed as either
660             # and EOF (normal case) or an ENDIF (some entries are MISSING
661             # the '%;' ENDIF tag at the very end). I am not going to change
662             # the grammar when documentation says that a string follows
663             # the ALGOL68, which has introduced the ENDIF tag to solve the
664             # IF-THEN-ELSE-THEN ambiguity.
665             # There is no side-effect doing so, but keeping the grammar clean.
666 2         6 my $string = "$featurevalue,";
667 2 50       8 if ($log->is_trace) {
668 0         0 $log->tracef('Parsing \'%s\'', $string);
669             }
670 2         33 my $parseTreeValue = MarpaX::Database::Terminfo::String->new()->parse(\$string)->value();
671             #
672             # Enclose the result for anonymous subroutine evaluation
673             # We reindent everything by two spaces
674             #
675 2         810 my $indent = join("\n", @{${$parseTreeValue}});
  2         7  
  2         19  
676 2         41 $indent =~ s/^/ /smg;
677 2         14 $stub_as_txt = "
678             #
679             # Stub version of: $featurevalue
680             #
681             sub {
682             my (\$self, \$dynamicp, \$staticp, \@param) = \@_;
683             # Initialized with \@param to be termcap compatible
684             my \@iparam = \@param;
685             my \$rc = '';
686              
687             $indent
688             return \$rc;
689             }
690             ";
691 2 50       22 if ($log->is_trace) {
692 0         0 $log->tracef('Parsing \'%s\' gives stub: %s', $string, $stub_as_txt);
693             }
694 2 50       20 if ($self->{_cache_stubs_as_txt}) {
695 2         15 $self->{_cached_stubs_as_txt}->{$featurevalue} = $stub_as_txt;
696             }
697             }
698 8554 50       28608 if ($log->is_trace) {
699 0         0 $log->tracef('Compiling \'%s\' stub', $featurevalue);
700             }
701 8554         12624201 $self->{_stubs}->{$featurevalue} = eval $stub_as_txt; ## no critic
702 8554 50       42095 if ($@) {
703 0         0 carp "Problem with $featurevalue\n$stub_as_txt\n$@\nReplaced by a stub returning empty string...";
704 0     0   0 $self->{_stubs}->{$featurevalue} = sub {return '';};
  0         0  
705             }
706 8554 50       27009 if ($self->{_cache_stubs}) {
707 8554         34147 $self->{_cached_stubs}->{$featurevalue} = $self->{_stubs}->{$featurevalue};
708             }
709             }
710              
711 185265         508455 return $self->{_stubs}->{$featurevalue};
712             }
713              
714             sub _stubs {
715 2675     2675   9825 my ($self, $name) = @_;
716              
717 2675         9747 $self->{_stubs} = {};
718              
719 2675         91206 foreach (values %{$self->_terminfo_current->{terminfo}}) {
  2675         8152  
720 213625         304309 my $feature = $_;
721 213625 100       571531 if ($feature->{type} == TERMINFO_STRING) {
722 185259         421023 $self->_stub($feature->{value});
723             }
724             }
725             }
726              
727             #
728             # _get_ospeed_and_baudrate calculates baudrate and ospeed
729             #
730             # POSIX module does not contain all the constants. Here they are.
731             #
732             our %OSPEED_TO_BAUDRATE = (
733             0 => 0,
734             1 => 50,
735             2 => 75,
736             3 => 110,
737             4 => 134,
738             5 => 150,
739             6 => 200,
740             7 => 300,
741             8 => 600,
742             9 => 1200,
743             10 => 1800,
744             11 => 2400,
745             12 => 4800,
746             13 => 9600,
747             14 => 19200,
748             15 => 38400,
749             4097 => 57600,
750             4098 => 115200,
751             4099 => 230400,
752             4100 => 460800,
753             4101 => 500000,
754             4102 => 576000,
755             4103 => 921600,
756             4104 => 1000000,
757             4105 => 1152000,
758             4107 => 2000000,
759             4108 => 2500000,
760             4109 => 3000000,
761             4110 => 3500000,
762             4111 => 4000000,
763             );
764              
765             sub _get_ospeed_and_baudrate {
766 2675     2675   7807 my ($self, $fh) = (@_);
767              
768 2675         5241 my $baudrate = 0;
769 2675         5346 my $ospeed = 0;
770              
771 2675 50       7807 if (defined($fh)) {
772 0         0 my $reffh = ref($fh);
773 0 0       0 if ($reffh ne 'GLOB') {
774 0 0       0 if ($log->is_warn) {
775 0   0     0 $log->warnf('filehandle should be a reference to GLOB instead of %s', $reffh || '');
776             }
777             }
778 0         0 $fh = undef;
779             }
780              
781 2675 100       10529 if (defined($ENV{MARPAX_DATABASE_TERMINFO_OSPEED})) {
782 2         7 $ospeed = $ENV{MARPAX_DATABASE_TERMINFO_OSPEED};
783             } else {
784 2673 50       9410 if ($HAVE_POSIX) {
785 2673         7654 my $termios = eval { POSIX::Termios->new() };
  2673         40054  
786 2673 50       8282 if (! defined($termios)) {
787 0 0       0 if ($log->is_trace) {
788 0         0 $log->tracef('POSIX::Termios->new() failure, %s', $@);
789             }
790             } else {
791 2673 50 50     28342 my $fileno = defined($fh) ? fileno($fh) : (fileno(\*STDIN) || 0);
792 2673 50       19997 if ($log->is_trace) {
793 0         0 $log->tracef('Trying to get attributes on fileno %d', $fileno);
794             }
795 2673         14618 eval {$termios->getattr($fileno)};
  2673         34444  
796 2673 50       41641 if ($@) {
797 0 0       0 if ($log->is_trace) {
798 0         0 $log->tracef('POSIX::Termios::getattr(%d) failure, %s', $fileno, $@);
799             }
800 0         0 $termios = undef;
801             }
802             }
803 2673 50       10781 if (defined($termios)) {
804 2673         6426 my $this = eval { $termios->getospeed() };
  2673         14206  
805 2673 50       10154 if (! defined($this)) {
806 0 0       0 if ($log->is_trace) {
807 0         0 $log->tracef('getospeed() failure, %s', $@);
808             }
809             } else {
810 2673         7409 $ospeed = $this;
811 2673 50       12695 if ($log->is_trace) {
812 0         0 $log->tracef('getospeed() returned %d', $ospeed);
813             }
814             }
815             }
816             }
817             }
818              
819              
820              
821 2675 50       28904 if (! exists($OSPEED_TO_BAUDRATE{$ospeed})) {
822 0 0       0 if ($log->is_warn) {
823 0         0 $log->warnf('ospeed %d is an unknown value', $ospeed);
824             }
825 0         0 $ospeed = 0;
826             }
827              
828 2675 100       8548 if (! $ospeed) {
829 2673         4506 $ospeed = 13;
830 2673 50       10878 if ($log->is_warn) {
831 0         0 $log->warnf('ospeed defaulting to %d', $ospeed);
832             }
833             }
834              
835 2675   50     35788 $baudrate = $ENV{MARPAX_DATABASE_TERMINFO_BAUDRATE} || $OSPEED_TO_BAUDRATE{$ospeed} || 0;
836              
837 2675 50       10189 if ($log->is_trace) {
838 0         0 $log->tracef('ospeed/baudrate: %d/%d', $ospeed, $baudrate);
839             }
840              
841 2675         24693 return ($baudrate, $ospeed);
842             }
843              
844             #
845             # space refers to termcap, feature (i.e. terminfo) or variable
846             #
847             sub _tget {
848 45     45   117 my ($self, $space, $default, $default_if_cancelled, $default_if_wrong_type, $default_if_found, $type, $id, $areap) = (@_);
849              
850 45         75 my $rc = $default;
851 45         79 my $found = undef;
852              
853 45 50       167 if ($self->_terminfo_init()) {
854             #
855             # First lookup in the hashes. If found, we will get the raw terminfo feature entry.
856             #
857 45 100       124 if (! exists($self->_terminfo_current->{$space}->{$id})) {
858             #
859             # No such entry
860             #
861 15 50       55 if ($log->is_trace) {
862 0         0 $log->tracef('No %s entry with id \'%s\'', $space, $id);
863             }
864             } else {
865             #
866             # Get the raw terminfo entry. The only entries for which it may not There is no check, it must exist by construction, c.f.
867             # routine tgetent(), even for variables ospeed and baudrate that are add-ons.
868             #
869 30         94 my $t = $self->_terminfo_current->{$space}->{$id};
870 30         109 my $feature = $self->_terminfo_current->{terminfo}->{$t->{name}};
871 30 50       219 if ($log->is_trace) {
872 0         0 $log->tracef('%s entry with id \'%s\' maps to terminfo feature %s', $space, $id, $feature);
873             }
874 30 50 66     350 if (defined($default_if_cancelled) && exists($self->_terminfo_current->{cancelled}->{$feature->{name}})) {
875 0 0       0 if ($log->is_trace) {
876 0         0 $log->tracef('Cancelled %s feature %s', $space, $feature->{name});
877             }
878 0         0 $rc = $default_if_cancelled;
879             } else {
880             #
881             # Check if this is the correct type
882             #
883 30 100       132 if ($feature->{type} == $type) {
    50          
884 27         54 $found = $feature;
885 27 100       179 if ($type == TERMINFO_STRING) {
886 20 100       90 $rc = defined($default_if_found) ? $default_if_found : \$feature->{value};
887             } else {
888 7 100       43 $rc = defined($default_if_found) ? $default_if_found : $feature->{value};
889             }
890             } elsif (defined($default_if_wrong_type)) {
891 3 50       17 if ($log->is_trace) {
892 0         0 $log->tracef('Found %s feature %s with type %d != %d', $space, $id, $feature->{type}, $type);
893             }
894 3         25 $rc = $default_if_wrong_type;
895             }
896             }
897             }
898             }
899              
900 45 100 100     511 if (defined($found) && defined($areap) && ref($areap)) {
      66        
901 12 100       34 if ($type == TERMINFO_STRING) {
902 10 100       14 if (! defined(${$areap})) {
  10         37  
903 3         7 ${$areap} = '';
  3         8  
904             }
905 10   100     17 my $pos = pos(${$areap}) || 0;
906 10         18 substr(${$areap}, $pos, 0, $found->{value});
  10         33  
907 10         27 pos(${$areap}) = $pos + length($found->{value});
  10         33  
908             } else {
909 2         7 ${$areap} = $found->{value};
  2         6  
910             }
911             }
912              
913 45         300 return $rc;
914             }
915              
916              
917             sub delay {
918 2     2 1 4 my ($self, $ms) = @_;
919              
920             #
921             # $self->{_outc} and $self->{_outcArgs} are created/destroyed by tputs() and al.
922             #
923 2         6 my $outc = $self->{_outc};
924 2 50       9 if (defined($outc)) {
925 2         3 my $PC;
926 2 100 66     13 if ($self->tvgetflag('no_pad_char') || ! $self->tvgetstr('PC', \$PC)) {
927             #
928             # usleep() unit is micro-second
929             #
930 1         1000249 usleep($ms * 1000);
931             } else {
932             #
933             # baudrate is always defined.
934             #
935 1         3 my $baudrate;
936 1         6 $self->tvgetnum('baudrate', \$baudrate);
937 1         5 my $nullcount = int(($ms * $baudrate) / (BAUDBYTE * 1000));
938             #
939             # We have no interface to 'tack' program, so no need to have a global for _nulls_sent
940             #
941 1         5 while ($nullcount-- > 0) {
942 1         6 &$outc($self->tparm($PC), @{$self->{_outcArgs}});
  1         6  
943             }
944             #
945             # Call for a flush
946             #
947 1         10 my ($flushcb, @flushargs) = @{$self->flush};
  1         6  
948 1         5 &$flushcb(@flushargs);
949             }
950             }
951             }
952              
953              
954             sub tgetflag {
955 1     1 1 7 my ($self, $id) = @_;
956 1         6 return $self->_tget('termcap', 0, undef, undef, undef, TERMINFO_BOOLEAN, $id, undef);
957             }
958              
959              
960             sub tigetflag {
961 4     4 1 17 my ($self, $id) = @_;
962 4         17 return $self->_tget('terminfo', 0, 0, -1, undef, TERMINFO_BOOLEAN, $id, undef);
963             }
964              
965              
966             sub tvgetflag {
967 4     4 1 15 my ($self, $id) = @_;
968 4         18 return $self->_tget('variable', 0, 0, 0, 1, TERMINFO_BOOLEAN, $id);
969             }
970              
971              
972             sub tgetnum {
973 1     1 1 7 my ($self, $id) = @_;
974 1         7 return $self->_tget('termcap', -1, undef, undef, undef, TERMINFO_NUMERIC, $id, undef);
975             }
976              
977              
978             sub tigetnum {
979 4     4 1 16 my ($self, $id) = @_;
980 4         19 return $self->_tget('terminfo', -1, -1, -2, undef, TERMINFO_NUMERIC, $id, undef);
981             }
982              
983              
984             sub tvgetnum {
985 3     3 1 18 my ($self, $id, $areap) = @_;
986 3         19 return $self->_tget('variable', 0, 0, 0, 1, TERMINFO_NUMERIC, $id, $areap);
987             }
988              
989              
990             sub tgetstr {
991 7     7 1 27 my ($self, $id, $areap) = @_;
992 7         27 return $self->_tget('termcap', 0, undef, undef, undef, TERMINFO_STRING, $id, $areap);
993             }
994              
995              
996             sub tigetstr {
997 9     9 1 49 my ($self, $id) = @_;
998 9         48 return $self->_tget('terminfo', 0, 0, -1, undef, TERMINFO_STRING, $id, undef);
999             }
1000              
1001              
1002             sub tvgetstr {
1003 12     12 1 40 my ($self, $id, $areap
1004             ) = @_;
1005 12         48 return $self->_tget('variable', 0, 0, 0, 1, TERMINFO_STRING, $id, $areap);
1006             }
1007              
1008              
1009             sub tputs {
1010 3     3 1 11 my ($self, $str, $affcnt, $outc, @outcArgs) = @_;
1011              
1012 3         20 $self->{_outc} = $outc;
1013 3         10 $self->{_outcArgs} = \@outcArgs;
1014              
1015 3         19 $self->_tputs($str, $affcnt, $outc, @outcArgs);
1016              
1017 3         13 $self->{_outc} = undef;
1018 3         19049 $self->{_outcArgs} = undef;
1019             }
1020              
1021             sub _tputs {
1022 3     3   11 my ($self, $str, $affcnt, $outc, @outcArgs) = @_;
1023              
1024 3   50     11 $affcnt //= 1;
1025              
1026 3         10 my $bell = '';
1027 3         17 $self->tvgetstr('bell', \$bell);
1028 3         7 my $flash_screen = '';
1029 3         11 $self->tvgetstr('flash_screen', \$flash_screen);
1030              
1031 3         6 my $always_delay;
1032             my $normal_delay;
1033              
1034 3 50       14 if (! defined($self->{_term})) {
1035             #
1036             # No current terminal: setuppterm() has not been called
1037             #
1038 3         7 $always_delay = 0;
1039 3         5 $normal_delay = 1;
1040             } else {
1041 0         0 my $xon_xoff = $self->tvgetflag('xon_xoff');
1042 0         0 my $padding_baud_rate = 0;
1043 0         0 $self->tvgetnum('padding_baud_rate', \$padding_baud_rate);
1044 0         0 my $baudrate = 0;
1045 0         0 $self->tvgetnum('baudrate', \$baudrate);
1046              
1047 0 0 0     0 $always_delay = ($str eq $bell || $str eq $flash_screen) ? 1 : 0;
1048 0 0 0     0 $normal_delay = (! $xon_xoff && $padding_baud_rate && $baudrate >= $padding_baud_rate) ? 1 : 0;
1049             }
1050              
1051 3         7 my $trailpad = 0;
1052 3         10 pos($str) = undef;
1053 3 50 33     18 if ($self->{_bsd_tputs} && length($str) > 0) {
1054 0 0       0 if ($str =~ /^([[:digit:]]+)(?:\.([[:digit:]])?[[:digit:]]*)?(\*)?/) {
1055 0 0       0 my ($one, $two, $three) = (
    0          
1056             substr($str, $-[1], $+[1] - $-[1]),
1057             defined($-[2]) ? substr($str, $-[2], $+[2] - $-[2]) : 0,
1058             defined($-[3]) ? 1 : 0);
1059 0         0 $trailpad = $one * 10;
1060 0         0 $trailpad += $two;
1061 0 0       0 if ($three) {
1062 0         0 $trailpad *= $affcnt;
1063             }
1064 0         0 pos($str) = $+[0];
1065             }
1066             }
1067 3         11 my $indexmax = length($str);
1068 3   50     20 my $index = pos($str) || 0;
1069 3         14 while ($index <= $indexmax) {
1070 53         97 my $c = substr($str, $index, 1);
1071 53 100       105 if ($c ne '$') {
1072 51         118 &$outc($c, @outcArgs);
1073             } else {
1074 2         5 $index++;
1075 2 50       11 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1076 2 50       10 if ($c ne '<') {
1077 0         0 &$outc('$', @outcArgs);
1078 0 0       0 if ($c) {
1079 0         0 &$outc($c, @outcArgs);
1080             }
1081             } else {
1082 2 50       10 $c = (++$index <= $indexmax) ? substr($str, $index, 1) : '';
1083 2 50 33     29 if ((! ($c =~ /[[:digit:]]/) && $c ne '.') ||
      33        
1084             # Note: if $index is after the end $str, perl treat it as the end
1085             index($str, '>', $index) < $index) {
1086 0         0 &$outc('$', @outcArgs);
1087 0         0 &$outc('<', @outcArgs);
1088             #
1089             # The EOF will automatically go here
1090             #
1091 0         0 next;
1092             }
1093              
1094 2         5 my $number = 0;
1095 2 50       10 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1096 2         12 while ($c =~ /[[:digit:]]/) {
1097 5         13 $number = $number * 10 + $c;
1098 5 50       25 $c = (++$index <= $indexmax) ? substr($str, $index, 1) : '';
1099             }
1100 2         4 $number *= 10;
1101 2 50       9 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1102 2 50       9 if ($c eq '.') {
1103 0 0       0 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1104 0 0       0 if ($c =~ /[[:digit:]]/) {
1105 0         0 $number += $c;
1106 0         0 $index++;
1107             }
1108 0   0     0 while (($index <= $indexmax) && substr($str, $index, 1) =~ /[[:digit:]]/) {
1109 0         0 $index++;
1110             }
1111             }
1112 2         4 my $mandatory = 0;
1113 2 50       9 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1114 2   33     17 while ($c eq '*' || $c eq '/') {
1115 0 0       0 if ($c eq '*') {
1116 0         0 $number *= $affcnt;
1117 0         0 $index++;
1118             } else {
1119 0         0 $mandatory = 1;
1120 0         0 $index++;
1121             }
1122 0 0       0 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1123             }
1124              
1125 2 50 33     23 if ($number > 0 && ($always_delay || $normal_delay || $mandatory)) {
      33        
1126 2         17 $self->delay(int($number / 10));
1127             }
1128             }
1129             }
1130              
1131 53         229 $index++;
1132             }
1133              
1134 3 0 0     28 if ($trailpad > 0 && ($always_delay || $normal_delay)) {
      33        
1135 0         0 $self->delay(int($trailpad / 10));
1136             }
1137             }
1138              
1139              
1140             sub putp {
1141 1     1 1 8 my ($self, $str) = @_;
1142              
1143 1     38   9 return $self->tputs($str, 1, sub {print STDOUT shift});
  38         1211  
1144             }
1145              
1146              
1147             sub _tparm {
1148 6     6   21 my ($self, $string, @param) = (@_);
1149              
1150 6         77 my $stub = $self->_stub($string);
1151              
1152 6         113 return $self->$stub($self->_terminfo_current->{_dynamic_vars}, $self->_terminfo_current->{_static_vars}, @param);
1153             }
1154              
1155             sub tparm {
1156 3     3 1 22 my ($self, $string, @param) = (@_);
1157              
1158 3         16 return $self->_tparm($string, @param);
1159             }
1160              
1161              
1162             sub tgoto {
1163 3     3 1 39 my ($self, $string, $col, $row) = (@_);
1164             #
1165             # We are in a pure terminfo workflow: capnames capability are translated to a terminfo feature, and the
1166             # string feature is derived from the found terminfo feature.
1167             # Reversal of arguments is intentional
1168             #
1169 3         17 return $self->_tparm($string, $row, $col);
1170             }
1171              
1172              
1173             sub use_env {
1174 0     0 1   my $self = shift;
1175              
1176 0 0         if (@_) {
1177 0           $self->{_use_env} = shift;
1178             #
1179             # If user gave undef as argument, convert it to 0.
1180             #
1181 0 0         if (! defined($self->{_use_env})) {
1182 0           $self->{_use_env} = 0;
1183             }
1184             #
1185             # Finally convert it to 1 if ! false
1186             #
1187 0 0         if (! $self->{_use_env}) {
1188 0           $self->{_use_env} = 1;
1189             }
1190             }
1191              
1192 0           return $self->{_use_env};
1193             }
1194              
1195              
1196             1;
1197              
1198             __END__