dav/tags.c

changeset 400
90c6bfa94fa2
parent 393
438c8fe7d62f
child 403
8e1948eebef5
equal deleted inserted replaced
399:a985a587787f 400:90c6bfa94fa2
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29 #include <stdio.h> 29 #include <stdio.h>
30 #include <stdlib.h> 30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
31 33
32 #include <ucx/string.h> 34 #include <ucx/string.h>
33 #include <ucx/utils.h> 35 #include <ucx/utils.h>
34 36
35 #include "libxattr.h" 37 #include "libxattr.h"
322 fprintf(stderr, "Error: macos tags not supported on this platform.\n"); 324 fprintf(stderr, "Error: macos tags not supported on this platform.\n");
323 return NULL; 325 return NULL;
324 } 326 }
325 #endif 327 #endif
326 328
329
330 /* ----------- ----------- tag filter ---------------------- */
331
332 // TODO: use scstr_t after update to UCX 2.0
333 static size_t rtrimskip(sstr_t str, size_t skip) {
334 while (skip < str.length && isspace(str.ptr[skip])) skip++;
335 return skip;
336 }
337
338 static size_t parse_tagfilter_taglist(sstr_t fs, SyncTagFilter* tagfilter) {
339 size_t csvlen;
340 for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) {
341 if (fs.ptr[csvlen] == ')') break;
342 }
343 fs.length = csvlen;
344
345 tagfilter->tags = parse_csv_taglist(fs.ptr, fs.length);
346
347 return csvlen;
348 }
349
350 static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* tagfilter);
351
352 static size_t parse_tagfilter_filter(sstr_t fs, SyncTagFilter* tagfilter) {
353
354 size_t consumed = rtrimskip(fs, 0);
355 fs = sstrsubs(fs, consumed);
356
357 if (fs.length == 0) {
358 return consumed;
359 } else {
360
361 // optional operator
362 int hasop = 0;
363 if (fs.ptr[0] == '&') {
364 tagfilter->mode = DAV_SYNC_TAGFILTER_AND;
365 hasop = 1;
366 } else if (fs.ptr[0] == '|') {
367 tagfilter->mode = DAV_SYNC_TAGFILTER_OR;
368 hasop = 1;
369 } else if (fs.ptr[0] == '0') {
370 tagfilter->mode = DAV_SYNC_TAGFILTER_NONE;
371 hasop = 1;
372 } else if (fs.ptr[0] == '1') {
373 tagfilter->mode = DAV_SYNC_TAGFILTER_ONE;
374 hasop = 1;
375 } else {
376 // default operator is AND
377 tagfilter->mode = DAV_SYNC_TAGFILTER_AND;
378 }
379
380 if (hasop) {
381 size_t skip = rtrimskip(fs, 1);
382 consumed += skip;
383 fs = sstrsubs(fs, skip);
384 }
385
386 if (fs.length > 0 && fs.ptr[0] == '(') {
387 size_t c = parse_tagfilter_subfilters(fs, tagfilter);
388 if (c) {
389 return consumed + c;
390 } else {
391 return 0;
392 }
393 } else {
394 tagfilter->subfilter_count = 0;
395 tagfilter->subfilters = NULL;
396 return consumed + parse_tagfilter_taglist(fs, tagfilter);
397 }
398 }
399 }
400
401 /*
402 * Parses: ( "(" , filter , ")" )+
403 */
404 static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* f) {
405
406 // strategy: allocate much and give back later (instead of reallocs in loop)
407 size_t subfilter_cap = 8;
408 f->subfilters = calloc(subfilter_cap, sizeof(SyncTagFilter*));
409 f->subfilter_count = 0;
410
411 size_t total_consumed = 0;
412 size_t c;
413 do {
414 // skip leading parenthesis (and white spaces)
415 c = rtrimskip(fs, 1);
416 fs = sstrsubs(fs, c);
417 total_consumed += c;
418
419 // increase array capacity, if necessary
420 if (f->subfilter_count >= subfilter_cap) {
421 subfilter_cap *= 2;
422 SyncTagFilter** newarr = realloc(f->subfilters,
423 subfilter_cap * sizeof(SyncTagFilter*));
424 if (newarr) {
425 f->subfilters = newarr;
426 } else {
427 abort(); // no error handling reachable, so we are fucked
428 }
429 }
430
431 // allocate space for a new filter
432 SyncTagFilter* subf = calloc(1, sizeof(SyncTagFilter));
433
434 // parse that filter
435 c = parse_tagfilter_filter(fs, subf);
436
437 // sanity check: we must end with a closing parenthesis
438 if (c > 0 && fs.ptr[c] == ')') {
439 f->subfilters[f->subfilter_count++] = subf;
440
441 // consume ')' and find the next parenthesis or the end-of-string
442 c = rtrimskip(fs, 1+c);
443 fs = sstrsubs(fs, c);
444 total_consumed += c;
445
446 if (fs.length == 0 || fs.ptr[0] == ')') {
447 // our job is done
448 break;
449 } else if (fs.ptr[0] != '(') {
450 // anything else than a parenthesis or end-of-string is an error
451 return 0;
452 }
453 } else {
454 free(subf);
455 break;
456 }
457
458 } while(1);
459
460 // try to shrink the array
461 if (f->subfilter_count > 0) {
462 SyncTagFilter** shrinked_array = realloc(f->subfilters,
463 f->subfilter_count * sizeof(SyncTagFilter*));
464 if (shrinked_array) {
465 f->subfilters = shrinked_array;
466 }
467 } else {
468 free(f->subfilters);
469 f->subfilters = NULL;
470 }
471
472 return total_consumed;
473 }
474
475 SyncTagFilter* parse_tagfilter_string(const char* filterstring) {
476 SyncTagFilter* tagfilter = calloc(1, sizeof(SyncTagFilter));
477 if (!filterstring) {
478 return tagfilter;
479 }
480
481 // TODO: use scstr_t after update to UCX 2.0
482 sstr_t fs = sstr((char*) filterstring);
483 size_t consumed = parse_tagfilter_filter(fs, tagfilter);
484 if (!consumed) {
485 free_tagfilter(tagfilter);
486 return NULL;
487 }
488
489 // consume trailing white spaces
490 consumed = rtrimskip(fs, consumed);
491
492 // sanity check: have we consumed the whole string?
493 if (consumed != fs.length) {
494 free_tagfilter(tagfilter);
495 return NULL;
496 }
497
498 return tagfilter;
499 }
500
501 void free_tagfilter(SyncTagFilter* filter) {
502 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
503 free_tagfilter(filter->subfilters[i]);
504 }
505 free(filter->subfilters);
506 free(filter);
507 }
508
509
510 static int matches_tags_and(UcxList *dav_tags, UcxList *tags, int ignorecase) {
511 UCX_FOREACH(e, tags) {
512 if (!ucx_list_contains(dav_tags, e->data,
513 (cmp_func) compare_tagname, &ignorecase)) {
514 return 0;
515 }
516 }
517 return 1;
518 }
519
520 static int matches_tags_or(UcxList *dav_tags, UcxList *tags, int ignorecase) {
521 UCX_FOREACH(e, tags) {
522 if (ucx_list_contains(dav_tags, e->data,
523 (cmp_func) compare_tagname, &ignorecase)) {
524 return 1;
525 }
526 }
527 return 0;
528 }
529
530 static int matches_tags_one(UcxList *dav_tags, UcxList *tags, int ignorecase) {
531 int matches_exactly_one = 0;
532 UCX_FOREACH(e, tags) {
533 if (ucx_list_contains(dav_tags, e->data,
534 (cmp_func) compare_tagname, &ignorecase)) {
535 if (matches_exactly_one) {
536 return 0;
537 } else {
538 matches_exactly_one = 1;
539 }
540 }
541 }
542 return matches_exactly_one;
543 }
544
545 static int matches_subfilters_and(UcxList *dav_tags, SyncTagFilter *filter) {
546 int ret = 1;
547 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
548 ret &= matches_tagfilter(dav_tags, filter->subfilters[i]);
549 }
550 return ret;
551 }
552
553 static int matches_subfilters_or(UcxList *dav_tags, SyncTagFilter *filter) {
554 int ret = 0;
555 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
556 ret |= matches_tagfilter(dav_tags, filter->subfilters[i]);
557 }
558 return ret;
559 }
560
561 static int matches_subfilters_one(UcxList *dav_tags, SyncTagFilter *filter) {
562 int one = 0;
563 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
564 if (matches_tagfilter(dav_tags, filter->subfilters[i])) {
565 if (one) {
566 return 0;
567 } else {
568 one = 1;
569 }
570 }
571 }
572 return one;
573 }
574
575 int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter) {
576
577 if (tagfilter->subfilter_count > 0) {
578 switch (tagfilter->mode) {
579 case DAV_SYNC_TAGFILTER_OR:
580 return matches_subfilters_or(dav_tags, tagfilter);
581 case DAV_SYNC_TAGFILTER_AND:
582 return matches_subfilters_and(dav_tags, tagfilter);
583 case DAV_SYNC_TAGFILTER_NONE:
584 return !matches_subfilters_or(dav_tags, tagfilter);
585 case DAV_SYNC_TAGFILTER_ONE:
586 return matches_subfilters_one(dav_tags, tagfilter);
587 default:
588 abort();
589 }
590 } else {
591 int ignorecase = 0; // TODO: maybe add support later
592 switch (tagfilter->mode) {
593 case DAV_SYNC_TAGFILTER_OR:
594 return matches_tags_or(dav_tags, tagfilter->tags, ignorecase);
595 case DAV_SYNC_TAGFILTER_AND:
596 return matches_tags_and(dav_tags, tagfilter->tags, ignorecase);
597 case DAV_SYNC_TAGFILTER_NONE:
598 return !matches_tags_or(dav_tags, tagfilter->tags, ignorecase);
599 case DAV_SYNC_TAGFILTER_ONE:
600 return matches_tags_one(dav_tags, tagfilter->tags, ignorecase);
601 default:
602 abort();
603 }
604 }
605 }
606

mercurial