File Coverage

blib/lib/Config/INI/RefVars/Builtins.pm
Criterion Covered Total %
statement 82 83 98.8
branch 46 62 74.1
condition 17 25 68.0
subroutine 23 23 100.0
pod 1 1 100.0
total 169 194 87.1


line stmt bran cond sub pod time code
1             # lib/Config/INI/RefVars/Builtins.pm
2             package Config::INI::RefVars::Builtins;
3              
4 31     31   483 use 5.010;
  31         101  
5 31     31   144 use strict;
  31         80  
  31         931  
6 31     31   180 use warnings;
  31         88  
  31         1848  
7              
8 31     31   214 use File::Spec::Functions qw(catdir catfile);
  31         86  
  31         2073  
9 31     31   186 use File::Basename qw(dirname basename);
  31         64  
  31         34118  
10              
11              
12             our $VERSION = '1.02';
13              
14              
15             sub default_dispatch_table {
16             return {
17 188     188 1 2803 catdir => \&catdir,
18             catfile => \&catfile,
19             ignore => \&_ignore,
20             concat => \&_concat,
21             join => \&_join,
22             substr => \&_substr,
23             x => \&_x,
24             'and' => \&_and,
25             'or' => \&_or,
26             'if' => \&_if,
27             s => \&_s,
28             tr => \&_tr,
29             m => \&_m,
30             not => \&_not,
31             eq => \&_eq,
32             dirname => \&dirname,
33             basename => \&basename,
34             };
35             }
36              
37              
38             sub _clean_error {
39 2     2   4 my ($error) = @_;
40 2         3 chomp($error);
41 2         14 $error =~ s/\s+at\s+\S+\s+line\s+\d+\.?\z//;
42 2         8 return $error;
43             }
44              
45              
46             sub _ignore {
47 12     12   48 return "";
48             }
49              
50              
51             sub _concat {
52 9     9   97 return join("", @_);
53             }
54              
55              
56             sub _join {
57 14 50   14   127 return @_ ? join(shift(@_), @_) : "";
58             }
59              
60              
61             sub _substr {
62 8 100 100 8   137 die("substr: expected 2 or 3 arguments\n") if @_ < 2 || @_ > 3;
63              
64 5         13 my $warning = "";
65              
66             local $SIG{__WARN__} = sub {
67 2     2   6 $warning = _clean_error($_[0]);
68 5         39 };
69              
70 5 100       44 my $result = @_ == 2
71             ? substr($_[0], $_[1]) : substr($_[0], $_[1], $_[2]);
72              
73 5 100       76 die("substr: $warning\n") if $warning ne "";
74 3         27 return $result;
75             }
76              
77              
78             sub _x {
79 3 50   3   41 die("x: expected 2 arguments\n") if @_ != 2;
80              
81 3         9 my ($str, $n) = @_;
82              
83 3 50       18 die("x: second argument must be a non-negative integer\n")
84             unless $n =~ /^\+?[0-9]+$/;
85              
86 3         20 return $str x $n;
87             }
88              
89              
90             sub _and {
91 5     5   8 foreach my $arg (@_) {
92 8 100       16 return "" if $arg eq "";
93             }
94 3 100       10 return @_ ? $_[-1] : "";
95             }
96              
97              
98             sub _or {
99 5     5   6 foreach my $arg (@_) {
100 8 100       21 return $arg if $arg ne "";
101             }
102 2         6 return "";
103             }
104              
105              
106             sub _if {
107 6 50 33 6   20 die("if: expected 2 or 3 arguments\n") if @_ < 2 || @_ > 3;
108 6 100 100     25 return $_[0] ne "" ? $_[1] : ($_[2] // "");
109             }
110              
111              
112             sub _s {
113 10 100 66 10   40 die("s: expected 3 or 4 arguments\n") if @_ < 3 || @_ > 4;
114              
115 9         16 my ($str, $pattern, $replacement, $mods) = @_;
116 9   100     19 $mods //= "";
117              
118 9 100       62 die("s: unsupported modifier '$mods'\n") if $mods !~ /^[gimsx]*$/;
119              
120 7 100       76 die("s: regex code blocks are not allowed\n") if $pattern =~ /\(\?\??\{/;
121              
122 5         10 my $global = $mods =~ s/g//g;
123 5 100       10 my $re = eval { $mods eq "" ? qr/$pattern/ : qr/(?$mods:$pattern)/; };
  5         55  
124 5 50       10 die("s: ", _clean_error($@), "\n") if $@;
125              
126 5 100       8 if ($global) {
127 2         12 $str =~ s/$re/$replacement/g;
128             }
129             else {
130 3         25 $str =~ s/$re/$replacement/;
131             }
132 5         22 return $str;
133             }
134              
135              
136             sub _pick_tr_delim {
137 5     5   8 my @values = @_;
138              
139 31     31   254 no warnings 'qw';
  31         83  
  31         20786  
140 5         10 foreach my $delim (qw(| ! / : ; # ~ @ % ^ * + = ?)) {
141 5 50 50     6 return $delim if !grep { index($_ // "", $delim) >= 0 } @values;
  10         26  
142             }
143 0         0 die("tr: no safe delimiter found\n");
144             }
145              
146              
147             sub _tr {
148 8 100 66 8   42 die("tr: expected 3 or 4 arguments\n") if @_ < 3 || @_ > 4;
149              
150 7         14 my ($str, $search, $replacement, $mods) = @_;
151 7   100     14 $mods //= "";
152              
153 7 100       54 die("tr: unsupported modifier '$mods'\n") if $mods !~ /^[cds]*$/;
154              
155 5         45 my $delim = _pick_tr_delim($search, $replacement);
156 5         10 my $code = "\$str =~ tr${delim}${search}${delim}${replacement}${delim}${mods};";
157 5 50       304 eval "$code; 1" or die("tr: ", _clean_error($@), "\n");
158              
159 5         27 return $str;
160             }
161              
162              
163             sub _m {
164 6 50 33 6   14 die("m: expected 2 or 3 arguments\n") if @_ < 2 || @_ > 3;
165              
166 6         11 my ($str, $pattern, $mods) = @_;
167 6   50     16 $mods //= "";
168              
169 6 50       16 die("m: unsupported modifier '$mods'\n") if $mods !~ /^[imsx]*$/;
170 6 50       12 die("m: regex code blocks are not allowed\n") if $pattern =~ /\(\?\??\{/;
171              
172 6 50       9 my $re = eval { $mods eq "" ? qr/$pattern/ : qr/(?$mods:$pattern)/; };
  6         63  
173 6 50       13 die("m: ", _clean_error($@), "\n") if $@;
174              
175 6 100       58 return $str =~ $re ? "1" : "";
176             }
177              
178              
179             sub _not {
180 1 50   1   4 die("not: expected 1 argument\n") if @_ != 1;
181 1 50       7 return $_[0] eq "" ? "1" : "";
182             }
183              
184              
185             sub _eq {
186 1 50   1   3 die("eq: expected 2 arguments\n") if @_ != 2;
187 1 50       4 return $_[0] eq $_[1] ? "1" : "";
188             }
189              
190             1;
191              
192              
193             __END__