procmail
[Top] [All Lists]

Re: re-sort headers? (Longish, Perlish, not a Procmail answer really)

1999-04-16 07:50:03
On Fri, 16 Apr 1999 03:37:38 -0400, Timothy J Luoma <luomat(_at_)peak(_dot_)org>
wrote:
Is it possible to use procmail to resort headers?

Yes, of course.
^L

;-)

I would like all my messages to come in with this order of headers:
(for these, all other headers can be in whatever order they want)
Sender:
From:
Reply-To:
Organization:
Date:
To:
Cc:
Subject:
Message-Id:
Any (even hackish) ways to do this?

This is a shell programming question.
^L

;-)

Perl is pretty nice for this kind of task. You can define arbitrary
mappings for the built-in sort operator. Something like this (close
your eyes while reading this if you think Perl code is ugly):


#!/usr/local/bin/perl

%mapping = (
    # anything not identified gets a monotonically increasing negative key
    'sender' => 1,
    'from' => 2,
    'reply-to' => 3,
    'organization' => 4,
    'date' => 5,
    'to' => 6,
    'cc' => 7,
    'subject' => 8,
    'message-id' => 9,
);

$unkey = -65535;                                # Year 2000! Year 2000!

# This doesn't cope with continued headers -- pipe thru formail -c first

# This also doesn't really do anything really well-defined if there are
#  two identical unkeyed lines

while (<>)
{
    # Keep From_ on very first line if present
    if (/^From\s/) { print; next }

    last if (m/^$/);  # We got to the end of the headers; exit first loop

    my ($mapkey);
    if (($mapkey) = m/^([^: ]+) *:/ and
        $mapkey = lc($mapkey) and defined $mapping{$mapkey})
    {
        $key{$_} = $mapping{$mapkey};
    }
    else
    {
        # Keep other lines in the order they were
        $key{$_} = $unkey++;
    }

#warn "# $key{$_}: $_";                 # Uncomment for diagnostic stderr

    push @headers, $_;
}

print sort { $key{$a} <=> $key{$b} } @headers;

# Print neck and read && print body
print "\n";
print (<>);


I wish I could say this is untested and completely off the top of my
head, but I did run it on one message. (At least there should be no
trivial syntax errors left.) I have relied on Perl's "informed guess"
defaults in a few places, which looks to the beginner like the same
statement does different things in different contexts (which, come to
think of it, is precisely what's happening, too). Bof, explaining Perl
scripts without really explaining Perl is pretty futile. If you want a
screenplay, write me in private and I'll give it a try.

In case the comments scare you almost as much as the code, the
ordering imposed on "unknown" header lines is that the first one gets
a sort key of -65535, the next one gets -65534, etc. The whole sort
key business is implemented via a hash lookup with the entire line as
key, which means if you have many identical lines (case sensitive and
all that), the hash lookup returns whatever was reported for the
latest line in the lot.

This is what you could call a "sponge filter" -- it sucks into it
everything it needs and keeps it in memory until it can spit it all
out again (squeezing the sponge, if you will). If this is a problem
(whoa, multi-megabyte headers are not going to be trouble-free with
other programs either I suspect) you could nominally attempt to
undefine the variables when you're done with them (but I don't think
that actually helps at all in practice).

For what it's worth, the Emacs VM mail reader does this kind of
reordering by default. I hate it. (Makes your spam complaints look
unprofessional when the headers are all messed up and reordered.) And
it's not very well documented how you turn it off.

/* era */

-- 
.obBotBait: It shouldn't even matter whether     <http://www.iki.fi/era/>
I am a resident of the state of Washington. <http://members.xoom.com/procmail/>
 * Sign the European spam petition! <http://www.politik-digital.de/spam/en/> *

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