MH & nmh: Email for Users & Programmers

May, 2006

MIME Configuration

You can change the default MIME configuration by editing the profiles that MH and nmh read. There are two personal profiles; a system-wide profile is used if yours don't have the needed entries. This section of the book has detailed information and examples of the entries you can put in the profiles.

MIME message handling differs between MH and nmh. Here's an overview of the differences:

The Section Showing MIME Messages has an introduction that you may want to read before you get started with the detailed explanation here.
NOTE: In general, nmh-1.0 seems to read old MH-style entries for the mhn command. But, as the nmh mhn(1) manual page says, mhn is deprecated (obsolete) from nmh version 1.0 and up. If you won't be using MH, I'd suggest learning the nmh syntax. I cover both MH and nmh here, emphasizing MH.

MIME Profiles

Like all MH commands, a MIME-handling command reads an MH profile entry with its own name: mhn: for MH, or mhbuild:, etc., for nmh. That entry sets command-line options you want to use every time the command runs. There's a list of those options in the MH Reference Guides for mhn, mhbuild, mhlist, mhshow, and mhstore.

But MIME commands also use special profile entries that start with mhn- (like mhn-store-text/plain:) for MH -- or mhbuild-, etc. (like mhbuild-compose-text/plain:) for nmh. These can't be typed on the command line; they're only read from files. This section explains those special profile entries and how to set them.

MIME commands read those entries from three files, in this order:

  1. If you've set the MHN environment variable, the MH mhn command reads the file that it points to. Or, for nmh, each command (mhbuild, etc.) checks its own environment variable: MHBUILD, MHLIST, MHSHOW, and MHSTORE.
  2. If the command didn't find the entry it needed in file named by the environment variable (or if there wasn't one defined), it checks your MH profile. If the MH environment variable is set, that file is read; otherwise, the default is the .mh_profile in your home directory.
  3. If the command didn't find the entry in the first two places, it reads the system-wide profile. For MH, that's the mhn_defaults file in the MH library directory (often /usr/local/lib/mh). nmh checks the mhn.defaults file in its etc directory (often /usr/local/nmh/etc).
The order that the profiles are read is important.

The person who installed MH on your host also installed the mhn_defaults or mhn.defaults file. It should have global entries, reasonable defaults for most accounts on the system to use. If you're lucky, it contains entries for all the specialized programs needed for handling different content types. Those programs may be scattered around your filesystem.

The entries in mhn_defaults or mhn.defaults may not be right for you, though. For instance, most of your system's accounts may use the X Window System, but you always work on an old ASCII terminal. The default profile entries that start X programs will just print errors on your ASCII terminal. In that case, you can add entries to your MH profile that work on your terminal; these will be used in place of the entries in mhn_defaults or mhn.defaults. (You don't have to duplicate the mhn_defaults or mhn.defaults entries that are correct for you. For instance, if the mhn-show-text/enriched: entry in mhn_defaults works on your ASCII terminal, there's no need to put another mhn-show-text/enriched: entry in your MH profile.)

Or, you may use several different types of terminals. You could have X at work and a terminal emulator program on your PC at home. The system defaults might be right on your X system, but not on your PC. So, you'll set the MHN or MHSHOW environment variable when you're at home; it can point to an alternate profile with entries that your PC needs.

Of course, a mixture of profile entries might be right for you: some in your MH profile, some in an MHN file for one of your displays, another in a second MHN file for another situation, a third MHN file that's read by a custom shell script you've set up. Who knows? :-)

Making a MIME Profile

If you need the special profile pointed to by the environment variable MHN (for MH) or MHBUILD, etc. (for nmh), read this section. It has a few ideas for setting up your account. Remember that, if you only log in from one type of terminal or window system, you probably won't need to set the environment variable -- instead, you can probably put all the entries in your main MH profile.

Here's the basic idea. As a process starts, the UNIX environment variables set in the parent process are copied to the child process. The MHN environment variable must be set in the parent process of MH's mhn command. In the same way, for nmh, MHBUILD must be set for mhbuild, and so on.

In most cases, the right place to set the environment variable is the startup file, like .login or .profile, that your login shell reads. But if you run MIME commands from a process that isn't started by your login shell -- for instance, in some window systems or as a shell script that runs from cron(8)-- you'll need to set the appropriate environment variable there. X Window System users may want to use a file like .xsession.

If you'll have more than one MHN file, it helps to have a system for naming them. I put all of mine in my MH directory with filenames that include the TERM environment variable setting -- like mhn-prf.vt100 and mhn-prf.xterm. You may want to put all of them in a separate directory. (That's especially true for nmh users who could have multiple versions of the multiple files that nmh MIME commands need!)

To set a variable like MHN automatically from your login shell, you'll need to decide what characteristics of your login session change from place to place. Have your shell setup file execute the correct command to set the environment variable. As an example, for the MH mhn command:

setenv MHN /full/pathname/of/file    ...csh-type shell

MHN=/full/pathname/of/file; export MHN shell

Here are some ideas for MH; if you use nmh, change the variable names. A book about UNIX shells will give the overview of the constructs below. If you need help with nitty-gritty details of your particular system, ask your system administrator:

Mix and match those techniques for your situation. Remember that you can use UNIX links (the ln(1) command) to give the same file several different names. And there's one unfortunate problem: some systems give the answer Not a tty when you run tty(1) from inside backquotes (`tty`).

To be sure the environment variable is being set correctly, type one of the following commands just before you use mhshow, mhn, or other MIME commands. You're set to go if you see the value you want (which might be an empty answer, if you don't want the variable set in that situation!):

    % printenv MHN
    % env | grep MHN=
And now you're ready for the real fun! ;-)

What Profile Entries Are There?

Start by finding your system's mhn_defaults or mhn.defaults file and reading it. These are entries you'll never have to duplicate -- unless you want to override them. Your MH profile (usually named .mh_profile) may also have some entries -- including one (like mhn:, mhshow:, etc.) for the command itself.

Any profile may have six kinds of mhn profile entries:

Entry Purpose
MH nmh-1.0+
mhn-show-type mhshow-show-type showing a message of this MIME type
mhn-compose-type mhbuild-compose-type composing a draft message content of this MIME type when directive doesn't give a filename to read content from
mhn-charset-charset mhshow-charset-charset how to render a message in the character set charset
where and how to store messages of this MIME type
where external body parts are cached
mhn-access-ftp nmh-access-ftp program to handle FTP transfers of external body parts

