dav/sync.c

changeset 400
90c6bfa94fa2
parent 399
a985a587787f
child 402
babdf40dd22c
equal deleted inserted replaced
399:a985a587787f 400:90c6bfa94fa2
253 } 253 }
254 return 0; 254 return 0;
255 } 255 }
256 } 256 }
257 return 1; 257 return 1;
258 }
259
260 // TODO: use scstr_t after update to UCX 2.0
261 static size_t rtrimskip(sstr_t str, size_t skip) {
262 while (skip < str.length && isspace(str.ptr[skip])) skip++;
263 return skip;
264 }
265
266 static size_t parse_tagfilter_taglist(sstr_t fs, SyncTagFilter* tagfilter) {
267 size_t csvlen;
268 for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) {
269 if (fs.ptr[csvlen] == ')') break;
270 }
271 fs.length = csvlen;
272
273 tagfilter->tags = parse_csv_taglist(fs.ptr, fs.length);
274
275 return csvlen;
276 }
277
278 static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* tagfilter);
279
280 static size_t parse_tagfilter_filter(sstr_t fs, SyncTagFilter* tagfilter) {
281
282 size_t consumed = rtrimskip(fs, 0);
283 fs = sstrsubs(fs, consumed);
284
285 if (fs.length == 0) {
286 return consumed;
287 } else {
288
289 // optional operator
290 int hasop = 0;
291 if (fs.ptr[0] == '&') {
292 tagfilter->mode = DAV_SYNC_TAGFILTER_AND;
293 hasop = 1;
294 } else if (fs.ptr[0] == '|') {
295 tagfilter->mode = DAV_SYNC_TAGFILTER_OR;
296 hasop = 1;
297 } else if (fs.ptr[0] == '0') {
298 tagfilter->mode = DAV_SYNC_TAGFILTER_NONE;
299 hasop = 1;
300 } else if (fs.ptr[0] == '1') {
301 tagfilter->mode = DAV_SYNC_TAGFILTER_ONE;
302 hasop = 1;
303 } else {
304 // default operator is AND
305 tagfilter->mode = DAV_SYNC_TAGFILTER_AND;
306 }
307
308 if (hasop) {
309 size_t skip = rtrimskip(fs, 1);
310 consumed += skip;
311 fs = sstrsubs(fs, skip);
312 }
313
314 if (fs.length > 0 && fs.ptr[0] == '(') {
315 size_t c = parse_tagfilter_subfilters(fs, tagfilter);
316 if (c) {
317 return consumed + c;
318 } else {
319 return 0;
320 }
321 } else {
322 tagfilter->subfilter_count = 0;
323 tagfilter->subfilters = NULL;
324 return consumed + parse_tagfilter_taglist(fs, tagfilter);
325 }
326 }
327 }
328
329 /*
330 * Parses: ( "(" , filter , ")" )+
331 */
332 static size_t parse_tagfilter_subfilters(sstr_t fs, SyncTagFilter* f) {
333
334 // strategy: allocate much and give back later (instead of reallocs in loop)
335 size_t subfilter_cap = 8;
336 f->subfilters = calloc(subfilter_cap, sizeof(SyncTagFilter*));
337 f->subfilter_count = 0;
338
339 size_t total_consumed = 0;
340 size_t c;
341 do {
342 // skip leading parenthesis (and white spaces)
343 c = rtrimskip(fs, 1);
344 fs = sstrsubs(fs, c);
345 total_consumed += c;
346
347 // increase array capacity, if necessary
348 if (f->subfilter_count >= subfilter_cap) {
349 subfilter_cap *= 2;
350 SyncTagFilter** newarr = realloc(f->subfilters,
351 subfilter_cap * sizeof(SyncTagFilter*));
352 if (newarr) {
353 f->subfilters = newarr;
354 } else {
355 abort(); // no error handling reachable, so we are fucked
356 }
357 }
358
359 // allocate space for a new filter
360 SyncTagFilter* subf = calloc(1, sizeof(SyncTagFilter));
361
362 // parse that filter
363 c = parse_tagfilter_filter(fs, subf);
364
365 // sanity check: we must end with a closing parenthesis
366 if (c > 0 && fs.ptr[c] == ')') {
367 f->subfilters[f->subfilter_count++] = subf;
368
369 // consume ')' and find the next parenthesis or the end-of-string
370 c = rtrimskip(fs, 1+c);
371 fs = sstrsubs(fs, c);
372 total_consumed += c;
373
374 if (fs.length == 0 || fs.ptr[0] == ')') {
375 // our job is done
376 break;
377 } else if (fs.ptr[0] != '(') {
378 // anything else than a parenthesis or end-of-string is an error
379 return 0;
380 }
381 } else {
382 free(subf);
383 break;
384 }
385
386 } while(1);
387
388 // try to shrink the array
389 if (f->subfilter_count > 0) {
390 SyncTagFilter** shrinked_array = realloc(f->subfilters,
391 f->subfilter_count * sizeof(SyncTagFilter*));
392 if (shrinked_array) {
393 f->subfilters = shrinked_array;
394 }
395 } else {
396 free(f->subfilters);
397 f->subfilters = NULL;
398 }
399
400 return total_consumed;
401 }
402
403 SyncTagFilter* parse_tagfilter_string(const char* filterstring) {
404 SyncTagFilter* tagfilter = calloc(1, sizeof(SyncTagFilter));
405 if (!filterstring) {
406 return tagfilter;
407 }
408
409 // TODO: use scstr_t after update to UCX 2.0
410 sstr_t fs = sstr((char*) filterstring);
411 size_t consumed = parse_tagfilter_filter(fs, tagfilter);
412 if (!consumed) {
413 free_tagfilter(tagfilter);
414 return NULL;
415 }
416
417 // consume trailing white spaces
418 consumed = rtrimskip(fs, consumed);
419
420 // sanity check: have we consumed the whole string?
421 if (consumed != fs.length) {
422 free_tagfilter(tagfilter);
423 return NULL;
424 }
425
426 return tagfilter;
427 }
428
429 void free_tagfilter(SyncTagFilter* filter) {
430 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
431 free_tagfilter(filter->subfilters[i]);
432 }
433 free(filter->subfilters);
434 free(filter);
435 }
436
437 static int matches_tags_and(UcxList *dav_tags, UcxList *tags, int ignorecase) {
438 UCX_FOREACH(e, tags) {
439 if (!ucx_list_contains(dav_tags, e->data,
440 (cmp_func) compare_tagname, &ignorecase)) {
441 return 0;
442 }
443 }
444 return 1;
445 }
446
447 static int matches_tags_or(UcxList *dav_tags, UcxList *tags, int ignorecase) {
448 UCX_FOREACH(e, tags) {
449 if (ucx_list_contains(dav_tags, e->data,
450 (cmp_func) compare_tagname, &ignorecase)) {
451 return 1;
452 }
453 }
454 return 0;
455 }
456
457 static int matches_tags_one(UcxList *dav_tags, UcxList *tags, int ignorecase) {
458 int matches_exactly_one = 0;
459 UCX_FOREACH(e, tags) {
460 if (ucx_list_contains(dav_tags, e->data,
461 (cmp_func) compare_tagname, &ignorecase)) {
462 if (matches_exactly_one) {
463 return 0;
464 } else {
465 matches_exactly_one = 1;
466 }
467 }
468 }
469 return matches_exactly_one;
470 }
471
472 static int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter);
473
474 static int matches_subfilters_and(UcxList *dav_tags, SyncTagFilter *filter) {
475 int ret = 1;
476 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
477 ret &= matches_tagfilter(dav_tags, filter->subfilters[i]);
478 }
479 return ret;
480 }
481
482 static int matches_subfilters_or(UcxList *dav_tags, SyncTagFilter *filter) {
483 int ret = 0;
484 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
485 ret |= matches_tagfilter(dav_tags, filter->subfilters[i]);
486 }
487 return ret;
488 }
489
490 static int matches_subfilters_one(UcxList *dav_tags, SyncTagFilter *filter) {
491 int one = 0;
492 for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
493 if (matches_tagfilter(dav_tags, filter->subfilters[i])) {
494 if (one) {
495 return 0;
496 } else {
497 one = 1;
498 }
499 }
500 }
501 return one;
502 }
503
504 static int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter) {
505
506 if (tagfilter->subfilter_count > 0) {
507 switch (tagfilter->mode) {
508 case DAV_SYNC_TAGFILTER_OR:
509 return matches_subfilters_or(dav_tags, tagfilter);
510 case DAV_SYNC_TAGFILTER_AND:
511 return matches_subfilters_and(dav_tags, tagfilter);
512 case DAV_SYNC_TAGFILTER_NONE:
513 return !matches_subfilters_or(dav_tags, tagfilter);
514 case DAV_SYNC_TAGFILTER_ONE:
515 return matches_subfilters_one(dav_tags, tagfilter);
516 default:
517 abort();
518 }
519 } else {
520 int ignorecase = 0; // TODO: maybe add support later
521 switch (tagfilter->mode) {
522 case DAV_SYNC_TAGFILTER_OR:
523 return matches_tags_or(dav_tags, tagfilter->tags, ignorecase);
524 case DAV_SYNC_TAGFILTER_AND:
525 return matches_tags_and(dav_tags, tagfilter->tags, ignorecase);
526 case DAV_SYNC_TAGFILTER_NONE:
527 return !matches_tags_or(dav_tags, tagfilter->tags, ignorecase);
528 case DAV_SYNC_TAGFILTER_ONE:
529 return matches_tags_one(dav_tags, tagfilter->tags, ignorecase);
530 default:
531 abort();
532 }
533 }
534 } 258 }
535 259
536 static int res_matches_tags(DavResource *res, SyncTagFilter *tagfilter) { 260 static int res_matches_tags(DavResource *res, SyncTagFilter *tagfilter) {
537 if(tagfilter->mode == DAV_SYNC_TAGFILTER_OFF) { 261 if(tagfilter->mode == DAV_SYNC_TAGFILTER_OFF) {
538 return 1; 262 return 1;

mercurial