summaryrefslogtreecommitdiff
path: root/libmailutils/msgset/sub.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/msgset/sub.c')
-rw-r--r--libmailutils/msgset/sub.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/libmailutils/msgset/sub.c b/libmailutils/msgset/sub.c
new file mode 100644
index 000000000..2840781dc
--- /dev/null
+++ b/libmailutils/msgset/sub.c
@@ -0,0 +1,233 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2011 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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <mailutils/types.h>
+#include <mailutils/errno.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+#include <mailutils/msgset.h>
+#include <mailutils/sys/msgset.h>
+
+/* Remove range R from the set S.
+
+ There are following basic cases:
+
+ 1. S does not contain R.
+
+ Return imeediately, there is nothing to do here.
+
+ 2. S contains a range X that matches exactly R.
+
+ Action: Remove X entirely and return.
+
+ 3. There is a range X in S such that R falls entirely within it.
+
+ Action: Split X into two ranges:
+
+ X.msg_beg : R.msg_beg-1
+ R.msg_end+1 : X.msg_end
+
+ and return.
+ In border cases one of the new ranges may be empty.
+
+ 4. There is a range X which contains an initial subrange of R.
+
+ Action: Remove the subrange from X. If the resulting range is
+ empty, remove it. Continue iteration with X.msg_end:R.msg_end
+ in place of R.
+
+ 5. There is a range X such that its initial subrange is contained
+ in R.
+
+ Action: Remove X.msg_beg:R.msg_end and return.
+
+ 6. There is a range X that is contained within R.
+
+ Action: Remove X and continue.
+
+There are two special cases. First is when R.msg_end is 0, meaning the last
+available message number in the mailbox. In this case the problem is reduced
+to either 2 or a combination of 4 and zero or more 6. It is handled by the
+sub_msgno_last function below.
+
+Yet another special case is when the last element of S has msg_end == 0. If
+so, the problem is reduced to either 2 or 4.
+*/
+
+static int
+sub_msgno_last (mu_msgset_t mset, size_t beg)
+{
+ int rc;
+ struct mu_msgrange *range;
+
+ if (beg == 1)
+ mu_list_clear (mset->list);
+ else
+ {
+ mu_iterator_t itr;
+
+ rc = mu_list_get_iterator (mset->list, &itr);
+ if (rc)
+ return rc;
+ rc = 1;
+ rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &rc);
+ if (rc)
+ {
+ mu_iterator_destroy (&itr);
+ return rc;
+ }
+
+ for (mu_iterator_first (itr); rc == 0 && !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ mu_iterator_current (itr, (void **)&range);
+ if (range->msg_beg > beg)
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ else if (range->msg_beg == beg)
+ {
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ break;
+ }
+ else
+ break;
+ }
+ mu_iterator_destroy (&itr);
+ }
+ return 0;
+}
+
+int
+mu_msgset_sub_range (mu_msgset_t mset, size_t beg, size_t end)
+{
+ int rc;
+ mu_iterator_t itr;
+ struct mu_msgrange *mr;
+
+ if (!mset)
+ return EINVAL;
+ if (mu_list_is_empty (mset->list))
+ return MU_ERR_NOENT;
+ rc = mu_msgset_aggregate (mset);
+ if (rc)
+ return rc;
+
+ if (end == MU_MSGNO_LAST)
+ return sub_msgno_last (mset, beg);
+
+ /* Test border cases */
+ rc = mu_list_head (mset->list, (void**)&mr);
+ if (rc)
+ return rc;
+ if (end < mr->msg_beg)
+ return 0;
+ if (beg < mr->msg_beg)
+ beg = mr->msg_beg;
+
+ if (rc)
+ return rc;
+ rc = mu_list_tail (mset->list, (void**) &mr);
+ if (mr->msg_end != MU_MSGNO_LAST)
+ {
+ if (beg > mr->msg_end)
+ return 0;
+ if (end > mr->msg_end)
+ end = mr->msg_end;
+ }
+
+ rc = mu_list_get_iterator (mset->list, &itr);
+ if (rc)
+ return rc;
+ for (mu_iterator_first (itr); rc == 0 && !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ mu_iterator_current (itr, (void **)&mr);
+
+ if (mr->msg_end == MU_MSGNO_LAST)
+ {
+ /* This is the last element in list. */
+ if (mr->msg_beg == beg)
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ else if (mr->msg_beg > beg)
+ mr->msg_beg = end + 1;
+ break;
+ }
+
+ if (mr->msg_beg == beg && mr->msg_end == end) /* See case 2 above */
+ {
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ break;
+ }
+ else if (mr->msg_beg <= beg && beg <= mr->msg_end)
+ {
+ if (mr->msg_beg <= end && end <= mr->msg_end) /* Case 3 */
+ {
+ /* Split the range */
+ if (end != mr->msg_end)
+ {
+ struct mu_msgrange *newrange = calloc (1,
+ sizeof (*newrange));
+ if (!newrange)
+ {
+ rc = ENOMEM;
+ break;
+ }
+ newrange->msg_beg = end + 1;
+ newrange->msg_end = mr->msg_end;
+ rc = mu_iterator_ctl (itr, mu_itrctl_insert, newrange);
+ if (rc)
+ {
+ free (newrange);
+ break;
+ }
+ }
+
+ if (mr->msg_beg == beg)
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ else
+ mr->msg_end = beg - 1;
+ break;
+ }
+ else if (mr->msg_beg == beg) /* Case 4 */
+ {
+ beg = mr->msg_end;
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ }
+ else
+ {
+ size_t n = mr->msg_end;
+ mr->msg_end = beg - 1;
+ beg = n;
+ }
+ }
+ else if (mr->msg_beg <= end && end <= mr->msg_end) /* Case 5 */
+ {
+ mr->msg_beg = end + 1;
+ if (mr->msg_beg >= mr->msg_end)
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ break;
+ }
+ else if (beg <= mr->msg_beg && mr->msg_beg <= end)
+ {
+ rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
+ }
+ }
+ mu_iterator_destroy (&itr);
+
+ return rc;
+}
+

Return to:

Send suggestions and report system problems to the System administrator.