File Coverage

blib/lib/MarpaX/Database/Terminfo/Interface.pm
Criterion Covered Total %
statement 460 635 72.4
branch 187 374 50.0
condition 64 149 42.9
subroutine 43 48 89.5
pod 18 18 100.0
total 772 1224 63.0


line stmt bran cond sub pod time code
1 16     16   3571427 use strict;
  16         28  
  16         505  
2 16     16   62 use warnings FATAL => 'all';
  16         24  
  16         888  
3              
4             package MarpaX::Database::Terminfo::Interface;
5 16     16   5827 use MarpaX::Database::Terminfo;
  16         38  
  16         444  
6 16     16   7256 use MarpaX::Database::Terminfo::String;
  16         33  
  16         431  
7 16     16   89 use MarpaX::Database::Terminfo::Constants qw/:all/;
  16         18  
  16         2138  
8 16     16   91 use File::ShareDir qw/:ALL/;
  16         25  
  16         1874  
9 16     16   68 use Carp qw/carp croak/;
  16         19  
  16         652  
10 16     16   10279 use Storable qw/fd_retrieve/;
  16         36613  
  16         928  
11 16     16   8407 use Time::HiRes qw/usleep/;
  16         16446  
  16         60  
12 16     16   2539 use Log::Any qw/$log/;
  16         20  
  16         113  
13 16     16   2259 use constant BAUDBYTE => 9; # From GNU Ncurses: 9 = 7 bits + 1 parity + 1 stop
  16         22  
  16         14012  
14 16     16   9183 our $HAVE_POSIX = eval "use POSIX; 1;" || 0;
  16         72683  
  16         230  
