/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2003-2021 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 . */
/* MH pick command */
#include
#include
#include
#include
static char prog_doc[] = N_("Search for messages by content");
static char args_doc[] = N_("[--COMPONENT PATTERN]... [MSGLIST]");
static int public_option = 1;
static int zero_option = 0;
static int list = 1;
static int seq_flags = 0; /* Create public sequences;
Do not zero the sequence before addition */
static mu_list_t seq_list; /* List of sequence names to operate upon */
static mu_list_t lexlist; /* List of input tokens */
static mu_msgset_t picked_message_uids;
static void
add_component_name (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
{
pick_add_token (&lexlist, T_COMP, arg);
}
static void
add_component_pattern (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
{
pick_add_token (&lexlist, T_STRING, arg);
}
static void
add_cflags (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
{
pick_add_token (&lexlist, T_CFLAGS, arg);
}
static void
add_comp_match (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_COMP, opt->opt_default);
pick_add_token (&lexlist, T_STRING, arg);
}
static void
add_datefield (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_DATEFIELD, arg);
}
static void
add_after (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_AFTER, NULL);
pick_add_token (&lexlist, T_STRING, arg);
}
static void
add_before (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_BEFORE, NULL);
pick_add_token (&lexlist, T_STRING, arg);
}
static void
add_and (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_AND, NULL);
}
static void
add_or (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_OR, NULL);
}
static void
add_not (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_NOT, NULL);
}
static void
add_lbrace (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_LBRACE, NULL);
}
static void
add_rbrace (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
pick_add_token (&lexlist, T_RBRACE, NULL);
}
static void
add_to_sequence (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
{
if (!seq_list && mu_list_create (&seq_list))
{
mu_error (_("cannot create sequence list"));
exit (1);
}
mu_list_append (seq_list, mu_strdup (arg));
list = 0;
}
static struct mu_option options[] = {
MU_OPTION_GROUP (N_("Search patterns")),
{ "component", 0, N_("FIELD"), MU_OPTION_DEFAULT,
N_("search the named header field"),
mu_c_string, NULL, add_component_name },
{ "pattern", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("set pattern to look for"),
mu_c_string, NULL, add_component_pattern },
{ "search", 0, NULL, MU_OPTION_ALIAS },
{ "cflags", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("flags controlling the type of regular expressions. "
"STRING must consist of one or more of the following letters: "
"B=basic, E=extended, I=ignore case, C=case sensitive. "
"Default is \"EI\". The flags remain in effect until the next "
"occurrence of --cflags option. The option must occur right "
"before --pattern or --component option (or its alias)."),
mu_c_string, NULL, add_cflags },
{ "cc", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("search for Cc matching STRING"),
mu_c_string, NULL, add_comp_match, "cc" },
{ "date", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("search for Date matching STRING"),
mu_c_string, NULL, add_comp_match, "date" },
{ "from", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("search for From matching STRING"),
mu_c_string, NULL, add_comp_match, "from" },
{ "subject", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("search for Subject matching STRING"),
mu_c_string, NULL, add_comp_match, "subject" },
{ "to", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("search for To matching STRING"),
mu_c_string, NULL, add_comp_match, "to" },
MU_OPTION_GROUP (N_("Date constraint operations")),
{ "datefield", 0, N_("STRING"), MU_OPTION_DEFAULT,
N_("search in the named date header field (default is `Date:')"),
mu_c_string, NULL, add_datefield },
{ "after", 0, N_("DATE"), MU_OPTION_DEFAULT,
N_("match messages after the given date"),
mu_c_string, NULL, add_after },
{ "before", 0, N_("DATE"), MU_OPTION_DEFAULT,
N_("match messages before the given date"),
mu_c_string, NULL, add_before },
MU_OPTION_GROUP (N_("Logical operations and grouping")),
{ "and", 0, NULL, MU_OPTION_DEFAULT,
N_("logical AND (default)"),
mu_c_string, NULL, add_and },
{ "or", 0, NULL, MU_OPTION_DEFAULT,
N_("logical OR"),
mu_c_string, NULL, add_or },
{ "not", 0, NULL, MU_OPTION_DEFAULT,
N_("logical NOT"),
mu_c_string, NULL, add_not },
{ "lbrace", 0, NULL, MU_OPTION_DEFAULT,
N_("open group"),
mu_c_string, NULL, add_lbrace },
{ "(", 0, NULL, MU_OPTION_ALIAS },
{ "rbrace", 0, NULL, MU_OPTION_DEFAULT,
N_("close group"),
mu_c_string, NULL, add_rbrace },
{ ")", 0, NULL, MU_OPTION_ALIAS },
MU_OPTION_GROUP (N_("Operations over the selected messages")),
{ "list", 0, NULL, MU_OPTION_DEFAULT,
N_("list the numbers of the selected messages (default)"),
mu_c_bool, &list },
{ "sequence", 0, N_("NAME"), MU_OPTION_DEFAULT,
N_("add matching messages to the given sequence"),
mu_c_string, NULL, add_to_sequence },
{ "public", 0, NULL, MU_OPTION_DEFAULT,
N_("create public sequence"),
mu_c_bool, &public_option },
{ "zero", 0, NULL, MU_OPTION_DEFAULT,
N_("empty the sequence before adding messages"),
mu_c_bool, &zero_option },
MU_OPTION_END
};
static int
pick_message (size_t num, mu_message_t msg, void *data)
{
if (pick_eval (msg))
{
mh_message_number (msg, &num);
if (list)
printf ("%s\n", mu_umaxtostr (0, num));
if (picked_message_uids)
mu_msgset_add_range (picked_message_uids, num, num, MU_MSGSET_UID);
}
return 0;
}
static int
action_add (void *item, void *data)
{
mu_mailbox_t mbox = data;
mh_seq_add (mbox, (char *)item, picked_message_uids, seq_flags);
return 0;
}
int
main (int argc, char **argv)
{
int i, status;
mu_mailbox_t mbox;
mu_msgset_t msgset;
int argv_alloc = 0;
/* Expand eventual --COMPONENT NAME pairs */
for (i = 1; i < argc;)
{
if (strncmp (argv[i], "--", 2) == 0 && argv[i][3] && i + 1 < argc)
{
size_t n = argc + 2;
if (argv_alloc)
argv = mu_realloc (argv, (n + 1) * sizeof (argv[0]));
else
{
char **p = mu_calloc (n + 1, sizeof p[0]);
memcpy (p, argv, (argc + 1) * sizeof argv[0]);
argv = p;
argv_alloc = 1;
}
memmove (argv + i + 4, argv + i + 2,
(argc - i - 1) * sizeof argv[0]);
argv[i + 3] = argv[i + 1];
argv[i + 2] = mu_strdup ("-pattern");
argv[i + 1] = mu_strdup (argv[i] + 2);
argv[i] = mu_strdup ("-component");
argc = n;
i += 4;
}
else
i++;
}
mh_getopt (&argc, &argv, options, MH_GETOPT_DEFAULT_FOLDER,
args_doc, prog_doc, NULL);
if (pick_parse (lexlist))
return 1;
seq_flags = (public_option ? 0 : SEQ_PRIVATE) | (zero_option ? SEQ_ZERO : 0);
mbox = mh_open_folder (mh_current_folder (),
seq_list ? MU_STREAM_RDWR : MU_STREAM_READ);
if (seq_list)
mu_msgset_create (&picked_message_uids, NULL, MU_MSGSET_UID);
mh_msgset_parse (&msgset, mbox, argc, argv, "all");
status = mu_msgset_foreach_message (msgset, pick_message, NULL);
if (picked_message_uids)
mu_list_foreach (seq_list, action_add, mbox);
mh_global_save_state ();
mu_mailbox_close (mbox);
mu_mailbox_destroy (&mbox);
return status;
}