MH & nmh: Email for Users & Programmers

May, 2006

Draft Message Template Files

When you start to compose a mail message with MH, the MH command (comp, repl, etc.) reads a template file for the draft message. Because xmh, exmh and MH-E call MH commands to build their drafts, these same template files also set those message headers.

How the Draft Message is Built

This section explains how commands like comp and forw create a draft message from their template files. Four sections explain how the individual template files are used.

Each mail composition command has its own template file, as shown in the next Table. A file from your MH directory will be used if you've put one there. Otherwise, the system default in the MH library directory or nmh etcetera directory is used.

Table: Draft Message Template Filenames

    Program        Filename
comp components repl replcomps forw forwcomps forw -digest digestcomps dist distcomps
For instance, the default template file for comp, named components, looks like this (unless someone on your system has changed it):
When you start a mail-composition program like comp, the program reads its template file and builds a draft file. For comp and dist, this is easy -- just copy the template file into the draft file. The forw command copies the template file and the forwarded message(s) into the draft file. The forw -digest and repl commands read formatting rules (mh-format strings) from the template file. For instance, here's a line from the default replcomps file:
    %<{subject}Subject: Re: %{subject}\n%>\
That line says, "If the original message had a Subject: field, output Subject: Re: followed by the original subject text and a newline character." (See The replcomps File and MH Format Strings.)

After the MH program has built a draft file, it starts whatnow. Then, whatnow starts your message editor -- by default, it's prompter (it may be different at your site).

Let's look at what prompter does with the draft file. (If you'd like to see some sample draft files while you read this description, look back at the Figures Sending a message. Step 1: Making draft from template and Sending a message. Steps 2-4: Draft message, before and after prompter edits it. For an overview of the whole process of sending messages, see the Section Overview: Sending MH Messages.

  1. When it reads a draft header line which is an empty field -- a line that ends with a colon (:) -- prompter prints that and a space to the screen and leaves the cursor after the space. For example, when prompter reads the line:
    it prompts you with To: and leaves the cursor there. Then:
  2. Each time you press RETURN, prompter reads the next line from the draft file. If the line is an empty field, prompter works like step 1 above. If a line in the draft file already has something after the colon, like this:
    then prompter won't stop or prompt you. It'll leave the field in the draft message.
  3. When prompter sees a separator line (a blank line or a line with only dashes) as it's reading the header from the draft file, it assumes that the separator line is the start of the message body. From here, prompter changes its behavior -- instead of looking for header fields and prompting you, it starts processing the message body. One of three things will happen as prompter processes the body:
To add or change the fields in a header, you can edit the header (by starting an editor from the What now? prompt) when you compose the message. Or you can make your own private template files, as the next four sections explain.

The components File

The comp command uses this file. It's a pretty basic file; there's an explanation at How the Draft Message is Built.

My components file (in my MH directory) looks like this:

    X-Mailer: MH 6.8.3
    Fcc: outbox

The Section Fields You Add to a Header explains Fcc:, Bcc:, Reply-to:, and X-. The X-Mailer: helps me identify messages I've sent with MH and does a little bragging, too. :-)

The replcomps File

The repl command builds the header of your draft reply automatically. The default reply header looks like this (the italicized text comes from those fields of the message you're replying to):

    To: Reply-To or From
    cc: cc, To, and yourself
    Fcc: from -fcc switch, if any
    Subject: Re: Subject
    In-reply-to: Your message of "Date."
That's set up in the system replcomps file. You can customize the heading and the way that fields are filled in by making your own replcomps file. To do this well, you'll need to study the Chapter MH Formatting. But you can do a lot by imitating the changes I show you here and by experimenting. Ready? Let's dig in. The next Example is a copy of the default replcomps file in MH 6.8.3. An Example at the end of this section shows a copy of the updated replcomps file. (You can also get these files from this book's online archive. They're in examples/mh/Mail/replcomps.default and examples/mh/Mail/replcomps.jerry.)
NOTE for users of the online version of this book: The next example is followed by long explanation. To avoid jumping between the example and its explanation, it's a good idea to open a new browser window to show the example. (Check your browser's menu for a command like New Web Browser or Open in New Window). Then, use the original browser to read the explanation while you view the example in the second browser window.