15              
16             # ABSTRACT: Terminfo interface
17              
18             our $VERSION = '0.011'; # VERSION
19              
20              
21             sub new {
22 16     16 1 2051732 my ($class, $optp) = @_;
23              
24 16   100     114 $optp //= {};
25              
26 16 50       86 if (ref($optp) ne 'HASH') {
27 0         0 croak 'Options must be a reference to a HASH';
28             }
29              
30 16   33     153 my $file = $optp->{file} // $ENV{MARPAX_DATABASE_TERMINFO_FILE} // '';
      50        
31 16   33     257 my $txt = $optp->{txt} // $ENV{MARPAX_DATABASE_TERMINFO_TXT} // '';
      50        
32 16   33     172 my $bin = $optp->{bin} // $ENV{MARPAX_DATABASE_TERMINFO_BIN} // dist_file('MarpaX-Database-Terminfo', 'share/ncurses-terminfo.storable');
      33        
33             my $caps = $optp->{caps} // $ENV{MARPAX_DATABASE_TERMINFO_CAPS} // (
34 16 50 33     2758 $^O eq 'aix' ? dist_file('MarpaX-Database-Terminfo', 'share/ncurses-Caps.aix4') :
    50 33        
    50          
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     1452 my $cache_stubs_as_txt = $optp->{cache_stubs_as_txt} // $ENV{MARPAX_DATABASE_TERMINFO_CACHE_STUBS_AS_TXT} // 1;
      50        
40 16   33     123 my $cache_stubs = $optp->{cache_stubs} // $ENV{MARPAX_DATABASE_TERMINFO_CACHE_STUBS} // 1;
      50        
41 16         30 my $stubs_txt;
42             my $stubs_bin;
43 16 50       46 if ($cache_stubs) {
44 16   33     138 $stubs_txt = $optp->{stubs_txt} // $ENV{MARPAX_DATABASE_TERMINFO_STUBS_TXT} // '';
      100        
45 16   33     162 $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     1404 my $bsd_tputs = $optp->{bsd_tputs} // $ENV{MARPAX_DATABASE_TERMINFO_BSD_TPUTS} // 0;
      50        
51 16   66     100 my $use_env = $optp->{use_env } // $ENV{MARPAX_DATABASE_TERMINFO_USE_ENV} // 1;
      50        
52              
53             # -------------
54             # Load Database
55             # -------------
56 16         28 my $db = undef;
57 16         26 my $db_ok = 0;
58 16 50       53 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     90 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       81 if (! $db_ok) {
91 16         26 my $fh;
92 16 50       125 if ($log->is_debug) {
93 0         0 $log->debugf('Loading %s', $bin);
94             }
95 16 50       916 if (! open($fh, '<', $bin)) {
96 0         0 carp "Cannot open $bin, $!";
97             } else {
98 16         34 eval {$db = fd_retrieve($fh)};
  16         103  
99 16 50       823605 if ($@) {
100 0         0 carp "$bin: $@";
101             } else {
102 16         76 $db_ok = 1;
103             }
104 16 50       553 close($fh) || carp "Cannot close $bin, $!";
105             }
106             }
107 16 50       74 if (! $db_ok) {
108 0         0 croak 'Cannot get a valid terminfo database';
109             }
110             # -----------------------
111             # Load terminfo<->termcap
112             # -----------------------
113 16         56 my %t2other = ();
114 16         53 my %c2other = ();
115 16         37 my %capalias = ();
116 16         34 my %infoalias = ();
117             {
118 16 50       28 if ($log->is_debug) {
  16         161  
119 0         0 $log->debugf('Loading %s', $caps);
120             }
121 16         263 my $fh;
122 16 50       1048 if (! open($fh, '<', $caps)) {
123 0         0 carp "Cannot open $caps, $!";
124             } else {
125             #
126             # Get translations
127             #
128 16         41 my $line = 0;
129 16         366 while (defined($_ = <$fh>)) {
130 20160         14245 ++$line;
131 20160 100       35821 if (/^\s*#/) {
132 11408         23735 next;
133             }
134 8752         59258 s/\s*$//;
135 8752 100       17761 if (/^\s*capalias\b/) {
    100          
136 704         3966 my ($capalias, $alias, $name, $set, $description) = split(/\s+/, $_, 5);
137 704         4346 $capalias{$alias} = {name => $name, set => $set, description => $description};
138             } elsif (/^\s*infoalias\b/) {
139 96         567 my ($infoalias, $alias, $name, $set, $description) = split(/\s+/, $_, 5);
140 96         577 $infoalias{$alias} = {name => $name, set => $set, description => $description};
141             } else {
142 7952         64384 my ($variable, $feature, $type, $termcap, $keyname, $keyvalue, $translation, $description) = split(/\s+/, $_, 8);
143 7952 100       28119 if ($type eq 'bool') {
    100          
    50          
144 704         899 $type = TERMINFO_BOOLEAN;
145             } elsif ($type eq 'num') {
146 624         624 $type = TERMINFO_NUMERIC;
147             } elsif ($type eq 'str') {
148 6624         6229 $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         20350 $t2other{$feature} = {type => $type, termcap => $termcap, variable => $variable};
154 7952         43496 $c2other{$termcap} = {type => $type, feature => $feature, variable => $variable};
155             }
156             }
157 16 50       306 close($fh) || carp "Cannot close $caps, $!";
158             }
159             }
160             # -----------------
161             # Load stubs as txt
162             # -----------------
163 16         37 my $cached_stubs_as_txt = {};
164 16         31 my $cached_stubs_as_txt_ok = 0;
165 16 50       56 if ($cache_stubs) {
166 16 100       56 if ($stubs_txt) {
167 1         1 my $fh;
168 1 50       7 if ($log->is_debug) {
169 0         0 $log->debugf('Loading %s', $stubs_txt);
170             }
171 1 50       51 if (! open($fh, '<', $stubs_txt)) {
172 0         0 carp "Cannot open $stubs_txt, $!";
173             } else {
174 1         1 my $content = do {local $/; <$fh>;};
  1         4  
  1         1909  
175 1 50       12 close($fh) || carp "Cannot close $stubs_txt, $!";
176 1 50       5 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   108 no strict 'vars';
  16         22  
  16         62594  
  1         9  
184             #
185             # Untaint data
186             #
187 1         852 my ($untainted) = $content =~ m/(.*)/s;
188 1         46813 $cached_stubs_as_txt = eval $untainted; ## no critic
189 1 50       7 if ($@) {
190 0         0 carp "$stubs_txt: $@";
191             } else {
192 1         7 $cached_stubs_as_txt_ok = 1;
193             }
194             }
195             }
196             }
197 16 100 66     124 if (! $cached_stubs_as_txt_ok && $stubs_bin) {
198 15         27 my $fh;
199 15 50       128 if ($log->is_debug) {
200 0         0 $log->debugf('Loading %s', $stubs_bin);
201             }
202 15 50       1051 if (! open($fh, '<', $stubs_bin)) {
203 0         0 carp "Cannot open $stubs_bin, $!";
204             } else {
205 15         41 eval {$cached_stubs_as_txt = fd_retrieve($fh)};
  15         83  
206 15 50       107988 if ($@) {
207 0         0 carp "$stubs_bin: $@";
208             } else {
209 15         32 $cached_stubs_as_txt_ok = 1;
210             }
211 15 50       259 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   242 _flush => [ sub {} ],
229             _bsd_tputs => $bsd_tputs,
230             _term => undef, # Current terminal
231             _use_env => $use_env,
232             };
233              
234 16         104 bless($self, $class);
235              
236             #
237             # Initialize
238             #
239 16         66 $self->_terminfo_init();
240              
241 16         164 return $self;
242             }
243              
244              
245             sub _terminfo_db {
246 7817     7817   7968 my ($self) = (@_);
247 7817 50 33     17966 if ($log->is_warn && ! defined($self->{_terminfo_db})) {
248 0         0 $log->warnf('Undefined database');
249             }
250 7817         49770 return $self->{_terminfo_db};
251             }
252              
253              
254             sub _terminfo_current {
255 5549     5549   6239 my $self = shift;
256 5549 100       12114 if (@_) {
257 2675         5925 $self->{_terminfo_current} = shift;
258             }
259 5549 50 33     10734 if ($log->is_warn && ! defined($self->{_terminfo_current})) {
260 0         0 $log->warnf('Undefined current terminfo entry');
261             }
262 5549         49947 return $self->{_terminfo_current};
263             }
264              
265              
266             sub _t2other {
267 819324     819324   573307 my ($self) = (@_);
268 819324 50 33     1015549 if ($log->is_warn && ! defined($self->{_t2other})) {
269 0         0 $log->warnf('Undefined terminfo->termcap translation hash');
270             }
271 819324         4036360 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   79 my ($self) = (@_);
304 61 100       256 if (! defined($self->{_terminfo_current})) {
305 16   50     126 $self->tgetent($ENV{TERM} || 'unknown');
306             }
307 61         181 return defined($self->_terminfo_current);
308             }
309              
310              
311             sub flush {
312 1     1 1 2 my ($self, $cb, @args) = @_;
313 1 50       3 if (defined($cb)) {
314 0         0 $self->{_flush} = [ $cb, @args ];
315             }
316 1         3 return $self->{_flush};
317             }
318              
319              
320             sub _find {
321 5141     5141   9159 my ($self, $name, $from) = @_;
322              
323 5141         7746 my $rc = undef;
324 5141   100     16411 $from //= '';
325              
326 5141 50       16136 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         31896 my $terminfo_db = $self->_terminfo_db;
335 5141 50       10847 if (defined($terminfo_db)) {
336 5141         6028 foreach (@{$terminfo_db}) {
  5141         10099  
337 3542482         2237950 my $terminfo = $_;
338              
339 3542482 100       2148903 if (grep {$_ eq $name} @{$terminfo->{alias}}) {
  5459044         8043188  
  3542482         4647890  
340 5141 50       27097 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         42414 $rc = $terminfo;
344 5141         9512 last;
345             }
346             }
347             }
348 5141         11687 return $rc;
349             }
350              
351             sub tgetent {
352 2675     2675 1 1754137 my ($self, $name, $fh) = (@_);
353              
354 2675 50       8652 if (! defined($self->_terminfo_db)) {
355 0         0 return -1;
356             }
357 2675         7768 my $found = $self->_find($name);
358 2675 50       9009 if (! defined($found)) {
359 0         0 return 0;
360             }
361             #
362             # Process cancellations and use=
363             #
364 2675         7259 my %cancelled = ();
365             {
366 2675         3593 my %featured = ();
  2675         4068  
367 2675         3139 my $i = 0;
368 2675         3276 while ($i <= $#{$found->{feature}}) {
  214469         340376  
369 211794         169826 my $feature = $found->{feature}->[$i];
370 211794 100 100     774550 if ($feature->{type} == TERMINFO_BOOLEAN && substr($feature->{name}, -1, 1) eq '@') {
    100 100        
371 1051         1340 my $cancelled = $feature->{name};
372 1051         1408 substr($cancelled, -1, 1, '');
373 1051         2114 $cancelled{$cancelled} = 1;
374 1051 50       1962 if ($log->is_trace) {
375 0         0 $log->tracef('[Loading %s] New cancellation %s', $name, $cancelled);
376             }
377 1051         5513 ++$i;
378             } elsif ($feature->{type} == TERMINFO_STRING && $feature->{name} eq 'use') {
379 2466 50       6203 if ($log->is_trace) {
380 0         0 $log->tracef('[Loading %s] use=\'%s\' with cancellations %s', $name, $feature->{value}, [ keys %cancelled ]);
381             }
382 2466         15050 my $insert = $self->_find($feature->{value}, $name);
383 2466 50       6164 if (! defined($insert)) {
384 0         0 return 0;
385             }
386 2466         5174 my @keep = ();
387 2466         2628 foreach (@{$insert->{feature}}) {
  2466         5348  
388 107761 100       143818 if (exists($cancelled{$_->{name}})) {
389 910 50       1958 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         4340 next;
393             }
394 106851 100       132632 if (exists($featured{$_->{name}})) {
395 6713 50       9011 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         26817 next;
399             }
400 100138 50       118021 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         382895 push(@keep, $_);
404             }
405 2466         2785 splice(@{$found->{feature}}, $i, 1, @keep);
  2466         20243  
406             } else {
407 208277 50       271993 if ($log->is_trace) {
408 0         0 $log->tracef('[Loading %s] New feature %s', $name, $feature);
409             }
410 208277         904462 $featured{$feature->{name}} = 1;
411 208277         185789 ++$i;
412             }
413             }
414             }
415             #
416             # Remember cancelled things
417             #
418 2675         8030 $found->{cancelled} = \%cancelled;
419             #
420             # Drop needless cancellations
421             #
422             {
423 2675         4431 my $i = $#{$found->{feature}};
  2675         7159  
424 2675         5049 foreach (reverse @{$found->{feature}}) {
  2675         7035  
425 209328 100 100     317778 if ($_->{type} == TERMINFO_BOOLEAN && substr($_->{name}, -1, 1) eq '@') {
426 1051 50       2205 if ($log->is_trace) {
427 0         0 $log->tracef('[Loading %s] Dropping cancellation \'%s\' from terminfo', $name, $found->{feature}->[$i]->{name});
428             }
429 1051         5130 splice(@{$found->{feature}}, $i, 1);
  1051         1774  
430             }
431 209328         155864 --$i;
432             }
433             }
434             #
435             # Drop commented features
436             #
437             {
438 2675         4584 my $i = $#{$found->{feature}};
  2675         4681  
  2675         3461  
  2675         7053  
439 2675         3713 foreach (reverse @{$found->{feature}}) {
  2675         5746  
440 208277 100       273959 if (substr($_->{name}, 0, 1) eq '.') {
441 2 50       7 if ($log->is_trace) {
442 0         0 $log->tracef('[Loading %s] Dropping commented \'%s\' from terminfo', $name, $found->{feature}->[$i]->{name});
443             }
444 2         13 splice(@{$found->{feature}}, $i, 1);
  2         5  
445             }
446 208277         158394 --$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         6985 $found->{terminfo} = {};
455 2675         17285 $found->{termcap} = {};
456 2675         11600 $found->{variable} = {};
457 2675         12810 my $pad_char = undef;
458 2675         4358 my $cursor_up = undef;
459 2675         4281 my $backspace_if_not_bs = undef;
460             {
461 2675         6697 foreach (@{$found->{feature}}) {
  2675         4715  
  2675         6123  
462 208275         202209 my $feature = $_;
463 208275         184568 my $key = $feature->{name};
464             #
465             # For terminfo lookup
466             #
467 208275 50       246740 if (! exists($found->{terminfo}->{$key})) {
468 208275         259649 $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       233280 if (! exists($self->_t2other->{$key})) {
478 4592 50       6658 if ($log->is_trace) {
479 0         0 $log->tracef('[Loading %s] Untranslated feature \'%s\'', $name, $key);
480             }
481 4592         18581 next;
482             }
483             #
484             # Yes, check consistency
485             #
486 203683         236461 my $type = $self->_t2other->{$key}->{type};
487 203683 50       291435 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         205695 my $termcap = $self->_t2other->{$key}->{termcap};
497 203683 50       312520 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       305134 if ($log->is_trace) {
503 0         0 $log->tracef('[Loading %s] Pushing termcap feature \'%s\'', $name, $termcap);
504             }
505 203683 50       925684 if (! exists($found->{termcap}->{$termcap})) {
506 203683         324456 $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         227284 my $variable = $self->_t2other->{$key}->{variable};
517 203683 50       302989 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       295580 if ($log->is_trace) {
523 0         0 $log->tracef('[Loading %s] Pushing variable feature \'%s\'', $name, $variable);
524             }
525 203683 50       956665 if (! exists($found->{variable}->{$variable})) {
526 203683         286927 $found->{variable}->{$variable} = $feature;
527             #
528             # Keep track of pad_char, cursor_up and backspace_if_not_bs
529 203683 100       299056 if ($type == TERMINFO_STRING) {
530 180968 100       651995 if ($variable eq 'pad_char') {
    100          
    100          
531 15         25 $pad_char = $feature;
532 15 50       38 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         3261 $cursor_up = $feature;
537 2452 50       4699 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         53 $backspace_if_not_bs = $feature;
542 20 50       64 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       8812 if (defined($pad_char)) {
560 15 50       56 if ($log->is_trace) {
561 0         0 $log->tracef('[Loading %s] Initialized PC to \'%s\'', $name, $pad_char->{value});
562             }
563 15         94 $found->{variable}->{PC} = $pad_char;
564             }
565             #
566             # UP is not used by ncurses.
567             #
568 2675 100       5458 if (defined($cursor_up)) {
569 2452 50       5789 if ($log->is_trace) {
570 0         0 $log->tracef('[Loading %s] Initialized UP to \'%s\'', $name, $cursor_up->{value});
571             }
572 2452         14135 $found->{variable}->{UP} = $cursor_up;
573             }
574             #
575             # BC is used in the tgoto emulation.
576             #
577 2675 100       7355 if (defined($backspace_if_not_bs)) {
578 20 50       47 if ($log->is_trace) {
579 0         0 $log->tracef('[Loading %s] Initialized BC to \'%s\'', $name, $backspace_if_not_bs->{value});
580             }
581 20         131 $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         9983 my ($baudrate, $ospeed) = $self->_get_ospeed_and_baudrate($fh);
587 2675         13715 my $OSPEED = {name => 'ospeed', type => TERMINFO_NUMERIC, value => $ospeed};
588 2675 50       7814 if ($log->is_trace) {
589 0         0 $log->tracef('[Loading %s] Initialized ospeed to %d', $name, $OSPEED->{value});
590             }
591 2675         15839 $found->{variable}->{ospeed} = $OSPEED;
592             #
593             # The variable baudrate is used eventually in delay
594             #
595 2675         8803 my $BAUDRATE = {name => 'baudrate', type => TERMINFO_NUMERIC, value => $baudrate};
596 2675 50       6115 if ($log->is_trace) {
597 0         0 $log->tracef('[Loading %s] Initialized baudrate to %d', $name, $BAUDRATE->{value});
598             }
599 2675         15231 $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       6296 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         6666 $found->{terminfo}->{ospeed} = $found->{variable}->{ospeed};
612 2675 50       5669 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         6848 $found->{terminfo}->{baudrate} = $found->{variable}->{baudrate};
618             }
619              
620             #
621             # Remove any static/dynamic var
622             #
623 2675         8016 $found->{_static_vars} = [];
624 2675         5151 $found->{_dynamic_vars} = [];
625              
626 2675         8143 $self->_terminfo_current($found);
627              
628             #
629             # Create stubs for every string
630             #
631 2675         9640 $self->_stubs($name);
632              
633 2675         25766 return 1;
634             }
635              
636             sub _stub {
637 185265     185265   154471 my ($self, $featurevalue) = @_;
638              
639 185265 50       221370 if ($self->{_cache_stubs}) {
640 185265 100       319820 if (exists($self->{_cached_stubs}->{$featurevalue})) {
641 176711 50       226332 if ($log->is_trace) {
642 0         0 $log->tracef('Getting \'%s\' compiled stub from cache', $featurevalue);
643             }
644 176711         813116 $self->{_stubs}->{$featurevalue} = $self->{_cached_stubs}->{$featurevalue};
645             }
646             }
647 185265 100       246529 if (! exists($self->{_stubs}->{$featurevalue})) {
648 8554         7610 my $stub_as_txt = undef;
649 8554 50       12371 if ($self->{_cache_stubs_as_txt}) {
650 8554 100       18633 if (exists($self->{_cached_stubs_as_txt}->{$featurevalue})) {
651 8552 50       16208 if ($log->is_trace) {
652 0         0 $log->tracef('Getting \'%s\' stub as txt from cache', $featurevalue);
653             }
654 8552         48301 $stub_as_txt = $self->{_cached_stubs_as_txt}->{$featurevalue};
655             }
656             }
657 8554 100       12977 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       6 if ($log->is_trace) {
668 0         0 $log->tracef('Parsing \'%s\'', $string);
669             }
670 2         30 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         610 my $indent = join("\n", @{${$parseTreeValue}});
  2         5  
  2         14  
676 2         32 $indent =~ s/^/ /smg;
677 2         11 $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       10 if ($log->is_trace) {
692 0         0 $log->tracef('Parsing \'%s\' gives stub: %s', $string, $stub_as_txt);
693             }
694 2 50       23 if ($self->{_cache_stubs_as_txt}) {
695 2         13 $self->{_cached_stubs_as_txt}->{$featurevalue} = $stub_as_txt;
696             }
697             }
698 8554 50       12433 if ($log->is_trace) {
699 0         0 $log->tracef('Compiling \'%s\' stub', $featurevalue);
700             }
701 8554         1028373 $self->{_stubs}->{$featurevalue} = eval $stub_as_txt; ## no critic
702 8554 50       18292 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       14691 if ($self->{_cache_stubs}) {
707 8554         16621 $self->{_cached_stubs}->{$featurevalue} = $self->{_stubs}->{$featurevalue};
708             }
709             }
710              
711 185265         232064 return $self->{_stubs}->{$featurevalue};
712             }
713              
714             sub _stubs {
715 2675     2675   4504 my ($self, $name) = @_;
716              
717 2675         7648 $self->{_stubs} = {};
718              
719 2675         53679 foreach (values %{$self->_terminfo_current->{terminfo}}) {
  2675         6797  
720 213625         143607 my $feature = $_;
721 213625 100       306236 if ($feature->{type} == TERMINFO_STRING) {
722 185259         196699 $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   3041 my ($self, $fh) = (@_);
767              
768 2675         3426 my $baudrate = 0;
769 2675         3174 my $ospeed = 0;
770              
771 2675 50       6698 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       8343 if (defined($ENV{MARPAX_DATABASE_TERMINFO_OSPEED})) {
782 2         5 $ospeed = $ENV{MARPAX_DATABASE_TERMINFO_OSPEED};
783             } else {
784 2673 50       6558 if ($HAVE_POSIX) {
785 2673         4852 my $termios = eval { POSIX::Termios->new() };
  2673         18180  
786 2673 50       6581 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     19303 my $fileno = defined($fh) ? fileno($fh) : (fileno(\*STDIN) || 0);
792 2673 50       7813 if ($log->is_trace) {
793 0         0 $log->tracef('Trying to get attributes on fileno %d', $fileno);
794             }
795 2673         15465 eval {$termios->getattr($fileno)};
  2673         17012  
796 2673 50       7503 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       7490 if (defined($termios)) {
804 2673         2828 my $this = eval { $termios->getospeed() };
  2673         8502  
805 2673 50       5422 if (! defined($this)) {
806 0 0       0 if ($log->is_trace) {
807 0         0 $log->tracef('getospeed() failure, %s', $@);
808             }
809             } else {
810 2673         3435 $ospeed = $this;
811 2673 50       5637 if ($log->is_trace) {
812 0         0 $log->tracef('getospeed() returned %d', $ospeed);
813             }
814             }
815             }
816             }
817             }
818              
819              
820              
821 2675 50       24775 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       5725 if (! $ospeed) {
829 2673         3483 $ospeed = 13;
830 2673 50       5264 if ($log->is_warn) {
831 0         0 $log->warnf('ospeed defaulting to %d', $ospeed);
832             }
833             }
834              
835 2675   50     25602 $baudrate = $ENV{MARPAX_DATABASE_TERMINFO_BAUDRATE} || $OSPEED_TO_BAUDRATE{$ospeed} || 0;
836              
837 2675 50       7413 if ($log->is_trace) {
838 0         0 $log->tracef('ospeed/baudrate: %d/%d', $ospeed, $baudrate);
839             }
840              
841 2675         16523 return ($baudrate, $ospeed);
842             }
843              
844             #
845             # space refers to termcap, feature (i.e. terminfo) or variable
846             #
847             sub _tget {
848 45     45   77 my ($self, $space, $default, $default_if_cancelled, $default_if_wrong_type, $default_if_found, $type, $id, $areap) = (@_);
849              
850 45         53 my $rc = $default;
851 45         44 my $found = undef;
852              
853 45 50       93 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       75 if (! exists($self->_terminfo_current->{$space}->{$id})) {
858             #
859             # No such entry
860             #
861 15 50       36 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         58 my $t = $self->_terminfo_current->{$space}->{$id};
870 30         59 my $feature = $self->_terminfo_current->{terminfo}->{$t->{name}};
871 30 50       77 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     221 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       81 if ($feature->{type} == $type) {
    50          
884 27         40 $found = $feature;
885 27 100       80 if ($type == TERMINFO_STRING) {
886 20 100       84 $rc = defined($default_if_found) ? $default_if_found : \$feature->{value};
887             } else {
888 7 100       44 $rc = defined($default_if_found) ? $default_if_found : $feature->{value};
889             }
890             } elsif (defined($default_if_wrong_type)) {
891 3 50       11 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         18 $rc = $default_if_wrong_type;
895             }
896             }
897             }
898             }
899              
900 45 100 100     326 if (defined($found) && defined($areap) && ref($areap)) {
      66        
901 12 100       27 if ($type == TERMINFO_STRING) {
902 10 100       9 if (! defined(${$areap})) {
  10         26  
903 3         4 ${$areap} = '';
  3         6  
904             }
905 10   100     9 my $pos = pos(${$areap}) || 0;
906 10         12 substr(${$areap}, $pos, 0, $found->{value});
  10         20  
907 10         13 pos(${$areap}) = $pos + length($found->{value});
  10         25  
908             } else {
909 2         4 ${$areap} = $found->{value};
  2         5  
910             }
911             }
912              
913 45         200 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         3 my $outc = $self->{_outc};
924 2 50       6 if (defined($outc)) {
925 2         2 my $PC;
926 2 100 66     10 if ($self->tvgetflag('no_pad_char') || ! $self->tvgetstr('PC', \$PC)) {
927             #
928             # usleep() unit is micro-second
929             #
930 1         1000239 usleep($ms * 1000);
931             } else {
932             #
933             # baudrate is always defined.
934             #
935 1         1 my $baudrate;
936 1         4 $self->tvgetnum('baudrate', \$baudrate);
937 1         3 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         3 while ($nullcount-- > 0) {
942 1         3 &$outc($self->tparm($PC), @{$self->{_outcArgs}});
  1         4  
943             }
944             #
945             # Call for a flush
946             #
947 1         6 my ($flushcb, @flushargs) = @{$self->flush};
  1         4  
948 1         3 &$flushcb(@flushargs);
949             }
950             }
951             }
952              
953              
954             sub tgetflag {
955 1     1 1 6 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 13 my ($self, $id) = @_;
962 4         11 return $self->_tget('terminfo', 0, 0, -1, undef, TERMINFO_BOOLEAN, $id, undef);
963             }
964              
965              
966             sub tvgetflag {
967 4     4 1 10 my ($self, $id) = @_;
968 4         12 return $self->_tget('variable', 0, 0, 0, 1, TERMINFO_BOOLEAN, $id);
969             }
970              
971              
972             sub tgetnum {
973 1     1 1 6 my ($self, $id) = @_;
974 1         6 return $self->_tget('termcap', -1, undef, undef, undef, TERMINFO_NUMERIC, $id, undef);
975             }
976              
977              
978             sub tigetnum {
979 4     4 1 14 my ($self, $id) = @_;
980 4         12 return $self->_tget('terminfo', -1, -1, -2, undef, TERMINFO_NUMERIC, $id, undef);
981             }
982              
983              
984             sub tvgetnum {
985 3     3 1 12 my ($self, $id, $areap) = @_;
986 3         9 return $self->_tget('variable', 0, 0, 0, 1, TERMINFO_NUMERIC, $id, $areap);
987             }
988              
989              
990             sub tgetstr {
991 7     7 1 23 my ($self, $id, $areap) = @_;
992 7         21 return $self->_tget('termcap', 0, undef, undef, undef, TERMINFO_STRING, $id, $areap);
993             }
994              
995              
996             sub tigetstr {
997 9     9 1 35 my ($self, $id) = @_;
998 9         38 return $self->_tget('terminfo', 0, 0, -1, undef, TERMINFO_STRING, $id, undef);
999             }
1000              
1001              
1002             sub tvgetstr {
1003 12     12 1 22 my ($self, $id, $areap
1004             ) = @_;
1005 12         32 return $self->_tget('variable', 0, 0, 0, 1, TERMINFO_STRING, $id, $areap);
1006             }
1007              
1008              
1009             sub tputs {
1010 3     3 1 8 my ($self, $str, $affcnt, $outc, @outcArgs) = @_;
1011              
1012 3         12 $self->{_outc} = $outc;
1013 3         7 $self->{_outcArgs} = \@outcArgs;
1014              
1015 3         11 $self->_tputs($str, $affcnt, $outc, @outcArgs);
1016              
1017 3         10 $self->{_outc} = undef;
1018 3         23732 $self->{_outcArgs} = undef;
1019             }
1020              
1021             sub _tputs {
1022 3     3   5 my ($self, $str, $affcnt, $outc, @outcArgs) = @_;
1023              
1024 3   50     12 $affcnt //= 1;
1025              
1026 3         6 my $bell = '';
1027 3         12 $self->tvgetstr('bell', \$bell);
1028 3         4 my $flash_screen = '';
1029 3         10 $self->tvgetstr('flash_screen', \$flash_screen);
1030              
1031 3         4 my $always_delay;
1032             my $normal_delay;
1033              
1034 3 50       12 if (! defined($self->{_term})) {
1035             #
1036             # No current terminal: setuppterm() has not been called
1037             #
1038 3         5 $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         5 my $trailpad = 0;
1052 3         5 pos($str) = undef;
1053 3 50 33     15 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         6 my $indexmax = length($str);
1068 3   50     15 my $index = pos($str) || 0;
1069 3         28 while ($index <= $indexmax) {
1070 53         65 my $c = substr($str, $index, 1);
1071 53 100       67 if ($c ne '$') {
1072 51         81 &$outc($c, @outcArgs);
1073             } else {
1074 2         3 $index++;
1075 2 50       7 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1076 2 50       7 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       7 $c = (++$index <= $indexmax) ? substr($str, $index, 1) : '';
1083 2 50 33     21 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         4 my $number = 0;
1095 2 50       8 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1096 2         8 while ($c =~ /[[:digit:]]/) {
1097 5         6 $number = $number * 10 + $c;
1098 5 50       20 $c = (++$index <= $indexmax) ? substr($str, $index, 1) : '';
1099             }
1100 2         4 $number *= 10;
1101 2 50       5 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1102 2 50       7 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         3 my $mandatory = 0;
1113 2 50       6 $c = ($index <= $indexmax) ? substr($str, $index, 1) : '';
1114 2   33     13 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     18 if ($number > 0 && ($always_delay || $normal_delay || $mandatory)) {
      33        
1126 2         11 $self->delay(int($number / 10));
1127             }
1128             }
1129             }
1130              
1131 53         160 $index++;
1132             }
1133              
1134 3 0 0     25 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 7 my ($self, $str) = @_;
1142              
1143 1     38   7 return $self->tputs($str, 1, sub {print STDOUT shift});
  38         827  
1144             }
1145              
1146              
1147             sub _tparm {
1148 6     6   11 my ($self, $string, @param) = (@_);
1149              
1150 6         22 my $stub = $self->_stub($string);
1151              
1152 6         22 return $self->$stub($self->_terminfo_current->{_dynamic_vars}, $self->_terminfo_current->{_static_vars}, @param);
1153             }
1154              
1155             sub tparm {
1156 3     3 1 20 my ($self, $string, @param) = (@_);
1157              
1158 3         11 return $self->_tparm($string, @param);
1159             }
1160              
1161              
1162             sub tgoto {
1163 3     3 1 31 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         14 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__