| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Module::List::Wildcard; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | our $DATE = '2020-12-22'; # DATE | 
| 4 |  |  |  |  |  |  | our $VERSION = '0.004007'; # VERSION | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | #IFUNBUILT | 
| 7 |  |  |  |  |  |  | # # use strict 'subs', 'vars'; | 
| 8 |  |  |  |  |  |  | # # use warnings; | 
| 9 |  |  |  |  |  |  | #END IFUNBUILT | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | my $has_globstar; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | # do our own exporting to start faster | 
| 14 |  |  |  |  |  |  | sub import { | 
| 15 | 1 |  |  | 1 |  | 8 | my $pkg = shift; | 
| 16 | 1 |  |  |  |  | 2 | my $caller = caller; | 
| 17 | 1 |  |  |  |  | 2 | for my $sym (@_) { | 
| 18 | 1 | 50 |  |  |  | 4 | if ($sym eq 'list_modules') { *{"$caller\::$sym"} = \&{$sym} } | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 69735 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 19 | 0 |  |  |  |  | 0 | else { die "$sym is not exported!" } | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  | } | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | sub list_modules($$) { | 
| 24 | 12 |  |  | 12 | 0 | 20424 | my($prefix, $options) = @_; | 
| 25 | 12 |  |  |  |  | 22 | my $trivial_syntax = $options->{trivial_syntax}; | 
| 26 | 12 |  |  |  |  | 20 | my($root_leaf_rx, $root_notleaf_rx); | 
| 27 | 12 |  |  |  |  | 0 | my($notroot_leaf_rx, $notroot_notleaf_rx); | 
| 28 | 12 | 50 |  |  |  | 23 | if($trivial_syntax) { | 
| 29 | 0 |  |  |  |  | 0 | $root_leaf_rx = $notroot_leaf_rx = qr#:?(?:[^/:]+:)*[^/:]+:?#; | 
| 30 | 0 |  |  |  |  | 0 | $root_notleaf_rx = $notroot_notleaf_rx = | 
| 31 |  |  |  |  |  |  | qr#:?(?:[^/:]+:)*[^/:]+#; | 
| 32 |  |  |  |  |  |  | } else { | 
| 33 | 12 |  |  |  |  | 37 | $root_leaf_rx = $root_notleaf_rx = qr/[a-zA-Z_][0-9a-zA-Z_]*/; | 
| 34 | 12 |  |  |  |  | 23 | $notroot_leaf_rx = $notroot_notleaf_rx = qr/[0-9a-zA-Z_]+/; | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 12 |  |  |  |  | 15 | my $recurse = $options->{recurse}; | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | # filter by wildcard. we cannot do this sooner because wildcard can be put | 
| 40 |  |  |  |  |  |  | # at the end or at the beginning (e.g. '*::Path') so we still need | 
| 41 | 12 |  |  |  |  | 13 | my $re_wildcard; | 
| 42 | 12 | 100 |  |  |  | 25 | if ($options->{wildcard}) { | 
| 43 | 8 |  |  |  |  | 36 | require String::Wildcard::Bash; | 
| 44 | 8 |  |  |  |  | 12 | my $orig_prefix = $prefix; | 
| 45 | 8 |  |  |  |  | 19 | my @prefix_parts = split /::/, $prefix; | 
| 46 | 8 |  |  |  |  | 13 | $prefix = ""; | 
| 47 | 8 |  |  |  |  | 9 | my $has_wildcard; | 
| 48 | 8 |  |  |  |  | 17 | while (defined(my $part = shift @prefix_parts)) { | 
| 49 | 8 | 50 |  |  |  | 19 | if (String::Wildcard::Bash::contains_wildcard($part)) { | 
| 50 | 8 |  |  |  |  | 215 | $has_wildcard++; | 
| 51 |  |  |  |  |  |  | # XXX limit recurse level to scalar(@prefix_parts), or -1 if has_globstar | 
| 52 | 8 | 100 |  |  |  | 16 | $recurse = 1 if @prefix_parts; | 
| 53 | 8 |  |  |  |  | 23 | last; | 
| 54 |  |  |  |  |  |  | } else { | 
| 55 | 0 |  |  |  |  | 0 | $prefix .= "$part\::"; | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  | } | 
| 58 | 8 | 50 |  |  |  | 14 | if ($has_wildcard) { | 
| 59 | 8 |  |  |  |  | 15 | $re_wildcard = convert_wildcard_to_re($orig_prefix); | 
| 60 |  |  |  |  |  |  | } | 
| 61 | 8 | 100 |  |  |  | 19 | $recurse = 1 if $has_globstar; | 
| 62 |  |  |  |  |  |  | } | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 12 | 50 | 33 |  |  | 113 | die "bad module name prefix `$prefix'" | 
| 65 |  |  |  |  |  |  | unless $prefix =~ /\A(?:${root_notleaf_rx}:: | 
| 66 |  |  |  |  |  |  | (?:${notroot_notleaf_rx}::)*)?\z/x && | 
| 67 |  |  |  |  |  |  | $prefix !~ /(?:\A|[^:]::)\.\.?::/; | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 12 |  |  |  |  | 21 | my $list_modules = $options->{list_modules}; | 
| 70 | 12 |  |  |  |  | 16 | my $list_prefixes = $options->{list_prefixes}; | 
| 71 | 12 |  |  |  |  | 17 | my $list_pod = $options->{list_pod}; | 
| 72 | 12 |  |  |  |  | 14 | my $use_pod_dir = $options->{use_pod_dir}; | 
| 73 | 12 | 50 | 66 |  |  | 31 | return {} unless $list_modules || $list_prefixes || $list_pod; | 
|  |  |  | 33 |  |  |  |  | 
| 74 | 12 |  |  |  |  | 14 | my $return_path = $options->{return_path}; | 
| 75 | 12 |  |  |  |  | 22 | my @prefixes = ($prefix); | 
| 76 | 12 |  |  |  |  | 16 | my %seen_prefixes; | 
| 77 |  |  |  |  |  |  | my %results; | 
| 78 | 12 |  |  |  |  | 21 | while(@prefixes) { | 
| 79 | 40 |  |  |  |  | 83 | my $prefix = pop(@prefixes); | 
| 80 | 40 |  |  |  |  | 95 | my @dir_suffix = split(/::/, $prefix); | 
| 81 | 40 | 100 |  |  |  | 84 | my $module_rx = | 
| 82 |  |  |  |  |  |  | $prefix eq "" ? $root_leaf_rx : $notroot_leaf_rx; | 
| 83 | 40 |  |  |  |  | 395 | my $pm_rx = qr/\A($module_rx)\.pmc?\z/; | 
| 84 | 40 |  |  |  |  | 337 | my $pod_rx = qr/\A($module_rx)\.pod\z/; | 
| 85 | 40 | 100 |  |  |  | 77 | my $dir_rx = | 
| 86 |  |  |  |  |  |  | $prefix eq "" ? $root_notleaf_rx : $notroot_notleaf_rx; | 
| 87 | 40 |  |  |  |  | 211 | $dir_rx = qr/\A$dir_rx\z/; | 
| 88 | 40 |  |  |  |  | 83 | foreach my $incdir (@INC) { | 
| 89 | 80 |  |  |  |  | 194 | my $dir = join("/", $incdir, @dir_suffix); | 
| 90 | 80 | 100 |  |  |  | 1584 | opendir(my $dh, $dir) or next; | 
| 91 | 52 |  |  |  |  | 690 | while(defined(my $entry = readdir($dh))) { | 
| 92 | 211 | 100 | 100 |  |  | 2299 | if(($list_modules && $entry =~ $pm_rx) || | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 93 |  |  |  |  |  |  | ($list_pod && | 
| 94 |  |  |  |  |  |  | $entry =~ $pod_rx)) { | 
| 95 | 48 |  |  |  |  | 143 | my $key = $prefix.$1; | 
| 96 | 48 | 100 | 100 |  |  | 312 | next if $re_wildcard && $key !~ $re_wildcard; | 
| 97 |  |  |  |  |  |  | $results{$key} = $return_path ? "$dir/$entry" : undef | 
| 98 | 21 | 50 | 66 |  |  | 124 | if $return_path || !exists($results{$key}); | 
|  |  | 100 |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | } elsif(($list_prefixes || $recurse) && | 
| 100 |  |  |  |  |  |  | ($entry ne '.' && $entry ne '..') && | 
| 101 |  |  |  |  |  |  | $entry =~ $dir_rx && | 
| 102 |  |  |  |  |  |  | -d join("/", $dir, | 
| 103 |  |  |  |  |  |  | $entry)) { | 
| 104 | 37 |  |  |  |  | 101 | my $newmod = $prefix.$entry; | 
| 105 | 37 |  |  |  |  | 53 | my $newpfx = $newmod."::"; | 
| 106 | 37 | 50 |  |  |  | 72 | next if exists $seen_prefixes{$newpfx}; | 
| 107 |  |  |  |  |  |  | $results{$newpfx} = $return_path ? "$dir/$entry/" : undef | 
| 108 | 37 | 50 | 33 |  |  | 140 | if ($return_path || !exists($results{$newpfx})) && $list_prefixes; | 
|  |  | 100 | 66 |  |  |  |  | 
| 109 | 37 | 100 |  |  |  | 137 | push @prefixes, $newpfx if $recurse; | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  | } | 
| 112 | 52 | 50 | 33 |  |  | 659 | next unless $list_pod && $use_pod_dir; | 
| 113 | 0 |  |  |  |  | 0 | $dir = join("/", $dir, "pod"); | 
| 114 | 0 | 0 |  |  |  | 0 | opendir($dh, $dir) or next; | 
| 115 | 0 |  |  |  |  | 0 | while(defined(my $entry = readdir($dh))) { | 
| 116 | 0 | 0 |  |  |  | 0 | if($entry =~ $pod_rx) { | 
| 117 | 0 |  |  |  |  | 0 | my $key = $prefix.$1; | 
| 118 | 0 | 0 | 0 |  |  | 0 | next if $re_wildcard && $key !~ $re_wildcard; | 
| 119 | 0 | 0 |  |  |  | 0 | $results{$key} = $return_path ? "$dir/$entry" : undef; | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  | } | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | # we cannot filter prefixes early with wildcard because we need to dig down | 
| 126 |  |  |  |  |  |  | # first and that would've been prevented if we had a wildcard like *::Foo. | 
| 127 | 12 | 100 | 100 |  |  | 37 | if ($list_prefixes && $re_wildcard) { | 
| 128 | 2 |  |  |  |  | 7 | for my $k (keys %results) { | 
| 129 | 6 | 50 |  |  |  | 18 | next unless $k =~ /::\z/; | 
| 130 | 6 |  |  |  |  | 16 | (my $k_nocolon = $k) =~ s/::\z//; | 
| 131 | 6 | 100 | 100 |  |  | 47 | delete $results{$k} unless $k =~ $re_wildcard || $k_nocolon =~ $re_wildcard; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 12 |  |  |  |  | 64 | return \%results; | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub convert_wildcard_to_re { | 
| 139 | 8 |  |  | 8 | 0 | 9 | $has_globstar = 0; | 
| 140 | 8 |  |  |  |  | 17 | my $re = _convert_wildcard_to_re(@_); | 
| 141 | 8 |  |  |  |  | 147 | $re = qr/\A$re\z/; | 
| 142 |  |  |  |  |  |  | #print "DEBUG: has_globstar=<$has_globstar>, re=$re\n"; | 
| 143 | 8 |  |  |  |  | 20 | $re; | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | # modified from String::Wildcard::Bash 0.040's convert_wildcard_to_re | 
| 147 |  |  |  |  |  |  | sub _convert_wildcard_to_re { | 
| 148 | 8 | 50 |  | 8 |  | 16 | my $opts = ref $_[0] eq 'HASH' ? shift : {}; | 
| 149 | 8 |  |  |  |  | 10 | my $str = shift; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 8 |  | 50 |  |  | 23 | my $opt_brace   = $opts->{brace} // 1; | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 8 |  |  |  |  | 12 | my @res; | 
| 154 |  |  |  |  |  |  | my $p; | 
| 155 | 8 |  |  |  |  | 45 | while ($str =~ /$String::Wildcard::Bash::RE_WILDCARD_BASH/g) { | 
| 156 | 1 |  |  | 1 |  | 88252 | my %m = %+; | 
|  | 1 |  |  |  |  | 330 |  | 
|  | 1 |  |  |  |  | 289 |  | 
|  | 25 |  |  |  |  | 149 |  | 
| 157 | 25 | 50 |  |  |  | 101 | if (defined($p = $m{bash_brace_content})) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | push @res, quotemeta($m{slashes_before_bash_brace}) if | 
| 159 | 0 | 0 |  |  |  | 0 | $m{slashes_before_bash_brace}; | 
| 160 | 0 | 0 |  |  |  | 0 | if ($opt_brace) { | 
| 161 | 0 |  |  |  |  | 0 | my @elems; | 
| 162 | 0 |  |  |  |  | 0 | while ($p =~ /($String::Wildcard::Bash::re_bash_brace_element)(,|\z)/g) { | 
| 163 | 0 |  |  |  |  | 0 | push @elems, $1; | 
| 164 | 0 | 0 |  |  |  | 0 | last unless $2; | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  | #use DD; dd \@elems; | 
| 167 |  |  |  |  |  |  | push @res, "(?:", join("|", map { | 
| 168 | 0 |  |  |  |  | 0 | convert_wildcard_to_re({ | 
|  | 0 |  |  |  |  | 0 |  | 
| 169 |  |  |  |  |  |  | bash_brace => 0, | 
| 170 |  |  |  |  |  |  | }, $_)} @elems), ")"; | 
| 171 |  |  |  |  |  |  | } else { | 
| 172 | 0 |  |  |  |  | 0 | push @res, quotemeta($m{bash_brace}); | 
| 173 |  |  |  |  |  |  | } | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | } elsif (defined($p = $m{bash_joker})) { | 
| 176 | 10 | 50 |  |  |  | 23 | if ($p eq '?') { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 177 | 0 |  |  |  |  | 0 | push @res, '[^:]'; | 
| 178 |  |  |  |  |  |  | } elsif ($p eq '*') { | 
| 179 | 9 |  |  |  |  | 44 | push @res, '[^:]*'; | 
| 180 |  |  |  |  |  |  | } elsif ($p eq '**') { | 
| 181 | 1 |  |  |  |  | 2 | $has_globstar++; | 
| 182 | 1 |  |  |  |  | 6 | push @res, '.*'; | 
| 183 |  |  |  |  |  |  | } | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | } elsif (defined($p = $m{literal_brace_single_element})) { | 
| 186 | 0 |  |  |  |  | 0 | push @res, quotemeta($p); | 
| 187 |  |  |  |  |  |  | } elsif (defined($p = $m{bash_class})) { | 
| 188 |  |  |  |  |  |  | # XXX no need to escape some characters? | 
| 189 | 4 |  |  |  |  | 34 | push @res, $p; | 
| 190 |  |  |  |  |  |  | } elsif (defined($p = $m{sql_joker})) { | 
| 191 | 0 |  |  |  |  | 0 | push @res, quotemeta($p); | 
| 192 |  |  |  |  |  |  | } elsif (defined($p = $m{literal})) { | 
| 193 | 11 |  |  |  |  | 55 | push @res, quotemeta($p); | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 | 8 |  |  |  |  | 27 | join "", @res; | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | 1; | 
| 201 |  |  |  |  |  |  | # ABSTRACT: A fork of Module::List that groks wildcard | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | __END__ |