Below is part of an mhn_defaults file for MH. An mhn.defaults file for nmh has the same format but directives have different prefixes. (For instance, the MH entry mhn-charset-iso-8859-1 would be replaced by mhshow-charset-iso-8859-1. See the table above.)

    mhn-charset-iso-8859-1: xterm -fn '-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-1' -e %s
    mhn-charset-iso-8859-8: xterm -fn '-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-8' -e %s
    mhn-compose-audio/basic: cat < /dev/audio
    mhn-show-application/PostScript: %plpr -Pps
    mhn-show-audio/basic: %pcat > /dev/audio
    mhn-show-image/x-pbm: %ppbmtoxwd | /usr/bin/X11/xwud -geometry =-0+0
    mhn-show-image/x-pgm: %ppgmtopbm | pbmtoxwd | /usr/bin/X11/xwud -geometry =-0+0
    mhn-show-image/xwd: %p/usr/bin/X11/xwud -geometry =-0+0
    mhn-show-image/x-xwd: %p/usr/bin/X11/xwud -geometry =-0+0
    mhn-show-image: %p/usr/local/bin/xv -geometry =-0+0 '%f'
    mhn-store-text: %m%P.txt
    mhn-store-video/mpeg: %m%P.mpg
Each entry has two parts. The first part is the entry name, like mhn-store-text:. The rest of the entry has instructions for handling that content type. These instructions are called a string: Most of the entries have a UNIX command. Some entries have escapes like %p. Escapes are used for two things: If an entry holds a UNIX command, that command will be passed to a Bourne shell for processing. This means you can use Bourne shell features: I/O redirection (operators like <, >, m>&n, and |), quoting, command substitution (backquotes), and so on. You can basically write a small shell script in the profile entry! Or, of course, the entry can invoke a script file written for a shell, Perl, Tcl, and so on.

Here's an example of what you can do. The mhn_defaults file above is installed on a SPARCstation named server with its own built-in audio. If I do a remote login to server to get my email, I don't want audio to be played and recorded on server; it could be hundreds of miles away from me. I want audio to be processed on the host I'm using, slug. So I've added the entries in the next Example to a MIME profile on server. They use the rsh(1) command to send audio across the network, to and from slug:

Example: Server profile entry to handle audio on a remote client

    mhn-compose-audio/basic: rsh slug cat \< /dev/audio
    mhn-show-audio/basic: %prsh slug 'echo "Sending audio to slug...";
        set t=/tmp/mhn$$; cat > $t; cp $t /dev/audio; rm -f $t'
If I run comp on server to send an audio/basic content, mhn or mhbuild will run the command rsh slug cat \< /dev/audio. Because the input redirection character is escaped (\<), the shell on server will ignore it; the redirection will be done on slug, and the cat command will run on slug and send the audio down the network to server. Next, if a message on server has audio to "show" (play), the profile entry tells mhn or mhshow to output a warning that reminds me to wait (audio files can be big). Then it stores a temporary filename in a shell variable, sends the audio down the network to the temporary file on slug, and runs cp on slug to send the audio to slug's speaker. I used a temporary file to collect all the audio before playing it; this avoids network delays that could make the audio sound "choppy."

This example wasn't meant to impress you. :-) I wanted to show what you can do if you understand how UNIX shells work. For instance, even though the commands in MIME profile entries are interpreted by the Bourne shell, my account on slug uses the C shell... so I knew that the rsh commands had to use C shell syntax. Your entries probably won't need to be this complex -- but it's nice to know what MH and nmh let you do!

Next, let's take a close look at each kind of profile entry. The first section below is a tutorial. Sections near the end provide reference information.

Showing MIME Contents

The Section Showing MIME Messages introduced the way that mhn and mhshow display the message header and body. There's much more information about all of this in the mhshow(1) manual page; if you use nmh-1.0 or higher, that's a good place to get the gory details. This section of the book starts with some details (not as many as the man page) about the display of MIME body contents, and it ends with several examples of changing that display.

After showing the header, MIME body parts are shown one-by-one. Or, for a multipart/parallel message, parts are shown at the same time unless a %e escape (listed in the Table Display String Escapes) prevents it. In a MIME message, each body part has a content-type. To display a content, mhn and mhshow run a UNIX command. The UNIX command is found in a profile entry, as explained in the Section MIME Profiles.

If mhn or mhshow can't find a profile entry, it has two defaults: the moreproc: (your MH profile entry, or the system default) is used for text/plain contents, and the MH show command is used for message/rfc822. (Yes, mhn and mhshow can run show -- and, if the message/rfc822 content is itself a MIME message, show can start mhn or mhshow again. MIME messages can contain other MIME messages, so this behavior makes sense.)

If mhn or mhshow runs out of choices, it complains and quits with an error like:

    mhn: don't know how to display content
         (content application/x-foobar in message 6, part 2)
You might want to add defaults for all content-types -- as a fallback, in case there isn't a command for a particular subtype. Be careful that the defaults won't do anything unexpected with subtypes you haven't heard of (like x-foobar above). Because all MIME-encoded messages are 7-bit ASCII text (for now, at least), a safe way to display unknown content-types is with a pager -- for instance, your moreproc. Then, if the pager shows you garbage, you can just quit it and go on to the next part of the message.

(Although I said "all MIME messages are 7-bit for now," some companies use 8-bit or binary content, and more will do that as mail transfer starts to handle 8-bit data. In that case, be sure that the default display program you choose can handle any 8-bit data (including characters like NUL) without locking up your terminal or causing you other grief. It might be better to let mhn or mhshow complain and quit instead of trying to handle all defaults.)

CAUTION: Some graphic file formats may contain commands that can be dangerous security holes. For instance, PostScript files can hold commands that delete other files.

File viewers are beginning to have security features built in. For instance, GNU ghostscript has a -safer option to disable the PostScript file-deletion "feature." I can't possibly tell you how safe every file viewing program is. But, if you're concerned about security, you should check before you use an unknown file viewer to display an unknown content.

Here are three example mhn entries to get you started. Remember that you can change the mhn- to mhshow- if you're using nmh-1.0 or above:

    mhn-show-image: %pxv '%f'
    mhn-show-audio/basic: %lraw2audio 2>/dev/null | play
