diff options
author | Andrey Nazarov <skuller@skuller.net> | 2010-10-09 17:37:46 +0400 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2010-10-09 17:37:46 +0400 |
commit | d66fa13fe464e3504cd488a3884b9410623deeba (patch) | |
tree | 7ac4bb67319dbe85f7b8e5b395d44d8ea338fe04 /src/common.c | |
parent | 1f476c8de03427883ab76c504d4949dc4fa894c7 (diff) |
Avoid recursion in wildcard comparsion code.
Diffstat (limited to 'src/common.c')
-rw-r--r-- | src/common.c | 137 |
1 files changed, 113 insertions, 24 deletions
diff --git a/src/common.c b/src/common.c index 5bd1914..e6c803a 100644 --- a/src/common.c +++ b/src/common.c @@ -1140,6 +1140,119 @@ int BoxOnPlaneSide( vec3_t emins, vec3_t emaxs, cplane_t *p ) { } #endif // USE_ASM +/* +============================================================================== + + WILDCARD COMPARE + +============================================================================== +*/ + +static qboolean match_char( int c1, int c2, qboolean ignorecase ) { + if( c1 == '?' ) { + return !!c2; // match any char except NUL + } + + if( c1 != c2 ) { + if( !ignorecase ) { + return qfalse; + } +#ifdef _WIN32 + // ugly hack for file listing + c1 = c1 == '\\' ? '/' : Q_tolower( c1 ); + c2 = c2 == '\\' ? '/' : Q_tolower( c2 ); +#else + c1 = Q_tolower( c1 ); + c2 = Q_tolower( c2 ); +#endif + if( c1 != c2 ) { + return qfalse; + } + } + + return qtrue; +} + +static qboolean match_part( const char *filter, const char *string, size_t len, qboolean ignorecase ) { + do { + int c1 = *filter++; + int c2 = *string++; + + if( !match_char( c1, c2, ignorecase ) ) { + return qfalse; + } + } while( --len ); + + return qtrue; +} + +// match the longest possible part +static const char *match_filter( const char *filter, const char *string, size_t len, qboolean ignorecase ) { + const char *ret = NULL; + size_t remaining = strlen( string ); + + while( remaining >= len ) { + if( match_part( filter, string, len, ignorecase ) ) { + string += len; + remaining -= len; + ret = string; + continue; + } + string++; + remaining--; + } + + return ret; +} + +/* +================= +Com_WildCmpEx + +Wildcard compare. +Returns non-zero if matches, zero otherwise. +================= +*/ +qboolean Com_WildCmpEx( const char *filter, const char *string, int term, qboolean ignorecase ) { + const char *sub; + size_t len; + + while( *filter && *filter != term ) { + if( *filter == '*' ) { + // skip consecutive wildcards + do { + filter++; + } while( *filter == '*' ); + + // wildcard at the end matches everything + if( !*filter || *filter == term ) { + return qtrue; + } + + // scan out filter part to match + sub = filter; len = 0; + do { + filter++; len++; + } while( *filter && *filter != term && *filter != '*' ); + + string = match_filter( sub, string, len, ignorecase ); + if( !string ) { + return qfalse; + } + } else { + int c1 = *filter++; + int c2 = *string++; + + // match single character + if( !match_char( c1, c2, ignorecase ) ) { + return qfalse; + } + } + } + + // match NUL at the end + return !*string; +} /* ============================================================================== @@ -1201,30 +1314,6 @@ void Com_PlayerToEntityState( const player_state_t *ps, entity_state_t *es ) { } /* -================= -Com_WildCmp - -Wildcard compare. -Returns non-zero if matches, zero otherwise. -================= -*/ -int Com_WildCmp( const char *filter, const char *string, qboolean ignoreCase ) { - switch( *filter ) { - case '\0': - return !*string; - - case '*': - return Com_WildCmp( filter + 1, string, ignoreCase ) || (*string && Com_WildCmp( filter, string + 1, ignoreCase )); - - case '?': - return *string && Com_WildCmp( filter + 1, string + 1, ignoreCase ); - - default: - return ((*filter == *string) || (ignoreCase && (Q_toupper( *filter ) == Q_toupper( *string )))) && Com_WildCmp( filter + 1, string + 1, ignoreCase ); - } -} - -/* ================ Com_HashString ================ |