File Coverage

lib/Devel/Trepan/Util.pm
Criterion Covered Total %
statement 69 73 94.5
branch 37 44 84.0
condition 7 12 58.3
subroutine 16 17 94.1
pod 0 11 0.0
total 129 157 82.1


line stmt bran cond sub pod time code
1             # -*- coding: utf-8 -*-
2             # Copyright (C) 2011, 2012, 2014 Rocky Bernstein <rocky@cpan.org>
3              
4             package Devel::Trepan::Util;
5 20     20   29492 use strict; use warnings; use English qw( -no_match_vars );
  20     20   51  
  20     20   547  
  20         111  
  20         46  
  20         593  
  20         4544  
  20         31153  
  20         124  
6              
7 20     20   7147 use vars qw(@EXPORT @ISA @YN);
  20         51  
  20         1733  
8             @EXPORT = qw( hash_merge safe_repr uniq_abbrev extract_expression
9             parse_eval_suffix parse_eval_sigil
10             YES NO YES_OR_NO @YN bool2YN);
11             @ISA = qw(Exporter);
12              
13 20     20   131 use constant YES => qw(y yes oui si yep ja);
  20         43  
  20         1813  
14             @YN = YES;
15 20     20   232 use constant NO => qw(n no non nope nein);
  20         98  
  20         33343  
