2014-08-07 03:09:23 +04:00
# include <linux/module.h>
# include <linux/glob.h>
/*
* The only reason this code can be compiled as a module is because the
* ATA code that depends on it can be as well . In practice , they ' re
* both usually compiled in and the module overhead goes away .
*/
MODULE_DESCRIPTION ( " glob(7) matching " ) ;
MODULE_LICENSE ( " Dual MIT/GPL " ) ;
/**
* glob_match - Shell - style pattern matching , like ! fnmatch ( pat , str , 0 )
* @ pat : Shell - style pattern to match , e . g . " *.[ch] " .
* @ str : String to match . The pattern must match the entire string .
*
* Perform shell - style glob matching , returning true ( 1 ) if the match
* succeeds , or false ( 0 ) if it fails . Equivalent to ! fnmatch ( @ pat , @ str , 0 ) .
*
* Pattern metacharacters are ? , * , [ and \ .
* ( And , inside character classes , ! , - and ] . )
*
* This is small and simple implementation intended for device blacklists
* where a string is matched against a number of patterns . Thus , it
* does not preprocess the patterns . It is non - recursive , and run - time
* is at most quadratic : strlen ( @ str ) * strlen ( @ pat ) .
*
* An example of the worst case is glob_match ( " *aaaaa " , " aaaaaaaaaa " ) ;
* it takes 6 passes over the pattern before matching the string .
*
* Like ! fnmatch ( @ pat , @ str , 0 ) and unlike the shell , this does NOT
* treat / or leading . specially ; it isn ' t actually used for pathnames .
*
* Note that according to glob ( 7 ) ( and unlike bash ) , character classes
* are complemented by a leading ! ; this does not support the regex - style
* [ ^ a - z ] syntax .
*
* An opening bracket without a matching close is matched literally .
*/
bool __pure glob_match ( char const * pat , char const * str )
{
/*
* Backtrack to previous * on mismatch and retry starting one
* character later in the string . Because * matches all characters
* ( no exception for / ) , it can be easily proved that there ' s
* never a need to backtrack multiple levels .
*/
char const * back_pat = NULL , * back_str = back_str ;
/*
* Loop over each token ( character or class ) in pat , matching
* it against the remaining unmatched tail of str . Return false
* on mismatch , or true after matching the trailing nul bytes .
*/
for ( ; ; ) {
unsigned char c = * str + + ;
unsigned char d = * pat + + ;
switch ( d ) {
case ' ? ' : /* Wildcard: anything but nul */
if ( c = = ' \0 ' )
return false ;
break ;
case ' * ' : /* Any-length wildcard */
if ( * pat = = ' \0 ' ) /* Optimize trailing * case */
return true ;
back_pat = pat ;
back_str = - - str ; /* Allow zero-length match */
break ;
case ' [ ' : { /* Character class */
bool match = false , inverted = ( * pat = = ' ! ' ) ;
char const * class = pat + inverted ;
unsigned char a = * class + + ;
/*
* Iterate over each span in the character class .
* A span is either a single character a , or a
* range a - b . The first span may begin with ' ] ' .
*/
do {
unsigned char b = a ;
if ( a = = ' \0 ' ) /* Malformed */
goto literal ;
if ( class [ 0 ] = = ' - ' & & class [ 1 ] ! = ' ] ' ) {
b = class [ 1 ] ;
if ( b = = ' \0 ' )
goto literal ;
class + = 2 ;
/* Any special action if a > b? */
}
match | = ( a < = c & & c < = b ) ;
} while ( ( a = * class + + ) ! = ' ] ' ) ;
if ( match = = inverted )
goto backtrack ;
pat = class ;
}
break ;
case ' \\ ' :
d = * pat + + ;
2020-11-16 07:35:31 +03:00
fallthrough ;
2014-08-07 03:09:23 +04:00
default : /* Literal character */
literal :
if ( c = = d ) {
if ( d = = ' \0 ' )
return true ;
break ;
}
backtrack :
if ( c = = ' \0 ' | | ! back_pat )
return false ; /* No point continuing */
/* Try again from last *, one character later in str. */
pat = back_pat ;
str = + + back_str ;
break ;
}
}
}
EXPORT_SYMBOL ( glob_match ) ;