# HG changeset patch # User Olaf Wintermann # Date 1527836460 -7200 # Node ID 90c6bfa94fa2e388faf479e3d27b651c4f5d6603 # Parent a985a587787f5621bfff12bf95e1608747f877ed moves tag filter to tags.c diff -r a985a587787f -r 90c6bfa94fa2 dav/scfg.h --- a/dav/scfg.h Fri Jun 01 08:52:05 2018 +0200 +++ b/dav/scfg.h Fri Jun 01 09:01:00 2018 +0200 @@ -35,6 +35,7 @@ #include #include "db.h" +#include "tags.h" #ifdef __cplusplus extern "C" { diff -r a985a587787f -r 90c6bfa94fa2 dav/sync.c --- a/dav/sync.c Fri Jun 01 08:52:05 2018 +0200 +++ b/dav/sync.c Fri Jun 01 09:01:00 2018 +0200 @@ -257,282 +257,6 @@ return 1; } -// TODO: use scstr_t after update to UCX 2.0 -static size_t rtrimskip(sstr_t str, size_t skip) { - while (skip < str.length && isspace(str.ptr[skip])) skip++; - return skip; -} - -static size_t parse_tagfilter_taglist(sstr_t fs, SyncTagFilter* tagfilter) { - size_t csvlen; - for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) { - if (fs.ptr[csvlen] == ')') break; - } - fs.length = csvlen; - - tagfilter->tags = parse_csv_taglist(fs.ptr, fs.length); - - return csvlen; -} - -static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* tagfilter); - -static size_t parse_tagfilter_filter(sstr_t fs, SyncTagFilter* tagfilter) { - - size_t consumed = rtrimskip(fs, 0); - fs = sstrsubs(fs, consumed); - - if (fs.length == 0) { - return consumed; - } else { - - // optional operator - int hasop = 0; - if (fs.ptr[0] == '&') { - tagfilter->mode = DAV_SYNC_TAGFILTER_AND; - hasop = 1; - } else if (fs.ptr[0] == '|') { - tagfilter->mode = DAV_SYNC_TAGFILTER_OR; - hasop = 1; - } else if (fs.ptr[0] == '0') { - tagfilter->mode = DAV_SYNC_TAGFILTER_NONE; - hasop = 1; - } else if (fs.ptr[0] == '1') { - tagfilter->mode = DAV_SYNC_TAGFILTER_ONE; - hasop = 1; - } else { - // default operator is AND - tagfilter->mode = DAV_SYNC_TAGFILTER_AND; - } - - if (hasop) { - size_t skip = rtrimskip(fs, 1); - consumed += skip; - fs = sstrsubs(fs, skip); - } - - if (fs.length > 0 && fs.ptr[0] == '(') { - size_t c = parse_tagfilter_subfilters(fs, tagfilter); - if (c) { - return consumed + c; - } else { - return 0; - } - } else { - tagfilter->subfilter_count = 0; - tagfilter->subfilters = NULL; - return consumed + parse_tagfilter_taglist(fs, tagfilter); - } - } -} - -/* - * Parses: ( "(" , filter , ")" )+ - */ -static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* f) { - - // strategy: allocate much and give back later (instead of reallocs in loop) - size_t subfilter_cap = 8; - f->subfilters = calloc(subfilter_cap, sizeof(SyncTagFilter*)); - f->subfilter_count = 0; - - size_t total_consumed = 0; - size_t c; - do { - // skip leading parenthesis (and white spaces) - c = rtrimskip(fs, 1); - fs = sstrsubs(fs, c); - total_consumed += c; - - // increase array capacity, if necessary - if (f->subfilter_count >= subfilter_cap) { - subfilter_cap *= 2; - SyncTagFilter** newarr = realloc(f->subfilters, - subfilter_cap * sizeof(SyncTagFilter*)); - if (newarr) { - f->subfilters = newarr; - } else { - abort(); // no error handling reachable, so we are fucked - } - } - - // allocate space for a new filter - SyncTagFilter* subf = calloc(1, sizeof(SyncTagFilter)); - - // parse that filter - c = parse_tagfilter_filter(fs, subf); - - // sanity check: we must end with a closing parenthesis - if (c > 0 && fs.ptr[c] == ')') { - f->subfilters[f->subfilter_count++] = subf; - - // consume ')' and find the next parenthesis or the end-of-string - c = rtrimskip(fs, 1+c); - fs = sstrsubs(fs, c); - total_consumed += c; - - if (fs.length == 0 || fs.ptr[0] == ')') { - // our job is done - break; - } else if (fs.ptr[0] != '(') { - // anything else than a parenthesis or end-of-string is an error - return 0; - } - } else { - free(subf); - break; - } - - } while(1); - - // try to shrink the array - if (f->subfilter_count > 0) { - SyncTagFilter** shrinked_array = realloc(f->subfilters, - f->subfilter_count * sizeof(SyncTagFilter*)); - if (shrinked_array) { - f->subfilters = shrinked_array; - } - } else { - free(f->subfilters); - f->subfilters = NULL; - } - - return total_consumed; -} - -SyncTagFilter* parse_tagfilter_string(const char* filterstring) { - SyncTagFilter* tagfilter = calloc(1, sizeof(SyncTagFilter)); - if (!filterstring) { - return tagfilter; - } - - // TODO: use scstr_t after update to UCX 2.0 - sstr_t fs = sstr((char*) filterstring); - size_t consumed = parse_tagfilter_filter(fs, tagfilter); - if (!consumed) { - free_tagfilter(tagfilter); - return NULL; - } - - // consume trailing white spaces - consumed = rtrimskip(fs, consumed); - - // sanity check: have we consumed the whole string? - if (consumed != fs.length) { - free_tagfilter(tagfilter); - return NULL; - } - - return tagfilter; -} - -void free_tagfilter(SyncTagFilter* filter) { - for (size_t i = 0 ; i < filter->subfilter_count ; i++) { - free_tagfilter(filter->subfilters[i]); - } - free(filter->subfilters); - free(filter); -} - -static int matches_tags_and(UcxList *dav_tags, UcxList *tags, int ignorecase) { - UCX_FOREACH(e, tags) { - if (!ucx_list_contains(dav_tags, e->data, - (cmp_func) compare_tagname, &ignorecase)) { - return 0; - } - } - return 1; -} - -static int matches_tags_or(UcxList *dav_tags, UcxList *tags, int ignorecase) { - UCX_FOREACH(e, tags) { - if (ucx_list_contains(dav_tags, e->data, - (cmp_func) compare_tagname, &ignorecase)) { - return 1; - } - } - return 0; -} - -static int matches_tags_one(UcxList *dav_tags, UcxList *tags, int ignorecase) { - int matches_exactly_one = 0; - UCX_FOREACH(e, tags) { - if (ucx_list_contains(dav_tags, e->data, - (cmp_func) compare_tagname, &ignorecase)) { - if (matches_exactly_one) { - return 0; - } else { - matches_exactly_one = 1; - } - } - } - return matches_exactly_one; -} - -static int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter); - -static int matches_subfilters_and(UcxList *dav_tags, SyncTagFilter *filter) { - int ret = 1; - for (size_t i = 0 ; i < filter->subfilter_count ; i++) { - ret &= matches_tagfilter(dav_tags, filter->subfilters[i]); - } - return ret; -} - -static int matches_subfilters_or(UcxList *dav_tags, SyncTagFilter *filter) { - int ret = 0; - for (size_t i = 0 ; i < filter->subfilter_count ; i++) { - ret |= matches_tagfilter(dav_tags, filter->subfilters[i]); - } - return ret; -} - -static int matches_subfilters_one(UcxList *dav_tags, SyncTagFilter *filter) { - int one = 0; - for (size_t i = 0 ; i < filter->subfilter_count ; i++) { - if (matches_tagfilter(dav_tags, filter->subfilters[i])) { - if (one) { - return 0; - } else { - one = 1; - } - } - } - return one; -} - -static int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter) { - - if (tagfilter->subfilter_count > 0) { - switch (tagfilter->mode) { - case DAV_SYNC_TAGFILTER_OR: - return matches_subfilters_or(dav_tags, tagfilter); - case DAV_SYNC_TAGFILTER_AND: - return matches_subfilters_and(dav_tags, tagfilter); - case DAV_SYNC_TAGFILTER_NONE: - return !matches_subfilters_or(dav_tags, tagfilter); - case DAV_SYNC_TAGFILTER_ONE: - return matches_subfilters_one(dav_tags, tagfilter); - default: - abort(); - } - } else { - int ignorecase = 0; // TODO: maybe add support later - switch (tagfilter->mode) { - case DAV_SYNC_TAGFILTER_OR: - return matches_tags_or(dav_tags, tagfilter->tags, ignorecase); - case DAV_SYNC_TAGFILTER_AND: - return matches_tags_and(dav_tags, tagfilter->tags, ignorecase); - case DAV_SYNC_TAGFILTER_NONE: - return !matches_tags_or(dav_tags, tagfilter->tags, ignorecase); - case DAV_SYNC_TAGFILTER_ONE: - return matches_tags_one(dav_tags, tagfilter->tags, ignorecase); - default: - abort(); - } - } -} - static int res_matches_tags(DavResource *res, SyncTagFilter *tagfilter) { if(tagfilter->mode == DAV_SYNC_TAGFILTER_OFF) { return 1; diff -r a985a587787f -r 90c6bfa94fa2 dav/sync.h --- a/dav/sync.h Fri Jun 01 08:52:05 2018 +0200 +++ b/dav/sync.h Fri Jun 01 09:01:00 2018 +0200 @@ -113,29 +113,6 @@ int cmd_trash_info(CmdArgs *args); int cmd_empty_trash(CmdArgs *args); -/** - * filter ::= operator? , (tag_list | ("(" , filter , ")")+) - * tag_list ::= tag , ("," tag)* - * operator ::= "&" | "|" | "1" | "0" - */ -typedef struct SyncTagFilter SyncTagFilter; - -/* OFF must be zero, other constants are arbitrary */ -#define DAV_SYNC_TAGFILTER_OFF 0 -#define DAV_SYNC_TAGFILTER_AND 1 -#define DAV_SYNC_TAGFILTER_OR 2 -#define DAV_SYNC_TAGFILTER_ONE 3 -#define DAV_SYNC_TAGFILTER_NONE 4 - -struct SyncTagFilter { - int mode; - UcxList* tags; - size_t subfilter_count; - SyncTagFilter** subfilters; -}; -SyncTagFilter* parse_tagfilter_string(const char* filterstring); -void free_tagfilter(SyncTagFilter* filter); - int cmd_add_tag(CmdArgs *args); int cmd_remove_tag(CmdArgs *args); int cmd_set_tags(CmdArgs *args); diff -r a985a587787f -r 90c6bfa94fa2 dav/tags.c --- a/dav/tags.c Fri Jun 01 08:52:05 2018 +0200 +++ b/dav/tags.c Fri Jun 01 09:01:00 2018 +0200 @@ -28,6 +28,8 @@ #include #include +#include +#include #include #include @@ -324,3 +326,281 @@ } #endif + +/* ----------- ----------- tag filter ---------------------- */ + +// TODO: use scstr_t after update to UCX 2.0 +static size_t rtrimskip(sstr_t str, size_t skip) { + while (skip < str.length && isspace(str.ptr[skip])) skip++; + return skip; +} + +static size_t parse_tagfilter_taglist(sstr_t fs, SyncTagFilter* tagfilter) { + size_t csvlen; + for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) { + if (fs.ptr[csvlen] == ')') break; + } + fs.length = csvlen; + + tagfilter->tags = parse_csv_taglist(fs.ptr, fs.length); + + return csvlen; +} + +static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* tagfilter); + +static size_t parse_tagfilter_filter(sstr_t fs, SyncTagFilter* tagfilter) { + + size_t consumed = rtrimskip(fs, 0); + fs = sstrsubs(fs, consumed); + + if (fs.length == 0) { + return consumed; + } else { + + // optional operator + int hasop = 0; + if (fs.ptr[0] == '&') { + tagfilter->mode = DAV_SYNC_TAGFILTER_AND; + hasop = 1; + } else if (fs.ptr[0] == '|') { + tagfilter->mode = DAV_SYNC_TAGFILTER_OR; + hasop = 1; + } else if (fs.ptr[0] == '0') { + tagfilter->mode = DAV_SYNC_TAGFILTER_NONE; + hasop = 1; + } else if (fs.ptr[0] == '1') { + tagfilter->mode = DAV_SYNC_TAGFILTER_ONE; + hasop = 1; + } else { + // default operator is AND + tagfilter->mode = DAV_SYNC_TAGFILTER_AND; + } + + if (hasop) { + size_t skip = rtrimskip(fs, 1); + consumed += skip; + fs = sstrsubs(fs, skip); + } + + if (fs.length > 0 && fs.ptr[0] == '(') { + size_t c = parse_tagfilter_subfilters(fs, tagfilter); + if (c) { + return consumed + c; + } else { + return 0; + } + } else { + tagfilter->subfilter_count = 0; + tagfilter->subfilters = NULL; + return consumed + parse_tagfilter_taglist(fs, tagfilter); + } + } +} + +/* + * Parses: ( "(" , filter , ")" )+ + */ +static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* f) { + + // strategy: allocate much and give back later (instead of reallocs in loop) + size_t subfilter_cap = 8; + f->subfilters = calloc(subfilter_cap, sizeof(SyncTagFilter*)); + f->subfilter_count = 0; + + size_t total_consumed = 0; + size_t c; + do { + // skip leading parenthesis (and white spaces) + c = rtrimskip(fs, 1); + fs = sstrsubs(fs, c); + total_consumed += c; + + // increase array capacity, if necessary + if (f->subfilter_count >= subfilter_cap) { + subfilter_cap *= 2; + SyncTagFilter** newarr = realloc(f->subfilters, + subfilter_cap * sizeof(SyncTagFilter*)); + if (newarr) { + f->subfilters = newarr; + } else { + abort(); // no error handling reachable, so we are fucked + } + } + + // allocate space for a new filter + SyncTagFilter* subf = calloc(1, sizeof(SyncTagFilter)); + + // parse that filter + c = parse_tagfilter_filter(fs, subf); + + // sanity check: we must end with a closing parenthesis + if (c > 0 && fs.ptr[c] == ')') { + f->subfilters[f->subfilter_count++] = subf; + + // consume ')' and find the next parenthesis or the end-of-string + c = rtrimskip(fs, 1+c); + fs = sstrsubs(fs, c); + total_consumed += c; + + if (fs.length == 0 || fs.ptr[0] == ')') { + // our job is done + break; + } else if (fs.ptr[0] != '(') { + // anything else than a parenthesis or end-of-string is an error + return 0; + } + } else { + free(subf); + break; + } + + } while(1); + + // try to shrink the array + if (f->subfilter_count > 0) { + SyncTagFilter** shrinked_array = realloc(f->subfilters, + f->subfilter_count * sizeof(SyncTagFilter*)); + if (shrinked_array) { + f->subfilters = shrinked_array; + } + } else { + free(f->subfilters); + f->subfilters = NULL; + } + + return total_consumed; +} + +SyncTagFilter* parse_tagfilter_string(const char* filterstring) { + SyncTagFilter* tagfilter = calloc(1, sizeof(SyncTagFilter)); + if (!filterstring) { + return tagfilter; + } + + // TODO: use scstr_t after update to UCX 2.0 + sstr_t fs = sstr((char*) filterstring); + size_t consumed = parse_tagfilter_filter(fs, tagfilter); + if (!consumed) { + free_tagfilter(tagfilter); + return NULL; + } + + // consume trailing white spaces + consumed = rtrimskip(fs, consumed); + + // sanity check: have we consumed the whole string? + if (consumed != fs.length) { + free_tagfilter(tagfilter); + return NULL; + } + + return tagfilter; +} + +void free_tagfilter(SyncTagFilter* filter) { + for (size_t i = 0 ; i < filter->subfilter_count ; i++) { + free_tagfilter(filter->subfilters[i]); + } + free(filter->subfilters); + free(filter); +} + + +static int matches_tags_and(UcxList *dav_tags, UcxList *tags, int ignorecase) { + UCX_FOREACH(e, tags) { + if (!ucx_list_contains(dav_tags, e->data, + (cmp_func) compare_tagname, &ignorecase)) { + return 0; + } + } + return 1; +} + +static int matches_tags_or(UcxList *dav_tags, UcxList *tags, int ignorecase) { + UCX_FOREACH(e, tags) { + if (ucx_list_contains(dav_tags, e->data, + (cmp_func) compare_tagname, &ignorecase)) { + return 1; + } + } + return 0; +} + +static int matches_tags_one(UcxList *dav_tags, UcxList *tags, int ignorecase) { + int matches_exactly_one = 0; + UCX_FOREACH(e, tags) { + if (ucx_list_contains(dav_tags, e->data, + (cmp_func) compare_tagname, &ignorecase)) { + if (matches_exactly_one) { + return 0; + } else { + matches_exactly_one = 1; + } + } + } + return matches_exactly_one; +} + +static int matches_subfilters_and(UcxList *dav_tags, SyncTagFilter *filter) { + int ret = 1; + for (size_t i = 0 ; i < filter->subfilter_count ; i++) { + ret &= matches_tagfilter(dav_tags, filter->subfilters[i]); + } + return ret; +} + +static int matches_subfilters_or(UcxList *dav_tags, SyncTagFilter *filter) { + int ret = 0; + for (size_t i = 0 ; i < filter->subfilter_count ; i++) { + ret |= matches_tagfilter(dav_tags, filter->subfilters[i]); + } + return ret; +} + +static int matches_subfilters_one(UcxList *dav_tags, SyncTagFilter *filter) { + int one = 0; + for (size_t i = 0 ; i < filter->subfilter_count ; i++) { + if (matches_tagfilter(dav_tags, filter->subfilters[i])) { + if (one) { + return 0; + } else { + one = 1; + } + } + } + return one; +} + +int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter) { + + if (tagfilter->subfilter_count > 0) { + switch (tagfilter->mode) { + case DAV_SYNC_TAGFILTER_OR: + return matches_subfilters_or(dav_tags, tagfilter); + case DAV_SYNC_TAGFILTER_AND: + return matches_subfilters_and(dav_tags, tagfilter); + case DAV_SYNC_TAGFILTER_NONE: + return !matches_subfilters_or(dav_tags, tagfilter); + case DAV_SYNC_TAGFILTER_ONE: + return matches_subfilters_one(dav_tags, tagfilter); + default: + abort(); + } + } else { + int ignorecase = 0; // TODO: maybe add support later + switch (tagfilter->mode) { + case DAV_SYNC_TAGFILTER_OR: + return matches_tags_or(dav_tags, tagfilter->tags, ignorecase); + case DAV_SYNC_TAGFILTER_AND: + return matches_tags_and(dav_tags, tagfilter->tags, ignorecase); + case DAV_SYNC_TAGFILTER_NONE: + return !matches_tags_or(dav_tags, tagfilter->tags, ignorecase); + case DAV_SYNC_TAGFILTER_ONE: + return matches_tags_one(dav_tags, tagfilter->tags, ignorecase); + default: + abort(); + } + } +} + diff -r a985a587787f -r 90c6bfa94fa2 dav/tags.h --- a/dav/tags.h Fri Jun 01 08:52:05 2018 +0200 +++ b/dav/tags.h Fri Jun 01 09:01:00 2018 +0200 @@ -39,11 +39,33 @@ extern "C" { #endif + +/* OFF must be zero, other constants are arbitrary */ +#define DAV_SYNC_TAGFILTER_OFF 0 +#define DAV_SYNC_TAGFILTER_AND 1 +#define DAV_SYNC_TAGFILTER_OR 2 +#define DAV_SYNC_TAGFILTER_ONE 3 +#define DAV_SYNC_TAGFILTER_NONE 4 + typedef struct DavTag { char *name; char *color; } DavTag; +/** + * filter ::= operator? , (tag_list | ("(" , filter , ")")+) + * tag_list ::= tag , ("," tag)* + * operator ::= "&" | "|" | "1" | "0" + */ +typedef struct SyncTagFilter SyncTagFilter; + +struct SyncTagFilter { + int mode; + UcxList* tags; + size_t subfilter_count; + SyncTagFilter** subfilters; +}; + void free_dav_tag(DavTag* tag); int compare_tagname(DavTag* left, DavTag* right, void* ignorecase); @@ -60,6 +82,14 @@ UcxList* parse_macos_taglist(const char *buf, size_t length); UcxBuffer* create_macos_taglist(UcxList *tags); + +/* ----------- ----------- tag filter ---------------------- */ + +SyncTagFilter* parse_tagfilter_string(const char* filterstring); +void free_tagfilter(SyncTagFilter* filter); + +int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter); + #ifdef __cplusplus } #endif