File Coverage

blib/lib/App/optex/glob.pm
Criterion Covered Total %
statement 29 61 47.5
branch 0 18 0.0
condition n/a
subroutine 10 15 66.6
pod 0 2 0.0
total 39 96 40.6


line stmt bran cond sub pod time code
1             package App::optex::glob;
2              
3             our $VERSION = '1.01';
4              
5 1     1   238096 use v5.14;
  1         3  
6 1     1   4 use warnings;
  1         2  
  1         64  
7 1     1   561 use utf8;
  1         211  
  1         4  
8 1     1   502 use Data::Dumper;
  1         8438  
  1         99  
9              
10 1     1   7 use List::Util qw(first);
  1         1  
  1         95  
11 1     1   748 use Hash::Util qw(lock_keys);
  1         4997  
  1         9  
12 1     1   774 use Text::Glob qw(glob_to_regex);
  1         1129  
  1         74  
13 1     1   10 use File::Basename qw(basename);
  1         2  
  1         60  
14              
15 1     1   726 use Getopt::EX::Config qw(config);
  1         21056  
  1         9  
16             my $config = Getopt::EX::Config->new(
17             regex => undef,
18             path => undef,
19             include => \my @include,
20             exclude => \my @exclude,
21             debug => undef,
22             );
23             lock_keys %$config;
24              
25 1     1   125 use List::Util qw(pairmap);
  1         3  
  1         846  
26              
27             sub hash_to_spec {
28             pairmap {
29 0     0     $a = "$a|${\(uc(substr($a,0,1)))}";
  0            
30 0 0         if (not defined $b) { "$a" }
  0 0          
31 0           elsif ($b =~ /^\d+$/) { "$a=i" }
32 0           else { "$a=s" }
33 0     0 0   } %{+shift};
  0            
34             }
35              
36             sub finalize {
37 0     0 0   my($mod, $argv) = @_;
38             $config->deal_with(
39             $argv,
40             hash_to_spec($config),
41             '<>' => sub {
42 0     0     my $pattern = shift;
43 0 0         if ($pattern =~ s/^!//) {
44 0           push @exclude, $pattern;
45             } else {
46 0           push @include, $pattern;
47             }
48             },
49 0           );
50 0 0         return if @include + @exclude == 0;
51              
52 0           my(@include_re, @exclude_re);
53 0           for ( [ \@include_re, \@include ] ,
54             [ \@exclude_re, \@exclude ] ) {
55 0           my($a, $b) = @$_;
56 0           @$a = do {
57 0 0         if (config('regex')) {
58 0           map qr/$_/, @$b;
59             } else {
60 0           map glob_to_regex($_), @$b;
61             }
62             };
63             }
64              
65             my $test = sub {
66 0     0     local $_ = shift;
67 0 0         -e or return 1;
68 0 0         $_ = basename($_) if not config('path');
69 0 0         for my $re (@exclude_re) { /$re/ and return 0 }
  0            
70 0 0         for my $re (@include_re) { /$re/ and return 1 }
  0            
71 0           return @include_re == 0;
72 0           };
73              
74 0           @$argv = grep $test->($_), @$argv;
75             }
76              
77             1;
78              
79             =encoding utf-8
80              
81             =head1 NAME
82              
83             glob - optex filter to glob filenames
84              
85             =head1 SYNOPSIS
86              
87             optex -Mglob [ option ] pattern -- command
88              
89             =head1 DESCRIPTION
90              
91             This module is used to select filenames given as arguments by pattern.
92              
93             For example, the following will pass only files matching C<*.c> from
94             C<*/*> as arguments to C.
95              
96             optex -Mglob '*.c' -- ls -l */*
97              
98             Only existing file names will be selected. Any arguments that do not
99             correspond to files will be passed through as is. In this example,
100             the command name and options remain as they are because no
101             corresponding file exists. Be aware that the existence of a
102             corresponding file for unexpected parameter could lead to confusing
103             results.
104              
105             There are several unique options that are valid only for this module.
106              
107             =over 7
108              
109             =item BI
110              
111             =item B<--exclude> I
112              
113             Option C<--exclude> will mean the opposite.
114              
115             optex -Mglob --exclude '*.c' -- ls */*
116              
117             Preceding pattern with C will also exclude the pattern.
118              
119             optex -Mglob '!*.c' -- ls */*
120              
121             If the C<--exclude> option is used with positive patterns, the exclude
122             pattern takes precedence. The following command selects files
123             matching C<*.c>, but excludes those begin with a capital letter.
124              
125             optex -Mglob --exclude '[A-Z]*' '*.c' -- ls */*
126              
127             This opiton can be used multiple times.
128              
129             =item B<--regex>
130              
131             If the C<--regex> option is given, patterns are evaluated as a regular
132             expression instead of a glob pattern.
133              
134             optex -Mglob --regex '\.c$' -- ls */*
135              
136             =item B<--path>
137              
138             With the C<--path> option it matches against the entire path, not just
139             the filename.
140              
141             optex -Mglob --path '^*_test/' -- ls */*
142              
143             =back
144              
145             =head1 CONSIDERATION
146              
147             You should also consider using the extended globbing (extglob) feature
148             of L or similar. For example, you can use C,
149             which would specify files matching C<*.md> minus those matching
150             C<*.EN.md>.
151              
152             =head1 AUTHOR
153              
154             Kazumasa Utashiro
155              
156             =head1 LICENSE
157              
158             Copyright ©︎ 2024-2025 Kazumasa Utashiro.
159              
160             This library is free software; you can redistribute it and/or modify
161             it under the same terms as Perl itself.
162              
163             =cut
164              
165             __DATA__