Let's look at those three entries.
  1. The first entry displays image content-types if there's no entry for the particular subtype used in your message. For example, let's say that your message has an image/x-pbm part, but the only other entry is for image/gif. mhn and mhshow would default to the mhn-show-image: display string for general image types.

    The message part (for example, translate 7-bit base64 into an 8-bit binary file) would be decoded and stored in a temporary file. Then the first profile entry would be used, %f would get the temporary filename, and the command xv 'tempfile' will run. (xv is a shareware image viewing program for the X Window System that knows how to display a lot of graphic file formats.)

    The %p in the display string above is an escape that tells mhn and mhshow to list content information on the terminal (like mhn -list or mhlist would), then prompt and wait:

          part 3     image/x-pbm                40K Sample waveform
          Press <return> to show content...
    When you see that prompt, you can skip the part (not run xv to display it) by pressing your interrupt key -- often, that's CTRL-C. (In MH version 6.8.4, you can also type q to skip a part.) Or, you can skip the rest of the message and get back to a shell prompt by pressing your QUIT key -- typically CTRL-\. There are two ways to prevent pausing or printing the description of the part:
  2. In the second entry, there's no %f to stand for the temporary file. So the decoded part is fed to the standard input of the command line given. As always, a Bourne shell executes the command line. (So, the Bourne shell's 2>/dev/null redirection operator throws away the standard error from the raw2audio command.) In this example, the decoded raw audio data will be fed to the standard input of the raw2audio program. The standard output of raw2audio is piped to the play program.

    The escape %l tells mhn or mhshow to give a listing, like mhn -list or mhlist would, before it runs the "display" command. Unlike %p, the %l doesn't prompt and wait for you to press RETURN. So this command will print a one-line listing of the part, then play the sound immediately.

  3. The third entry is empty. It makes mhn and mhshow complain "don't know how to display content" when you try to display video/mpeg. Use an entry like this in your personal profile when the system default isn't what you want -- and you don't have anything else acceptable.
NOTE: A good general-purpose viewer for text, especially text with 8-bit characters, is less. Add moreproc: less to your MH profile. MH 6.8.3 comes with less version 177. The latest versions of less are even better at handling international text. Check an up-to-date freeware archive site.

If you use less, read its manual page for configuration information. In less version 177, you can display the ISO-8859-1 character set by setting the LESSCHARSET environment variable to latin1 and the LESS environment variable to r.

Now is a good time to edit your MH profile (or an MHN or MHSHOW file, if you'd rather) and experiment. The steps below will lead you through the basics of display strings.

The examples above showed the %f, %l and %p escapes. The Table below lists all of the escapes for display strings.

Table: Display String Escapes

Insert parameters from Content-type: field.
Insert content description.
Exclusive execution.
Insert filename with content. Take standard input from content.
Insert filename with content. Take standard input from terminal. Exclusive execution.
Display listing, then display content.
Display listing, prompt, then display content.
Insert content subtype.
The %a, %d, and %s escapes are the most straightforward. They pass information about the content to the display program. Let's start with a simple profile entry. Add the following entry to your profile. (In nmh-1.0 and above, you can replace mhn- with mhshow-.) The command head -3 should display the first three lines of the content. If your system doesn't have the head(1) command, use the command sed 3q instead:
    mhn-show-text/plain: %phead -3
Find a junk text/plain message that you'll want to remove. If your folders are full of various MIME messages, use mhn -list to find a message that has the content types you want. (If you don't have any, send yourself one now.) When you show the message, you should see the header, the prompt from %p, and the first three lines of the body:
    % show
    (Message inbox:172)
    Date:    Thu, 25 Aug 1994 06:37:23 PDT
    To:      Randy Wyse <>
    From:    Jerry Peek <>
    Subject: Re: Book outline
    MIME-Version: 1.0

    part       text/plain                2441
    Press <return> to show content...
    First three lines
    of the body
    appear on your screen
Of course, that's not a useful way to display most messages: you'd usually like to see all of the content. But don't delete that entry in your profile yet.

Notice that you didn't give a filename to that head command. mhn or mhshow passed the content to the standard input of the head command. You can give a filename wih the %f or %F escapes. We'll try that next.

Let's add a more complex entry to your profile. This one runs the pr(1) command, which formats a file for printing. The pr -h option specifies a heading you want to print at the top of a page. But your entry won't send the content to a printer. Instead, it will display the pr-formatted version on your terminal -- using the more(1) pager program. If you don't have more, substitute the pager you use -- like pg or less -- in the command. Use mhshow- instead of mhn- with nmh-1.0 or above. Be sure to use the escape %F (uppercase letter "F"), not %f:

    mhn-show-text: pr -h '%d (content-type: text/%s)' '%F' | more
That display string is a command like one you might type at a shell prompt. In fact, before you show a message, try typing that command at a shell prompt to see what it does. Replace the %F escape with the filename of a text file in your current directory:
    % pr -h '%d (content-type: text/%s)' 'somefile' | more

    Jul 31 09:17 1994  %d (content-type: text/%s) Page 1

        first screenful of somefile appears...

     -- More -- 
Because mhn or mhshow wasn't interpreting the %s and %d escapes, the pr command displays them literally in its heading.

Next, let's see if we can make that profile entry work on a real message. (Note: I'm trying to surprise you here.) Let's show the same message with the content-type text/plain that you did in the previous example:

    % show
What happens? You should see the first three lines of the body; that's all. Your new mhn-show-text: display string should not have been used! Why? Because your profile entry, mhn-show-text:, is the default for text contents. It's used when there isn't a profile entry for the particular subtype (in this example, text/plain). But you also have a mhn-show-text/plain: entry, so it's used instead.

Now, let's make your profile entry take effect. Change the content-type of the message by editing it with your favorite editor (emacs, vi, etc.):

    % emacs `mhpath cur`
(The `mhpath cur` command, in backquotes, puts the absolute pathname of the current message on your editor command line.) Find the Content-type: header field and change it to the unlikely value text/x-haha. If there's a ; charset=something parameter on that Content-type: header field, remove it. Also, if the message doesn't have a Content-Description: field, add one. The two fields should look like this:
    Content-type: text/x-haha
    Content-Description: Test message!
Okay? Now your test message has a different content-type that (probably!) no other profile entry will match. Let's try it:
    % show

    Jan 09 09:17 1995  Test message! (content-type: text/x-haha) Page 1

        first screenful of content appears...

     -- More -- 
Please do not read the rest of the message yet. Think: Which command is in control of your display at this moment? It's the command started by the shell, which was started by mhn or mhshow. The shell's command line looks like this:
    pr -h 'Test message! (content-type: text/x-haha)' 'filename' | more
mhn or mhshow filled in the escapes as the Table Display String Escapes showed: %d got the description, %s got the subtype, and %F got the temporary filename (shown here as filename) that mhn or mhshow chose for holding the content.

The more pager is printing a prompt. You can give more's "quit" command (usually, q) to make the pager stop. Or, you can finish displaying the message and quit the pager. You wouldn't use the mhn/mhshow "quit" command (CTRL-C or CTRL-\) at the -- More -- prompt because mhn or mhshow isn't in control now. When your display command finishes, mhn or mhshow is in control again: it can show the next part of the message, if any, or quit.

