> I use the following recipe to store mailing list messages in specific
> folders:
>
> :0:
> * ^TO.*\/(kernel|x11|mutt|procmail|zsh|laptop|jdbc|vimdev)
> * !^TOmito@
> * !^From:.*(linus|torvalds)
> * !^References:(_dot_)*mito(_at_)retriever
> $MATCH
>
> The only problem is sometimes someone writes to (for example) Mutt-user
> instead of mutt-user and I get two folders. How do I convert the
> '$MATCH' to lower-case before writing the folder?
First, there is an error in the first condition line: "^TO.*" is an
incorrect regexp. The ".*" defeats part of the regexp embedded within
the ^TO" macro. Just say:
* ^TOwhatever
or, if you are uncomfortable with this syntax (as I am, because it is
counter-intuitive, and fails the axiom of "Least Surprise"), then you
can use these alternatives:
* ^TO()whatever
* ^TO(whatever)
* ^TO\/whatever
The regexp pattern "()" simply matches the null string, and
syntactically separates whatever's on either side.
Now, to the real issue of your email.
This is a general problem: relying on input to form the basis of your
internal filenames. Basically, it is dangerous unless you apply very
strict filters.
However, there are several ways to accomplish what you want.
Take your pick.
1. Using a pair of recipes joined by the "AND" conjunction, take the
matched list name, downcase it, and capture it in a variable. Then
file using it.
:0 i
* ^TO.*\/(kernel|x11|mutt|procmail|zsh|laptop|jdbc|vimdev)
* !^TOmito@
* !^From:.*(linus|torvalds)
* !^References:(_dot_)*mito(_at_)retriever
FOLDER=|echo $MATCH | tr 'A-Z' 'a-z'
:0 A:
$FOLDER
2. Same thing, but using a nested block, instead of a pair of recipes.
IMHO, The above recipe is more elegant, but is difficult to expand
to include other actions, without some procmail "flag" gymnastics.
The below recipe allows for easy insertion of future actions within
the nested block.
:0
* ^TO.*\/(kernel|x11|mutt|procmail|zsh|laptop|jdbc|vimdev)
* !^TOmito@
* !^From:.*(linus|torvalds)
* !^References:(_dot_)*mito(_at_)retriever
{ FOLDER=`echo $MATCH | tr 'A-Z' 'a-z'`
:0:
$FOLDER
}
3. I like the following way best, because it decouples the name of the
list from the name of the folder. In other words, I can store mail
from my "perl-users" list into a mail folder named "info-perl".
Moreover, each list can have a distinct folder naming pattern from
the other lists. This recipe also allows me to "prefile" the mail:
file it and then view it in my default mailbox, from which it gets
deleted after reading or if I'm too busy.
I've posted this solution before on this list: basically, I'm
creating a simple lookup table, where the table can be either
directly in the procmail recipe file (3a), or, it can be a separate
file (3b).
3a. Using an internal procmail "table" of addresses and lists:
First we need a "subroutine" recipe file:
# listcheck.rc
#
# This is a procmail subroutine recipe file
# Include it with LIST and DEST set to the listname
# to check for, and the folder name to file to.
#
# If AVOID is set, then use it to AVOID certain
# mails.
#
# In addition, if COPY is set, I want to keep
# processing the mail, marking it filed with a
# new header (X-Filed:), and letting it end up in my
# default mailbox.
:0 ${COPY:+c}:
* $^TO_$LIST\>
* $${AVOID:+!$AVOID}
* $ DEST ?? .
$DEST
:0 afh # we copy-filed the mail; mark it with the destination
| formail -A"X-Filed: $LASTFOLDER"
Then, we need to check on these lists, in our ~/.procmailrc:
# Avoid list mail with any of the these conditions
AVOID='!(^TOmito@|^From:.*(linus|torvalds)|^References:(_dot_)*mito(_at_)retriever)'
COPY # I don't copy these; I read them at my leisure
LIST=kernel DEST=info/kernel INCLUDERC=listcheck.rc
LIST=x11 DEST=info/x11 INCLUDERC=listcheck.rc
LIST=mutt DEST=info/mutt INCLUDERC=listcheck.rc
LIST=zsh DEST=sw/zsh INCLUDERC=listcheck.rc
LIST=laptop DEST=hw/laptop INCLUDERC=listcheck.rc
COPY=yes # I'm active on these, so copy-file them
LIST=procmail DEST=sw/procmail INCLUDERC=listcheck.rc
LIST=jdbc DEST=info/jdbc INCLUDERC=listcheck.rc
LIST=vimdev DEST=info/vimdev INCLUDERC=listcheck.rc
The primary advantages of this method are:
* the DESTination can be commpletely independent of the LIST address
* the mail can filed only to its folder, or copied to its folder and
also to my system mailbox. In the latter case, the mail will
acquire an "X-Filed:" header with the folder name.
3b. Table-driven, using an external lookup file.
Prepare the lookup file first; let's call it "lists". The first
field is the list name, the second field is the folder name, and the
third field is the "copy" flag, either present or not.
% cat <<EOF >lists
# Table of LISTS
# LISTNAME FOLDERNAME COPYFLAG
# These lists I don't copy to my system mailbox
kernel info/kernel
x11 info/x11
mutt info/mutt
# These lists I copy-file
procmail sw/procmail copy
jdbc info/jdbc copy
vimdev info/vimdev copy
EOF
Now, here's the procmail recipe which can use this file:
# read in the list names (which must start with an alphabetic
LISTS=`awk '$1 ~ /^[a-zA-Z]/{printf "%s|", $1}' lists | sed -e 's/|$//'`
:0 # is this mail from one of my lists?
* $^TO_\/($LISTS)
* !^TOmito@
* !^From:.*(linus|torvalds)
* !^References:(_dot_)*mito(_at_)retriever
{ LIST=`echo $MATCH | tr 'A-Z' 'a-z'`
FOLDER = `awk '$1 ~ /^'$LIST'$/{print $2} lists`
COPY = `awk '$1 ~ /^'$LIST'$/{print $3} lists`
:0 ${COPY:+c}:
$FOLDER
:0 afh # the mail got filed; add a new header
|formail -A"X-Filed: $LASTFOLDER"
}
Hope this helps you understand some of procmail's flexibility.
___________________________________________________________
Alan Stebbens <aks(_at_)sgi(_dot_)com> http://reality.sgi.com/aks