From 19ddaee88f69bb0ff325e793ffc67a35c5126714 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Tue, 5 Jul 2011 07:41:41 +0300 Subject: [PATCH 01/17] * update the cheatsheet --- www/cheatsheet.org | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/www/cheatsheet.org b/www/cheatsheet.org index f8d82de9..2af8d7ab 100644 --- a/www/cheatsheet.org +++ b/www/cheatsheet.org @@ -32,11 +32,16 @@ If =mu= did not guess the right Maildir, you can set it explicitly: #+html:
 $ mu find to:Jack subject:jellyfish tumbleweed
*** messages between 2 kilobytes and a 2Mb, written in December 2009 with an attachment from Bill -#+html: $ mu find size:2k..2m date:20091201..20093112 flag:attach from:bill - -*** unread messages about soccer or socrates or ... -#+html:
  $ mu find 'subject:soc*' flag:unread
+#+html:
 $ mu find size:2k..2m date:20091201..20093112 flag:attach from:bill
+*** signed messages about apples *OR* oranges +#+html:
  $ mu find flag:signed apples OR oranges
+ +*** unread messages about things starting with 'soc' (soccer, society, socrates, ...) +#+html:
  $ mu find 'subject:soc*' flag:unread
+ + (search using the '*' wildcard is available since mu 0.9.6) + ** Finding contacts Contacts (names + email addresses) are cached separately, and can be @@ -87,6 +92,20 @@ If =mu= did not guess the right Maildir, you can set it explicitly: (since =mu= version 0.9.6) +** Integration with mail clients + + The =mu-find= man page contains examples for =mutt= and =wanderlust=. + +** Viewing messages + + You can view message contents with =mu view=; it does not use the database + and simply takes a message file as it's argument: + +#+html:
 $ mu view ~/Maildir/inbox/cur/message24
+ + You can also use =--color= to get colorized output, and =--summary= to get a + summary of the message contents instead of the whole thing. + ** Further processing of matched messages If you need to process the results of your queries with some other program, @@ -106,20 +125,8 @@ If =mu= did not guess the right Maildir, you can set it explicitly: #+html:
 $ mu find --format=xml pancake
will give you a list of pancake-related messages in XML-format. - -** Integration with mail clients - The =mu-find= man page contains examples for =mutt= and =wanderlust=. -** Viewing messages - - You can view message contents with =mu view=; it does not use the database - and simply takes a message file as it's argument: - -#+html:
 $ mu view ~/Maildir/inbox/cur/message24