Ready for another example? Before you get started, make a note of the message number you used in the previous example. You'll be using it again in the Section Displaying Other Character Sets.

The Metamail package is a group of programs for working with MIME mail. If it isn't on your system, The Section Metamail explains where to get it. The MH and nmh MIME programs do some of the things that Metamail was written to do: create and understand MIME messages and deal with multipart contents. But mhn and mhshow don't know how to display other contents -- for instance, they can't handle enriched text (the text/enriched content type). The Metamail package comes with a program named richtext that was written for handling the obsolete text/richtext content. The -e option makes versions 2.7 and above of richtext display enriched text.

Let's make a profile entry that runs richtext -e. You may already have a profile entry for displaying enriched text. Search for it with grep -i, which ignores the difference between upper- and lowercase letters as it searches. Fill in the filenames below (though you can leave $MHN or $MHSHOW as it is; your shell will replace it with the filename from the environment variable):

    % grep -i "text/enriched" $MHN .mh_profile /path/to/library/directory/mhn_defaults

    % grep -i "text/enriched" $MHSHOW .mh_profile /path/to/library/directory/mhn.defaults
If you didn't see a matching line, you're ready to go. If you saw a matching line in your system mhn_defaults or mhn.defaults file, remember that your personal entry will override it. Make an entry like one of the two below. If the richtext program isn't in a standard system directory (in your shell's search path), you'll need to use an absolute pathname:
    mhn-show-text/enriched: %prichtext -e -t '%F'

    mhshow-show-text/enriched: %prichtext -e -t '%F'
The -t option is just for this exercise. It puts asterisks around bold text (*bold*) and underscores around italic text (_italic_).

Now, you'll need an enriched text message. You can search for a non-multipart message by using pick and its --content-type switch. If that doesn't match a message, you can search all the messages, multipart included, with pick -search:

pick --content-type text/enriched   ...single-part messages
pick -search "^content-type: text/enriched"   ...multipart, too

No luck? Just create a simple enriched text message with a text editor. The section Sending MIME Mail has an example. Include at least one word in boldface and one word in italic text.

Next, show the message. The %p escape should make mhn pause before the text/enriched content. The whole body should be shown at once, without stopping, because you haven't used a pager. richtext has a simple pager built in; you can get it by using the -p option. Now that you've seen what the -t option does, you can remove it (unless you have a dumb terminal, in which case you might want to keep it). If you have the richtext(1) manual page on your system, check it for other interesting options that you might want. Your entry should look something like this (maybe with mhshow- instead):

    mhn-show-text/enriched: %prichtext -e -p '%F'
Try it again. Optimize the boldface and italic handling for your display. If you have other displays, you can try showing the message there, too; if you need different settings, make a MIME profile for one display or the other.

I haven't shown the %a escape, the Content-Type: parameters, yet. There's an example in the getrich script in Section Composing MIME Content.

At this point, if you haven't read the MH mhn(1) manual page, you're ready to read the first few pages of it. Read through the end of the section called "Showing the Contents." It mentions character sets, which will be a good introduction to the next section of this tutorial. Or, if you're using nmh-1.0 or above, read the mhshow(1) manpage.

Displaying Other Character Sets

Not everyone in the world uses the same character set you do (whatever that is). English-speaking people probably use the us-ascii character set, a series of 128 characters that include every letter used in the English language. Other people use other character sets. One of the most common is iso-8859-1, a 256-character set that handles many non-English languages.

(MH has other support for "international" characters -- that is, non-English characters. See the Section International Character Support.)

There are three things mhn and mhshow need to know to work with multiple character sets:

For comparison, here are three typical mhn-charset-iso-8859-1: entries:
    mhn-charset-iso-8859-1: xterm -fn '-*-*-medium-r-normal-*-*-120-*-*-c-*-iso8859-1' -e '%s'
    mhn-charset-iso-8859-1: %s | iso2asc 0
    mhn-charset-iso-8859-1: %s
All examples use the %s escape; it is required.
  1. The first entry starts xterm, an X Window System terminal emulator. This opens a new window on your X display with a font that includes all iso-8859-1 characters. (Although I haven't seen the problems myself, I've heard that some X fonts which are labelled as iso8859 don't contain all of the ISO-8859 characters.)
  2. The second entry is for people who can't display iso-8859-1 because they have an ASCII-only terminal. It uses the iso2asc ISOto-ASCII converter program. (If iso2asc isn't on your system, you can get a copy from this book's online archive. The code is in ../examples/mh/iso2asc/iso2asc.c, and there's extensive documentation in ../examples/mh/iso2asc/iso2asc.txt.
  3. The third example entry is the simplest. It tells mhn that (basically) your terminal can handle iso-8859-1 characters with no other help. So mhn will run the default display program.
To set up for the next exercise, please put the following two entries in your profile. (If you did the tutorial in the previous section, you probably already have the text/plain entry.) Do the usual mhn/mhshow swap for nmh-1.0+:
    mhn-show-text/plain: %phead -3
    mhn-charset-x-upperlower: %s | tr '[a-z]' '[A-Z]'
If you know the UNIX tr(1) command, the second entry should look familiar. It translates all lowercase characters to uppercase.

Next, edit the text/plain message you used in the previous section. Change the Content-type: field to read:

    Content-type: text/plain; charset="x-upperlower"
If the message body has any non-ASCII characters, please delete them for this example. (Sorry.) The message should have only upper- and lowercase English letters (A-Z and a-z), digits and punctuation.

Now on to the example. "What is this x-upperlower?," you're probably asking. To make a simpler example for people who are new to the idea of displaying other character sets -- and to be sure no one can accuse me of character-set favoritism :-) -- I've just invented a new character set named x-upperlower. The x-upperlower character set has all the English letters, both upper- and lowercase. (Actually, it's the us-ascii character set. But we'll ignore that.)

Here's our problem in this exercise. You'll play the part of a person with an old computer display. Your terminal can't display lowercase characters. It's ALL UPPERCASE. When people with these old terminals display a message that has this field:

    Content-type: text/something; charset="x-upperlower"
they can't see the lowercase letters. They need a mhn-charset-x-upperlower: profile entry to translate the lowercase letters into something they can see: that is, to translate the lowercase letters into ALL UPPERCASE.

Show the text/plain message that you just edited. The content (the body, that is) should come out ALL UPPERCASE. That's because the message has the parameter charset=x-upperlower in it. But the MM_CHARSET environment variable on your terminal isn't set to x-upperlower. So, mhn uses your mhn-charset-x-upperlower: profile entry to translate the x-upperlower characters to a character set you can see. (Please ignore those lowercase letters in the header. ;-) The MIME RFC 2045 spec only applies to the message body.)

    % show
    (Message inbox:172)
    Date:    Thu, 25 Aug 1994 06:37:23 PDT
    To:      Randy Wyse <>
    From:    Jerry Peek <>
    Subject: Re: Book outline
    MIME-Version: 1.0

    part       text/plain                2441
    Press <return> to show content...