Example: Default MH 6.8+ replcomps file

     1> %(lit)%(formataddr %<{reply-to}%?{from}%?{sender}%?{return-path}%>)\
     2> %<(nonnull)%(void(width))%(putaddr To: )\n%>\
     3> %(lit)%(formataddr{to})%(formataddr{cc})%(formataddr(me))\
     4> %<(nonnull)%(void(width))%(putaddr cc: )\n%>\
     5> %<{fcc}Fcc: %{fcc}\n%>\
     6> %<{subject}Subject: Re: %{subject}\n%>\
     7> %<{date}In-reply-to: Your message of "\
     8> %<(nodate{date})%{date}%|%(pretty{date})%>."%<{message-id}
     9>              %{message-id}%>\n%>\
    10> --------
Okay. One step at a time. I'll show you the important part of each line and how you might change it. Or, to go to the explanation of a particular line, click on the line number in the example above.
  1. Lines 1-2 build a To: address field for the draft. The format of line 1 changed in MH 6.8. If you're using a version of MH before 6.8, read the pre-6.8 explanation instead!

    This is the format of lines 1-2 in MH 6.8 and later:

          %(lit)%(formataddr %<{reply-to}%?{from}%?{sender}%?{return-path}%>)\
          %<(nonnull)%(void(width))%(putaddr To: )\n%>\
    Those lines look for the best address in the original message. The %(lit) erases a string register that'll store the address. The %(formataddr ...) formats an address and stores it in the register. The rest of line 1, the argument to formataddr, chooses the address:
    It's an if-elseif test. It means: If none of those tests found the address they needed, we don't have a To: address for the reply.

    Line 2 starts with a test (%<) that runs the (nonnull) function to see whether formataddr saved an address in line 1. If not, the test fails and line is skipped; no To: field is output. Otherwise, %(void(width)) saves the width of the address in a numeric register. (The Section scan Format Strings has more information about the string and numeric registers where those values are saved.) The %(putaddr To: )\n uses a function named putaddr. It gets the address field that was saved in line 1 and prints To: followed by the address and a newline character (\n). We've got the To: address, if any; the %> ends the test.

    The rest of the replcomps file is the same in all recent versions.

    The (nonnull) test at the beginning of line 2 causes a problem for repl -query users like me. There are times when I don't want my reply to go to any of the addresses that replcomps chooses for the To: field. The (nonnull) prevents a To: field if there are no addresses. In cases like these, I want to add To: addresses by hand. (A message really should have a To: field.) So I removed the test. Line 2 of my edited replcomps looks like this:

          %(void(width))%(putaddr To: )
    If there are no addresses, I still get a bare To: field. Then prompter will prompt me for addresses. I can just hit RETURN to leave the field blank (and let prompter delete it) in the rare case that I really don't want a To:.

    Note that I also removed the \n and \ at the end of the line. These aren't needed on lines that output fields (like this one) which will always be present in the output.

  2. Lines 3-4 of the default replcomps file build a cc: address field:
          %<(nonnull)%(void(width))%(putaddr cc: )\n%>\
    It's something like the To: field, but it doesn't have the if-elseif tests. Instead, it grabs all the To: and cc: addresses from the original message. It also includes your address (with formataddr(me)).

    Notice that line 3 has three separate calls to formataddr -- there are no %<if%|else%> tests. That's why it takes all of the addresses from the original message, not just the first one it finds.

    Messages can have header fields like the ones below that list other addresses where the message was delivered:

          X-To: nelson@mvus.bitnet
    (I've also seen X-vmsmail-to: and X-ccmail-to:.) Because those addresses aren't in the To: or cc: fields on the message I get, repl won't let me reply there. I usually want my reply to go to those addresses, too. To fix that, I changed line 3 to have fields like those. My fix made it pretty long, so I also split it onto three lines. In my edited replcomps, the lines are:
          %<(nonnull)%(void(width))%(putaddr cc: )\n%>\
    The repl -query switch helps me wade through this bunch of addresses.

    Sometimes I want to send a copy of my reply to addresses that didn't get the original message. That's easy for people who use a text editor (vi, for example) to start their draft messages. But I like to use prompter. So I've added an empty cc: field to my edited replcomps:

    If I don't have any extra cc:s when I'm composing my reply, I just press RETURN; prompter deletes the blank field.
  3. Lines 5-6 of the default replcomps file work about the same way -- they make the Fcc: and Subject: fields:
          %<{fcc}Fcc: %{fcc}\n%>\
          %<{subject}Subject: Re: %{subject}\n%>\
    Let's look at line 5. The %< tells repl to test for a -fcc option ({fcc}) on the command line or in the MH profile -- if there is one, it outputs Fcc: with the folder name (%{fcc}) and a newline (\n) after it.

    I like an Fcc: field in my draft, but I use different folders for different messages. I usually don't remember to use -fcc on the command line; I want to be prompted. So I changed line 5 to always output Fcc:. Then it adds the folder from any -fcc option. So, line 7 of my edited replcomps is:

          Fcc: %<{fcc}%{fcc}%>
    Compare that to the original version and you'll see two changes: First, I moved the Fcc: (and a space after it) outside of the test. Now replcomps always outputs Fcc:. Second, I always need to have a newline output at the end of the Fcc: field, whether there was a -fcc option or not. So I removed the \n (which outputs a newline) and the \ continuation (which "eats" a newline at the end of the line).

    This setup takes advantage of the way that prompter works. If there's a Fcc: folder defined, replcomps outputs Fcc:: folder; prompter keeps that line in the draft. Otherwise, prompter prompts me with Fcc:, and I can either fill in a folder -- or press RETURN and the empty Fcc: field will be deleted.

    If the incoming mail doesn't have a subject, the test on line 6 will see that and not add a subject to the reply. I'd like a choice: If there's no subject, I want to be prompted. So, in the same way I fixed line 5, I've changed line 6 of my edited replcomps to:

          Subject: %<{subject}Re: %{subject}%>
    Notice that the Re: is inside the test. So, if I'm prompted to enter a subject, the subject won't automatically start with Re: -- but I can add it if I want to.
  4. MH was designed long before message threading became popular. This step explains how to set up replcomps for threading. If you need an overview or details, please read References: Message Threading first.

    The default replcomps file puts the only threading info, the message-id, into the In-reply-to: field. It also adds the date and other text. Under a developing Internet draft email standard (draft-ietf-drums-msg-fmt-01), this extra text would be illegal.

    Let's fix this. First, if the new draft standard becomes a standard, any text other than a message-id in the In-reply-to: field will be illegal. So, to be safe, let's fill our new In-reply-to: field with the message-id of the message we're replying to. Because it's possible that the message you're replying to won't have a Message-ID: field, we'll only output the field (and a newline) if there is one. The backslash at the end (\) prevents a blank line if there's no In-reply-to: field output:

          %<{message-id}In-reply-to: %{message-id}\n%>\
    Next, let's add lines of code that make a new References: field. The order of the message-ids in this field is important: they should be listed from oldest to newest. So the field should contain the References:, if any, from the message you're replying to -- followed by its Message-ID: field. The simplest code would look like this:
          References: %{references} %{message-id}
    but it could leave multiple spaces that make the field sort of ugly. Let's be neat by adding the (trim) function to remove trailing whitespace from each field we copy in. Here are the three new lines for my edited replcomps:
          %<{message-id}References: \
          %<{references}%(void{references})%(trim)%(putstr) %>\
    In words, that says: "If the message I'm replying to has a Message-ID: field, write the word References: into the new draft. If the message has a References: field, store its text (%(void{references})), remove trailing whitespace (%(trim)), and output the trimmed text (%(putstr)). Next, do the same thing for the Message-ID: field."

    Time to fix lines 7-9 of the default replcomps file. Those three lines make the In-reply-to: field for the reply header. We've already made a new In-reply-to: field that meets the draft standard, so we'll be moving the text from the old replcomps into a new Comments: field. The old lines were:

          7>  %<{date}In-reply-to: Your message of "\
          8>  %<(nodate{date})%{date}%|%(pretty{date})%>."%<{message-id}
          9>               %{message-id}%>\n%>\
    Because there's no newline (\n) until the end of this, repl will format it to fit the header neatly.

    One thing I don't like about the original MH format is that the field says: "In-reply-to: Your message of ...". Sometimes -- like when I'm replying to a list of people -- the word "your" is wrong. I'll change my replcomps so it uses the From: address of the original message, instead of the word "your." For example, when I'm replying to a message like this:

          Date:       Mon, 09 Jan 1995 09:47:42 -0500
          From:       "Keith E Smith" <KES@MVUS.BITNET>
          Sender:     Help Squad <HELPOUT@MVUS.BITNET>
          Message-ID: <19950109094744.AA18273@MVUS.BITNET>
          To:         Jerry Peek <>
          Subject:    HELP!
    the heading of my reply message says:
          Subject: Re: HELP!
          In-reply-to: <19950109094744.AA18273@MVUS.BITNET>
          Comments: In-reply-to "Keith E Smith" <KES@MVUS.BITNET>
             message dated "Mon, 09 Jan 1995 09:47:42 -0500."
    Here's how I do that -- replace lines 7-9 with the following:
          Comments: In-reply-to \
             message dated "%<(nodate{date})%{date}%|%(tws{date})%>."
    This outputs Comments: In-reply-to followed by the From: address if there is one -- otherwise, the Apparently-From: or the Sender:. Then this code outputs a newline (\n), three spaces, and the words message dated. If it can't parse the date in the message, it prints the Date: field as is; but if it can, it uses the (tws) function to write the date in an "official" format. The leading space before the third line is output as-is. The third line is indented because it continues the Comments: field from the previons line.
  5. Line 10 of the default replcomps file is a row of separator dashes. When prompter sees this, it knows that the rest of the file (if any) is the message body.
You can do a lot more customizing. See the Chapter MH Formatting and read your mh-format(5) manual page.

The Example below shows the updated replcomps file. (Here's one last chance to see the original replcomps file.)

Example: Updated MH replcomps file

    %(lit)%(formataddr %<{reply-to}%?{from}%?{sender}%?{return-path}%>)\
    %(void(width))%(putaddr To: )
    %<(nonnull)%(void(width))%(putaddr cc: )\n%>\
    Fcc: %<{fcc}%{fcc}%>
    Subject: %<{subject}Re: %{subject}%>
    %<{message-id}In-reply-to: %{message-id}\n%>\
    %<{message-id}References: \
    %<{references}%(void{references})%(trim)%(putstr) %>\
    Comments: In-reply-to \
       message dated "%<(nodate{date})%{date}%|%(tws{date})%>."

The forwcomps File

The forwcomps file for the forw command looks like the components file. I won't explain it here.

In fact, I don't even have a forwcomps file. I use my components file for forw, as well as for comp, by adding this entry to my MH profile:

    forw: -form components
I could have done the same thing by using the UNIX ln command to make a link called forwcomps to my components file. If you want a separate forwcomps file, though, be my guest. Just look at Section The components File.

The digestcomps File

The default digestcomps file, in the Example below, is an MH format file. But, unlike replcomps, digestcomps is straightforward:

Example: Default MH digestcomps file

    1> From:     %{digest}-Request
    2> To:   %{digest} Distribution: dist-%{digest};
    3> Subject:  %{digest} Digest V%(cur) #%(msg)
    4> Reply-To: %{digest}
    5> --------
    6> %{digest} Digest    %(weekday{date}), %2(mday{date}) %(month{date}) 19%02(year{date})
    7>         Volume %(cur) : Issue %(msg)
    9> Today's Topics:
The header From: field doesn't have @hostname; the mail transport system should add that when it sends the message. (As the Section Signature and From: explains, you can change this field: add a hostname, for instance.) The escape %{digest} contains the digest name, like octopus-eaters. The volume number goes into %(cur); the issue number is in %(msg). The other escapes include the date; the Table MH-format Function Escapes (2 of 3) lists them.

The distcomps File

Don't put anything in the distcomps file except the header fields and a row of dashes. That's because dist distributes messages as is. If you want to add text along with the message body, use the forw command instead.

You can add extra fields to the message header, though. For instance, your distcomps could look like the one below. (You can also get this little file from this book's online archive. It's in examples/mh/Mail/distcomps.)

The field names that end in To:, cc:, Bcc:, and Fcc: work just like the corresponding fields in, say, components. The Resent-Comments: fields give you a place to type a one- or two-line message. (Resent-Comments: isn't mentioned explicitly in the RFC 822 mail guidelines, though it's apparently not illegal. Some people say that it violates the idea of redistributing messages exactly as they were. I think it's really handy.) I have two Resent-Comments fields in case I want to write more than one line of comments. Of course, you can leave any of these empty when you dist your message; prompter will delete the fields you leave empty.