diff --git a/guile/mu-guile.texi b/guile/mu-guile.texi index 21c98c60..83ff0b59 100644 --- a/guile/mu-guile.texi +++ b/guile/mu-guile.texi @@ -28,7 +28,7 @@ Documentation License.'' @titlepage @title @t{mu-guile} - extending @t{mu} with Guile Scheme -@subtitle{version @value{mu-version}} +@subtitle version @value{mu-version} @author Dirk-Jan C. Binnema @c The following two commands start the copyright page. @@ -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/guile/mu.scm b/guile/mu.scm index 9fba1a4b..0da93325 100644 --- a/guile/mu.scm +++ b/guile/mu.scm @@ -277,7 +277,6 @@ either \"org-contact\", \"mutt-alias\", \"mutt-ab\", "\"")) (else (error "Unsupported format"))))) - ;; message parts diff --git a/guile/scripts/Makefile.am b/guile/scripts/Makefile.am index bacb0501..c8465965 100644 --- a/guile/scripts/Makefile.am +++ b/guile/scripts/Makefile.am @@ -22,7 +22,8 @@ EXTRA_DIST= \ msgs-per-hour.scm \ msgs-per-month.scm \ msgs-per-day.scm \ - msgs-per-year-month.scm + msgs-per-year-month.scm \ + find-dups.scm muguiledistscriptdir = $(pkgdatadir)/scripts/ muguiledistscript_SCRIPTS = $(EXTRA_DIST) diff --git a/guile/scripts/find-dups.scm b/guile/scripts/find-dups.scm new file mode 100755 index 00000000..c6845a8b --- /dev/null +++ b/guile/scripts/find-dups.scm @@ -0,0 +1,97 @@ +#!/bin/sh +exec guile -e main -s $0 $@ +!# +;; +;; Copyright (C) 2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> +;; +;; This program 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. +;; +;; This program 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 this program; if not, write to the Free Software Foundation, +;; Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +;; INFO: find duplicate messages +;; INFO: options: +;; INFO: --muhome=<muhome>: path to mu home dir + +(use-modules (mu) (mu script) (mu stats)) +(use-modules (ice-9 getopt-long) (ice-9 optargs) + (ice-9 popen) (ice-9 format) (ice-9 rdelim)) + +(define (md5sum path) + (let* ((port (open-pipe* OPEN_READ "md5sum" path)) + (md5 (read-delimited " " port))) + (close-pipe port) + md5)) + + +(define (find-dups) + (let ((id-table (make-hash-table 20000))) + ;; fill the hash with <msgid-size> => <list of paths> + (mu:for-each-message + (lambda (msg) + (let* ((id (format #f "~a-~d" (mu:message-id msg) + (mu:size msg))) + (lst (hash-ref id-table id))) + (if lst + (set! lst (cons (mu:path msg) lst)) + (set! lst (list (mu:path msg)))) + (hash-set! id-table id lst)))) + ;; list all the paths with multiple elements; check the md5sum to + ;; make 100%-minus-ε sure they are really the same file. + (hash-for-each + (lambda (id paths) + (if (> (length paths) 1) + (let ((hash (make-hash-table 10))) + (for-each + (lambda (path) + (let* ((md5 (md5sum path)) (lst (hash-ref hash md5))) + (if lst + (set! lst (cons path lst)) + (set! lst (list path))) + (hash-set! hash md5 lst))) + paths) + ;; hash now maps the md5sum to the messages... + (hash-for-each + (lambda (md5 mpaths) + (if (> (length mpaths) 1) + (begin + (format #t "md5sum: ~a:\n" md5) + (let ((num 1)) + (for-each + (lambda (path) + (format #t "\t~d. ~a\n" num path) + (set! num (+ 1 num))) + mpaths))))) + hash)))) + id-table))) + + + +(define (main args) + "Run some statistics function. +Interpret argument-list ARGS (like command-line +arguments). Possible arguments are: + --muhome (path to alternative mu home directory)." + (setlocale LC_ALL "") + (let* ((optionspec '( (muhome (value #t)) + (help (single-char #\h) (value #f)))) + (options (getopt-long args optionspec)) + (help (option-ref options 'help #f)) + (muhome (option-ref options 'muhome #f))) + (mu:initialize muhome) + (find-dups))) + + +;; Local Variables: +;; mode: scheme +;; End: diff --git a/lib/mu-container.c b/lib/mu-container.c index e4349632..34db96a5 100644 --- a/lib/mu-container.c +++ b/lib/mu-container.c @@ -520,7 +520,7 @@ count_colons (const char *str) static MuMsgIterThreadInfo* -thread_info_new (gchar *threadpath, gboolean root, gboolean child, +thread_info_new (gchar *threadpath, gboolean root, gboolean first_child, gboolean empty_parent, gboolean has_child, gboolean is_dup) { MuMsgIterThreadInfo *ti; @@ -529,9 +529,9 @@ thread_info_new (gchar *threadpath, gboolean root, gboolean child, ti->threadpath = threadpath; ti->level = count_colons (threadpath); /* hacky... */ - ti->prop = 0; + ti->prop = MU_MSG_ITER_THREAD_PROP_NONE; ti->prop |= root ? MU_MSG_ITER_THREAD_PROP_ROOT : 0; - ti->prop |= child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0; + ti->prop |= first_child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0; ti->prop |= empty_parent ? MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT : 0; ti->prop |= is_dup ? MU_MSG_ITER_THREAD_PROP_DUP : 0; ti->prop |= has_child ? MU_MSG_ITER_THREAD_PROP_HAS_CHILD : 0; diff --git a/lib/mu-msg-iter.h b/lib/mu-msg-iter.h index d14dea6e..f98d8d81 100644 --- a/lib/mu-msg-iter.h +++ b/lib/mu-msg-iter.h @@ -159,6 +159,8 @@ gboolean mu_msg_iter_calculate_threads (MuMsgIter *iter); enum _MuMsgIterThreadProp { + MU_MSG_ITER_THREAD_PROP_NONE = 0 << 0, + MU_MSG_ITER_THREAD_PROP_ROOT = 1 << 0, MU_MSG_ITER_THREAD_PROP_FIRST_CHILD = 1 << 1, MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT = 1 << 2, diff --git a/lib/mu-util.c b/lib/mu-util.c index 3c2a3607..ae1e859c 100644 --- a/lib/mu-util.c +++ b/lib/mu-util.c @@ -438,7 +438,6 @@ mu_util_locale_is_utf8 (void) gboolean mu_util_fputs_encoded (const char *str, FILE *stream) { - char *conv; int rv; g_return_val_if_fail (str, FALSE); @@ -449,10 +448,14 @@ mu_util_fputs_encoded (const char *str, FILE *stream) rv = fputs (str, stream); else { /* charset is _not_ utf8, so we actually have to * convert it..*/ - GError *err; - unsigned bytes; - err = NULL; + + GError *err; + unsigned bytes; + char *conv; + + err = NULL; conv = g_locale_from_utf8 (str, -1, (gsize*)&bytes, NULL, &err); + if (!conv || err) { /* conversion failed; this happens because is * some cases GMime may gives us non-UTF-8 @@ -462,14 +465,15 @@ mu_util_fputs_encoded (const char *str, FILE *stream) g_warning ("%s: g_locale_from_utf8 failed: %s", __FUNCTION__, err ? err->message : "conversion failed"); - g_clear_error (&err); g_free (conv); conv = g_strescape (str, NULL); } + g_clear_error (&err); + rv = fputs (conv, stream); g_free (conv); - } + } return (rv == EOF) ? FALSE : TRUE; } diff --git a/man/mu-find.1 b/man/mu-find.1 index 335a3c78..518ee7a8 100644 --- a/man/mu-find.1 +++ b/man/mu-find.1 @@ -1,4 +1,4 @@ -.TH MU FIND 1 "December 2012" "User Manuals" +.TH MU FIND 1 "June 2013" "User Manuals" .SH NAME @@ -53,9 +53,9 @@ messages. A wildcard search is a search where a \fB*\fR matches the last \fIn\fR character(s) in some string. The string must always start with one or more -characters before the wildcards. Since version 0.9.6, \fBmu\fR also supports -wildcard searches for all fields except maildirs and paths. So, to get all -mails with a subject containing a word starting with \fBcom\fR, you can use: +characters before the wildcard. \fBmu\fR supports wildcard searches for all +fields except maildirs and paths. To get all mails with a subject containing a +word starting with \fBcom\fR, you can use: .nf $ mu find 'subject:com*' @@ -68,9 +68,6 @@ remember that the '*' invokes the wildcard search only when used as the rightmost character of a search term. Furthermore, it is \fBnot\fR a regular expression. -In older versions of mu, queries were logged in \fI<mu-home>/mu.log\fR; -however, since version 0.9, mu no longer does this. - The basic way to search a message is to type some words matching it, as you would do in an internet search engine. For example, @@ -656,7 +653,7 @@ non-zero return value, for example: Please report bugs if you find them: .BR http://code.google.com/p/mu0/issues/list If you have specific messages which are not matched correctly, please attach -them (appropriately censored of course). +them (appropriately censored if needed). .SH AUTHOR 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 612eaf98..8062b00d 100644 --- a/mu/mu-cmd.c +++ b/mu/mu-cmd.c @@ -367,7 +367,7 @@ mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err) g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_REMOVE, MU_ERROR_INTERNAL); - /* note: params[0] will be 'add' */ + /* note: params[0] will be 'remove' */ if (!opts->params[0] || !opts->params[1]) { g_warning ("usage: mu remove <file> [<files>]"); mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, @@ -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 diff --git a/mu4e/mu4e-compose.el b/mu4e/mu4e-compose.el index 547829db..67e2dd89 100644 --- a/mu4e/mu4e-compose.el +++ b/mu4e/mu4e-compose.el @@ -192,11 +192,16 @@ If needed, set the Fcc header, and register the handler function." -(defconst mu4e~compose-hidden-headers +(defvar mu4e-compose-hidden-headers `("^References:" "^Face:" "^X-Face:" "^X-Draft-From:" "^User-agent:") "Hidden headers when composing.") +(defun mu4e~compose-hide-headers () + "Hide the headers as per `mu4e-compose-hidden-headers'." + (let ((message-hidden-headers mu4e-compose-hidden-headers)) + (message-hide-headers))) + (defconst mu4e~compose-address-fields-regexp "^\\(To\\|B?Cc\\|Reply-To\\|From\\):") @@ -211,8 +216,7 @@ appear on disk." (mu4e~compose-set-friendly-buffer-name) (mu4e~draft-insert-mail-header-separator) ;; hide some headers again - (let ((message-hidden-headers mu4e~compose-hidden-headers)) - (message-hide-headers)) + (mu4e~compose-hide-headers) (set-buffer-modified-p nil) ;; update the file on disk -- ie., without the separator (mu4e~proc-add (buffer-file-name) mu4e~draft-drafts-folder)) nil t)) @@ -255,7 +259,7 @@ appear on disk." (define-derived-mode mu4e-compose-mode message-mode "mu4e:compose" "Major mode for the mu4e message composition, derived from `message-mode'. \\{message-mode-map}." - (let ((message-hidden-headers mu4e~compose-hidden-headers)) + (progn (use-local-map mu4e-compose-mode-map) (make-local-variable 'message-default-charset) ;; if the default charset is not set, use UTF-8 @@ -279,7 +283,7 @@ appear on disk." (define-key mu4e-compose-mode-map (kbd "C-S-u") 'mu4e-update-mail-and-index) (define-key mu4e-compose-mode-map (kbd "C-c C-u") 'mu4e-update-mail-and-index) - + ;; setup the fcc-stuff, if needed (add-hook 'message-send-hook (defun mu4e~compose-save-before-sending () @@ -293,7 +297,7 @@ appear on disk." (setq mu4e-sent-func 'mu4e-sent-handler) (mu4e~proc-sent (buffer-file-name) mu4e~draft-drafts-folder)) nil)) ;; mark these two hooks as permanent-local, so they'll survive mode-changes -;; (put 'mu4e~compose-save-before-sending 'permanent-local-hook t) + ;; (put 'mu4e~compose-save-before-sending 'permanent-local-hook t) (put 'mu4e~compose-mark-after-sending 'permanent-local-hook t)) (defconst mu4e~compose-buffer-max-name-length 30 @@ -312,12 +316,7 @@ appear on disk." (truncate-string-to-width str mu4e~compose-buffer-max-name-length nil nil t))))) - -(defconst mu4e~compose-hidden-headers - '("^References:" "^Face:" "^X-Face:" "^X-Draft-From:" - "^User-Agent:" "^In-Reply-To:") - "List of regexps with message headers that are to be hidden.") - + (defun mu4e~compose-handler (compose-type &optional original-msg includes) "Create a new draft message, or open an existing one. @@ -371,8 +370,7 @@ tempfile)." (set (make-local-variable 'mu4e-compose-parent-message) original-msg) (put 'mu4e-compose-parent-message 'permanent-local t) ;; hide some headers - (let ((message-hidden-headers mu4e~compose-hidden-headers)) - (message-hide-headers)) + (mu4e~compose-hide-headers) ;; switch on the mode (mu4e-compose-mode)) diff --git a/mu4e/mu4e-draft.el b/mu4e/mu4e-draft.el index 4305c7e9..1cedffcf 100644 --- a/mu4e/mu4e-draft.el +++ b/mu4e/mu4e-draft.el @@ -102,7 +102,7 @@ e-mail addresses. If LST is nil, returns nil." (let ((name (car addrcell)) (email (cdr addrcell))) (if name - (format "\"%s\" <%s>" name email) + (format "%s <%s>" (mu4e~rfc822-quoteit name) email) (format "%s" email)))) lst ", "))) diff --git a/mu4e/mu4e-mark.el b/mu4e/mu4e-mark.el index b2e49759..43ffc493 100644 --- a/mu4e/mu4e-mark.el +++ b/mu4e/mu4e-mark.el @@ -1,6 +1,6 @@ ;; mu4e-mark.el -- part of mu4e, the mu mail user agent ;; -;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2013 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> @@ -82,6 +82,24 @@ where "Clear the marks subsystem." (clrhash mu4e~mark-map)) + +(defmacro mu4e~mark-in-context (&rest body) + "Evaluate BODY in the context of the headers buffer in case this +is either a headers or view buffer, and " + `(cond + ((eq major-mode 'mu4e-headers-mode) ,@body) + ((eq major-mode 'mu4e-view-mode) + (if (buffer-live-p mu4e~view-headers-buffer) + (let* ((msg (mu4e-message-at-point)) + (docid (mu4e-message-field msg :docid))) + (with-current-buffer mu4e~view-headers-buffer + (if (mu4e~headers-goto-docid docid) + ,@body + (mu4e-error "cannot find message in headers buffer.")))) + (mu4e-error "no headers buffer connected to view"))) + (t (progn (mu4e-message "%S" major-mode) ,@body)))) + + (defun mu4e-mark-at-point (mark &optional target) "Mark (or unmark) message at point. MARK specifies the mark-type. For `move'-marks and `trash'-marks @@ -229,19 +247,19 @@ as well." If there are such marks, replace them with a _real_ mark (ask the user which one)." (interactive) - (let ((markpair)) - (maphash - (lambda (docid val) - (let ((mark (car val)) (target (cdr val))) - (when (eql mark 'something) - (unless markpair - (setq markpair - (mu4e~mark-get-markpair "Set deferred mark(s) to: " nil))) - (save-excursion - (when (mu4e~headers-goto-docid docid) - (mu4e-mark-set (car markpair) (cdr markpair))))))) - mu4e~mark-map))) - + (mu4e~mark-in-context + (let ((markpair)) + (maphash + (lambda (docid val) + (let ((mark (car val)) (target (cdr val))) + (when (eql mark 'something) + (unless markpair + (setq markpair + (mu4e~mark-get-markpair "Set deferred mark(s) to: " nil))) + (save-excursion + (when (mu4e~headers-goto-docid docid) + (mu4e-mark-set (car markpair) (cdr markpair))))))) + mu4e~mark-map)))) (defun mu4e~mark-check-target (target) "Check if the target exists if not, offer to create it." @@ -264,46 +282,48 @@ work well. If NO-CONFIRMATION is non-nil, don't ask user for confirmation." (interactive) - (let ((marknum (hash-table-count mu4e~mark-map))) - (if (zerop marknum) - (message "Nothing is marked") - (mu4e-mark-resolve-deferred-marks) - (when (or no-confirmation - (y-or-n-p - (format "Are you sure you want to execute %d mark%s?" - marknum (if (> marknum 1) "s" "")))) - (maphash - (lambda (docid val) - (let ((mark (car val)) (target (cdr val))) - ;; note: whenever you do something with the message, - ;; it looses its N (new) flag - (case mark - (refile (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")) - (delete (mu4e~proc-remove docid)) - (flag (mu4e~proc-move docid nil "+F-u-N")) - (move (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")) - (read (mu4e~proc-move docid nil "+S-u-N")) - (trash (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N")) - (unflag (mu4e~proc-move docid nil "-F-N")) - (unread (mu4e~proc-move docid nil "-S+u-N")) - (otherwise (mu4e-error "Unrecognized mark %S" mark))))) - mu4e~mark-map)) - (mu4e-mark-unmark-all) - (message nil)))) + (mu4e~mark-in-context + (let ((marknum (hash-table-count mu4e~mark-map))) + (if (zerop marknum) + (message "Nothing is marked") + (mu4e-mark-resolve-deferred-marks) + (when (or no-confirmation + (y-or-n-p + (format "Are you sure you want to execute %d mark%s?" + marknum (if (> marknum 1) "s" "")))) + (maphash + (lambda (docid val) + (let ((mark (car val)) (target (cdr val))) + ;; note: whenever you do something with the message, + ;; it looses its N (new) flag + (case mark + (refile (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")) + (delete (mu4e~proc-remove docid)) + (flag (mu4e~proc-move docid nil "+F-u-N")) + (move (mu4e~proc-move docid (mu4e~mark-check-target target) "-N")) + (read (mu4e~proc-move docid nil "+S-u-N")) + (trash (mu4e~proc-move docid (mu4e~mark-check-target target) "+T-N")) + (unflag (mu4e~proc-move docid nil "-F-N")) + (unread (mu4e~proc-move docid nil "-S+u-N")) + (otherwise (mu4e-error "Unrecognized mark %S" mark))))) + mu4e~mark-map)) + (mu4e-mark-unmark-all) + (message nil))))) (defun mu4e-mark-unmark-all () "Unmark all marked messages." (interactive) - (when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map))) - (mu4e-warn "Nothing is marked")) - (maphash - (lambda (docid val) - (save-excursion - (when (mu4e~headers-goto-docid docid) - (mu4e-mark-set 'unmark)))) - mu4e~mark-map) - ;; in any case, clear the marks map - (mu4e~mark-clear)) + (mu4e~mark-in-context + (when (or (null mu4e~mark-map) (zerop (hash-table-count mu4e~mark-map))) + (mu4e-warn "Nothing is marked")) + (maphash + (lambda (docid val) + (save-excursion + (when (mu4e~headers-goto-docid docid) + (mu4e-mark-set 'unmark)))) + mu4e~mark-map) + ;; in any case, clear the marks map + (mu4e~mark-clear))) (defun mu4e-mark-docid-marked-p (docid) "Is the given docid marked?" @@ -313,25 +333,27 @@ If NO-CONFIRMATION is non-nil, don't ask user for confirmation." (defun mu4e-mark-marks-num () "Return the number of marks in the current buffer." (if mu4e~mark-map (hash-table-count mu4e~mark-map) 0)) + (defun mu4e-mark-handle-when-leaving () "If there are any marks in the current buffer, handle those according to the value of `mu4e-headers-leave-behavior'. This function is to be called before any further action (like searching, quiting the buffer) is taken; returning t means 'take the following -action', return nil means 'don't do anything'" - (let ((marknum (mu4e-mark-marks-num)) - (what mu4e-headers-leave-behavior)) - (unless (zerop marknum) ;; nothing to do? - (when (eq what 'ask) - (setq what (mu4e-read-option - (format "There are %d existing mark(s); should we: " marknum) - '( ("apply marks" . apply) - ("ignore marks?" . ignore))))) - ;; we determined what to do... now do it - (when (eq what 'apply) - (mu4e-mark-execute-all t))))) - +action', return nil means 'don't do anything'." + (mu4e~mark-in-context + (let ((marknum (mu4e-mark-marks-num)) + (what mu4e-headers-leave-behavior)) + (unless (zerop marknum) ;; nothing to do? + (when (eq what 'ask) + (setq what (mu4e-read-option + (format "There are %d existing mark(s); should we: " marknum) + '( ("apply marks" . apply) + ("ignore marks?" . ignore))))) + ;; we determined what to do... now do it + (when (eq what 'apply) + (mu4e-mark-execute-all t)))))) + (provide 'mu4e-mark) ;; End of mu4e-mark.el diff --git a/mu4e/mu4e-utils.el b/mu4e/mu4e-utils.el index 0eaab14e..d888d32a 100644 --- a/mu4e/mu4e-utils.el +++ b/mu4e/mu4e-utils.el @@ -553,13 +553,14 @@ process." (defun mu4e~rfc822-phrase-type (ph) "Return either atom, quoted-string, a corner-case or nil. This - checks for quotes around the phrase first + checks for empty string first. Then quotes around the phrase (returning 'rfc822-quoted-string). Then whether there is a quote inside the phrase (returning 'rfc822-containing-quote). The reverse of the RFC atext definition is then tested. If it matches, nil is returned, if not, it is an 'rfc822-atom, which is returned." (cond + ((= (length ph) 0) 'rfc822-empty) ((= (aref ph 0) ?\") (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) 'rfc822-quoted-string diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index 519d606b..1bc4bd31 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -780,10 +780,10 @@ Also number them so they can be opened using `mu4e-view-go-to-url'." (defmacro mu4e~view-in-headers-context (&rest body) - "Evaluate BODY in the current headers buffer, with moved to the -current message." + "Evaluate BODY in the context of the headers buffer connected to +this view." `(progn - (unless '(buffer-live-p mu4e~view-headers-buffer) + (unless (buffer-live-p mu4e~view-headers-buffer) (mu4e-error "no headers-buffer connected")) (let* ((msg (mu4e-message-at-point)) (docid (mu4e-message-field msg :docid))) diff --git a/mu4e/mu4e.texi b/mu4e/mu4e.texi index 8016f1bd..3a10ad63 100644 --- a/mu4e/mu4e.texi +++ b/mu4e/mu4e.texi @@ -732,7 +732,7 @@ respectively. In the example, we use @code{:human-date}, which shows when the time when the message was sent today, and the date otherwise. @item The header field used for sorting is indicated by ``@t{V}'' or ``@t{^}''@footnote{or you can use little graphical triangles; see variable -@code{mu4e-use-fancy-chars}}, indicating the sort order (descending or +@code{mu4e-use-fancy-chars}}, corresponding to the sort order (descending or ascending, respectively). You can influence this by a mouse click, or @key{O}. Not all fields allow sorting. @item Instead of showing the @t{From:} and @t{To:} fields separately, you @@ -757,7 +757,7 @@ u=@emph{unread}. The tooltip for this field also contains this information. Jamie Zawinski's mail threading algorithm, @url{http://www.jwz.org/doc/threading.html}}. @item The headers view is @emph{automatically updated} if any changes are -found during the indexing process, and if there is not current +found during the indexing process, and if there is no current user-interaction. If you do not want such automatic updates, set @code{mu4e-headers-auto-update} to @code{nil}. @end itemize @@ -1134,8 +1134,8 @@ is used for images. @section Displaying rich-text messages @t{mu4e} normally prefers the plain-text version for messages that consist of -both a plain-text and html (rich-text) versions of the body-text. You change -this by setting @code{mu4e-view-prefer-html} to @t{t}. +both a plain-text and html (rich-text) versions of the body-text. You can +change this by setting @code{mu4e-view-prefer-html} to @t{t}. If there is only an html-version, or if the plain-text version is too short in comparison with the html part@footnote{this is for the case where the @@ -1225,7 +1225,8 @@ For more information, see the @command{mu-verify} manual page. @section Actions You can perform custom functions (``actions'') on messages and their -attachments. For a general discussion on how to define your own, see see @ref{Actions}. +attachments. For a general discussion on how to define your own, see see +@ref{Actions}. @subsection Message actions @@ -2403,16 +2404,17 @@ shortcut character @key{o} is due to the first character of The @command{emacs}-package @t{sauron}@footnote{Sauron can be found at @url{https://github.com/djcb/sauron}, or in the Marmalade package-repository at @url{http://http://marmalade-repo.org/}} (by the same author) can be used -to get notifications about new mails. If you put something like the below -script in your @t{crontab} (or have some other way of having it execute every -@emph{n} minutes) you receive notifications in the sauron-buffer when new -messages arrive. +to get notifications about new mails. If you run something like the below +script from your @t{crontab} (or have some other way of having it execute +every @emph{n} minutes), you receive notifications in the @t{sauron}-buffer +when new messages arrive. @verbatim #!/bin/sh -# put the path to your Inbox folder here +# put the path to your Inbox folder here CHECKDIR="/home/$LOGNAME/Maildir/Inbox" + sauron-msg () { DBUS_COOKIE="/home/$LOGNAME/.sauron-dbus" if test "x$DBUS_SESSION_BUS_ADDRESS" = "x"; then @@ -3011,7 +3013,7 @@ normal, synchronous fashion. @section Known issues Although they are not really @emph{questions}, we end this chapter with a list -of known issue and/or missing features in @t{mu4e}. Thus, users won't have to +of known issues and/or missing features in @t{mu4e}. Thus, users won't have to search in vain for things that are not there (yet), and the author can use it as a todo-list. @@ -3020,11 +3022,24 @@ as a todo-list. utf-8}; so, if you problems with encodings, be sure to have @code{(set-language-environment "UTF-8")} in your @file{~/.emacs}. @item @emph{Thread handling is incomplete.} While threads are calculated and are -visible in the headers buffer, you can not collapse/open them. +visible in the headers buffer, you cannot collapse/open them. @item @emph{The key-bindings are @emph{somewhat} hard-coded.} That is, the main menu assumes the default key-bindings, as do the clicks-on-bookmarks. +@item @emph{The @t{emacs} front-end of the @t{notmuch} e-mail indexer +conflicts with @t{mu4e}}. @t{notmuch} running in parallel with +@t{mu4e} leads to +@verbatim +error in process filter: mu4e-error-handler: Error 70: cannot read +~/Maildir/... +@end verbatim +when sending a reply to a new e-mail. This seems to be caused by @t{notmuch} +changing the name of the original message file while @t{mu4e} is working in on +it. To prevent this, deactivate @t{notmuch} in your Emacs setup. @end itemize +For a more complete list, please refer to the issues-list in the +github-repository. + @node Tips and Tricks @appendix Tips and Tricks @@ -3195,7 +3210,8 @@ If you have multiple accounts, you can accommodate them as well: ((string-match "Account1" maildir) (setq folder (or (catch 'found (dolist (mailing-list my-mu4e-mailing-lists) - (if (mu4e-message-contact-field-matches msg :to (car mailing-list)) + (if (mu4e-message-contact-field-matches + msg :to (car mailing-list)) (throw 'found (cdr mailing-list))))) "/Account1/General"))) ((string-match "Gmail" maildir) @@ -3203,7 +3219,8 @@ If you have multiple accounts, you can accommodate them as well: ((string-match "Account2" maildir) (setq folder (or (cdar (member* subject my-mu4e-subject-alist :test #'(lambda (x y) - (string-match (car y) x)))) + (string-match + (car y) x)))) "/Account2/General")))) folder)) @end lisp @@ -3216,10 +3233,12 @@ message based on the mailing list to which it was sent. This requires another variable: @lisp -(defvar my-mu4e-mailing-lists '(("mu-discuss@@googlegroups.com" . "/Account1/mu4e") - ("pandoc-discuss@@googlegroups.com" . "/Account1/Pandoc") - ("auctex@@gnu.org" . "/Account1/AUCTeX")) - "List of mailing list addresses and folders where their messages are saved.") +(defvar my-mu4e-mailing-lists + '(("mu-discuss@@googlegroups.com" . "/Account1/mu4e") + ("pandoc-discuss@@googlegroups.com" . "/Account1/Pandoc") + ("auctex@@gnu.org" . "/Account1/AUCTeX")) + "List of mailing list addresses and folders where + their messages are saved.") @end lisp @node Saving outgoing messages