- - You can also use =--color= to get colorized output, and =--summary= to get a - summary of the message contents instead of the whole thing. #+html:
© 2011 Dirk-Jan C. Binnema
#+begin_html From 88d761e6fbdcce0763f6a6c885a45071560813d4 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 02:09:46 +0300 Subject: [PATCH 02/17] * beginning of mu find --exec config (WIP) --- src/mu-config.c | 4 +++- src/mu-config.h | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/mu-config.c b/src/mu-config.c index b0fdaa86..732f16b8 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -176,7 +176,9 @@ config_options_group_find (MuConfig *opts) "clear old links before filling a linksdir (false)", NULL}, {"format", 'o', 0, G_OPTION_ARG_STRING, &opts->formatstr, "output format ('plain'(*), 'links', 'xml'," - "'json', 'sexp', 'xquery')", NULL}, + "'json', 'sexp', 'xquery')", NULL}, + /* {"exec", 'e', 0, G_OPTION_ARG_STRING, &opts->exec, */ + /* "execute command on each match message", NULL}, */ {NULL, 0, 0, 0, NULL, NULL, NULL} }; diff --git a/src/mu-config.h b/src/mu-config.h index e6617122..361b8b16 100644 --- a/src/mu-config.h +++ b/src/mu-config.h @@ -103,7 +103,10 @@ struct _MuConfig { char *bookmark; /* use bookmark */ char *formatstr; /* output type * (plain,links,xml,json,sexp) */ - + char *exec; /* command to execute on the + * files for the matched + * messages */ + /* options for view */ gboolean separate; /* add separator between * multiple messages in mu From efc7ad8af439c4cc943b35ce68187a9dd85e427a Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 02:10:58 +0300 Subject: [PATCH 03/17] * mu-msg-fields, mu-find.1: don't support references or tags in mu find output for now --- man/mu-find.1 | 3 --- src/mu-msg-fields.c | 7 +++---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/man/mu-find.1 b/man/mu-find.1 index d9a1747e..3edeccef 100644 --- a/man/mu-find.1 +++ b/man/mu-find.1 @@ -292,9 +292,6 @@ search parameters; the complete list: s Message \fBs\fRubject i Message-\fBi\fRd m \fBm\fRaildir - r \fBr\fReferences (message ids In-reply-to, References as - comma-separated list) - x \fBr\fTags (\fIX-Label\fR contents as comma-separated list) .fi diff --git a/src/mu-msg-fields.c b/src/mu-msg-fields.c index 5b1f8417..71b12359 100644 --- a/src/mu-msg-fields.c +++ b/src/mu-msg-fields.c @@ -204,16 +204,15 @@ static const MuMsgField FIELD_DATA[] = { MU_MSG_FIELD_ID_REFS, MU_MSG_FIELD_TYPE_STRING_LIST, NULL, 'r', 'R', - FLAG_GMIME | FLAG_XAPIAN_VALUE | - FLAG_XAPIAN_PREFIX_ONLY + FLAG_GMIME | FLAG_XAPIAN_VALUE | FLAG_XAPIAN_PREFIX_ONLY }, { MU_MSG_FIELD_ID_TAGS, MU_MSG_FIELD_TYPE_STRING_LIST, "tag", 'x', 'X', - FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_VALUE | - FLAG_XAPIAN_PREFIX_ONLY + FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_PREFIX_ONLY | + FLAG_NORMALIZE } }; From ca6212944c4bb606d201892d1a4e1ed18ca201c9 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 02:11:55 +0300 Subject: [PATCH 04/17] * mu-cmd-find.c: some preparation for mu find --exec= (WIP) --- src/mu-cmd-find.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/mu-cmd-find.c b/src/mu-cmd-find.c index b622403a..ec1c02ec 100644 --- a/src/mu-cmd-find.c +++ b/src/mu-cmd-find.c @@ -51,7 +51,7 @@ enum _OutputFormat { FORMAT_SEXP, FORMAT_XML, FORMAT_XQUERY, - + FORMAT_NONE }; typedef enum _OutputFormat OutputFormat; @@ -141,8 +141,8 @@ sort_field_from_string (const char* fieldstr) static gboolean -run_query_format (MuMsgIter *iter, MuConfig *opts, - OutputFormat format, size_t *count) +output_query_results (MuMsgIter *iter, MuConfig *opts, + OutputFormat format, size_t *count) { switch (format) { @@ -165,14 +165,12 @@ run_query_format (MuMsgIter *iter, MuConfig *opts, } -static gboolean -run_query (MuQuery *xapian, const gchar *query, MuConfig *opts, - OutputFormat format, size_t *count) +static MuMsgIter* +run_query (MuQuery *xapian, const gchar *query, MuConfig *opts, size_t *count) { GError *err; MuMsgIter *iter; MuMsgFieldId sortid; - gboolean rv; sortid = MU_MSG_FIELD_ID_NONE; if (opts->sortfield) { @@ -188,10 +186,25 @@ run_query (MuQuery *xapian, const gchar *query, MuConfig *opts, if (!iter) { g_warning ("error: %s", err->message); g_error_free (err); - return FALSE; + return NULL; } - rv = run_query_format (iter, opts, format, count); + return iter; +} + + +static gboolean +process_query (MuQuery *xapian, const gchar *query, MuConfig *opts, + OutputFormat format, size_t *count) +{ + MuMsgIter *iter; + gboolean rv; + + iter = run_query (xapian, query, opts, count); + if (!iter) + return FALSE; + + rv = output_query_results (iter, opts, format, count); if (rv && count && *count == 0) g_warning ("no matching messages found"); @@ -489,8 +502,6 @@ ansi_reset_maybe (MuMsgFieldId mfid, gboolean color) } - - static const char* display_field (MuMsgIter *iter, MuMsgFieldId mfid) { @@ -842,7 +853,7 @@ mu_cmd_find (MuConfig *opts) if (!query_params_valid (opts) || !format_params_valid(opts)) return MU_EXITCODE_ERROR; - + format = get_output_format (opts->formatstr); xapian = get_query_obj(); query = get_query (opts); @@ -853,7 +864,7 @@ mu_cmd_find (MuConfig *opts) if (format == FORMAT_XQUERY) rv = print_xapian_query (xapian, query, &count); else - rv = run_query (xapian, query, opts, format, &count); + rv = process_query (xapian, query, opts, format, &count); mu_query_destroy (xapian); g_free (query); From 86d7968bc27fbf9c6270638b1b0c00a2ee66f978 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 02:12:59 +0300 Subject: [PATCH 05/17] * make mu_msg_to_list optionally strip leading/trailing whitespace --- src/mu-str.c | 12 +++++++++--- src/mu-str.h | 5 +++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/mu-str.c b/src/mu-str.c index 950a9f8d..bf0e597b 100644 --- a/src/mu-str.c +++ b/src/mu-str.c @@ -399,7 +399,7 @@ mu_str_from_list (const GSList *lst, char sepa) } GSList* -mu_str_to_list (const char *str, char sepa) +mu_str_to_list (const char *str, char sepa, gboolean strip) { GSList *lst; gchar **strs, **cur; @@ -413,8 +413,14 @@ mu_str_to_list (const char *str, char sepa) sep[0] = sepa; strs = g_strsplit (str, sep, -1); - for (cur = strs, lst = NULL; cur && *cur; ++cur) - lst = g_slist_prepend (lst, g_strdup(*cur)); + for (cur = strs, lst = NULL; cur && *cur; ++cur) { + char *elm; + elm = g_strdup(*cur); + if (strip) + elm = g_strstrip (elm); + + lst = g_slist_prepend (lst, elm); + } lst = g_slist_reverse (lst); g_strfreev (strs); diff --git a/src/mu-str.h b/src/mu-str.h index 175cb51b..98bc1828 100644 --- a/src/mu-str.h +++ b/src/mu-str.h @@ -277,14 +277,15 @@ char* mu_str_from_list (const GSList *lst, char sepa); /** - * convert a #sepa-separated list of strings in to a GSList + * convert a #se0pa-separated list of strings in to a GSList * * @param str a #sepa-separated list of strings * @param the separator character + * @param remove leading/trailing whitespace from the string * * @return a newly allocated GSList (free with mu_str_free_list) */ -GSList* mu_str_to_list (const char *str, char sepa); +GSList* mu_str_to_list (const char *str, char sepa, gboolean strip); /** From 0ec6df636973bc1b8c14fc50edfd7bba49368fdd Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 02:14:00 +0300 Subject: [PATCH 06/17] * use the new strip option for list->str --- src/mu-msg-doc.cc | 2 +- src/mu-store.cc | 21 +++++++++++++++++++-- src/tests/test-mu-str.c | 20 +++++++++++++++++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/mu-msg-doc.cc b/src/mu-msg-doc.cc index 62ecac35..190a33c5 100644 --- a/src/mu-msg-doc.cc +++ b/src/mu-msg-doc.cc @@ -92,7 +92,7 @@ mu_msg_doc_get_str_list_field (MuMsgDoc *self, MuMsgFieldId mfid, try { /* return a comma-separated string as a GSList */ const std::string s (self->doc().get_value(mfid)); - return s.empty() ? NULL : mu_str_to_list(s.c_str(),','); + return s.empty() ? NULL : mu_str_to_list(s.c_str(),',',TRUE); } MU_XAPIAN_CATCH_BLOCK_RETURN(NULL); } diff --git a/src/mu-store.cc b/src/mu-store.cc index 253ae1e7..dc356d94 100644 --- a/src/mu-store.cc +++ b/src/mu-store.cc @@ -394,9 +394,26 @@ add_terms_values_string_list (Xapian::Document& doc, MuMsg *msg, if (lst && mu_msg_field_xapian_term (mfid)) { while (lst) { + size_t len; + char *val; + /* try stack-allocation, it's much faster*/ + len = strlen ((char*)lst->data); + if (G_LIKELY(len < 1024)) + val = (char*)g_alloca(len+1); + else + val = (char*)g_malloc(len+1); + + strcpy (val, (char*)lst->data); + + if (mu_msg_field_normalize (mfid)) + mu_str_normalize_in_place (val, TRUE); + doc.add_term (prefix(mfid) + - std::string((char*)lst->data, 0, - MU_STORE_MAX_TERM_LENGTH)); + std::string(val, 0, MU_STORE_MAX_TERM_LENGTH)); + + if (!(G_LIKELY(len < 1024))) + g_free (val); + lst = g_slist_next ((GSList*)lst); } } diff --git a/src/tests/test-mu-str.c b/src/tests/test-mu-str.c index 088b043e..d7fee6cd 100644 --- a/src/tests/test-mu-str.c +++ b/src/tests/test-mu-str.c @@ -323,19 +323,31 @@ static void test_mu_str_to_list (void) { { - const char *items[]= {"foo", "bar", "cuux", NULL}; - GSList *lst = mu_str_to_list ("foo@bar@cuux",'@'); + const char *items[]= {"foo", "bar ", "cuux", NULL}; + GSList *lst = mu_str_to_list ("foo@bar @cuux",'@', FALSE); assert_cmplst (lst, items); mu_str_free_list (lst); } { - GSList *lst = mu_str_to_list (NULL,'x'); + GSList *lst = mu_str_to_list (NULL,'x',FALSE); g_assert (lst == NULL); mu_str_free_list (lst); } } +static void +test_mu_str_to_list_strip (void) +{ + { + const char *items[]= {"foo", "bar", "cuux", NULL}; + GSList *lst = mu_str_to_list ("foo@bar @cuux",'@', TRUE); + assert_cmplst (lst, items); + mu_str_free_list (lst); + } +} + + static void @@ -474,6 +486,8 @@ main (int argc, char *argv[]) test_mu_str_from_list); g_test_add_func ("/mu-str/mu-str-to-list", test_mu_str_to_list); + g_test_add_func ("/mu-str/mu-str-to-list-strip", + test_mu_str_to_list_strip); g_test_add_func ("/mu-str/mu_str_date_parse_hdwmy", test_mu_str_date_parse_hdwmy); From 74409c3404a65910bc8b7aa5f964d78d6c21978b Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 02:14:33 +0300 Subject: [PATCH 07/17] * mu-msg-file: remove leading/trailing whitespace in tags --- src/mu-msg-file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mu-msg-file.c b/src/mu-msg-file.c index 19d7d4de..5409bb0d 100644 --- a/src/mu-msg-file.c +++ b/src/mu-msg-file.c @@ -715,7 +715,7 @@ get_tags (MuMsgFile *self) obj = GMIME_OBJECT(self->_mime_msg); return mu_str_to_list (g_mime_object_get_header - (obj, "X-Label"), ','); + (obj, "X-Label"), ',', TRUE); } From 8b687e678c605105d0959e6cd748f8c40bc0a560 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 02:14:53 +0300 Subject: [PATCH 08/17] * update test-cases --- src/tests/test-mu-msg.c | 4 ++-- src/tests/test-mu-query.c | 10 ++++------ src/tests/testdir2/bar/cur/mail1 | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/tests/test-mu-msg.c b/src/tests/test-mu-msg.c index 8c1f5760..bb26c2d8 100644 --- a/src/tests/test-mu-msg.c +++ b/src/tests/test-mu-msg.c @@ -321,8 +321,8 @@ test_mu_msg_tags (void) ==, 1217530645); tags = mu_msg_get_tags (msg); - g_assert_cmpstr ((char*)tags->data,==,"paradise"); - g_assert_cmpstr ((char*)tags->next->data,==,"lost"); + g_assert_cmpstr ((char*)tags->data,==,"Paradise"); + g_assert_cmpstr ((char*)tags->next->data,==,"losT"); g_assert (tags->next->next == NULL); mu_msg_unref (msg); diff --git a/src/tests/test-mu-query.c b/src/tests/test-mu-query.c index 6a5ec5be..9d97e226 100644 --- a/src/tests/test-mu-query.c +++ b/src/tests/test-mu-query.c @@ -45,7 +45,7 @@ fill_database (const char *testdir) " --quiet", MU_PROGRAM, tmpdir, testdir); - /* g_printerr ("\n%s\n", cmdline); */ + /* g_printerr ("\n%s\n", cmdline); */ g_assert (g_spawn_command_line_sync (cmdline, NULL, NULL, NULL, NULL)); @@ -450,8 +450,6 @@ test_mu_query_tags (void) } - - int main (int argc, char *argv[]) { @@ -477,9 +475,9 @@ main (int argc, char *argv[]) g_test_add_func ("/mu-query/test-mu-query-tags", test_mu_query_tags); - /* g_log_set_handler (NULL, */ - /* G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, */ - /* (GLogFunc)black_hole, NULL); */ + g_log_set_handler (NULL, + G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, + (GLogFunc)black_hole, NULL); rv = g_test_run (); diff --git a/src/tests/testdir2/bar/cur/mail1 b/src/tests/testdir2/bar/cur/mail1 index 801179a8..b5bb3075 100644 --- a/src/tests/testdir2/bar/cur/mail1 +++ b/src/tests/testdir2/bar/cur/mail1 @@ -4,7 +4,7 @@ Subject: Fere libenter homines id quod volunt credunt To: "Julius Caesar" Message-id: <3BE9E6535E3029448670913581E7A1A20D852173@emss35m06.us.lmco.com> MIME-version: 1.0 -x-label: paradise,lost +x-label: Paradise, losT Content-type: text/plain; charset=us-ascii Content-transfer-encoding: 7BIT Precedence: high From 543a4400abef4c9ae6b83243f1c6ba1d5dd94bb3 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 22:14:09 +0300 Subject: [PATCH 09/17] * configure.ac: don't error-out when there's not gtk+ and --with-gui=none is not provided. --- configure.ac | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index cbe65b71..73800528 100644 --- a/configure.ac +++ b/configure.ac @@ -149,6 +149,10 @@ AC_SUBST(XAPIAN_LIBS) # also, this has nothing to do with Xapian's software version AC_DEFINE(MU_XAPIAN_DB_VERSION,["9.7"], ['Schema' version of the database]) + +# +# we need gtk (2 or 3) for some of the graphical tools +# AC_ARG_WITH([gui], [AS_HELP_STRING([--with-gui=gtk2|gtk3|none])], [gui=$withval],[gui=auto]) @@ -175,7 +179,7 @@ AS_IF([test "x$gui" != "xnone" -a "x$have_gtk3" != "xyes"],[ gtk_version="`pkg-config --modversion gtk+-2.0`" ]) # only an error if we explicitly asked for it -AS_IF([test "x$have_gtk2" = "xno" -a "x$gui" != "auto"], +AS_IF([test "x$have_gtk2" = "xno" -a "x$gui" != "xauto"], AC_MSG_ERROR([GTK+ 2.x not found])) AM_CONDITIONAL(HAVE_GTK,[test "x$have_gtk2" = "xyes" -o "x$have_gtk3" = "xyes" ]) From a31ea71e0ad0d4b15517751cbc335944ccbdda27 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 6 Jul 2011 23:35:52 +0300 Subject: [PATCH 10/17] * basic implementation of --exec for mu find (WIP) --- src/mu-cmd-extract.c | 2 -- src/mu-cmd-find.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ src/mu-config.c | 4 ++-- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/mu-cmd-extract.c b/src/mu-cmd-extract.c index ba40bd9a..e99b10a7 100644 --- a/src/mu-cmd-extract.c +++ b/src/mu-cmd-extract.c @@ -296,8 +296,6 @@ save_parts (const char *path, const char *filename, MuConfig *opts) return rv; } - - #define color_maybe(C) do{ if (color) fputs ((C),stdout);}while(0) static void diff --git a/src/mu-cmd-find.c b/src/mu-cmd-find.c index ec1c02ec..564b0ac3 100644 --- a/src/mu-cmd-find.c +++ b/src/mu-cmd-find.c @@ -216,6 +216,54 @@ process_query (MuQuery *xapian, const gchar *query, MuConfig *opts, +static gboolean +exec_cmd_on_query (MuQuery *xapian, const gchar *query, MuConfig *opts, + size_t *count) +{ + MuMsgIter *iter; + gboolean rv; + + iter = run_query (xapian, query, opts, count); + if (!iter) + return FALSE; + + rv = TRUE; + while (!mu_msg_iter_is_done (iter)) { + const char* path; + path = mu_msg_get_path (mu_msg_iter_get_msg (iter, NULL)); + if (access (path, R_OK) == 0) { + gint status; + GError *err; + char *cmd; + cmd = g_strdup_printf ("%s %s", opts->exec, path); + err = NULL; /* FIXME: stdout/stderr */ + rv = g_spawn_command_line_sync (cmd, NULL, NULL, &status, + &err); + g_free (cmd); + if (!rv) { + g_warning ("command returned %d on %s: %s\n", + status, path, err->message); + g_error_free (err); + break; + } + } else + g_warning ("cannot execute command on %s: %s", + path, strerror(errno)); + + mu_msg_iter_next (iter); + } + + if (rv && count && *count == 0) + g_warning ("no matching messages found"); + + mu_msg_iter_destroy (iter); + + return rv; +} + + + + static gboolean format_params_valid (MuConfig *opts) { @@ -863,6 +911,8 @@ mu_cmd_find (MuConfig *opts) if (format == FORMAT_XQUERY) rv = print_xapian_query (xapian, query, &count); + else if (opts->exec) + rv = exec_cmd_on_query (xapian, query, opts, &count); else rv = process_query (xapian, query, opts, format, &count); diff --git a/src/mu-config.c b/src/mu-config.c index 732f16b8..4fb9b78f 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -177,8 +177,8 @@ config_options_group_find (MuConfig *opts) {"format", 'o', 0, G_OPTION_ARG_STRING, &opts->formatstr, "output format ('plain'(*), 'links', 'xml'," "'json', 'sexp', 'xquery')", NULL}, - /* {"exec", 'e', 0, G_OPTION_ARG_STRING, &opts->exec, */ - /* "execute command on each match message", NULL}, */ + {"exec", 'e', 0, G_OPTION_ARG_STRING, &opts->exec, + "execute command on each match message", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; From adbab879a22c8871bf8a24ef3f8df6500b5bbf42 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Thu, 7 Jul 2011 22:18:33 +0300 Subject: [PATCH 11/17] * mu-cmd-find.c: turn the ^L-separator into a terminator. --- src/mu-cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mu-cmd.c b/src/mu-cmd.c index 89a53197..20f7adaf 100644 --- a/src/mu-cmd.c +++ b/src/mu-cmd.c @@ -196,7 +196,7 @@ mu_cmd_view (MuConfig *opts) if (rv != MU_EXITCODE_OK) break; /* add a separator between two messages? */ - if (opts->params[i+1] && opts->separate) + if (opts->separate) g_print ("%c", VIEW_SEPARATOR); } From de150bd9f78c9bc929f487e07526d6beed0e34c6 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Thu, 7 Jul 2011 22:59:44 +0300 Subject: [PATCH 12/17] * rename the --separate option into --terminator (mu view) --- man/mu-view.1 | 7 +++---- src/mu-cmd.c | 6 +++--- src/mu-config.c | 4 ++-- src/mu-config.h | 6 +++--- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/man/mu-view.1 b/man/mu-view.1 index f7b62387..7d672fb5 100644 --- a/man/mu-view.1 +++ b/man/mu-view.1 @@ -25,10 +25,9 @@ any). instead of displaying the full message, output a summary based upon the first lines of the message. -\fB\-\-separate\fR -add an ascii \\014 (0x0c, or \fIform-feed\fR) between messages when displaying -multiple messages. - +\fB\-\-terminator\fR +terminate messaages with a \\f (\fIform-feed\fR) characters when displaying +them. This is useful when you want to further process them. .SH BUGS diff --git a/src/mu-cmd.c b/src/mu-cmd.c index 20f7adaf..e873870e 100644 --- a/src/mu-cmd.c +++ b/src/mu-cmd.c @@ -34,7 +34,7 @@ #include "mu-contacts.h" #include "mu-runtime.h" -#define VIEW_SEPARATOR '\f' /* form-feed */ +#define VIEW_TERMINATOR '\f' /* form-feed */ static void @@ -196,8 +196,8 @@ mu_cmd_view (MuConfig *opts) if (rv != MU_EXITCODE_OK) break; /* add a separator between two messages? */ - if (opts->separate) - g_print ("%c", VIEW_SEPARATOR); + if (opts->terminator) + g_print ("%c", VIEW_TERMINATOR); } return rv; diff --git a/src/mu-config.c b/src/mu-config.c index 732f16b8..c3ebc5a3 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -245,8 +245,8 @@ config_options_group_view (MuConfig *opts) GOptionEntry entries[] = { {"summary", 0, 0, G_OPTION_ARG_NONE, &opts->summary, "only show a short summary of the message (false)", NULL}, - {"separate", 0, 0, G_OPTION_ARG_NONE, &opts->separate, - "separate messages with ascii-0x07 (form-feed)", NULL}, + {"terminator", 0, 0, G_OPTION_ARG_NONE, &opts->terminator, + "terminate messages with ascii-0x07 (\\f, form-feed)", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; diff --git a/src/mu-config.h b/src/mu-config.h index 361b8b16..ff05fefe 100644 --- a/src/mu-config.h +++ b/src/mu-config.h @@ -108,9 +108,9 @@ struct _MuConfig { * messages */ /* options for view */ - gboolean separate; /* add separator between - * multiple messages in mu - * view */ + gboolean terminator; /* add separator \f between + * multiple messages in mu + * view */ /* output to a maildir with symlinks */ char *linksdir; /* maildir to output symlinks */ From 31d568e0760bc155ec2fbef1486faeee08cb6e05 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 10 Jul 2011 14:07:35 +0300 Subject: [PATCH 13/17] * rename the --separate option into --terminate (mu view), update test cases --- src/mu-config.c | 2 +- src/tests/test-mu-cmd.c | 7 +++---- src/tests/testdir2/bar/cur/mail6 | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 src/tests/testdir2/bar/cur/mail6 diff --git a/src/mu-config.c b/src/mu-config.c index c3ebc5a3..01b8e2cf 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -245,7 +245,7 @@ config_options_group_view (MuConfig *opts) GOptionEntry entries[] = { {"summary", 0, 0, G_OPTION_ARG_NONE, &opts->summary, "only show a short summary of the message (false)", NULL}, - {"terminator", 0, 0, G_OPTION_ARG_NONE, &opts->terminator, + {"terminate", 0, 0, G_OPTION_ARG_NONE, &opts->terminator, "terminate messages with ascii-0x07 (\\f, form-feed)", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; diff --git a/src/tests/test-mu-cmd.c b/src/tests/test-mu-cmd.c index 4e72def5..762609c1 100644 --- a/src/tests/test-mu-cmd.c +++ b/src/tests/test-mu-cmd.c @@ -115,7 +115,7 @@ test_mu_index (void) store = mu_store_new (xpath, NULL, NULL); g_assert (store); - g_assert_cmpuint (mu_store_count (store), ==, 8); + g_assert_cmpuint (mu_store_count (store), ==, 9); mu_store_destroy (store); g_free (muhome); @@ -498,7 +498,7 @@ test_mu_view_multi_separate (void) tmpdir = test_mu_common_get_random_tmpdir(); g_assert (g_mkdir_with_parents (tmpdir, 0700) == 0); - cmdline = g_strdup_printf ("%s view --separate --muhome=%s " + cmdline = g_strdup_printf ("%s view --terminate --muhome=%s " "%s%cbar%ccur%cmail5 " "%s%cbar%ccur%cmail5", MU_PROGRAM, @@ -517,8 +517,7 @@ test_mu_view_multi_separate (void) len = strlen(output); /* g_print ("\n[%s](%u)\n", output, len); */ - g_assert_cmpuint (len,==,165); - + g_assert_cmpuint (len,==,166); g_free (output); g_free (cmdline); diff --git a/src/tests/testdir2/bar/cur/mail6 b/src/tests/testdir2/bar/cur/mail6 new file mode 100644 index 00000000..1d5c1d74 --- /dev/null +++ b/src/tests/testdir2/bar/cur/mail6 @@ -0,0 +1,18 @@ +Date: Thu, 31 Jul 2008 14:57:25 -0400 +From: "Geoff Tate" +Subject: eyes of a stranger +To: "Enrico Fermi" +Message-id: <3BE9E6535E302944823E7A1A20D852173@msg.id> +MIME-version: 1.0 +X-label: @NextActions, operation:mindcrime, Queensrÿche +Content-type: text/plain; charset=us-ascii +Content-transfer-encoding: 7BIT +Precedence: high + +And I raise my head and stare +Into the eyes of a stranger +I've always known that the mirror never lies +People always turn away +From the eyes of a stranger +Afraid to know what +Lies behind the stare From f59ca5af2d5cdc728f81d1024c316bcb38bb19a7 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 10 Jul 2011 14:08:16 +0300 Subject: [PATCH 14/17] * support special characters in X-Labels --- src/mu-msg-fields.c | 2 +- src/mu-store.cc | 3 +++ src/tests/test-mu-query.c | 38 +++++++++++++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/mu-msg-fields.c b/src/mu-msg-fields.c index 71b12359..072a26b2 100644 --- a/src/mu-msg-fields.c +++ b/src/mu-msg-fields.c @@ -212,7 +212,7 @@ static const MuMsgField FIELD_DATA[] = { MU_MSG_FIELD_TYPE_STRING_LIST, "tag", 'x', 'X', FLAG_GMIME | FLAG_XAPIAN_TERM | FLAG_XAPIAN_PREFIX_ONLY | - FLAG_NORMALIZE + FLAG_NORMALIZE | FLAG_XAPIAN_ESCAPE } }; diff --git a/src/mu-store.cc b/src/mu-store.cc index dc356d94..284efbb7 100644 --- a/src/mu-store.cc +++ b/src/mu-store.cc @@ -407,6 +407,9 @@ add_terms_values_string_list (Xapian::Document& doc, MuMsg *msg, if (mu_msg_field_normalize (mfid)) mu_str_normalize_in_place (val, TRUE); + + if (mu_msg_field_xapian_escape (mfid)) + mu_str_ascii_xapian_escape_in_place (val); doc.add_term (prefix(mfid) + std::string(val, 0, MU_STORE_MAX_TERM_LENGTH)); diff --git a/src/tests/test-mu-query.c b/src/tests/test-mu-query.c index 9d97e226..6cfeff3f 100644 --- a/src/tests/test-mu-query.c +++ b/src/tests/test-mu-query.c @@ -87,7 +87,6 @@ run_and_count_matches (const char *xpath, const char *query) MuQuery *mquery; MuMsgIter *iter; guint count1, count2; - GHashTable *hash; mquery = mu_query_new (xpath, NULL); g_assert (query); @@ -107,9 +106,6 @@ run_and_count_matches (const char *xpath, const char *query) mu_query_destroy (mquery); g_assert (iter); - hash = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify)g_free, NULL); - assert_no_dups (iter); /* run query twice, to test mu_msg_iter_reset */ @@ -414,7 +410,7 @@ test_mu_query_attach (void) /* g_print ("(%s)\n", xpath); */ - for (i = 0; i != G_N_ELEMENTS(queries); ++i) + for (i = 0; i != G_N_ELEMENTS(queries); ++i) g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query), ==, queries[i].count); @@ -450,6 +446,36 @@ test_mu_query_tags (void) } +static void +test_mu_query_tags_02 (void) +{ + gchar *xpath; + int i; + + QResults queries[] = { + { "x:paradise", 1}, + { "tag:@NextActions", 1}, + { "x:queensrÿche", 1}, + { "tag:lost OR tag:operation:mindcrime", 2}, + }; + + xpath = fill_database (MU_TESTMAILDIR2); + g_assert (xpath != NULL); + + /* g_print ("(%s)\n", xpath); */ + + for (i = 0; i != G_N_ELEMENTS(queries); ++i) { + /* g_print ("%s\n", queries[i].query); */ + g_assert_cmpuint (run_and_count_matches (xpath, queries[i].query), + ==, queries[i].count); + } + + g_free (xpath); +} + + + + int main (int argc, char *argv[]) { @@ -474,6 +500,8 @@ main (int argc, char *argv[]) test_mu_query_attach); g_test_add_func ("/mu-query/test-mu-query-tags", test_mu_query_tags); + g_test_add_func ("/mu-query/test-mu-query-tags_02", + test_mu_query_tags_02); g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION, From 76b946e653845df30be2e9aeeefed5a8b4dc023e Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 10 Jul 2011 14:19:45 +0300 Subject: [PATCH 15/17] * update man page --- man/mu-find.1 | 17 +++++++++++++++-- man/mu-view.1 | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/man/mu-find.1 b/man/mu-find.1 index 3edeccef..eb14b1d8 100644 --- a/man/mu-find.1 +++ b/man/mu-find.1 @@ -1,4 +1,4 @@ -.TH MU FIND 1 "June 2011" "User Manuals" +.TH MU FIND 1 "July 2011" "User Manuals" .SH NAME @@ -374,6 +374,20 @@ Note: when \fBmu\fR creates a Maildir for these links, it automatically inserts a \fI.noindex\fR file, to exclude the directory from \fBmu index\fR. +.TP +\fB\-\-exec\fR=\fI\fR +the \fB\-\-exec\fR command causes the \fIcommand\fR to be executed on each +matched message; for example, to see the raw text of all messages +matching 'milkshake', you could use: +.nf + $ mu find milkshake --exec='less' +.fi +which is roughly equivalent to: +.nf + $ mu find milkshake --fields="l" | xargs less +.fi + + .TP \fB\-b\fR, \fB\-\-bookmark\fR=\fI\fR use a bookmarked search query. Using this option, a query from your bookmark @@ -448,7 +462,6 @@ Find all unread messages with attachments: .fi - .SS Integrating mu find with mail clients .TP diff --git a/man/mu-view.1 b/man/mu-view.1 index 7d672fb5..b78e2a88 100644 --- a/man/mu-view.1 +++ b/man/mu-view.1 @@ -1,4 +1,4 @@ -.TH MU VIEW 1 "May 2011" "User Manuals" +.TH MU VIEW 1 "July 2011" "User Manuals" .SH NAME @@ -25,7 +25,7 @@ any). instead of displaying the full message, output a summary based upon the first lines of the message. -\fB\-\-terminator\fR +\fB\-\-terminate\fR terminate messaages with a \\f (\fIform-feed\fR) characters when displaying them. This is useful when you want to further process them. From 5441b1945fa63ad5407583a06cd519ce23f3e919 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 10 Jul 2011 14:48:45 +0300 Subject: [PATCH 16/17] * cleanup --exec implementation --- Makefile.am | 1 + src/mu-cmd-find.c | 64 ++++++++++++++++++++++++++++------------------- src/mu-store.cc | 59 +++++++++++++++++++------------------------ 3 files changed, 65 insertions(+), 59 deletions(-) diff --git a/Makefile.am b/Makefile.am index 2c89d631..7ea8e8a1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,6 +53,7 @@ cc10: line33: @$(PMCCABE) -c `find -name '*.c' -o -name '*.cc'` \ | grep -v mu-str-normalize.c \ + | grep -v config_options_group_find \ | grep -v tests \ | awk '($$5 > 33)' diff --git a/src/mu-cmd-find.c b/src/mu-cmd-find.c index 564b0ac3..8dc7eb20 100644 --- a/src/mu-cmd-find.c +++ b/src/mu-cmd-find.c @@ -215,6 +215,38 @@ process_query (MuQuery *xapian, const gchar *query, MuConfig *opts, } +static gboolean +exec_cmd (const char *path, const char *cmd) +{ + gint status; + GError *err; + char *cmdline, *escpath; + gboolean rv; + + if (access (path, R_OK) != 0) { + g_warning ("cannot read %s: %s", path, strerror(errno)); + return FALSE; + } + + escpath = g_strescape (path, NULL); + + cmdline = g_strdup_printf ("%s %s", cmd, escpath); + err = NULL; + rv = g_spawn_command_line_sync (cmdline, NULL, NULL, + &status, &err); + g_free (cmdline); + g_free (escpath); + + if (!rv) { + g_warning ("command returned %d on %s: %s\n", + status, path, err->message); + g_error_free (err); + return FALSE; + } + + return TRUE; +} + static gboolean exec_cmd_on_query (MuQuery *xapian, const gchar *query, MuConfig *opts, @@ -223,34 +255,14 @@ exec_cmd_on_query (MuQuery *xapian, const gchar *query, MuConfig *opts, MuMsgIter *iter; gboolean rv; - iter = run_query (xapian, query, opts, count); - if (!iter) + if (!(iter = run_query (xapian, query, opts, count))) return FALSE; - rv = TRUE; - while (!mu_msg_iter_is_done (iter)) { - const char* path; - path = mu_msg_get_path (mu_msg_iter_get_msg (iter, NULL)); - if (access (path, R_OK) == 0) { - gint status; - GError *err; - char *cmd; - cmd = g_strdup_printf ("%s %s", opts->exec, path); - err = NULL; /* FIXME: stdout/stderr */ - rv = g_spawn_command_line_sync (cmd, NULL, NULL, &status, - &err); - g_free (cmd); - if (!rv) { - g_warning ("command returned %d on %s: %s\n", - status, path, err->message); - g_error_free (err); - break; - } - } else - g_warning ("cannot execute command on %s: %s", - path, strerror(errno)); - - mu_msg_iter_next (iter); + for (rv = TRUE, *count = 0; !mu_msg_iter_is_done (iter); mu_msg_iter_next(iter)) { + rv = exec_cmd (mu_msg_get_path (mu_msg_iter_get_msg (iter, NULL)), + opts->exec); + if (rv) + ++*count; } if (rv && count && *count == 0) diff --git a/src/mu-store.cc b/src/mu-store.cc index 284efbb7..0e98647a 100644 --- a/src/mu-store.cc +++ b/src/mu-store.cc @@ -374,6 +374,30 @@ add_terms_values_str (Xapian::Document& doc, char *val, doc.add_term (prefix(mfid) + std::string(val, 0, MU_STORE_MAX_TERM_LENGTH)); } + + +static void +add_terms_values_string (Xapian::Document& doc, MuMsg *msg, + MuMsgFieldId mfid) +{ + const char *orig; + char *val; + size_t len; + + if (!(orig = mu_msg_get_field_string (msg, mfid))) + return; /* nothing to do */ + + /* try stack-allocation, it's much faster*/ + len = strlen (orig); + val = (char*)(G_LIKELY(len < 1024)?g_alloca(len+1):g_malloc(len+1)); + strcpy (val, orig); + + add_terms_values_str (doc, val, mfid); + + if (!(G_LIKELY(len < 1024))) + g_free (val); +} + static void @@ -402,18 +426,10 @@ add_terms_values_string_list (Xapian::Document& doc, MuMsg *msg, val = (char*)g_alloca(len+1); else val = (char*)g_malloc(len+1); - strcpy (val, (char*)lst->data); - - if (mu_msg_field_normalize (mfid)) - mu_str_normalize_in_place (val, TRUE); - - if (mu_msg_field_xapian_escape (mfid)) - mu_str_ascii_xapian_escape_in_place (val); - doc.add_term (prefix(mfid) + - std::string(val, 0, MU_STORE_MAX_TERM_LENGTH)); - + add_terms_values_str (doc, val, mfid); + if (!(G_LIKELY(len < 1024))) g_free (val); @@ -422,29 +438,6 @@ add_terms_values_string_list (Xapian::Document& doc, MuMsg *msg, } } - - -static void -add_terms_values_string (Xapian::Document& doc, MuMsg *msg, - MuMsgFieldId mfid) -{ - const char *orig; - char *val; - size_t len; - - if (!(orig = mu_msg_get_field_string (msg, mfid))) - return; /* nothing to do */ - - /* try stack-allocation, it's much faster*/ - len = strlen (orig); - val = (char*)(G_LIKELY(len < 1024)?g_alloca(len+1):g_malloc(len+1)); - strcpy (val, orig); - - add_terms_values_str (doc, val, mfid); - - if (!(G_LIKELY(len < 1024))) - g_free (val); -} struct PartData { PartData (Xapian::Document& doc, MuMsgFieldId mfid): From e06f036c72e80eea510eb5bf173e5af5e390a066 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 10 Jul 2011 14:53:09 +0300 Subject: [PATCH 17/17] * add test mail to Makefile.am --- src/tests/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 501a6eee..c25add06 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -124,6 +124,7 @@ EXTRA_DIST= \ testdir2/bar/cur/mail4 \ testdir2/bar/cur/mail5 \ testdir2/bar/cur/181736.eml \ + testdir2/bar/cur/mail6 \ testdir2/bar/tmp/.noindex \ testdir2/bar/new/.noindex \ testdir2/Foo/cur/mail5 \