No functional changes.
mail-namespace.c \
mail-search.c \
mail-search-args-imap.c \
+ mail-search-args-simplify.c \
mail-search-build.c \
mail-search-parser.c \
mail-search-parser-imap.c \
test_programs = \
test-mail-search-args-imap \
+ test-mail-search-args-simplify \
test-mailbox-get
noinst_PROGRAMS = $(test_programs)
libstorage.la \
$(LIBDOVECOT_DEPS)
+test_mail_search_args_simplify_SOURCES = test-mail-search-args-simplify.c
+test_mail_search_args_simplify_LDADD = \
+ $(LIBDOVECOT_STORAGE) \
+ $(LIBDOVECOT)
+test_mail_search_args_simplify_DEPENDENCIES = \
+ libstorage.la \
+ $(LIBDOVECOT_DEPS)
+
test_mailbox_get_SOURCES = test-mailbox-get.c
test_mailbox_get_LDADD = mailbox-get.lo $(test_libs)
test_mailbox_get_DEPENDENCIES = $(noinst_LTLIBRARIES) $(test_libs)
--- /dev/null
+/* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "mail-search.h"
+
+static void
+mail_search_args_simplify_sub(struct mailbox *box,
+ struct mail_search_arg *args, bool parent_and)
+{
+ struct mail_search_arg *sub, *prev = NULL;
+ struct mail_search_arg *prev_flags_arg, *prev_not_flags_arg;
+
+ prev_flags_arg = prev_not_flags_arg = NULL;
+ while (args != NULL) {
+ if (args->match_not && (args->type == SEARCH_SUB ||
+ args->type == SEARCH_OR)) {
+ /* neg(p and q and ..) == neg(p) or neg(q) or ..
+ neg(p or q or ..) == neg(p) and neg(q) and .. */
+ args->type = args->type == SEARCH_SUB ?
+ SEARCH_OR : SEARCH_SUB;
+ args->match_not = FALSE;
+ sub = args->value.subargs;
+ do {
+ sub->match_not = !sub->match_not;
+ sub = sub->next;
+ } while (sub != NULL);
+ }
+
+ if ((args->type == SEARCH_SUB && parent_and) ||
+ (args->type == SEARCH_OR && !parent_and) ||
+ ((args->type == SEARCH_SUB || args->type == SEARCH_OR) &&
+ args->value.subargs->next == NULL)) {
+ /* p and (q and ..) == p and q and ..
+ p or (q or ..) == p or q or ..
+ (p) = p */
+ sub = args->value.subargs;
+ for (; sub->next != NULL; sub = sub->next) ;
+ sub->next = args->next;
+ *args = *args->value.subargs;
+ continue;
+ }
+
+ if (args->type == SEARCH_SUB ||
+ args->type == SEARCH_OR ||
+ args->type == SEARCH_INTHREAD) {
+ mail_search_args_simplify_sub(box, args->value.subargs,
+ args->type != SEARCH_OR);
+ }
+
+ /* merge all flags arguments */
+ if (args->type == SEARCH_FLAGS &&
+ !args->match_not && parent_and) {
+ if (prev_flags_arg == NULL)
+ prev_flags_arg = args;
+ else {
+ prev_flags_arg->value.flags |=
+ args->value.flags;
+ prev->next = args->next;
+ args = args->next;
+ continue;
+ }
+ } else if (args->type == SEARCH_FLAGS && args->match_not &&
+ !parent_and) {
+ if (prev_not_flags_arg == NULL)
+ prev_not_flags_arg = args;
+ else {
+ prev_not_flags_arg->value.flags |=
+ args->value.flags;
+ prev->next = args->next;
+ args = args->next;
+ continue;
+ }
+ }
+
+ prev = args;
+ args = args->next;
+ }
+}
+
+static bool
+mail_search_args_unnest_inthreads(struct mail_search_args *args,
+ struct mail_search_arg **argp,
+ bool parent_inthreads, bool parent_and)
+{
+ struct mail_search_arg *arg, *thread_arg, *or_arg;
+ bool child_inthreads = FALSE, non_inthreads = FALSE;
+
+ for (arg = *argp; arg != NULL; arg = arg->next) {
+ switch (arg->type) {
+ case SEARCH_SUB:
+ case SEARCH_OR:
+ if (!mail_search_args_unnest_inthreads(args,
+ &arg->value.subargs, parent_inthreads,
+ arg->type != SEARCH_OR)) {
+ arg->result = 1;
+ child_inthreads = TRUE;
+ } else {
+ arg->result = 0;
+ non_inthreads = TRUE;
+ }
+ break;
+ case SEARCH_INTHREAD:
+ if (mail_search_args_unnest_inthreads(args,
+ &arg->value.subargs, TRUE, TRUE)) {
+ /* children converted to SEARCH_INTHREADs */
+ arg->type = SEARCH_SUB;
+ }
+ args->have_inthreads = TRUE;
+ arg->result = 1;
+ child_inthreads = TRUE;
+ break;
+ default:
+ arg->result = 0;
+ non_inthreads = TRUE;
+ break;
+ }
+ }
+
+ if (!parent_inthreads || !child_inthreads || !non_inthreads)
+ return FALSE;
+
+ /* put all non-INTHREADs under a single INTHREAD */
+ thread_arg = p_new(args->pool, struct mail_search_arg, 1);
+ thread_arg->type = SEARCH_INTHREAD;
+
+ while (*argp != NULL) {
+ arg = *argp;
+ argp = &(*argp)->next;
+
+ if (arg->result == 0) {
+ /* not an INTHREAD or a SUB/OR with only INTHREADs */
+ arg->next = thread_arg->value.subargs;
+ thread_arg->value.subargs = arg;
+ }
+ }
+ if (!parent_and) {
+ /* We want to OR the args */
+ or_arg = p_new(args->pool, struct mail_search_arg, 1);
+ or_arg->type = SEARCH_OR;
+ or_arg->value.subargs = thread_arg->value.subargs;
+ thread_arg->value.subargs = or_arg;
+ }
+ return TRUE;
+}
+
+void mail_search_args_simplify(struct mail_search_args *args)
+{
+ args->simplified = TRUE;
+
+ mail_search_args_simplify_sub(args->box, args->args, TRUE);
+ if (mail_search_args_unnest_inthreads(args, &args->args,
+ FALSE, TRUE)) {
+ /* we may have added some extra SUBs that could be dropped */
+ mail_search_args_simplify_sub(args->box, args->args, TRUE);
+ }
+}
return TRUE;
}
-static void
-mail_search_args_simplify_sub(struct mailbox *box,
- struct mail_search_arg *args, bool parent_and)
-{
- struct mail_search_arg *sub, *prev = NULL;
- struct mail_search_arg *prev_flags_arg, *prev_not_flags_arg;
-
- prev_flags_arg = prev_not_flags_arg = NULL;
- while (args != NULL) {
- if (args->match_not && (args->type == SEARCH_SUB ||
- args->type == SEARCH_OR)) {
- /* neg(p and q and ..) == neg(p) or neg(q) or ..
- neg(p or q or ..) == neg(p) and neg(q) and .. */
- args->type = args->type == SEARCH_SUB ?
- SEARCH_OR : SEARCH_SUB;
- args->match_not = FALSE;
- sub = args->value.subargs;
- do {
- sub->match_not = !sub->match_not;
- sub = sub->next;
- } while (sub != NULL);
- }
-
- if ((args->type == SEARCH_SUB && parent_and) ||
- (args->type == SEARCH_OR && !parent_and) ||
- ((args->type == SEARCH_SUB || args->type == SEARCH_OR) &&
- args->value.subargs->next == NULL)) {
- /* p and (q and ..) == p and q and ..
- p or (q or ..) == p or q or ..
- (p) = p */
- sub = args->value.subargs;
- for (; sub->next != NULL; sub = sub->next) ;
- sub->next = args->next;
- *args = *args->value.subargs;
- continue;
- }
-
- if (args->type == SEARCH_SUB ||
- args->type == SEARCH_OR ||
- args->type == SEARCH_INTHREAD) {
- mail_search_args_simplify_sub(box, args->value.subargs,
- args->type != SEARCH_OR);
- }
-
- /* merge all flags arguments */
- if (args->type == SEARCH_FLAGS &&
- !args->match_not && parent_and) {
- if (prev_flags_arg == NULL)
- prev_flags_arg = args;
- else {
- prev_flags_arg->value.flags |=
- args->value.flags;
- prev->next = args->next;
- args = args->next;
- continue;
- }
- } else if (args->type == SEARCH_FLAGS && args->match_not &&
- !parent_and) {
- if (prev_not_flags_arg == NULL)
- prev_not_flags_arg = args;
- else {
- prev_not_flags_arg->value.flags |=
- args->value.flags;
- prev->next = args->next;
- args = args->next;
- continue;
- }
- }
-
- prev = args;
- args = args->next;
- }
-}
-
-static bool
-mail_search_args_unnest_inthreads(struct mail_search_args *args,
- struct mail_search_arg **argp,
- bool parent_inthreads, bool parent_and)
-{
- struct mail_search_arg *arg, *thread_arg, *or_arg;
- bool child_inthreads = FALSE, non_inthreads = FALSE;
-
- for (arg = *argp; arg != NULL; arg = arg->next) {
- switch (arg->type) {
- case SEARCH_SUB:
- case SEARCH_OR:
- if (!mail_search_args_unnest_inthreads(args,
- &arg->value.subargs, parent_inthreads,
- arg->type != SEARCH_OR)) {
- arg->result = 1;
- child_inthreads = TRUE;
- } else {
- arg->result = 0;
- non_inthreads = TRUE;
- }
- break;
- case SEARCH_INTHREAD:
- if (mail_search_args_unnest_inthreads(args,
- &arg->value.subargs, TRUE, TRUE)) {
- /* children converted to SEARCH_INTHREADs */
- arg->type = SEARCH_SUB;
- }
- args->have_inthreads = TRUE;
- arg->result = 1;
- child_inthreads = TRUE;
- break;
- default:
- arg->result = 0;
- non_inthreads = TRUE;
- break;
- }
- }
-
- if (!parent_inthreads || !child_inthreads || !non_inthreads)
- return FALSE;
-
- /* put all non-INTHREADs under a single INTHREAD */
- thread_arg = p_new(args->pool, struct mail_search_arg, 1);
- thread_arg->type = SEARCH_INTHREAD;
-
- while (*argp != NULL) {
- arg = *argp;
- argp = &(*argp)->next;
-
- if (arg->result == 0) {
- /* not an INTHREAD or a SUB/OR with only INTHREADs */
- arg->next = thread_arg->value.subargs;
- thread_arg->value.subargs = arg;
- }
- }
- if (!parent_and) {
- /* We want to OR the args */
- or_arg = p_new(args->pool, struct mail_search_arg, 1);
- or_arg->type = SEARCH_OR;
- or_arg->value.subargs = thread_arg->value.subargs;
- thread_arg->value.subargs = or_arg;
- }
- return TRUE;
-}
-
-void mail_search_args_simplify(struct mail_search_args *args)
-{
- args->simplified = TRUE;
-
- mail_search_args_simplify_sub(args->box, args->args, TRUE);
- if (mail_search_args_unnest_inthreads(args, &args->args,
- FALSE, TRUE)) {
- /* we may have added some extra SUBs that could be dropped */
- mail_search_args_simplify_sub(args->box, args->args, TRUE);
- }
-}
-
static bool mail_search_arg_one_equals(const struct mail_search_arg *arg1,
const struct mail_search_arg *arg2)
{
--- /dev/null
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "test-common.h"
+#include "mail-search-build.h"
+#include "mail-search-parser.h"
+#include "mail-search.h"
+
+struct {
+ const char *input;
+ const char *output;
+} tests[] = {
+ { "TEXT foo", "TEXT foo" },
+ { "( TEXT foo )", "TEXT foo" },
+ { "( ( TEXT foo ) )", "TEXT foo" },
+ { "( ( TEXT foo ) ( TEXT bar ) )", "TEXT foo TEXT bar" },
+
+ { "OR ( TEXT foo ) ( TEXT bar )", "(OR TEXT foo TEXT bar)" },
+ { "OR ( TEXT foo ) OR ( TEXT bar ) ( TEXT baz )",
+ "(OR TEXT foo OR TEXT bar TEXT baz)" },
+ { "OR ( ( TEXT foo TEXT foo2 ) ) ( ( TEXT bar ( TEXT baz ) ) )",
+ "(OR (TEXT foo TEXT foo2) (TEXT bar TEXT baz))" },
+
+ { "NOT ( TEXT foo )", "NOT TEXT foo" },
+ { "NOT ( NOT ( TEXT foo ) )", "TEXT foo" },
+ { "NOT OR ( TEXT foo ) ( TEXT bar )", "NOT TEXT foo NOT TEXT bar" },
+ { "NOT ( OR ( TEXT foo ) ( TEXT bar ) )", "NOT TEXT foo NOT TEXT bar" },
+ { "NOT ( TEXT foo TEXT bar )", "(OR NOT TEXT foo NOT TEXT bar)" },
+
+ { "ANSWERED FLAGGED SEEN", "(ANSWERED FLAGGED SEEN)" },
+ { "ANSWERED TEXT foo FLAGGED SEEN", "(ANSWERED FLAGGED SEEN) TEXT foo" },
+ { "NOT ( ANSWERED FLAGGED SEEN )", "(NOT (ANSWERED FLAGGED SEEN))" },
+ { "OR NOT ANSWERED OR NOT FLAGGED NOT SEEN", "(NOT (ANSWERED FLAGGED SEEN))" }
+};
+
+static struct mail_search_args *
+test_build_search_args(const char *args)
+{
+ struct mail_search_parser *parser;
+ struct mail_search_args *sargs;
+ const char *error, *charset = "UTF-8";
+
+ parser = mail_search_parser_init_cmdline(t_strsplit(args, " "));
+ if (mail_search_build(mail_search_register_get_imap(),
+ parser, &charset, &sargs, &error) < 0)
+ i_panic("%s", error);
+ mail_search_parser_deinit(&parser);
+ return sargs;
+}
+
+static void test_mail_search_args_simplify(void)
+{
+ struct mail_search_args *args;
+ string_t *str = t_str_new(256);
+ const char *error;
+ unsigned int i;
+
+ test_begin("mail search args simplify");
+ for (i = 0; i < N_ELEMENTS(tests); i++) {
+ args = test_build_search_args(tests[i].input);
+ mail_search_args_simplify(args);
+
+ str_truncate(str, 0);
+ test_assert(mail_search_args_to_imap(str, args->args, &error));
+ test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i);
+ mail_search_args_unref(&args);
+ }
+ test_end();
+}
+
+int main(void)
+{
+ static void (*test_functions[])(void) = {
+ test_mail_search_args_simplify,
+ NULL
+ };
+
+ return test_run(test_functions);
+}