From 5b69ce81ec29afeea0e67bd8b0f0b193a7b0c022 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Tue, 26 Mar 2019 21:12:03 +0200 Subject: Implement selection of requested mailbox formats in scanning the local file system folders. * include/mailutils/registrar.h (mu_record_match): New struct. (mu_registrar_match_records): New proto. * libmailutils/base/registrar.c (mu_registrar_match_records): New function. * libmailutils/mailbox/fsfolder.c (list_helper): Actually select the matching record from scn->records (if supplied). * libmailutils/tests/Makefile.am: Add new tests. * libmailutils/tests/fsfolder.c: Minor changes. * libmailutils/tests/testsuite.at: Add new tests. * libmailutils/tests/fsfolder03.at: New file. * libmailutils/tests/fsfolder04.at: New file. --- include/mailutils/list.h | 2 +- include/mailutils/registrar.h | 9 ++ libmailutils/base/registrar.c | 63 ++++++++++++++ libmailutils/mailbox/fsfolder.c | 182 ++++++++++++++++++++++++++++----------- libmailutils/tests/Makefile.am | 2 + libmailutils/tests/fsfolder.c | 38 +++----- libmailutils/tests/fsfolder03.at | 44 ++++++++++ libmailutils/tests/fsfolder04.at | 42 +++++++++ libmailutils/tests/testsuite.at | 2 + 9 files changed, 305 insertions(+), 79 deletions(-) create mode 100644 libmailutils/tests/fsfolder03.at create mode 100644 libmailutils/tests/fsfolder04.at diff --git a/include/mailutils/list.h b/include/mailutils/list.h index e01913283..2614a882a 100644 --- a/include/mailutils/list.h +++ b/include/mailutils/list.h @@ -317,7 +317,7 @@ typedef int (*mu_list_folder_t) (void *_item, void *_data, 0 - success, EINVAL - _list or _fold is NULL, - MU_ERR_OUT_PTR_NULL - _return_code is NULL + MU_ERR_OUT_PTR_NULL - _return_value is NULL other value - non-zero value returned by _fold. The _fold function is not allowed to alter the list it is being applied diff --git a/include/mailutils/registrar.h b/include/mailutils/registrar.h index a2cd76b0b..a9e621235 100644 --- a/include/mailutils/registrar.h +++ b/include/mailutils/registrar.h @@ -74,8 +74,17 @@ int mu_registrar_lookup_url (mu_url_t url, int flags, mu_record_t *precord, int *pflags); int mu_registrar_record (mu_record_t); int mu_unregistrar_record (mu_record_t); + int mu_registrar_apply_filter (int (*flt) (mu_record_t, void *), void *data); +struct mu_record_match +{ + mu_record_t record; + int flags; +}; + +int mu_registrar_match_records (char const *name, int flags, mu_list_t *ret); + int mu_record_is_local (mu_record_t); /* Scheme. */ diff --git a/libmailutils/base/registrar.c b/libmailutils/base/registrar.c index e25e6d810..debac377d 100644 --- a/libmailutils/base/registrar.c +++ b/libmailutils/base/registrar.c @@ -446,3 +446,66 @@ mu_registrar_apply_filter (int (*flt) (mu_record_t, void *), void *data) mu_monitor_unlock (®istrar_monitor); return 0; } + +struct match_closure +{ + mu_url_t url; + int flags; + int err; +}; + +static int +select_match (void **itmv, size_t itmc, void *call_data) +{ + struct match_closure *mc = call_data; + int rc = mu_record_is_scheme (itmv[0], mc->url, mc->flags); + if (rc) + { + struct mu_record_match *match = malloc (sizeof (*match)); + if (!match) + { + mc->err = errno; + return MU_LIST_MAP_STOP; + } + match->record = itmv[0]; + match->flags = rc; + itmv[0] = match; + return MU_LIST_MAP_OK; + } + return MU_LIST_MAP_SKIP; +} + +/* Select records matching pathname NAME with given FLAGS + (MU_FOLDER_ATTRIBUTE_* bitmask). On success, store each + match as a pointer to struct mu_record_match in *RET. +*/ +int +mu_registrar_match_records (char const *name, int flags, mu_list_t *ret) +{ + int rc; + struct match_closure mc; + mu_list_t lst; + + rc = mu_url_create (&mc.url, name); + if (rc) + return rc; + mc.flags = flags; + mc.err = 0; + + mu_monitor_wrlock (®istrar_monitor); + rc = mu_list_map (registrar_list, select_match, &mc, 1, &lst); + mu_monitor_unlock (®istrar_monitor); + mu_url_destroy (&mc.url); + if (rc == 0) + { + mu_list_set_destroy_item (lst, mu_list_free_item); + if (mc.err) + { + mu_list_destroy (&lst); + rc = mc.err; + } + } + if (rc == 0) + *ret = lst; + return rc; +} diff --git a/libmailutils/mailbox/fsfolder.c b/libmailutils/mailbox/fsfolder.c index 30a706c3b..553c9fddf 100644 --- a/libmailutils/mailbox/fsfolder.c +++ b/libmailutils/mailbox/fsfolder.c @@ -199,7 +199,97 @@ inode_list_lookup (struct inode_list *list, struct stat *st) return 1; return 0; } + +static int +fold_record_match (void *item, void *data, void *prev, void **ret) +{ + struct mu_record_match *cur_match = item; + struct mu_record_match *prev_match = prev; + if (prev == NULL || cur_match->flags >= prev_match->flags) + *ret = cur_match; + else + *ret = prev_match; + return 0; +} + +/* List item comparator for computing an intersection between a list + of mu_record_t objects and a list of struct mi_record_match pointers. +*/ +static int +mcomp (const void *a, const void *b) +{ + struct _mu_record const * r = a; + struct mu_record_match const *m = b; + return !(m->record == r); +} + +/* Find a record from RECORDS that is the best match for mailbox REFNAME. + Return the record found in PREC and mailbox attribute flags (the + MU_FOLDER_ATTRIBUTE_* bitmask) in PFLAGS. + Return 0 on success, MU_ERR_NOENT if no match was found and mailutils + error code on error. + */ +static int +best_match (mu_list_t records, char const *refname, + mu_record_t *prec, int *pflags) +{ + int rc; + mu_list_t mlist, isect; + mu_list_comparator_t prev; + struct mu_record_match *m; + + rc = mu_registrar_match_records (refname, + MU_FOLDER_ATTRIBUTE_ALL, + &mlist); + if (rc) + { + mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, + ("%s():%s: %s", + __func__, + "mu_registrar_match_records", + mu_strerror (rc))); + return rc; + } + + prev = mu_list_set_comparator (records, mcomp); + rc = mu_list_intersect (&isect, mlist, records); + mu_list_set_comparator (records, prev); + if (rc) + { + mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, + ("%s():%s: %s", + __func__, + "mu_list_intersect", + mu_strerror (rc))); + mu_list_destroy (&mlist); + return rc; + } + + rc = mu_list_fold (isect, fold_record_match, NULL, NULL, &m); + if (rc == 0) + { + if (m == NULL) + rc = MU_ERR_NOENT; + else + { + *prec = m->record; + *pflags = m->flags; + } + } + else + { + mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, + ("%s():%s: %s", + __func__, + "mu_list_fold", + mu_strerror (rc))); + } + mu_list_destroy (&mlist); + mu_list_destroy (&isect); + return rc; +} + static int list_helper (struct mu_folder_scanner *scn, struct folder_scan_data *data, @@ -260,6 +350,7 @@ list_helper (struct mu_folder_scanner *scn, int type = 0; struct mu_list_response *resp; mu_record_t rec = NULL; + int rc; resp = malloc (sizeof (*resp)); if (resp == NULL) @@ -270,65 +361,56 @@ list_helper (struct mu_folder_scanner *scn, free (fname); continue; } - - mu_registrar_lookup (refname, MU_FOLDER_ATTRIBUTE_ALL, - &rec, &type); + if (scn->records) + rc = best_match (scn->records, refname, &rec, &type); + else + rc = mu_registrar_lookup (refname, MU_FOLDER_ATTRIBUTE_ALL, + &rec, &type); + + if (rc || type == 0) { - int rc; - rc = mu_list_locate (scn->records, rec, NULL); - if (rc) - { - if (rc != MU_ERR_NOENT) - mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, - ("%s(%s):%s: %s", - __func__, dirname, "mu_list_locate", - mu_strerror (rc))); - continue; - } - } - - if (type == 0) - { - free (fname); free (resp); - continue; + if (f == MU_FOLDER_ATTRIBUTE_DIRECTORY) + type = f; } - - resp->name = fname; - resp->level = level; - resp->separator = '/'; - resp->type = type; - resp->format = rec; - - if (scn->enumfun) + else { - if (scn->enumfun (data->folder, resp, scn->enumdata)) + resp->name = fname; + resp->level = level; + resp->separator = '/'; + resp->type = type; + resp->format = rec; + + if (scn->enumfun) { - free (resp->name); - free (resp); - stop = 1; - break; + if (scn->enumfun (data->folder, resp, scn->enumdata)) + { + free (resp->name); + free (resp); + stop = 1; + break; + } } - } - if (scn->result) - { - int rc; - rc = mu_list_append (scn->result, resp); - if (rc) - mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, - ("%s(%s):%s: %s", - __func__, dirname, "mu_list_append", - mu_strerror (rc))); - - /* Prevent fname from being freed at the end of the - loop - */ - fname = NULL; + if (scn->result) + { + int rc; + rc = mu_list_append (scn->result, resp); + if (rc) + mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR, + ("%s(%s):%s: %s", + __func__, dirname, "mu_list_append", + mu_strerror (rc))); + + /* Prevent fname from being freed at the end of the + loop + */ + fname = NULL; + } + else + free (resp); } - else - free (resp); if ((type & MU_FOLDER_ATTRIBUTE_DIRECTORY) && !inode_list_lookup (ilist, &st)) diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am index ef95577f9..b589c42cc 100644 --- a/libmailutils/tests/Makefile.am +++ b/libmailutils/tests/Makefile.am @@ -119,6 +119,8 @@ TESTSUITE_AT = \ fsfolder00.at\ fsfolder01.at\ fsfolder02.at\ + fsfolder03.at\ + fsfolder04.at\ hdrcpy.at\ hdrflt.at\ htmlent.at\ diff --git a/libmailutils/tests/fsfolder.c b/libmailutils/tests/fsfolder.c index 8c2579838..5a4d9df7d 100644 --- a/libmailutils/tests/fsfolder.c +++ b/libmailutils/tests/fsfolder.c @@ -152,28 +152,6 @@ com_unsubscribe (int argc, char **argv, mu_assoc_t options, void *env) return 0; } -static mu_record_t -find_record (char const *scheme) -{ - mu_iterator_t itr; - mu_record_t result = NULL; - - MU_ASSERT (mu_registrar_get_iterator (&itr)); - for (mu_iterator_first (itr); !mu_iterator_is_done (itr); - mu_iterator_next (itr)) - { - mu_record_t rec; - mu_iterator_current (itr, (void **)&rec); - if (strcmp (rec->scheme, scheme) == 0) - { - result = rec; - break; - } - } - mu_iterator_destroy (&itr); - return result; -} - static int com_scan (int argc, char **argv, mu_assoc_t options, void *env) { @@ -205,9 +183,9 @@ com_scan (int argc, char **argv, mu_assoc_t options, void *env) if (mu_assoc_lookup (options, "type", &s) == 0) { - mu_record_t rec = find_record (s); - - if (rec) + mu_record_t rec; + rc = mu_registrar_lookup_scheme (s, &rec); + if (rc == 0) { if (!scn.records) MU_ASSERT (mu_list_create (&scn.records)); @@ -215,7 +193,11 @@ com_scan (int argc, char **argv, mu_assoc_t options, void *env) } else { - mu_error ("%s: no such record found", s); + if (rc == MU_ERR_NOENT) + mu_error ("%s: no such record found", s); + else + mu_diag_funcall (MU_DIAG_ERROR, "mu_registrar_lookup_scheme", + NULL, rc); mu_list_destroy (&scn.records); return 0; } @@ -284,7 +266,7 @@ _always_is_scheme (mu_record_t record, mu_url_t url, int flags) return res & flags; } -static struct _mu_record test_record = +static struct _mu_record any_record = { 10, "any", @@ -339,7 +321,7 @@ main (int argc, char **argv) int glob_option = 0; mu_tesh_init (argv[0]); - mu_registrar_record (&test_record); + mu_registrar_record (&any_record); mu_registrar_record (®_record); if (argc == 1) diff --git a/libmailutils/tests/fsfolder03.at b/libmailutils/tests/fsfolder03.at new file mode 100644 index 000000000..8bf44b01d --- /dev/null +++ b/libmailutils/tests/fsfolder03.at @@ -0,0 +1,44 @@ +# This file is part of GNU Mailutils. -*- Autotest -*- +# Copyright (C) 2011-2019 Free Software Foundation, Inc. +# +# GNU Mailutils is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3, or (at +# your option) any later version. +# +# GNU Mailutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Mailutils. If not, see . + +AT_SETUP([Maxdepth]) +AT_KEYWORDS([fsfolder maxdepth]) + +AT_CHECK([mkdir dir +mkdir dir/a +mkdir dir/a/b +mkdir dir/a/b/c +mkdir dir/a/b/c/d +mkdir dir/b +mkdir dir/b/b +mkdir dir/b/c +echo maxdepth=1 +fsfolder -name=dir -sort scan -maxdepth=1 +echo maxdepth=2 +fsfolder -name=dir -sort scan -maxdepth=2 +], +[0], +[maxdepth=1 +d- / 0 dir/a +d- / 0 dir/b +maxdepth=2 +d- / 0 dir/a +d- / 0 dir/b +d- / 1 dir/a/b +d- / 1 dir/b/b +d- / 1 dir/b/c +]) +AT_CLEANUP diff --git a/libmailutils/tests/fsfolder04.at b/libmailutils/tests/fsfolder04.at new file mode 100644 index 000000000..35b6842e5 --- /dev/null +++ b/libmailutils/tests/fsfolder04.at @@ -0,0 +1,42 @@ +# This file is part of GNU Mailutils. -*- Autotest -*- +# Copyright (C) 2011-2019 Free Software Foundation, Inc. +# +# GNU Mailutils is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 3, or (at +# your option) any later version. +# +# GNU Mailutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Mailutils. If not, see . + +AT_SETUP([Type selection]) +AT_KEYWORDS([fsfolder types]) + +AT_CHECK([mkdir dir +mkdir dir/a +> dir/1 +> dir/a/2 +mkdir dir/b +echo type=any +fsfolder -name=dir -sort scan -type=any +echo type=reg +fsfolder -name=dir -sort scan -type=reg +], +[0], +[type=any +-f / 0 dir/1 +d- / 0 dir/a +d- / 0 dir/b +-f / 1 dir/a/2 +type=reg +-f / 0 dir/1 +-f / 1 dir/a/2 +]) + +AT_CLEANUP + diff --git a/libmailutils/tests/testsuite.at b/libmailutils/tests/testsuite.at index 5e302ee6a..05b77b116 100644 --- a/libmailutils/tests/testsuite.at +++ b/libmailutils/tests/testsuite.at @@ -198,6 +198,8 @@ AT_BANNER(File-system folder) m4_include([fsfolder00.at]) m4_include([fsfolder01.at]) m4_include([fsfolder02.at]) +m4_include([fsfolder03.at]) +m4_include([fsfolder04.at]) AT_BANNER(Base64) m4_include([base64e.at]) -- cgit v1.2.1