FileKGlob
view release on metacpan or search on metacpan
require File::KGlob2RE;
# &glob( "pat" [, ...] ) - Expands Unix file glob(s) into the list of
# matching Unix files. The following contructs are supported:
# \x matches x
# [abc] matches the single character "a", "b", or "c"
# [a-c] same as above
# [^a-c] matches any single character but "a", "b", "c", "/", or "\0"
# ? matches any single character except "/" and "\0"
# * matches zero or more characters not including "/" and "\0"
# {X,Y,Z} matches one of the patterns X, Y, or Z
%GlobContext= ();
sub glob {
my( $pkg, $file, $line )= caller;
if( ! $GlobContext{$file,$line} ) {
my( @list )= &kglob( @_ );
@list= sort( @list ) if $Sort;
$GlobContext{$file,$line}= \@list;
}
if( wantarray ) {
my( @return )= @{$GlobContext{$file,$line}};
delete $GlobContext{$file,$line};
@return;
} else {
my( $return )= shift( @{$GlobContext{$file,$line}} );
delete $GlobContext{$file,$line} if ! defined($return);
$return;
}
}
# &kglob() always returns an array of matches; the complexity of the
# algorythm would require a great deal of saved context to allow each
# match to be returned separately like is possible with &fglob().
# The array of values is not necessarilly sorted (that is easy enough
# to do if you want it so we won't waste the time to do it in case you
# don't want to).
# kglob may suprise you in the following ways:
# - {a,b} expands to ("a","b") even if files "a" and/or "b" do not exist
# - [^a-z] is supported (any character except a through z, /, and \0)
# - a leading dot (.) in any component of the path must be matched
# explicitly (with a dot, not with [^a-z], nor [.x], etc.)
# - {.,x}* matches .* (as well as x*)
# - setting $File::KGlob::Safe to a true value prevents "." and ".."
# in any component of the path from matching except exactly (by the
# pattern "." or "..")
# - \x is supported (expands to just "x")
# - % is not support (just matches "%") but File::KGlob2RE supports it
# - ~user and ~/ are supported as is ~{user1,user2} etc.
sub kglob {
my( @alts, @return, $user, $home );
foreach( @_ ) {
# If unquoted "{" in string, generate all possible combinations: #}
@alts= m#(^|[^\\])(\\\\)*\{# ? &unbrac( $_ ) : ( $_ ); #}
foreach( @alts ) {
if( m#^~([^/]+)# ) { # Expand ~user to user's home directory:
$user= $1 || getlogin(); # ~/ means "my" home directory
$home= $1 ? ( (getpwnam($1))[7] || "~$user" )
: ( (getpwuid($<))[7] || $ENV{'HOME'} || "/" );
s##$home#;
# Replace "~user" with user's home directory (unless no such
# user, then leave as is), unless is "~/" and getlogin()
# failed, then try by current UID then $HOME then "/".
}
if( m#(^|[^\\])(\\\\)*[\[\?\*]# ) { # Some kind of wildcard:
push( @return, &pglob($_) ); # Find matching files.
} else { # Just a string, perhaps with \-quoting:
s/\\(.)/\1/g; # Remove the \'s used for quoting.
push( @return, $_ );
}
}
}
@return;
}
# &unbrac( $str ) - Expands a string containing "{a,b}" constructs. Returns
# an array of strings. "\" may be used to quote "{", ",", or "}" to suppress
# its special meaning (the "\"s are left in the returned strings).
# This is a more efficient method than &glob() to expand these contructs
# where no file wildcards are involved.
sub unbrac {
local( $glob )= @_;
local( $pos, $bef, @bef, $temp, $mid, @mid, $aft, @aft, @return );
$pos= rindex($glob,"{"); # Find the last "{" #}}
while( 0 <= $pos ) { # Until there are no more "{"s to find: #}
$bef= substr( $glob, 0, $pos ); # Part before "{" #}
$temp= substr( $glob, 1 + $pos ); # Part after "{" #}
if( $bef =~ m#(^|[^\\])(\\\\)*$# ) { # The "{" is unquoted: #}{
$pos= index( $temp, "}" ); #{ Find the next nearest "}"
while( 0 <= $pos ) { #{ Until we run out of "}"s:
$mid= substr( $temp, 0, $pos ); # Part between "{" and "}" #{
$aft= substr( $temp, 1 + $pos ); # Part after "}"
if( $mid =~ m#(^|[^\\])(\\\\)*$# ) { #{ The "}" is unquoted:
$mid =~ s/((^|[^\\])(\\\\))*,/\1\0/g; # Most unquoted ","s
$mid =~ s/((^|[^\\])(\\\\))*,/\1\0/g; # Remaining ones
return &mcat( $bef, $aft, split(/\0/,$mid) ); # Done!
} # &mcat builds all of the resulting strings.
} # &mcat also "unbrac"s $bef and $aft.
if( $Debug ) {
die "Unclosed `{' in pattern string: `", #}
$bef, "' . `{' . `", $aft, "'\n"; #}
}
}
$pos= rindex( $glob, "{", $pos - 1 ); #}
}
( $glob ); # No unquoted "{"s to be expanded #}
}
# &File::KGlob::mcat( $bef, $aft, @mids ) - Used by &unbrac to make the code
# easier to follow. Builds all of the strings $bef . $mids[$i] . $aft and
# then calls &unbrac on each of them.
sub mcat {
local( $bef, $aft, @mid )= @_;
local( @bef, @aft, $one, $two, $three, @return );
foreach( @mid ) {
push( @return, &unbrac( $bef . $_ . $aft ) );
}
( run in 2.398 seconds using v1.01-cache-2.11-cpan-99c4e6809bf )