To build its command line for displaying the message in a different character set, mhn or mhshow:
  1. Finds the mhn-show- or mhshow-show- entry for this content type (here, mhn-show-text/plain:).
  2. Finds the mhn-charset- or mhshow-charset- entry for this character set. It replaces the %s with the command to show the content.
In this example, the command line mhn uses with your test message is:
    head -3 | tr '[a-z]' '[A-Z]'
(Also look back at the profile entries, if you'd like.) The head -3 command displays the first three lines of the content. That output is piped to tr '[a-z]' '[A-Z]', which translates the content into uppercase for your ancient uppercase-only terminal.

Now, let's say you've moved to a terminal that can show both upper- and lowercase letters. To tell mhn/mhshow about this, you need to set the MM_CHARSET environment variable to the name of your character set -- that is, to x-upperlower. This syntax works for both MH and nmh (believe it or not!!):

setenv MM_CHARSET "x-upperlower"    ...csh-type shell

MM_CHARSET="x-upperlower" shell
$ export MM_CHARSET

Now when you show your text/plain message, two interesting things should happen. Try it:

    % show
    (Message inbox:172)
    Date:    Thu, 25 Aug 1994 06:37:23 PDT
    To:      Randy Wyse <>
    From:    Jerry Peek <>
    Subject: Re: Book outline
    MIME-Version: 1.0

    All the lines
    of the body
    appear on your screen
    with no "Press <return>..." prompt.
Because your terminal can display x-upperlower, and because the message is plain text, MH/nmh don't need a special display command to show it on your terminal. It ignores the %phead -3 command for text/plain. It shows the message as it would show a non-MIME message. In fact, show doesn't even invoke mhn or mhshow for this message.

If you want to see what mhn/mhshow would do with the message if show had invoked mhn/mhshow, try it. (Just type mhn or mhshow at the shell prompt.) mhn should use your mhn-show-text/plain: profile entry, and mhshow should use your mhshow-show-text/plain: entry. It'll prompt you to Press <return>..., then run %phead -3 to show the first three lines.

To avoid confusion later, you should probably take the mhn-show-text/plain: display string out of your profile. Also set the MM_CHARSET environment variable to the character set your terminal can actually handle -- for example, us-ascii or iso-8859-1.

Composing MIME Content

The Sections Sending MIME Mail and Composing and Sending MIME Messages introduced message composition with mhn and mhbuild. If you haven't used mhn or mime (which invokes mhbuild) from a What now? prompt, please do that before you work through this section of the tutorial. In this section, to show you more of what happens "under the hood" as mhn and mhbuild compose a draft, you'll start by running them from a shell prompt instead of from the usual whatnow program prompt. Doing things this way should also help people who want to use mhn or mhbuild to build draft messages for their own email programs.

If you use a window system, open a new window for this part of the tutorial. Otherwise, start a subshell by typing the name of your shell at the prompt. For example, if you use the C shell:

    % csh
The separate window, or subshell, will make it easy to undo the changes to your environment in this tutorial.

Make a little shell script named showenv that shows the UNIX environment. If your system has the env(1) command, use it; otherwise, you probably have printenv(1). Put that command in the showenv file and make it executable. Then run the shell script:

    % vi showenv
    ...put these two lines in the file:
    % chmod 755 showenv
    % showenv
(If the current directory isn't in your search path, you may need to type ./showenv to run the script.)

Next, compose a draft message in the usual way, with the comp program. Make a multipart message with three parts: enriched text entered from the draft (a #< directive), a non-text file, and the output of the who(1) program. Then get to a What now? prompt and wait; do not run mhn or mime to encode the draft.

    % comp
    To: someone
    Subject: Testing mhn
    The stuff in this message is <bold>junk</bold>,
    just some stuff I'm using to test <italic>mhn</italic>.
    #text/plain [The output of who(1)] |who
    #application/octet-stream [The who(1) program binary] /bin/who


    What now?
At this point, the whatnow program is prompting you. If you haven't read the overview of what's happening at this point, you should probably look at the Section What now? -- and the whatnow Program.

Now, a question for you MH users. (nmh users, browse through and see why nmh created the mhbuild command... to avoid the messy situation with mhn, I suspect.) What makes mhn act the way it does when you type mhn at a What now? prompt? (Note: don't run it yet!) When you run mhn from a shell prompt, it displays a message. But when you run mhn from a What now? prompt, it encodes a message. What's happening? The difference is in the UNIX environment set by the whatnow program. Let's look at the environment. Tell whatnow to run your showenv script:

    What now? edit showenv
(If you get a "command not found" error, use a pathname -- for example, edit ./showenv.) The whatnow program has stored the absolute pathname of the draft file in the mhdraft environment variable. When you run mhn from within whatnow, mhn sees the mhdraft environment variable and becomes a message-encoder instead of a message-displayer. (nmh users: this is ugly, isn't it? mhbuild doesn't need this trick.)

Now quit and leave your unencoded draft message where it is. Then change your current directory to the one where your draft file is. (Note that older versions of whatnow don't show the draft pathname as they quit; if you don't know where your draft is, look back at the value of the mhdraft variable.) List the directory and look for your draft:

    What now? q
    whatnow: draft left on /u/joe/Mail/drafts/3
    % cd /u/joe/Mail/drafts
    % ls
    ...    3   ...
If you run showenv again, you'll see that the mhdraft variable isn't set. Because we need mhn to encode the draft, add mhdraft to your environment. Use the pathname to your draft message:

$ mhdraft=/u/joe/Mail/drafts/3 shells
$ export mhdraft

% setenv mhdraft /u/joe/Mail/drafts/3    ...csh-type shells

NOTE: If you're using nmh, you have another choice. Instead of setting mhdraft, you can simply use mhbuild and give a filename on the command line -- for example, mhnbuild draftfile.

Now you're ready to encode the draft. MH users, run mhn (nmh users, run mhbuild):

    % mhn /u/joe/Mail/drafts/3
    composing content text/plain from command
If you get errors -- for example, mhn can't read the file /bin/who because your system administrator has denied read permission -- edit the draft file and change the directives. (Use your favorite editor and the draft filename, like this: vi 3.) Then rerun mhn -- and be sure you don't get the error.

Now list your directory again. You should see the encoded draft and another file: the original draft with the directives in it. Read them both with a pager program like more(1):

    % ls
    ...   ,3.orig  ...  3   ...
    % more ,3.orig 3
    To: someone
    Subject: Testing mhn
    The stuff in this message is <bold>junk</bold>,
    just some stuff I'm using to test <italic>mhn</italic>.
    #text/plain [The output of who(1)] |who
    #application/octet-stream [The who(1) program binary] /bin/who
     -- More-- (Next file: 3)
    To: someone
    Subject: Testing mhn
    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
    Content-ID: <>

    ------- =_aaaaaaaaaa0
    Content-Type: text/enriched; charset="us-ascii"
    Content-ID: <>

    The stuff in this message is <bold>junk</bold>,
    just some stuff I'm using to test <italic>mhn</italic>.

    ------- =_aaaaaaaaaa0
    Content-Type: text/plain; charset="us-ascii"
    Content-ID: <>
    Content-Description: The output of who(1)

    joe      console Oct 28 06:23
    joe      ttyp0   Oct 28 06:24   (:0.0)
    joe      ttyp1   Oct 28 06:24   (:0.0)
    joe      ttyp2   Oct 28 06:24   (:0.0)

    ------- =_aaaaaaaaaa0
    Content-Type: application/octet-stream
    Content-ID: <>
    Content-Description: The who(1) program binary
    Content-Transfer-Encoding: base64


    ------- =_aaaaaaaaaa0--
Now assume you've changed your mind about your draft. You'd like to include another part: your signature file. Editing the draft (the file 3) can be a hassle; it isn't always easy to create MIME-format messages by hand. But you can replace the encoded draft with the original file that has the directives. Then edit the draft and re-run mhn. Let's do it:
    % mv ,3.orig 3
    % vi 3
        ...add this directive to the end:
    #text/plain [signature] /u/joe/.signature
    % mhn /u/joe/Mail/drafts/3
    composing content text/plain from command
    % more 3 draft appears...
That's enough experimenting with mhn+mhbuild at a shell prompt. Unless you want to keep working from a shell prompt, close the separate window or end the subshell by typing exit:
    % exit
You should be back at your original shell in the original current directory. The mhdraft environment variable should not be set. (If you want to check, run showenv again.) Now you're ready to experiment with MIME directives.

Let's start by looking at the type directives, #type/subtype. (If you need a reminder of the syntax, check the first three Tables in the Section Syntax of MIME Draft Directives.) mhn and mhbuild get the content for those directives by looking at the last argument:

The Table below lists the escapes that work in composition strings.

Table: Composition String Escapes

Insert parameters (example: x-conversions=compress) from directive.
Insert temporary filename where content should be written.
Insert temporary filename where content should be written. Don't redirect standard output to content.
Insert content subtype.
To make some use of those parameters, let's assume that you have a program for entering and editing enriched text. You'd like mhn or mhbuild to run your editor, let you compose the part, and then insert the edited part when the editor quits. Call the editor program getrich.

How do you give the editor a filename to edit from the composition string? The %f and %F escapes do that. Both escapes give the filename, but one of them changes the way that mhn/mhbuild handles output. Normally -- if you don't use any escapes, or if you use any escape except %F -- the standard output of the composition command is directed to the message content. This is good; it lets the output of the command be sent in the mail message. But you won't want that to happen when you use an interactive text editor. If it did, you couldn't see the screen or prompts that the editor makes: those would be redirected into the content, instead of shown on your screen! Notice in the previous Table that the %F escape is replaced with a filename and it tells mhn not to redirect the composition command standard output to the content. That's what you want in a case like this.

Add a composition string to your profile that runs getrich. The script will need some parameters, so add the escapes shown (and change mhn- to mhbuild- if you should):

    mhn-compose-text/enriched: getrich '%F' %a
If getrich was supposed to make more than one subtype (as in an mhn-compose-text: or mhbuild-compose-text: composition string), it would probably need to know what subtype it should produce. In that case, you would also put a %s parameter in the composition string. In this example, though, %s would be useless because getrich will always make text/enriched content.

To get started, make a simple getrich that displays the parameters passed to it and then outputs some dummy enriched text. (It won't start a text editor yet.) A Bourne shell script is shown below; you can also get it from this book's online archive in examples/mh/bin/getrich. You could write the script in Perl or some other language if you'd rather.

    ### getrich - create enriched text for mhn
    ### Usage (in MIME profile):   mhn-compose-text/enriched: getrich '%F' %a

    # show command line arguments.  Write to standard error so 
    # mhn won't add this text to the message content:
    echo "getrich: got these arguments: $*" 1>&2
    # Output two lines of enriched text:
    echo 'This is a <bold>bold</bold> word from the "getrich" script.
    This one is <italic>italic</italic>.' > "$1"
    # Make sure mhn keeps the output:
    exit 0
The two-line echo command at the end of the script writes two lines into $1, which is the filename from the first %F parameter. Use chmod +x to make the getrich script executable.

Next, make a short file named sampletext with two or three lines of enriched text in it.

Now, make a new draft with three different flavors of enriched text directive. One includes text you type into the draft, one reads the short enriched text you created above, and the third runs getrich. (If your directive doesn't give a pathname to another directory, mhn and mhbuild will read sampletext from the current directory.)

    #<text/enriched [text typed into the draft]
    The stuff in this part is <bold>junk</bold>,
    some text I'm using to test <italic>mhn</italic>.
    #text/enriched [text from a file] sampletext
    #text/enriched [text from getrich]
After you run mhn or mhbuild, take a look at the draft. It should contain the text from all three parts.
    What now? edit mhn   (on nmh, type mime)
    composing content text/enriched from command
            /tmp/getrich '/tmp/mhnb00987'
    getrich: got these arguments: /tmp/mhnb00987

    What now? list
    ------- =_aaaaaaaaaa0
    Content-Type: text/enriched; charset="us-ascii"
    Content-ID: <>
    Content-Description: text typed into the draft

    The stuff in this part is <bold>junk</bold>,
    some text I'm using to test <italic>mhn</italic>.

    ------- =_aaaaaaaaaa0
    Content-Type: text/enriched; charset="us-ascii"
    Content-ID: <>
    Content-Description: text from a file

    This is sample enriched text from the <italic>sampletext</italic>
    file.  Are we having fun yet???

    ------- =_aaaaaaaaaa0
    Content-Type: text/enriched; charset="us-ascii"
    Content-ID: <>
    Content-Description: text from getrich

    This is a <bold>bold</bold> word from the "getrich" script.
    This one is <italic>italic</italic>.

    ------- =_aaaaaaaaaa0--

    What now? quit delete
(If your lproc is set to use show when you list the draft, you won't see the encoded draft. In that case, you can see the encoded draft with the more(1) pager by typing edit more.)

The last line of getrich sets a zero exit status. This is important: if the composition command returns a nonzero status, mhn/mhbuild will abort and not encode the draft. If you'd like to experiment, change the exit 0 to exit 1, compose a new message for getrich and run mhn/mhbuild:

    #text/enriched [output from getrich script?]

    What now? e mhn   (on nmh, type mime)
    composing content text/enriched from command
            /tmp/getrich '/tmp/mhnb00993' 
    getrich: got these arguments: /tmp/mhnb00993
    Exit 1

    What now?
Let's hack getrich to use the parameters passed from the %a escape and to actually run a text editor. The order of the composition string parameters is important. You put the content filename (%F) first, before the optional parameters. The script will grab the filename from its first argument; the rest of the arguments will be parameters from the directive. Here's the revised getrich:
    ### getrich - create enriched text for mhn
    ### Usage (in mhn profile):   mhn-compose-text/enriched: getrich '%F' %a

    # Grab content filename:
    file="$1"; shift
    # If there are parameters, "handle" them:
    for param
            echo "getrich: what is this '$param' parameter for??" 1>&2
    # Create content by running enriched text editor (actually, vi).
    vi "$file"
    # Make sure mhn keeps the output:
    exit 0
If you use a window system, you might change the editor line to open a separate window with a text editor in it. For instance, in the X Window System you could open a new xterm running the vi editor with:
    xterm -e "vi $file"
Now compose a draft with a directive that will run getrich. Add some parameters if you'd like to. (If you don't add parameters, the for loop in getrich won't run to process the parameters -- and you won't see the sassy remark about each parameter.)
    % comp
    #text/enriched; x-temperature="98.6 F"; x-foo=bar [text from getrich]

    What now? edit mhn   (on nmh, type mime)

    composing content text/enriched from command
            /tmp/getrich '/tmp/mhna01003' x-temperature="98.6 F" x-foo="bar"
    getrich: what is this 'x-temperature=98.6 F' parameter for??
    getrich: what is this 'x-foo=bar' parameter for??
       ...editor starts on the empty /tmp/mhna01003 file
    What now? list
    MIME-Version: 1.0
    Content-Type: text/enriched; x-temperature="98.6 F"; x-foo="bar";
    Content-ID: <>
    Content-Description: text from getrich

    This is the enriched text I'm entering with <italic>vi</italic>.

    What now?
When you list the draft, you should see the enriched text that you entered with the editor.

This simple version of getrich displayed the x-temperature and x-foo parameters you passed to it. The script could have used them for something -- for example, post-processing the output with a command like compress -- if you set it up that way. Notice that mhn or mhbuild also copied the parameters to the Content-Type: header field (and added a charset parameter, too).

Storing Content

As the Section Decoding and Storing MIME Messages explained, mhn and mhstore can decode message contents and store them in a file. Note that storing contents isn't the same as the same as caching contents. The cache is used for local copies of external body parts; it's for internal MH and nmh use. On the other hand, storing contents has the same purpose as storing a standard UNIX file: so you can use the stored content with any program you want to.

Because the Section Decoding and Storing MIME Messages introduced MIME message storage with examples from configuration files, this section will be mostly reference.

mhn -store and mhstore use the profile entries mhn-store-type/subtype: and mhstore-store-type/subtype:, respectively. otherwise they use mhn-store-type: and mhstore-store-type:. If a profile entry isn't found, the following defaults are attempted:

The string can be one of several types, as the Table below shows.

Table: MIME Storage Formatting Strings

Pass content to standard input of command. Can use escapes in the Table MIME Storage Escapes.
Write content to folder (default: current folder)
Write content to subfolder folder (see the Section Relative Folder Names)
Write content to standard output of mhn (Bug: this does not work in MH 6.8.3 or versions of nmh before 0.18. See the workaround in the mimecat script.)
Write file to pathname. If pathname starts with /, that directory is used; else, directory named by mhn-storage: (MH) or nmh-storage: (nmh-1.0+); else current directory. Can use escapes in the Table MIME Storage Escapes.
The next Table lists the escapes you can use in the | (pipe) and pathname storage formatting strings.

Table: MIME Storage Escapes

Parameters from Content-type: (only for |-type formatting strings).
Message number
Part number with leading dot. (Usually used in middle of formatting string.) Ignored if message not multipart.
Part number without leading dot. Ignored if message not multipart.
Content subtype.
Here are some examples.

Caching External Body Parts

When mhn, mhlist, mhshow, or mhsstore is run, and the message has an external body part with a Content-ID: field, the content might be read from a cache. The Section Cached Contents has a listing of a cache and examples of mhn and mhshow using caches. This section has tips for configuring the caches.

Each user can access two cache directories, public and private.

The Public Cache

The public cache directory is usually named in your system's mhn_defaults or mhn.defaults file. That lets everyone on the system share the public cache. If you need to set a different public cache directory, put an mhn-cache: or nmh-cache: entry in your MH profile or MIME profile. And, of course, any user can ignore the public cache by setting the -rcache and -wcache switches to private or never.

If everyone on the system is allowed to write to the public cache, the system administrator needs to make its directory world-writable (mode 777). mhn and mhshow will create all the cache files read-only (mode 444), but that doesn't stop people from removing cached files when they shouldn't. On many UNIX systems, the owner of a directory can set its sticky bit by using chmod 1777. On those systems, people will be able to add files to the directory, but they can't remove or rename files that were created by someone else.

Computers with networked filesystems can share a public cache between hosts. Any user on a host sharing that filesystem can save a public content. The mhn_defaults and mhn.defaults files on all those systems need to point to the right directory. Of course, if the network connections are slow, you may be undoing some of the benefit of cached contents: fast access to message parts without network delays.

The Private Cache

Each user can have her own private cache. Unless you name one in the mhn-private-cache: or nmh-private-cache: profile entry, the default will be a subdirectory of your MH directory named .cache. The default name starts with a dot (.) to "hide" it from the ls command. But the default cache will still show up in the folders command output. I put my cache somewhere else:

    mhn-private-cache: /home/jerry/tmp/mhn-private-cache
If you don't want to use space in your home directory, you could try putting the cache in a subdirectory of a system temporary-file directory like /tmp/mhn-cache-yourname. Remember to protect the directory (chmod 700) if you might be storing private messages. Also remember that a cache directory in /tmp may be deleted or emptied by the system. That may be just what you want, of course. But if you want your cache to be more permanent, put it somewhere else and use the cleanup scripts below.

Cleaning the Caches

Cached copies of external body parts can get out of date. For instance, a cached copy of today's weather map probably wouldn't be much use by next week. mhn and mhshow are good at writing to the caches, but they don't have a way to clean them up. This section has a few ideas.

The MIME standard, RFC 2045, describes an optional expiration= parameter for external body parts. The parameter says that after the date given, the content might be missing or invalid. Unfortunately, not all messages use that parameter. Even if they do, mhn and mhshow don't save that date anywhere that I can see. Tidy users can get the expiration date from mhn -list -verbose or mhlist -verbose and remove old cached contents. (If your system has many cache "housekeepers" like that, setting the sticky bit on the public cache directory might be a bad idea!) You'll probably still get plenty of old files in your caches.

Unless someone goes through the caches by hand every week or two to clean out old files, the best way to clean up is with a script run by cron(8) or at(1). The script can use find(1) to search for and remove files that are more than some number of days old. With its -mtime switch, find will remove the files based on the date they were stored. Or, if you use the -atime switch, the test will be on when the file was last accessed. If a cached file hasn't been accessed for a while, it might be a safe bet to remove. Finally, you might use the size of the file (find -size) as a factor. Big files take longer to copy over the network, so you could keep them longer. Of course, big files take more disk space, so you might want to delete them sooner. Whatever. :-)

If you have a better scheme for cleaning out caches, please tell me about it! Just click on the email link at the end of this section.

Getting External Body Parts by FTP

mhn, mhshow and mhstore don't run ftp(1) to get external body parts with ftp access types. Instead, they have their own simple built-in FTP code. You can also name your own FTP program at the mhn-access-ftp: profile entry; in nmh-1.0 and above, the entry is nmh-access-ftp:. If there's a program named in mhn-access-ftp:, it will be used instead of the built-in FTP code.

Why would you want to write your own program? You might be on a site that's behind an Internet firewall -- and need access beyond the firewall. You might be on a site with no Internet access -- and need to send a request to an email-to-FTP gateway like ftpmail. Or your host might not support a "sockets" interface.

The mhnftpmail shell script below is an example of a program you can run from an mhn-access-ftp: or nhm-access-ftp: profile entry. It uses the seven parameters passed from MH or nmh to build an email message to an ftpmail server. You can customize it for any ftpmail server you want to use. (ftpmail is explained in detail in O'Reilly Media's book Managing Internet Information Services, now out of print.)

This example shows the script being called as I show a message. The script doesn't have to be this verbose: edit it if you'd like to.

    % next
    Retrieve book.catalog (content 1.2)
        using anonymous FTP from site y
    mhnftpmail: sending mail to '':

    open anonymous
    chdir pub
    get book.catalog

    The 'Exit 1' error below makes sure mhn won't try to cache the missing response:
    Exit 1
    mhn: don't know how to display any of the contents
         (content multipart/alternative in message 55)
The script returns a nonzero exit status to be sure that MH and nmh won't think that the FTP job succeeded. You'll have to manually handle the file that ftpmail sends.

Here's the mhnftpmail Bourne shell script. You can also get the script from this book's online archive in examples/mh/bin/mhnftpmail.

    ### mhnftpmail - submit ftpmail request for mhn
    ### Usage (in mhn profile):   mhn-access-ftp: mhnftpmail
    # Customize this script for your site and ftpmail server.

    mhmail=/usr/local/mh/mhmail     # Absolute path to mhmail program

    #ftpmail=yourname@yourhost      # Uncomment for debugging

    # Build message to send in $msg (multi-line) shell variable.
    # arguments passed by mhn:  $1 - domain name of ftp site, $2 - username,
    # $3 - password, $4 - remote directory, $5 - remote filename,
    # $6 - local filename (not used here), $7 - "ascii" or "binary":
    msg="open $1 $2 $3
    chdir $4
    get $5"

    # Say what's happening (on standard error):
    cat << END_OF_STUFF 1>&2
    `basename $0`: sending mail to '$ftpmail':


    The 'Exit 1' error below makes sure mhn won't try to cache the missing response:

    # Send mail, exit with status 1 so mhn won't think we have the content.
    $mhmail "$ftpmail" -body "$msg"
    exit 1

Building MIME Drafts Automatically

By default, when you compose a message, comp (actually, the whatnow command) doesn't run mhn or mhbuild. If you forget that as well, you can send a message full of directives instead of the encoded body parts you want. The easy way to take care of this is by adding an MH profile entry that runs mhn, mhbuild, and/or another program you choose to build the MIME draft. The program you name will be run when you type send or push at the What now? prompt. (If you use push, the automatic processing runs before the message delivery is moved to the background. You'll be able to handle any interactive prompts or problems first.) Here are the methods for MH and nmh:

The automhnproc has another advantage if you use 8-bit (non-ASCII) characters: mhn will use the minimum required character set (for example, us-ascii for English-only) when it sends the message.

Automatic MIME processing isn't right for everyone. If you send drafts that have lines starting with a hash mark (#) which aren't directives, mhn will complain about an "unknown directive." You'll have to edit the message and change the non-directive lines starting with # to start with ## instead (or, use a script like automhn).

You can use any program you want for the automhnproc: or buildmimeproc:. For instance, if you've switched to MH from another MIME MUA, you could use the familiar Metamail program and your own .mailcap file (with some hackery, that is). The program you use should be able to take the absolute pathname of the draft from its first argument, encode the draft, and write it back. It should also return a zero exit status if it succeeded. Otherwise, if whatnow gets a nonzero status from the automhnproc, it will prompt you again with What now?.

Here's a simple but very useful example: a script named automhn. One problem with using mhn or mhbuild as the automhnproc or buildmimeproc is that they complain about lines in a draft that start with # but aren't MIME directives. The automhn script reads the draft message and adds an extra # to the start of any line which doesn't seem to be a directive; then mhn or mhbuild will change the ## back to a single # when it sends the message. For example, automhn will convert this draft message:

    To: joe
    Subject: My new script
    Hey Joe, here's my new shell script.  It wipes out your directory!
    <BOLD>Cool</BOLD>, eh, d00d?
    #<text/plain [nukeall script]
    # Clean thoroughly:
    rm -rf $HOME  # Maybe we should try / next time...
Into this:
    To: joe
    Subject: My new script
    Hey Joe, here's my new shell script.  It wipes out your directory!
    <BOLD>Cool</BOLD>, eh, d00d?
    #<text/plain [nukeall script]
    ## Clean thoroughly:
    rm -rf $HOME  # Maybe we should try / next time...
You can adapt automhn to do any automhnproc or buildmimeproc work you need to do. There's more information, and a link to the downloadable script, in the Section Explanation of automhn.