* initial plumbing and some documentation for xml/json/sexp output through

--format= parameter.

  also, add --format=links, --format=xquery
This commit is contained in:
Dirk-Jan C. Binnema 2011-01-05 20:35:50 +02:00
parent 56f1664d8c
commit 6539ae4bd7
6 changed files with 258 additions and 56 deletions

View File

@ -1,4 +1,4 @@
.TH MU FIND 1 "November 2010" "User Manuals"
.TH MU FIND 1 "January 2011" "User Manuals"
.SH NAME
@ -12,9 +12,9 @@ database
.SH DESCRIPTION
\fBmu find\fR is the \fBmu\fR sub-command for searching e-mail message that
\fBmu find\fR is the \fBmu\fR command for searching e-mail message that
were stored earlier using
\fBmu index(1)\bR.
\fBmu index(1)\fR.
.SH SEARCHING MAIL
@ -25,7 +25,18 @@ search pattern. For example:
$ mu find subject:snow from:john
.fi
would find all messages from John with 'snow' in the subject field.
would find all messages from John with 'snow' in the subject field, something
like:
.nf
2009-03-05 17:57:33 EET Lucia <lucia@example.com> running in the snow
2009-03-05 18:38:24 EET Marius <marius@foobar.com> Re: running in the snow
.fi
Note, this the default, plain-text output, which is the default, so you don't
have to use \fB--format=plain\fR. For other types of output (such as symlinks,
XML, JSON or s-expressions), see the discussion in the \fBOPTIONS\fR-section
below about \fB--format\fR.
The search pattern is taken as a command-line parameter. If the search
parameter consists of multiple parts (as in the example) they are treated as
@ -104,6 +115,7 @@ extra discusion.
First, the message flags field describes certain properties of the message, as
listed in the following table:
.nf
d,draft Draft Message
f,flagged Flagged
@ -162,16 +174,21 @@ at 23:59.
To get all messages between (inclusive) the 5th of May 2009 and the 2nd of
June 2010, you could use:
.nf
$ mu find date:20090505..20100602
.fi
Characters like ':', '/', '-' and single '.' are ignored, so the following is
equivalent but more readable:
.nf
$ mu find date:2009-05-05..2010-06-02
.fi
Precision is up to the minute and 24-hour notation for times is used, so
another example would be:
.nf
$ mu find date:2009-05-05/12:23..2010-06-02/17:18
.fi
@ -193,6 +210,7 @@ examples will explain this:
Using this notation, you can for example match messages between two and three
weeks old:
.nf
$ mu find date:3w..2w
.fi
@ -222,9 +240,11 @@ from), which will replace with the actual field in the output. Fields that are
not known will be output as-is, allowing for some simple formatting.
For example:
.nf
$ mu find subject:snow --fields "d f s"
.fi
would list the date, subject and sender of all messages with 'snow' in the
their subject.
@ -278,15 +298,33 @@ choice, but for dates it may be more useful to sort in the opposite direction.
.TP
\fB\-\-xquery\fR
shows the Xapian query corresponding to your search terms. This is primarily
meant for for debugging purposes.
.TP
\fB\-k\fR, \fB\-\-summary\-len\fR=\fI<len>\fR
output a summary based on up to \fI\len\fR lines of the message. The default is
.B 0
, or no summary.
output a summary based on up to \fI\len\fR lines of the message. The default
is \fB0\fR: no summary at all.
.TP
\fB\-\-format\fR=\fIplain|links|xquery|xml|json|sexp\fR
output results in the specified format.
The default is \fBplain\fR, i.e normal output with one line per message.
\fBlinks\fR outputs the results as a maildir with symbolic links to the found
messages. This enables easy integration with mail-clients (see below for more
information). See \fB\-\-linksdir\fR and \fB\-\-clearlinks\fR below.
\fBxml\fR formats the search results as XML.
\fBjson\fR formats the search results as JSON (\fIJavascript Object
Notation\fR).
\fBsexp\fR formats the search results as an s-expression as used in Lisp
programming environments.
\fBxquery\fR shows the Xapian query corresponding to your search terms. This
is meant for for debugging purposes.
.TP
\fB\-\-linksdir\fR \fR=\fI<dir>\fR and \fB\-c\fR, \fB\-\-clearlinks\fR
output the results as a maildir with symbolic links to the found
@ -364,51 +402,69 @@ Find all messages with attachments:
.TP
\fBmutt\fR
For \fBmutt\fR you can use the following in your \fImuttrc\fR; pressing the F8
key will start a search, and F9 will take you to the results.
.nf
# mutt macros for mu
macro index <F8> "<shell-escape>mu find -c -l ~/Maildir/search " \
macro index <F8> "<shell-escape>mu find -c -l ~/Maildir/search " \\
"mu find"
macro index <F9> "<change-folder-readonly>~/Maildir/search" \
"display mu find results"
macro index <F9> "<change-folder-readonly>~/Maildir/search" \\
"mu find results"
.fi
.TP
\fBWanderlust\fR
If you use the Wanderlust e-mail client for \fBemacs\fR, the following
definitions can be used; typing 'Q' will start a query.
.nf
;; mu integration for Wanderlust
(defvar mu-wl-mu-program "mu")
(defvar mu-wl-mu-program "/usr/local/bin/mu")
(defvar mu-wl-search-folder "search")
(defun mu-wl-search ()
"search for messages with `mu', and jump to the results"
(interactive)
(let* ((muexpr (read-string "Find messages matching: "))
(let* ((muexpr (read-string "Find messages matching: "))
(sfldr (concat elmo-maildir-folder-path "/"
mu-wl-search-folder))
(cmdline (concat mu-wl-mu-program " find "
"--clearlinks --linksdir='" sfldr "' "
muexpr)))
(= 0 (shell-command cmdline))))
"--clearlinks --format=links --linksdir='" sfldr "' "
muexpr))
(rv (shell-command cmdline)))
(cond
((= rv 0) (message "Query succeeded"))
((= rv 2) (message "No matches found"))
(t (message "Error running query")))
(= rv 0)))
(defun mu-wl-search-and-goto ()
"search and jump to the folder with the results"
(interactive)
(if (mu-wl-search)
(when (mu-wl-search)
(wl-summary-goto-folder-subr
(concat "." mu-wl-search-folder)
'force-update nil nil t)
(message "Query failed")))
(wl-summary-sort-by-date)))
;; querying both in summary and folder
(define-key wl-summary-mode-map (kbd "Q") ;; => query
'(lambda()(interactive)(mu-wl-search-and-goto)))
(define-key wl-folder-mode-map (kbd "Q") ;; => query
'(lambda()(interactive)(mu-wl-search-and-goto)))
.fi
.SH RETURN VALUE
\fBmu\fR returns 0 for searches with at least one matching message, 2 for
searches that do not match anything. In case of errors 1 or any number greater
than 2 will be returned.
.SH BUGS
Please report bugs if you find them:

View File

@ -37,10 +37,46 @@
#include "mu-runtime.h"
#include "mu-util.h"
#include "mu-util-db.h"
#include "mu-cmd.h"
#include "mu-output.h"
enum _OutputFormat {
FORMAT_JSON,
FORMAT_LINKS,
FORMAT_PLAIN,
FORMAT_SEXP,
FORMAT_XML,
FORMAT_XQUERY,
FORMAT_NONE
};
typedef enum _OutputFormat OutputFormat;
static OutputFormat
get_output_format (const char *formatstr)
{
int i;
struct {
const char* name;
OutputFormat format;
} formats [] = {
{MU_CONFIG_FORMAT_JSON, FORMAT_JSON},
{MU_CONFIG_FORMAT_LINKS, FORMAT_LINKS},
{MU_CONFIG_FORMAT_PLAIN, FORMAT_PLAIN},
{MU_CONFIG_FORMAT_SEXP, FORMAT_SEXP},
{MU_CONFIG_FORMAT_XML, FORMAT_XML},
{MU_CONFIG_FORMAT_XQUERY, FORMAT_XQUERY}
};
for (i = 0; i != G_N_ELEMENTS(formats); i++)
if (strcmp (formats[i].name, formatstr) == 0)
return formats[i].format;
return FORMAT_NONE;
}
static void
update_warning (void)
{
@ -55,7 +91,7 @@ print_xapian_query (MuQuery *xapian, const gchar *query)
{
char *querystr;
GError *err;
err = NULL;
querystr = mu_query_as_string (xapian, query, &err);
if (!querystr) {
@ -93,7 +129,7 @@ sort_field_from_string (const char* fieldstr)
static gboolean
run_query (MuQuery *xapian, const gchar *query, MuConfig *opts,
size_t *count)
OutputFormat format, size_t *count)
{
GError *err;
MuMsgIter *iter;
@ -116,14 +152,29 @@ run_query (MuQuery *xapian, const gchar *query, MuConfig *opts,
return FALSE;
}
if (opts->linksdir)
switch (format) {
case FORMAT_LINKS:
rv = mu_output_links (iter, opts->linksdir, opts->clearlinks,
count);
else
break;
case FORMAT_PLAIN:
rv = mu_output_plain (iter, opts->fields, opts->summary_len,
count);
break;
case FORMAT_XML:
rv = mu_output_xml (iter, count);
break;
case FORMAT_JSON:
rv = mu_output_json (iter, count);
break;
case FORMAT_SEXP:
rv = mu_output_sexp (iter, count);
break;
default:
g_assert_not_reached ();
return FALSE;
}
if (count && *count == 0)
g_warning ("no matches found");
@ -136,6 +187,7 @@ run_query (MuQuery *xapian, const gchar *query, MuConfig *opts,
static gboolean
query_params_valid (MuConfig *opts)
{
OutputFormat format;
const gchar *xpath;
if (opts->linksdir)
@ -144,6 +196,23 @@ query_params_valid (MuConfig *opts)
return FALSE;
}
format = get_output_format (opts->formatstr);
if (format == FORMAT_NONE) {
g_warning ("invalid output format %s",
opts->formatstr ? opts->formatstr : "<none>");
return FALSE;
}
if (format == FORMAT_LINKS && !opts->linksdir) {
g_warning ("missing --linksdir argument");
return FALSE;
}
if (opts->linksdir && format != FORMAT_LINKS) {
g_warning ("--linksdir is only valid with --format=links");
return FALSE;
}
xpath = mu_runtime_xapian_dir();
if (mu_util_check_dir (xpath, TRUE, FALSE))
@ -261,12 +330,15 @@ mu_cmd_find (MuConfig *opts)
gboolean rv;
gchar *query;
size_t count;
OutputFormat format;
g_return_val_if_fail (opts, FALSE);
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_FIND, FALSE);
if (!query_params_valid (opts))
return MU_EXITCODE_ERROR;
format = get_output_format (opts->formatstr);
xapian = get_query_obj ();
if (!xapian)
@ -276,11 +348,11 @@ mu_cmd_find (MuConfig *opts)
query = get_query (opts);
if (!query)
return MU_EXITCODE_ERROR;
if (opts->xquery)
if (format == FORMAT_XQUERY)
rv = print_xapian_query (xapian, query);
else
rv = run_query (xapian, query, opts, &count);
rv = run_query (xapian, query, opts, format, &count);
mu_query_destroy (xapian);
g_free (query);
@ -291,4 +363,3 @@ mu_cmd_find (MuConfig *opts)
return (count == 0) ?
MU_EXITCODE_NO_MATCHES : MU_EXITCODE_OK;
}

View File

@ -49,15 +49,15 @@ config_options_group_mu (MuConfig *opts)
GOptionGroup *og;
GOptionEntry entries[] = {
{"debug", 'd', 0, G_OPTION_ARG_NONE, &opts->debug,
"print debug output to standard error", NULL},
"print debug output to standard error (false)", NULL},
{"quiet", 'q', 0, G_OPTION_ARG_NONE, &opts->quiet,
"don't give any progress information", NULL},
"don't give any progress information (false)", NULL},
{"version", 'v', 0, G_OPTION_ARG_NONE, &opts->version,
"display version and copyright information", NULL},
"display version and copyright information (false)", NULL},
{"muhome", 0, 0, G_OPTION_ARG_FILENAME, &opts->muhome,
"specify an alternative mu directory", NULL},
{"log-stderr", 0, 0, G_OPTION_ARG_NONE, &opts->log_stderr,
"log to standard error", NULL},
"log to standard error (false)", NULL},
{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY,
&opts->params, "parameters", NULL},
{NULL, 0, 0, 0, NULL, NULL, NULL}
@ -94,16 +94,16 @@ config_options_group_index (MuConfig * opts)
{"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &opts->maildir,
"top of the maildir", NULL},
{"reindex", 0, 0, G_OPTION_ARG_NONE, &opts->reindex,
"index even already indexed messages", NULL},
"index even already indexed messages (false)", NULL},
{"rebuild", 0, 0, G_OPTION_ARG_NONE, &opts->rebuild,
"rebuild the database from scratch", NULL},
"rebuild the database from scratch (false)", NULL},
{"autoupgrade", 0, 0, G_OPTION_ARG_NONE, &opts->autoupgrade,
"automatically upgrade the database with new mu versions",
"auto-upgrade the database with new mu versions (false)",
NULL},
{"nocleanup", 0, 0, G_OPTION_ARG_NONE, &opts->nocleanup,
"don't clean up the database after indexing", NULL},
"don't clean up the database after indexing (false)", NULL},
{"xbatchsize", 0, 0, G_OPTION_ARG_INT, &opts->xbatchsize,
"set a custom batchsize for committing to xapian or 0 for default", NULL},
"set transaction batchsize for xapian commits (0)", NULL},
{NULL, 0, 0, 0, NULL, NULL, NULL}
};
@ -129,6 +129,9 @@ set_group_find_defaults (MuConfig *opts)
}
}
if (!opts->formatstr) /* by default, use plain output */
opts->formatstr = MU_CONFIG_FORMAT_PLAIN;
if (opts->linksdir) {
gchar *old = opts->linksdir;
opts->linksdir = mu_util_dir_expand(opts->linksdir);
@ -138,6 +141,7 @@ set_group_find_defaults (MuConfig *opts)
g_free(old);
}
/* FIXME: some warning when summary_len < 0? */
if (opts->summary_len < 1)
opts->summary_len = 0;
@ -148,8 +152,6 @@ config_options_group_find (MuConfig *opts)
{
GOptionGroup *og;
GOptionEntry entries[] = {
{"xquery", 0, 0, G_OPTION_ARG_NONE, &opts->xquery,
"print the Xapian query (for debugging)", NULL},
{"fields", 'f', 0, G_OPTION_ARG_STRING, &opts->fields,
"fields to display in the output", NULL},
{"sortfield", 's', 0, G_OPTION_ARG_STRING, &opts->sortfield,
@ -159,13 +161,14 @@ config_options_group_find (MuConfig *opts)
{"descending", 'z', 0, G_OPTION_ARG_NONE, &opts->descending,
"sort in descending order (z -> a)", NULL},
{"summary-len", 'k', 0, G_OPTION_ARG_INT, &opts->summary_len,
"max number of lines for summary", NULL},
"max number of lines for summary (0)", NULL},
{"linksdir", 0, 0, G_OPTION_ARG_STRING, &opts->linksdir,
"output as symbolic links to a target maildir", NULL},
{"clearlinks", 0, 0, G_OPTION_ARG_NONE, &opts->clearlinks,
"clear old links before filling a linksdir", NULL},
/* {"output", 'o', 0, G_OPTION_ARG_STRING, &opts->output, */
/* "output type ('plain'(*), 'links', 'xml', 'json', 'sexp')", NULL}, */
"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') (plain)", NULL},
{NULL, 0, 0, 0, NULL, NULL, NULL}
};
@ -190,8 +193,7 @@ config_options_group_mkdir (MuConfig *opts)
/* set dirmode before, because '0000' is a valid mode */
opts->dirmode = 0755;
og = g_option_group_new("mkdir",
"options for the 'mkdir' command",
og = g_option_group_new("mkdir", "options for the 'mkdir' command",
"", NULL, NULL);
g_option_group_add_entries(og, entries);
@ -205,15 +207,15 @@ config_options_group_extract(MuConfig *opts)
GOptionEntry entries[] = {
{"save-attachments", 'a', 0, G_OPTION_ARG_NONE,
&opts->save_attachments,
"save all attachments", NULL},
"save all attachments (false)", NULL},
{"save-all", 0, 0, G_OPTION_ARG_NONE, &opts->save_all,
"save all parts (incl. non-attachments)", NULL},
"save all parts (incl. non-attachments) (false)", NULL},
{"parts", 0, 0, G_OPTION_ARG_STRING, &opts->parts,
"save specific parts (comma-separated list)", NULL},
{"target-dir", 0, 0, G_OPTION_ARG_FILENAME, &opts->targetdir,
"target directory for saving", NULL},
{"overwrite", 0, 0, G_OPTION_ARG_NONE, &opts->overwrite,
"overwrite existing files", NULL},
"overwrite existing files (false)", NULL},
{NULL, 0, 0, 0, NULL, NULL, NULL}
};

