Meaw.fi

The stupidity loop

.procmailrc Checker

Reads a personal ~/.procmailrc and says, recipe by recipe, what it actually does with your mail.

A procmailrc is terse in a way that hides mistakes. :0 and :0: differ by one character, and one of them corrupts an mbox folder when two messages arrive at once. A folder name with a trailing slash is a maildir and the same name without one is a single file. Conditions are handed to procmail's own egrep, which is not PCRE — \d matches the letter d, and a $VAR in a condition is an end-of-line anchor followed by some letters unless you asked for substitution. And any recipe with no conditions that delivers quietly ends the file: everything below it is dead.

So this checks the structure — flags, locking, braces, the one action line every recipe must have — and then writes each recipe back out in plain English, including the ones that can never run.

Everything runs in your browser. The file is never uploaded, and no request is made when you press Check.

Findings

    📄 recipe outline

    Nothing yet.
    Paste a .procmailrc above, then press Check.

    What it checks, and what it cannot

    What a recipe is

    A recipe is :0, then its flags, then optionally a second colon and a lockfile name; then zero or more condition lines starting with *; then exactly one action line. Most structural mistakes are a recipe that lost its action line, or picked up an extra one.

    The action decides where the mail goes, and its last character decides what kind of destination it is. A name ending in / is a maildir and one ending in /. is an MH folder; anything else is a single file in mbox format. | pipes to a program, ! forwards to an address, and { opens a block of further recipes. Since no folder is ever looked at on disk, a name that was meant to be a maildir and lost its slash cannot be told apart from a deliberate mbox — which is why the outline says which one each recipe is getting, in as many words.

    Locking

    Appending to an mbox file is not atomic, so two messages delivered at the same moment can interleave and leave the folder unreadable. :0: takes a lockfile for the duration and is what an mbox delivery wants. Writing into a maildir creates a new file instead, which is already atomic, so a lock there is just work. On a pipe or a forward there is no folder name for procmail to derive the default lockfile name from — it uses the destination filename, or the one after the first >> — so a bare :0: there has nothing to work with and wants an explicit name.

    The regex engine

    Conditions go to procmail's own egrep. It has ^ $ . * + ? [ ] | ( ) and the backslash, plus procmail's own \<, \> and \/ — and nothing else. There are no character-class escapes, so \d is just a quoted d; no (?…) groups; no lazy quantifiers; and no named character classes. Matching ignores case unless the recipe carries the D flag.

    Condition lines are also the one place comments do not work. procmailrc(5) puts it plainly: those lines are fed to the internal egrep literally. So a # in a condition is part of the pattern, and this tool leaves it alone too — except when it is surrounded by spaces and looks like someone writing a comment, which it will point out.

    Left alone on purpose

    INCLUDERC and SWITCHRC name other files, and nothing is ever fetched, so their recipes are not read and the unreachable-recipe check can only speak for what you pasted. No folder, program or address is checked for existence. Nothing is rewritten and there is nothing to copy or download: the only safe normalisation of a working mail filter is the one you make yourself.

    Assumed

    A per-user ~/.procmailrc. A system-wide /etc/procmailrc runs as root with different defaults and is not what this checks.

    Index of /t/procmail/


    ../
    procmail.js                                       16-Sep-2026 06:37               53265