From cde5f4c9174c3d68fd3f1c845659f1e7779f91ff Mon Sep 17 00:00:00 2001 From: Ron Pedde Date: Mon, 28 Feb 2005 20:28:44 +0000 Subject: [PATCH] Add Timo's match patch --- contrib/mt-daapd.playlist | 10 ++++---- src/lexer.l | 1 + src/parser.y | 2 ++ src/playlist.c | 49 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/contrib/mt-daapd.playlist b/contrib/mt-daapd.playlist index a95030fe..033bf97f 100644 --- a/contrib/mt-daapd.playlist +++ b/contrib/mt-daapd.playlist @@ -28,13 +28,15 @@ # Date (date) # # Valid operators include: -# is, includes (string) +# is, includes (string), matches (string) # >, <, <=, >=, = (int) # after, before (date) # -# the "is" operator must exactly match the tag, -# while the "includes" operator matches a substring. -# Both matches are case-insensitive +# the "is" operator must exactly match the tag, while +# the "includes" operator matches a substring and +# "matches" operator makes trivial wildcard match using +# "?" and "*" as wildcards for any character and any +# string accordingly. All matches are case-insensitive. # # Valid operands include: # "string value" (string) diff --git a/src/lexer.l b/src/lexer.l index fda3f9a3..ca7403a6 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -81,6 +81,7 @@ after { yylval.ival=AFTER; return(AFTER); } is { yylval.ival=IS; return(IS); } includes { yylval.ival=INCLUDES; return(INCLUDES); } +matches { yylval.ival=INCLUDES; return(MATCHES); } = { yylval.ival=EQUALS; return(EQUALS); } or | diff --git a/src/parser.y b/src/parser.y index 92211f37..a0cafe76 100644 --- a/src/parser.y +++ b/src/parser.y @@ -72,6 +72,7 @@ int pl_number=2; %token GREATEREQUAL %token IS %token INCLUDES +%token MATCHES %token OR %token AND @@ -168,6 +169,7 @@ strtag: ARTIST strbool: IS { $$=$1; } | INCLUDES { $$=$1; } +| MATCHES { $$=$1; } | NOT strbool { $$=$2 | 0x80000000; } ; diff --git a/src/playlist.c b/src/playlist.c index 15f86efb..49d2fbf4 100644 --- a/src/playlist.c +++ b/src/playlist.c @@ -48,6 +48,48 @@ int pl_eval_node(MP3FILE *pmp3, PL_NODE *pnode); extern FILE *yyin; +#define MATCH_WILD_CHR '?' +#define MATCH_WILD_STR '*' +#define MATCH_CHAR_EQ(m, c, case_sensitive) \ + (((m) == (c)) || \ + ((! case_sensitive) && \ + ((isalpha((m)) ? toupper((m)) : (m)) == \ + (isalpha((c)) ? toupper((c)) : (c))))) + +/* + * match + * + * Trivial pattern matcher. + * - '*' matches to any (sub)string + * - '?' matches to any character + * Returs non-zero on match. + */ +static int match(char *mask, char *str, int case_sensitive) +{ + unsigned char *s, *m; + + m = (unsigned char *)mask; + s = (unsigned char *)str; + while (*m != '\0') { + if ((! MATCH_CHAR_EQ(*m, *s, case_sensitive)) && + (*m != MATCH_WILD_CHR) && (*m != MATCH_WILD_STR)) { + return 0; + } else if (*m == MATCH_WILD_STR) { + m++; + while (*s) { + if (match((char *)m, (char *)s, case_sensitive)) + return 1; + s++; + } + return ((!(*m != '\0')) ? 1 : 0); + } else { + s++; + m++; + } + } + return ((!(*s != '\0')) ? 1 : 0); +} + /* * pl_dump * @@ -150,6 +192,9 @@ void pl_dump_node(PL_NODE *pnode, int indent) { case INCLUDES: printf("%s",not? "DOES NOT INCLUDE " : "INCLUDES "); break; + case MATCHES: + printf("%s",not? "DOES NOT MATCH " : "MATCHES "); + break; case EQUALS: printf("EQUALS "); break; @@ -355,6 +400,10 @@ int pl_eval_node(MP3FILE *pmp3, PL_NODE *pnode) { r_arg=(int)strcasestr(cval,pnode->arg2.cval); retval = not ? !r_arg : r_arg; break; + case MATCHES: + r_arg = match(pnode->arg2.cval, cval, 0); + retval = not ? r_arg : !r_arg; + break; } }