procmail
[Top] [All Lists]

Re: Convert $MATCH to lowercase?

1997-01-23 21:34:57
    > 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

<Prev in Thread] Current Thread [Next in Thread>