16             push(@YN, NO);
17              
18             sub YN($)
19             {
20 7     7 0 3272 my $response = shift;
21 7         145 !!grep(/^${response}$/i, @YN);
22             }
23              
24             # Return 'Yes' for True and 'No' for False, and ?? for anything else
25             sub bool2YN($)
26             {
27 5     5 0 2798 my $bool = shift;
28 5 100       19 $bool ? 'Yes' : 'No';
29             }
30              
31             # Hash merge like Ruby has.
32             sub hash_merge($$) {
33 62     62 0 676 my ($config, $default_opts) = @_;
34 62         423 while (my ($field, $default_value) = each %$default_opts) {
35 472 100       2424 $config->{$field} = $default_value unless defined $config->{$field};
36             };
37 62         205 $config;
38             }
39              
40             sub safe_repr($$;$)
41             {
42 9     9 0 1774 my ($str, $max, $elipsis) = @_;
43 9 50       26 $elipsis = '... ' unless defined $elipsis;
44 9         15 my $strlen = length($str);
45 9 100       24 return '' unless $strlen;
46 8 50 33     24 $str = '' unless $str or $str =~ /\d+/;
47 8 100 66     53 if ($max > 0 && $strlen > $max && -1 == index($str, "\n")) {
      100        
48 1         13 sprintf("%s%s%s", substr($str, 0, $max/2),
49             $elipsis, substr($str, $strlen+1-($max)/2));
50             } else {
51 7         21 $str;
52             }
53             }
54              
55             # name is String and list is an Array of String.
56             # If name is a unique leading prefix of one of the entries of list,
57             # then return that. Otherwise return name.
58             sub uniq_abbrev($$)
59             {
60 5     5 0 1737 my ($list, $name) = @_;
61 5         13 my @candidates = ();
62 5         12 for my $try_name (@$list) {
63 20 100       59 push @candidates, $try_name if 0 == index($try_name, $name);
64             }
65 5 100       28 scalar @candidates == 1 ? $candidates[0] : $name;
66             }
67              
68             # extract the "expression" part of a line of source code.
69             # Specifically
70             # if (expression) -> expression
71             # elsif (expression) -> expression
72             # else (expression) -> expression
73             # until (expression) -> expression
74             # while (expression) -> expression
75             # return (expression) -> expression
76             # my (...) = (expression) -> (...) = (expression)
77             # my ... = expression -> expression
78             # ditto for "our" and "local", e.g.
79             # local (...) = (expression) -> (...) = (expression
80             # local ... = expression -> expression
81             # $... = expression -> expression
82             sub extract_expression($)
83             {
84 10     10 0 4456 my $text = shift;
85 10 100       86 if ($text =~ /^\s*(?:if|elsif|unless)\s*\(/) {
    100          
    100          
    100          
    100          
    50          
86 3         12 $text =~ s/^\s*(?:if|elsif|unless)\s*\(//;
87 3         14 $text =~ s/\s*\)\s*\{?\s*$//;
88             } elsif ($text =~ /^\s*(?:until|while)\s*\(/) {
89 2         9 $text =~ s/^\s*(?:until|while)\s*\(//;
90 2         10 $text =~ s/\s*\)\{?\s*$//;
91             } elsif ($text =~ /^\s*return\s+/) {
92             # EXPRESSION in: return EXPRESSION
93 2         11 $text =~ s/^\s*return\s+//;
94 2         9 $text =~ s/;\s*$//;
95             } elsif ($text =~ /^\s*(?:my|our|local)\s*(.+(\((?:.+)\s*\)\s*=.*);.*$)/) {
96             # my (...) = ...;
97             # Note: This has to appear before the below assignment
98 1         11 $text =~ s/^\s*(?:my|our|local)\s*(\((?:.+)\)\s*=.*)[^;]*;.*$/$1/;
99             } elsif ($text =~ /^\s*(?:my|our|local)\s+(?:.+)\s*=\s*(.+);.*$/) {
100             # my ... = ...;
101 1         3 $text = $1;
102             # } elsif ($text =~ /^\s*case\s+/) {
103             # # EXPRESSION in: case EXPESSION
104             # $text =~ s/^\s*case\s*//;
105             # } elsif ($text =~ /^\s*sub\s*.*\(.+\)/) {
106             # $text =~ s/^\s*sub\s*.*\((.*)\)/\(\1\)/;
107             } elsif ($text =~ /^\s*\$[A-Za-z_][A-Za-z0-9_\[\]]*\s*=[^=>]/) {
108             # RHS of an assignment statement.
109 0         0 $text =~ s/^\s*[A-Za-z_][A-Za-z0-9_\[\]]*\s*=//;
110             }
111 10         38 return $text;
112             }
113              
114             sub invalid_filename($)
115             {
116 2     2 0 835 my $filename = shift;
117 2 100       87 return "Command file '$filename' doesn't exist" unless -f $filename;
118 1 50       17 return "Command file '$filename' is not readable" unless -r $filename;
119 1         5 return undef;
120             }
121              
122             # Return 'undef' arg $cmd_str is ok. If not return the message a Perl -c
123             # gives, dropping off the "-e had complation errors" message.
124             sub invalid_perl_syntax($;$)
125             {
126 8     8 0 5320 my ($cmd_str, $have_e_opt) = @_;
127 8 100       71 my $cmd = sprintf("$EXECUTABLE_NAME -c %s",
128             $have_e_opt ? $cmd_str : "-e '$cmd_str'");
129 8         54271 my $output = `$cmd 2>&1`;
130 8         120 my $rc = $? >>8;
131 8 100       127 return undef if 0 == $rc;
132             # Drop off: -e had compilation errors.
133 5         69 my @errmsg = split(/\n/, $output);
134 5         23 pop @errmsg;
135 5         157 return join("\n", @errmsg);
136             }
137              
138             sub parse_eval_suffix($)
139             {
140 18     18 0 2712 my $cmd = shift;
141 18         37 my $suffix = substr($cmd, -1);
142 18 100       90 return ( index('%@$;>', $suffix) != -1) ? $suffix : '';
143             }
144              
145             sub parse_eval_sigil($)
146             {
147 0     0 0 0 my $cmd = shift;
148 0 0       0 return ($cmd =~ /^\s*([%\$\@>;])/) ? $1 : ';';
149             }
150              
151             # This routine makes sure $pager is set up so that '|' can use it.
152             sub pager()
153             {
154             # If PAGER is defined in the environment, use it.
155 2 100 33 2 0 647 if (defined $ENV{PAGER}) {
    50          
156 1         7 $ENV{PAGER};
157 1         125 } elsif (eval { require Config } && defined $Config::Config{pager} ) {
158             # if Config.pm defines it.
159 1         13 $Config::Config{pager};
160             } else {
161             # fall back to 'more'.
162 0           'more'
163             }
164             }
165              
166              
167             # Demo code
168             unless (caller) {
169             my $default_config = {a => 1, b => 'c'};
170             require Data::Dumper;
171             import Data::Dumper;
172             my $config = {};
173             hash_merge $config, $default_config;
174             print Dumper($config), "\n";
175              
176             for my $file (__FILE__, 'bogus') {
177             my $result = invalid_filename($file);
178             if (defined($result)) {
179             print "$result\n";
180             } else {
181             print "$file exists\n";
182             }
183             }
184              
185             $config = {
186             term_adjust => 1,
187             bogus => 'yep'
188             };
189             print Dumper($config), "\n";
190             hash_merge $config, $default_config;
191             print Dumper($config), "\n";
192              
193             my $string = 'The time has come to talk of many things.';
194             print safe_repr($string, 50), "\n";
195             print safe_repr($string, 17), "\n";
196             print safe_repr($string, 17, ''), "\n";
197              
198             my @list = qw(disassemble disable distance up);
199             uniq_abbrev(\@list, 'disas');
200             print join(' ', @list), "\n";
201             for my $name (qw(dis disas u upper foo)) {
202             printf("uniq_abbrev of %s is %s\n", $name,
203             uniq_abbrev(\@list, $name));
204             }
205             # ------------------------------------
206             # extract_expression
207             for my $stmt (
208             'if (condition("if"))',
209             'if (condition("if")) {',
210             'if(condition("if")){',
211             'until (until_termination)',
212             'until (until_termination){',
213             'return return_value',
214             'return return_value;',
215             'nothing to be done',
216             'my ($a,$b) = (5,6);',
217             ) {
218             print extract_expression($stmt), "\n";
219             }
220              
221             for my $cmd (qw(eval eval$ eval% eval@ evaluate% none)) {
222             print "parse_eval_suffix($cmd) => '". parse_eval_suffix($cmd) . "'\n";
223             }
224              
225             for my $resp (qw(yes no Y NO nein nien huh?)) {
226             printf "YN($resp) => '%s'\n", YN($resp);
227             }
228             for my $resp (1, 0, '', 'Foo', undef) {
229             my $resp_str = defined $resp ? $resp : 'undef';
230             printf "bool2YN($resp_str) => '%s'\n", bool2YN($resp);
231             }
232              
233             for my $expr ('1+', '{cmd=5}') {
234             print invalid_perl_syntax($expr), "\n";
235             }
236             for my $expr ('-e "$x="', '-e "(1,2"') {
237             print invalid_perl_syntax($expr, 1), "\n";
238             }
239              
240             $ENV{PAGER} = 'do-first';
241             print pager(), "\n";
242             delete $ENV{PAGER};
243             print pager(), "\n";
244             }
245              
246             1;