diff options
-rw-r--r-- | mh/pick.c | 220 |
1 files changed, 202 insertions, 18 deletions
@@ -19,6 +19,11 @@ #include <mh.h> #include <regex.h> +#include <pick.h> +#include <pick-gram.h> +#define obstack_chunk_alloc malloc +#define obstack_chunk_free free +#include <obstack.h> const char *argp_program_version = "pick (" PACKAGE_STRING ")"; static char doc[] = N_("GNU MH pick\v" @@ -37,6 +42,8 @@ static struct argp_option options[] = { {"pattern", ARG_PATTERN, N_("STRING"), 0, N_("A pattern to look for"), 1}, {"search", 0, NULL, OPTION_ALIAS, NULL, 1}, + {"cflags", ARG_CFLAGS, N_("STRING"), 0, + 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).") }, {"cc", ARG_CC, N_("STRING"), 0, N_("Same as --component cc --pattern STRING"), 1}, {"date", ARG_DATE, N_("STRING"), 0, @@ -50,11 +57,11 @@ static struct argp_option options[] = { {N_("Date constraint operations:"), 0, NULL, OPTION_DOC, NULL, 1}, {"datefield",ARG_DATEFIELD, N_("STRING"), 0, - N_("Search in the named date header field (default is `Date:')"), 2}, + N_("* Search in the named date header field (default is `Date:')"), 2}, {"after", ARG_AFTER, N_("DATE"), 0, - N_("Match messages after the given date"), 2}, + N_("* Match messages after the given date"), 2}, {"before", ARG_BEFORE, N_("DATE"), 0, - N_("Match messages before the given date"), 2}, + N_("* Match messages before the given date"), 2}, {N_("Logical operations and grouping:"), 0, NULL, OPTION_DOC, NULL, 2}, {"and", ARG_AND, NULL, 0, @@ -111,13 +118,32 @@ struct mh_option mh_option[] = { {NULL} }; -static int list; -static int mode_public = 1; -static int mode_zero = 0; +static int list = 1; +static int seq_flags = 0; /* Create public sequences; + Do not zero the sequence before addition */ +static list_t seq_list; /* List of sequence names to operate upon */ + +static list_t lexlist; /* List of input tokens */ + +static struct obstack msgno_stk; /* Stack of selected message numbers */ +static size_t msgno_count; /* Number of items on the stack */ + +static void +add_sequence (char *name) +{ + if (!seq_list && list_create (&seq_list)) + { + mh_error (_("can't create sequence list")); + exit (1); + } + list_append (seq_list, name); +} static int -opt_handler (int key, char *arg, void *unused) +opt_handler (int key, char *arg, void *unused, struct argp_state *state) { + char *s, *p; + switch (key) { case '+': @@ -126,7 +152,8 @@ opt_handler (int key, char *arg, void *unused) break; case ARG_SEQUENCE: - /* add_sequence (arg); */ + add_sequence (arg); + list = 0; break; case ARG_LIST: @@ -137,52 +164,209 @@ opt_handler (int key, char *arg, void *unused) list = 0; break; - case ARG_COMPONENT: - case ARG_PATTERN: - case ARG_CC: + case ARG_COMPONENT: + pick_add_token (&lexlist, T_COMP, arg); + break; + + case ARG_PATTERN: + pick_add_token (&lexlist, T_STRING, arg); + break; + + case ARG_CC: + pick_add_token (&lexlist, T_COMP, "cc"); + pick_add_token (&lexlist, T_STRING, arg); + break; + case ARG_DATE: + pick_add_token (&lexlist, T_COMP, "date"); + pick_add_token (&lexlist, T_STRING, arg); + break; + case ARG_FROM: + pick_add_token (&lexlist, T_COMP, "from"); + pick_add_token (&lexlist, T_STRING, arg); + break; + case ARG_SUBJECT: - case ARG_TO: + pick_add_token (&lexlist, T_COMP, "subject"); + pick_add_token (&lexlist, T_STRING, arg); + break; + + case ARG_TO: + pick_add_token (&lexlist, T_COMP, "to"); + pick_add_token (&lexlist, T_STRING, arg); + break; + case ARG_DATEFIELD: + pick_add_token (&lexlist, T_DATEFIELD, arg); + break; + case ARG_AFTER: + pick_add_token (&lexlist, T_AFTER, NULL); + pick_add_token (&lexlist, T_STRING, arg); + break; + case ARG_BEFORE: + pick_add_token (&lexlist, T_BEFORE, NULL); + pick_add_token (&lexlist, T_STRING, arg); + break; + case ARG_AND: + pick_add_token (&lexlist, T_AND, NULL); + break; + case ARG_OR: + pick_add_token (&lexlist, T_OR, NULL); + break; + case ARG_NOT: + pick_add_token (&lexlist, T_NOT, NULL); + break; + case ARG_LBRACE: + pick_add_token (&lexlist, T_LBRACE, NULL); + break; + case ARG_RBRACE: + pick_add_token (&lexlist, T_RBRACE, NULL); + break; + + case ARG_CFLAGS: + pick_add_token (&lexlist, T_CFLAGS, arg); break; case ARG_PUBLIC: - mode_public = is_true (arg); + if (is_true (arg)) + seq_flags &= ~SEQ_PRIVATE; + else + seq_flags |= SEQ_PRIVATE; break; case ARG_NOPUBLIC: - mode_public = 0; + seq_flags |= SEQ_PRIVATE; break; case ARG_ZERO: - mode_zero = is_true (arg); + if (is_true (arg)) + seq_flags |= SEQ_ZERO; + else + seq_flags &= ~SEQ_ZERO; break; case ARG_NOZERO: - mode_zero = 0; + seq_flags &= ~SEQ_ZERO; + break; + + case ARGP_KEY_ERROR: + s = state->argv[state->next - 1]; + if (memcmp (s, "--", 2)) + { + argp_error (state, _("invalid option -- %s"), s); + exit (1); + } + p = strchr (s, '='); + if (p) + *p++ = 0; + + pick_add_token (&lexlist, T_COMP, s + 2); + + if (!p) + { + if (state->next == state->argc) + { + mh_error (_("invalid option -- %s"), s); + exit (1); + } + p = state->argv[state->next++]; + } + + pick_add_token (&lexlist, T_STRING, p); break; default: return 1; } + return 0; } +void +pick_message (mailbox_t mbox, message_t msg, size_t num, void *data) +{ + if (pick_eval (msg)) + { + mh_message_number (msg, &num); + if (list) + printf ("%lu\n", (unsigned long) num); + if (seq_list) + { + obstack_grow (&msgno_stk, &num, sizeof (num)); + msgno_count++; + } + } +} + +static int +action_add (void *item, void *data) +{ + mh_seq_add ((char *)item, (mh_msgset_t *)data, seq_flags); + return 0; +} + +/* NOTICE: For the compatibility with the RAND MH we have to support + the following command line syntax: + + --FIELD STRING + + where `FIELD' may be any string and which is equivalent to + `--field FIELD --pattern STRING'. Obviously this is in conflict + with the usual GNU long options paradigm which requires that any + unrecognized long option produce an error. Unfortunately, mh-pick.el + relies heavily on this syntax, so it can't be simply removed. + The approach taken here allows to properly recognize such syntax, + however it has an undesirable side effect: due to the specifics of + the underlying arpg library the --help and --usage options get + disabled. To make them work as well, the following approach is + taken: the mh-compatible syntax gets enabled only if the file + descriptor of stdin is not connected to a terminal, which is true + when invoked from mh-pick.el module. Otherwise, it is disabled + and the standard GNU long option syntax is in force. */ int main (int argc, char **argv) { + int status; int index; + mailbox_t mbox; + mh_msgset_t msgset; + int flags; + flags = isatty (0) ? 0 : ARGP_NO_ERRS; mu_init_nls (); - mh_argp_parse (argc, argv, options, mh_option, args_doc, doc, - opt_handler, NULL, &index); + mh_argp_parse (argc, argv, flags, options, mh_option, + args_doc, doc, opt_handler, NULL, &index); + if (pick_parse (lexlist)) + return 1; + + mbox = mh_open_folder (current_folder, 0); + + argc -= index; + argv += index; + + if (seq_list) + obstack_init (&msgno_stk); + + mh_msgset_parse (mbox, &msgset, argc, argv, "all"); + status = mh_iterate (mbox, &msgset, pick_message, NULL); + + if (seq_list) + { + mh_msgset_t msgset; + msgset.count = msgno_count; + msgset.list = obstack_finish (&msgno_stk); + list_do (seq_list, action_add, (void*) &msgset); + } + + mh_global_save_state (); + return status; } |