View File

@ -27,6 +27,13 @@
G_BEGIN_DECLS
#define MU_CONFIG_FORMAT_PLAIN "plain" /* plain text output */
#define MU_CONFIG_FORMAT_LINKS "links" /* output as symlinks */
#define MU_CONFIG_FORMAT_XML "xml" /* output xml */
#define MU_CONFIG_FORMAT_JSON "json" /* output json */
#define MU_CONFIG_FORMAT_SEXP "sexp" /* output sexps */
#define MU_CONFIG_FORMAT_XQUERY "xquery" /* output the xapian query */
enum _MuConfigCmd {
MU_CONFIG_CMD_INDEX,
MU_CONFIG_CMD_FIND,
@ -79,6 +86,9 @@ struct _MuConfig {
unsigned summary_len; /* max # of lines of msg in summary */
char *bookmark; /* use bookmark */
char *formatstr; /* output type
* (plain*,links,xml,json,sexp) */
/* output to a maildir with symlinks */
char *linksdir; /* maildir to output symlinks */
gboolean clearlinks; /* clear a linksdir before filling */
@ -103,8 +113,8 @@ typedef struct _MuConfig MuConfig;
*
* @param opts options
*/
MuConfig *mu_config_new (int *argcp, char ***argvp);
MuConfig *mu_config_new (int *argcp, char ***argvp)
G_GNUC_WARN_UNUSED_RESULT;
/**
* free the MuOptionsConfig structure; the the muhome and maildir
* members are heap-allocated, so must be freed.

View File

@ -100,7 +100,6 @@ mu_output_links (MuMsgIter *iter, const char* linksdir,
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (linksdir, FALSE);
g_return_val_if_fail (!mu_msg_iter_is_done (iter), FALSE);
/* note: we create the linksdir even if there are no search results */
if (!create_linksdir_maybe (linksdir, clearlinks))
@ -230,3 +229,32 @@ mu_output_plain (MuMsgIter *iter, const char *fields, size_t summary_len,
return TRUE;
}
gboolean
mu_output_xml (MuMsgIter *iter, size_t *count)
{
g_print ("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
g_print ("%s\n", __FUNCTION__);
return TRUE;
}
gboolean
mu_output_json (MuMsgIter *iter, size_t *count)
{
g_print ("{\n");
g_print ("\t%s\n", __FUNCTION__);
g_print ("}\n");
return TRUE;
}
gboolean
mu_output_sexp (MuMsgIter *iter, size_t *count)
{
g_print ("(%s)\n", __FUNCTION__);
return TRUE;
}

View File

@ -52,6 +52,41 @@ gboolean mu_output_plain (MuMsgIter *iter, const char *fields,
gboolean mu_output_links (MuMsgIter *iter, const char *linksdir,
gboolean clearlinks, size_t *count);
/**
* output the search results (MsgIter) as XML to standard
* output
*
* @param iter iterator pointing to a message row
* @param count output param to receive the number of messages found, or NULL
*
* @return TRUE if the printing succeeded, FALSE in case of error
*/
gboolean mu_output_xml (MuMsgIter *iter, size_t *count);
/**
* output the search results (MsgIter) as JSON to standard
* output
*
* @param iter iterator pointing to a message row
* @param count output param to receive the number of messages found, or NULL
*
* @return TRUE if the printing succeeded, FALSE in case of error
*/
gboolean mu_output_json (MuMsgIter *iter, size_t *count);
/**
* output the search results (MsgIter) as s-expressions to standard
* output
*
* @param iter iterator pointing to a message row
* @param count output param to receive the number of messages found, or NULL
*
* @return TRUE if the printing succeeded, FALSE in case of error
*/
gboolean mu_output_sexp (MuMsgIter *iter, size_t *count);
G_END_DECLS
#endif /*__MU_OUTPUT_H__*/