diff --git a/guile/mu-guile.texi b/guile/mu-guile.texi index 21c98c60..bfdc1064 100644 --- a/guile/mu-guile.texi +++ b/guile/mu-guile.texi @@ -59,7 +59,7 @@ Welcome to @t{mu-guile}! @t{mu} is a program for indexing and searching your e-mails. It can search your messages in many different ways, but sometimes that may not be enough. If you have very specific queries, or want do generate some -statistics, you need some more power. +statistics, you need some more power. @t{mu-guile} is made for those cases. @t{mu-guile} exposes the internals of @t{mu} and its database to the @t{guile} programming language. Guile is the @@ -80,6 +80,7 @@ Trust me, it's not very hard -- and it it's @emph{fun}! * Attachments and other parts:: * Statistics:: * Plotting data:: +* Writing scripts:: Appendices @@ -94,21 +95,22 @@ Appendices * Making sure it works:: @end menu -This chapter walks you through the installation and the basic setup. +This chapter walks you through the installation and the basic setup. @node Installation @section Installation @t{mu-guile} is part of @t{mu} - by installing the latter, the former is -installed as well. At the time of writing, there are no distribution-provided -packaged versions of @t{mu-guile}; so for now, you need to follow the steps -below. +necessarily installed as well. At the time of writing, there are no +distribution-provided packaged versions of @t{mu-guile}; so for now, you need +to follow the steps below. @subsection Guile 2.x @t{mu-guile} is built automatically when @t{mu} is built, if you have -@t{guile} installed (@t{mu} checks for this during @t{configure}). Thus, the -first step is to ensure you have @t{guile} installed. +@t{guile} version 2 or higher. (@t{mu} checks for this during +@t{configure}). Thus, the first step is to ensure you have @t{guile} +installed. On Debian/Ubuntu you can install @t{guile} 2.x using the @t{guile-2.0-dev} package (and its dependencies): @@ -116,7 +118,7 @@ package (and its dependencies): $ sudo apt-get install guile-2.0-dev @end example -At the time of write, there are no official packages for +At the time of writing, there are no official packages for Fedora@footnote{@url{https://bugzilla.redhat.com/show_bug.cgi?id=678238}}. If you are using Fedora or any other system that does not have packages, you need to compile @t{guile} from @@ -139,9 +141,6 @@ $ sudo yum install gnuplot @subsection mu -At the time of writing, there are no distribution packages for @t{mu-guile}, -so we are assuming installation from source packages. - Assuming @t{guile} 2.x is installed correctly, @t{mu} finds it during its @t{configure}-stage, and creates @t{mu-guile}. Building @t{mu} follows the normal steps -- please see the @t{mu} documentation for the details. @@ -227,7 +226,7 @@ Now, copy-paste the following after the prompt: @noindent After pressing @key{Enter}, you should get a list of all subjects of messages -that match @t{hello}: +that match @t{hello}: @verbatim ... @@ -240,7 +239,7 @@ Subject: When all is lost @noindent If all this works, congratulations! @t{mu-guile} is installed now, ready to -serve your every whim! +serve your every searching need! @node Initializing mu-guile @chapter Initializing mu-guile @@ -269,7 +268,7 @@ This program is free software, and you are welcome to redistribute it under certain conditions; type `,show c' for details. Enter `,help' for help. -scheme@(guile-user)> +scheme@(guile-user)> @end verbatim @end cartouche @@ -293,7 +292,7 @@ scheme@(guile-user)> (mu:initialize) This opens the database for reading, using the default location of @file{~/.mu}@footnote{If you keep your @t{mu} database in a non-standard place, use @code{(mu:initialize "/path/to/my/mu/")}} - + Now, @t{mu-guile} is ready to go. In the next chapter, we go through the modules and show what you can do with them. @@ -311,15 +310,15 @@ In this chapter, we discuss searching messages and doing things with them. @node Finding messages @section Finding messages Now we are ready to retrieve some messages from the system. There are two main -functions to do this: +procedures to do this: @itemize @item @code{(mu:message-list [])} -@item @code{(mu:for-each-message [])} +@item @code{(mu:for-each-message [])} @end itemize @noindent -The first function, @code{mu:message-list} returns a list of all messages +The first procedure, @code{mu:message-list} returns a list of all messages matching @t{}; if you leave @t{} out, it returns @emph{all} messages. For example, to get all messages with @t{coffee} in the subject line: @@ -333,7 +332,7 @@ $1 = (#< 9040640> #< 9040630> @noindent Apparently, we have three messages matching @t{subject:coffee}, so we get a list of three @code{} objects. Let's just use the -@code{mu:subject} function ('method') provided by @code{} objects +@code{mu:subject} procedure ('method') provided by @code{} objects to retrieve the subject-field (more about methods in the next section). For your convenience, @t{guile} has saved the result of our last query in a @@ -346,8 +345,8 @@ $2 = "Re: best coffee ever!" @end verbatim @noindent -The second function we mentioned, @code{mu:for-each-message}, executes some -function for each message matched by the search expression (or @emph{all} +The second procedure we mentioned, @code{mu:for-each-message}, executes some +procedure for each message matched by the search expression (or @emph{all} messages if the search expression is omitted): @verbatim @@ -380,7 +379,7 @@ Now that we've seen how to retrieve lists of message objects @code{} defines the following methods that all take a single @code{} object as a parameter. We won't go into the exact meanings -for all of these functions here - for the details about various flags / +for all of these procedures here - for the details about various flags / properties, please refer to the @t{mu-find} man-page. @itemize @@ -472,19 +471,19 @@ e-mail corpus. We can retrieve the sender and recipients of an e-mail message using methods like @code{mu:from}, @code{mu:to} etc.; @xref{Message methods}. These -functions return the list of recipients as a single string; however, often it +procedures return the list of recipients as a single string; however, often it is more useful to deal with recipients as separate objects. @menu -* Contact functions and objects:: +* Contact procedures and objects:: * All contacts:: -* Utility functions:: +* Utility procedures:: * Example - mutt export:: @end menu -@node Contact functions and objects -@section Contact functions and objects +@node Contact procedures and objects +@section Contact procedures and objects Message objects (@pxref{Messages}) have a method @t{mu:contacts}: @@ -529,17 +528,17 @@ Sometimes you may want to inspect @emph{all} the different contacts in the @t{mu} database. This is useful, for instance, when exporting contacts to some external format that can then be important in an e-mail program. -To enable this, there is the function @code{mu:for-each-contact}, defined as +To enable this, there is the procedure @code{mu:for-each-contact}, defined as - @code{(mu:for-each-contact function [search-expression])}. + @code{(mu:for-each-contact procedure [search-expression])}. This will aggregate the unique contacts from @emph{all} messages matching @t{} (when it is left empty, it will match all messages in -the database), and execute @t{function} for each of them. +the database), and execute @t{procedure} for each of them. -The @t{function} receives an object of the type @t{}, +The @t{procedure} receives an object of the type @t{}, which is a @emph{subclass} of the @t{} class discussed in -@xref{Contact functions and objects}. @t{} objects +@xref{Contact procedures and objects}. @t{} objects expose the following additional methods: @itemize @@ -553,11 +552,11 @@ The method assumes an e-mail address is unique for a certain contact; if a certain e-mail address occurs with different names, it uses the most recent non-empty name. -@node Utility functions -@section Utility functions +@node Utility procedures +@section Utility procedures To make dealing with contacts even easier, there are a number of utility -functions that can save you a bit of typing. +procedures that can save you a bit of typing. For converting contacts to some textual form, there is @code{(mu:contact->string format)}, which takes a contact and returns a text string with @@ -578,8 +577,8 @@ something like: alias [] "<" ">" @end verbatim -Anyway, there is the function @code{(mu:contact->string format)} -that we can use to do the conversion. +@t{mu guile} provides the procedure @code{(mu:contact->string +format)} that we can use to do the conversion. We may want to focus on people with whom we have frequent correspondence; so we may want to limit ourselves to people we have seen at least 10 times in the @@ -654,7 +653,7 @@ if there is none. Then, we may want to save the part to a file; this can be done using either: @itemize @item @code{(mu:save part )} - save a part to a temporary file, return the file -name@footnote{the temporary filename is a predictable function of (user-id, +name@footnote{the temporary filename is a predictable procedure of (user-id, msg-path, part-index)} @item @code{(mu:save-as )} - save part to file at path @end itemize @@ -703,7 +702,7 @@ probably be a bit more elegant. @node Statistics @chapter Statistics -@t{mu-guile} offers some convenience functions to determine various statistics +@t{mu-guile} offers some convenience procedures to determine various statistics about the messages in the database. @menu @@ -743,16 +742,16 @@ scheme@@(guile-user)> ;; subject length scheme@@(guile-user)> (mu:correl mu:size (lambda (msg) (string-length (mu:subject msg))) "subject:hello") $5 = -0.10804368622292 -scheme@@(guile-user)> +scheme@@(guile-user)> @end example @node Tabulating values @section Tabulating values -@code{(mu:tabulate [])} applies @t{} to each +@code{(mu:tabulate [])} applies @t{} to each message matching @t{} (leave empty to match @emph{all} messages), and returns a associative list (a list of pairs) with each of the different -results of @t{} and their frequencies. For fields that contain lists +results of @t{} and their frequencies. For fields that contain lists of values (such as address-fields), each of the values in the list is added separately. @@ -785,7 +784,7 @@ exec guile -s $0 $@ @end lisp -The function @code{weekday-table} uses @code{mu:tabulate-message} to get the +The procedure @code{weekday-table} uses @code{mu:tabulate-message} to get the frequencies per hour -- this returns a list of pairs: @verbatim ((5 . 2339) (0 . 2278) (4 . 2800) (2 . 3184) (6 . 1856) (3 . 2833) (1 . 2993)) @@ -826,10 +825,10 @@ something like this: (sort (mu:tabulate mu:subject) (lambda (a b) (> (cdr a) (cdr b)))) - 10) + 10) @end lisp -If this is not short enough, @t{mu-guile} offers a convenience function to do +If this is not short enough, @t{mu-guile} offers a convenience procedure to do this: @code{mu:top-n-most-frequent}. For example, to get the top-10 people we sent mail to most often: @@ -848,7 +847,7 @@ You can plot the results in the format produced by @code{mu:tabulate} with the @t{gnuplot}@footnote{@url{http://www.gnuplot.info/}} program to be installed on your system. -The @code{mu:plot-histogram} function takes the following arguments: +The @code{mu:plot-histogram} procedure takes the following arguments: @code{(mu:plot-histogram <x-label> <y-label> [<want-ascii>])} @@ -908,6 +907,57 @@ Frequency @end verbatim @end cartouche +@node Writing scripts +@chapter Writing scripts + +The @t{mu} program has built-in support for running guile-scripts, and comes +with a number of examples. + +You can get a list of all scripts with the @t{mu script} command: +@verbatim +$ mu script +Available scripts (use --verbose for details): + * find-dups: find duplicate messages + * msgs-count: count the number of messages matching some query + * msgs-per-day: graph the number of messages per day + * msgs-per-hour: graph the number of messages per hour + * msgs-per-month: graph the number of messages per month + * msgs-per-year: graph the number of messages per year + * msgs-per-year-month: graph the number of messages per year-month +@end verbatim + +You can then execute such a script by its name: +@verbatim +$ mu msgs-per-month --textonly --query=hello + + + Messages per month matching hello + + 240 ++-+-----+----+-----+-----+-----+----+-----+-----+-----+----+-----+-++ + | + + + + "/tmp/filewi9H0N" using 2:xticlabels(1) ****** | + 220 ++ * * ****** + | * * * * + 200 ++ * * * +* + | * * * * + 180 ++ ****** * * * +* + | * * * * * * + 160 ****** * * * * * +* + * * * * * * * * + * ******* * * * * ****** * * + 140 *+ ** * * * * * * ******** +* + * ** ******* * * * * * ** ** * + 120 *+ ** ** ******* * * * * ** ** +* + * ** ** ** * * * ******* ** ** * + 100 *+ ** ** ** * * * * ** ** ** +* + * + ** + ** + ** + * + * + + * + * + ** + ** + ** + * + 80 ********************************************************************** + Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + Month +@end verbatim + +Please refer to the @t{mu-script} man-page for some details on writing your +own scripts. + @node GNU Free Documentation License @appendix GNU Free Documentation License diff --git a/man/mu-script.1 b/man/mu-script.1 index db065d31..03d774a0 100644 --- a/man/mu-script.1 +++ b/man/mu-script.1 @@ -1,32 +1,31 @@ -.TH MU SCRIPT 1 "March 2013" "User Manuals" +.TH MU SCRIPT 1 "June 2013" "User Manuals" .SH NAME -mu script\- run a mu script +mu script\- show the available mu scripts, and run them. .SH SYNOPSIS -.B mu script [options] [--script=<script>] [<pattern>] [-- [script-options]] +.B mu script [options] [<pattern>] + +.B mu <script-name> [<script-options>] .SH DESCRIPTION -\fBmu script\fR is the \fBmu\fR command to list available \fBmu\fR scripts, -and run them. The scripts are implemented in the Guile programming language, -and thus only work if your \fBmu\fR is built with support for Guile. In +\fBmu script\fR is the \fBmu\fR command to list available \fBmu\fR scripts. +The scripts are to be implemented in the Guile programming language, and +therefore only work if your \fBmu\fR is built with support for Guile. In addition, many scripts require you to have \fBgnuplot\fR installed. Without any parameters, \fBmu script\fR lists the available scripts. If you -provide a pattern (regular expression), only the scripts whose name or -one-line description match this pattern, are listed. See the examples below. +provide a pattern (a regular expression), only the scripts whose name or +one-line description match this pattern are listed. See the examples below. \fBmu\fR ships with a number of scripts. .SH OPTIONS .TP -\fB\-\-script=\fR\fI<script>\fR -run the given script. - \fB\-\-verbose\fR,\fB\-v\fR when listing the available scripts, show the long descriptions. @@ -45,10 +44,10 @@ List all available scripts matching \fImonth\fR (long descriptions): $ mu script -v month .fi -Run the \fImsgs-per-month\fR script, and pass it the \fI--textonly\fR -parameter: +Run the \fImsgs-per-month\fR script for messages matching 'hello', and pass it +the \fI--textonly\fR parameter: .nf - $ mu script --script=msgs-per-month -- --textonly + $ mu msgs-per-month --query=hello --textonly .fi .SH RETURN VALUE @@ -60,8 +59,8 @@ code when this is not the case. You can make your own Scheme scripts accessible through \fBmu script\fR by putting them in \fI<muhome>/scripts\fR (which is typically -\fI~/.mu/scripts\fR). It is a good idea to document it using some special -comments in the source code: +\fI~/.mu/scripts\fR). It is a good idea to document the scripts by using some +special comments in the source code: .nf ;; INFO: this is my script -- one-line description ;; INFO: (longer description) diff --git a/mu/mu-cmd-script.c b/mu/mu-cmd-script.c index 2c2f02a5..b4fd1531 100644 --- a/mu/mu-cmd-script.c +++ b/mu/mu-cmd-script.c @@ -177,9 +177,9 @@ mu_cmd_script (MuConfig *opts, GError **err) if (err && *err) goto leave; - if (!opts->script) { + if (g_strcmp0 (opts->cmdstr, "script") == 0) { print_scripts (scripts, !opts->nocolor, opts->verbose, - opts->params[1], err); + opts->script_params[0], err); goto leave; } @@ -191,8 +191,7 @@ mu_cmd_script (MuConfig *opts, GError **err) } /* do it! */ - mu_script_guile_run (msi, opts->muhome, - (const gchar**)&opts->params[1], err); + mu_script_guile_run (msi, opts->muhome, opts->script_params, err); leave: /* this won't be reached, unless there is some error */ mu_script_info_list_destroy (scripts); diff --git a/mu/mu-cmd.c b/mu/mu-cmd.c index b1c2d393..8062b00d 100644 --- a/mu/mu-cmd.c +++ b/mu/mu-cmd.c @@ -593,6 +593,8 @@ set_log_options (MuConfig *opts) logopts |= MU_LOG_OPTIONS_DEBUG; } + + MuError mu_cmd_execute (MuConfig *opts, GError **err) { @@ -627,10 +629,7 @@ mu_cmd_execute (MuConfig *opts, GError **err) case MU_CONFIG_CMD_SERVER: merr = with_store (mu_cmd_server, opts, FALSE, err); break; default: - show_usage (); - mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, - "unknown command '%s'", opts->cmdstr); - return MU_ERROR_IN_PARAMETERS; + merr = MU_ERROR_IN_PARAMETERS; break; } return merr; diff --git a/mu/mu-config.c b/mu/mu-config.c index d967f0a0..4af45027 100644 --- a/mu/mu-config.c +++ b/mu/mu-config.c @@ -300,13 +300,14 @@ config_options_group_script (void) { GOptionGroup *og; GOptionEntry entries[] = { - {"script", 0, 0, G_OPTION_ARG_STRING, &MU_CONFIG.script, - "script to run (see `mu help script')", "<script>"}, - {NULL, 0, 0, 0, NULL, NULL, NULL} + {G_OPTION_REMAINING, 0,0, G_OPTION_ARG_STRING_ARRAY, + &MU_CONFIG.params, "script parameters", NULL}, + {NULL, 0, 0, 0, NULL, NULL, NULL} }; og = g_option_group_new("script", "Options for the 'script' command", "", NULL, NULL); + g_option_group_add_entries(og, entries); return og; @@ -461,7 +462,7 @@ cmd_from_string (const char *str) { "index", MU_CONFIG_CMD_INDEX }, { "mkdir", MU_CONFIG_CMD_MKDIR }, { "remove", MU_CONFIG_CMD_REMOVE }, - { "script", MU_CONFIG_CMD_SCRIPT }, + { "script", MU_CONFIG_CMD_SCRIPT }, { "server", MU_CONFIG_CMD_SERVER }, { "verify", MU_CONFIG_CMD_VERIFY }, { "view", MU_CONFIG_CMD_VIEW } @@ -474,7 +475,8 @@ cmd_from_string (const char *str) if (strcmp (str, cmd_map[i].name) == 0) return cmd_map[i].cmd; - return MU_CONFIG_CMD_UNKNOWN; + /* if we don't recognize it, it may be some script */ + return MU_CONFIG_CMD_SCRIPT; } @@ -673,6 +675,17 @@ parse_params (int *argcp, char ***argvp, GError **err) rv = g_option_context_parse (context, argcp, argvp, err) && cmd_help (); break; + case MU_CONFIG_CMD_SCRIPT: + /* all unknown commands are passed to 'script' */ + g_option_context_set_ignore_unknown_options (context, TRUE); + group = get_option_group (MU_CONFIG.cmd); + g_option_context_add_group (context, group); + rv = g_option_context_parse (context, argcp, argvp, err); + MU_CONFIG.script = g_strdup (MU_CONFIG.cmdstr); + /* argvp contains the script parameters */ + MU_CONFIG.script_params = (const char**)&((*argvp)[1]); + break; + default: group = get_option_group (MU_CONFIG.cmd); if (group) diff --git a/mu/mu-config.h b/mu/mu-config.h index 10154c0b..dfd278d2 100644 --- a/mu/mu-config.h +++ b/mu/mu-config.h @@ -183,6 +183,7 @@ struct _MuConfig { * (open) the attmnt using xdgopen */ /* options for mu-script */ gchar *script; /* script to run */ + const char **script_params; /* parameters for scripts */ }; typedef struct _MuConfig MuConfig; diff --git a/mu/mu-help-strings.txt b/mu/mu-help-strings.txt index b99268e3..c7be6b68 100644 --- a/mu/mu-help-strings.txt +++ b/mu/mu-help-strings.txt @@ -142,25 +142,21 @@ mu4e e-mail client. #BEGIN MU_CONFIG_CMD_SCRIPT #STRING -mu script [--script=<script>] [<pattern>] [-v] -- [script-options] +mu script [<pattern>] [-v] +mu <script-name> [<script options>] #STRING -Without any parameter, list the available scripts. With <pattern>, -list only those scripts whose name or one-line description matches it. -With -v, give longer descriptions of each script. -With --script=<script>, run the script whose name is <script>; pass -any arguments to the script after the '--' double-dash. +List the available scripts and/or run them (if mu was built with support for +scripts). With <pattern>, list only those scripts whose name or one-line +description matches it. With -v, get a longer description for each script. Some examples: -List all available scripts (one-line descriptions): - $ mu script List all available scripts matching 'month' (long descriptions): $ mu script -v month Run the 'msgs-per-month' script, and pass it the '--textonly' parameter: - $ mu script --script=msgs-per-month -- --textonly -(as mentioned, parameters to the script follow the '--') + $ mu msgs-per-month --textonly #END