


Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)

Permission to use, copy, modify, and distribute this material  for any
purpose and without fee is hereby granted, provided  that the above
copyright notice and this permission notice  appear in all copies, and
that the name of Bellcore not be  used in advertising or publicity
pertaining to this  material without the specific, prior written
permission  of an authorized representative of Bellcore.  BELLCORE 
MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY  OF THIS
MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",  WITHOUT ANY EXPRESS
OR IMPLIED WARRANTIES.


1	Adding Diverse Multimedia Format Support to Established RFC822 Mail
and Bulletin Board Readers

                         Nathaniel S. Borenstein
                       <nsb@thumper.bellcore.com>
                                Bellcore

Abstract

It is surprisingly easy to use the RFC1049 "Content-type" header to turn
virtually any mail reading interface into a multi-media mail reading
interface.  Mail readers are simply modified to use the new "metamail"
program whenever they receive non-text mail.  The metamail program is
itself easily customizable by the use of a "mailcap" file that specifies
the media types supported by a given site or user.  Given the existence
of the metamail program,  this document explains how to add multimedia
support to sixteen very different mail reading programs, including all
of the most popular UNIX mail reading programs and (so far) one DOS mail
reading program.  

Motivation

Multimedia mail has been a long time coming.  The biggest impediments to
multimedia mail have been the absence of standards and the pain users
experience when they convert to a new mail system.   Thus, for example,
to receive the benefits of the rich multimedia capabilities of the
Andrew mail format, you have, in the past, had to convert from whatever
other mailer you're using to one of the Andrew-based mailers.

In the context of a new research project (the MAGICMAIL language for
active messaging) the author of this document has discovered that it is
remarkably easy to extend nearly any mail reading interface so that it
recognizes certain "Content-type" headers and, when it receives mail
with such headers, calls an appropriate external program to display or
interpret the mail body.  Because this functionality seems so generally
useful, I have taken the time to create this document, in the hope that
it will help to make various kinds of multimedia mail functionality more
widely available.

The ideas and code in this system apply equally well to both mail and
bulletin board programs.  Although this document talks mostly about
mail, it applies equally to bulletin board readers, and in fact one
bulletin board reading program (the Berkeley "msgs" program) is included
in the set of programs discussed here.
The Basic Idea

Basically, there are only two things you have to do to each mail reading
program:

1.  Make the mail reader notice the special header ("Content-type") that
marks a message as a non-text message.  (In the case of mail readers
that already understand certain content-types, such as Andrew, the mail
reader must be modified only to deal with the content-types it does not
already know how to handle.

2.  When the special header appears, instead of (or, if it's much
easier, in addition to) showing the user the body of the message, the
mail reader must send that body off to the metamail interpreter.  The
metamail interpreter includes features that deal with the diverse
situations of terminal-oriented and window-oriented mail readers.

Beyond this, of course, you have to make sure that the appropriate
interpreters are available on your users' search paths.  In particular,
you'll need the metamail binary, an appropriately-configured mailcap
file, and interpreters for whatever mail formats you support locally.  

For information on how to use the metamail program or how to customize a
mailcap file, see the metamail program's manual entry.  This document
will describe the specific patches that can be used to make certain
established mail-reading programs work with metamail.
A Variety of Mail and Bulletin Board Reading Interfaces 

With this document, you can patch all of your site's mail reading
interfaces to support whatever multimedia formats are deemed useful at
your site.  This means that those who regularly use the multimedia tools
can begin to send mail in those formats freely, without worrying about
the ability of any local user to interpret the mail.  It is my intent to
make this document exhaustive; as time goes on, I hope it will grow to
include an ever widening set of mail reading interfaces.  Currently it
includes all of the mail reading interfaces that I know to be in use
anywhere in Bellcore's research laboratories.

Currently this document describes how to add support for the following
mail readers:

      Berkeley Mail (/usr/ucb/Mail, /usr/ucb/mail, and Tahoe mail)
      SunMail (another version of Berkeley mail, but rather different)
      Xmail (an X11 interface to Berkeley mail)
      Mailtool (Graphical Sun interface to Berkeley mail)
      Imail (Bellcore MICE mailer)
      PCS readmail/rdmail/sreadmail (another Bellcore mailer)
      MH -- Rand Message Handling System
      XMH -- X11 Interface to Rand Message Handling System
      Rmail -- GNU Emacs mail reading package
      VM -- Another GNU Emacs mail reading package
      MH-E -- Yet another GNU Emacs mail reading package (GNU interface to MH)
      CUI -- Andrew low-end mail reader 
      VUI -- Andrew termcap-based mail reader
      Messages -- Andrew multimedia mail reader
      BatMail -- Andrew Emacs mail-reading interface
      Elm -- Mail reader from HP.
      Mush -- Yet another popular mail reader
      Msgs -- simple Berkeley bulletin board reader
      UUPC --a mail reading program for MS-DOS
      TRN -- a threaded netnews reader.
      RN -- a widely-used netnews reader.
      MM -- Columbia University's mail reader

If you have mail readers that are not dicussed here, you will still
probably find some of this code useful as a model.  If you develop a
patch for some other mail reader, and you send it back to me, I'll
include it in future versions of this document.

NOTE:  For the programs that were written in C, all of the patches are
surrounded by "#ifndef NOMETAMAIL/#endif".  This was done so that a
single source could easily be maintained even for use at those sites
that for some reason desire to inhibit the metamail functionality.  Such
sites need only compile with "-DNOMETAMAIL" to have the modified mailers
behave exactly like their unmodified predecessors.

ADDITIONAL NOTE:  Most of these patches send a message to metamail if it
has ANY content-type header.  Technically, this is not necessary with a
content-type such as

Content-type: text/plain

or

Content-type: text/plain; charset=US-ASCII

However, this can't just be a literal string comparison -- the full MIME
content-type syntax must be parsed and the "charset" value checked.  For
example, you WOULD want to pass the following to metamail:

Content-type: text/plain; charset=iso-8859-8

However, there is not really a need to pass the following to metamail:

Content-type: text/plain; something-else=foobar; charset=us-ascii

Instead of adding the content-type parsing to each mailer, most of the
patches below simply call metamail for any message with a content-type
header.  This is a bit inefficient for plain ascii text, but most such
messages probably won't have content-type headers anyway, and it makes
the patches much simpler.  Programmers incorporating these patches into
"production" versions of mail readers to be released to the world might
consider parsing the content-type header in full.

1.1	Berkeley Mail (/usr/ucb/[Mm]ail, and Tahoe mail)

NOTE:  If you don't have the sources for Berkeley mail, you can get
about 95% of metamail functionality to work by setting your PAGER
environment to "metamail" and reading everything with the pager (e.g. by
using "more" instead of "type".)   Alternately, you can just put the
following two lines in your .mailrc file:

	set PAGER=metamail
	set crt=1

Alternately, you can modify NOTHING AT ALL, and can still read non-text
message by simply typing  "| metamail" in response to the "&" prompt.

However, things will work slightly better if you can modify the source files.

There are several versions of Berkeley mail; this describes the patch to
the versions I happened to have.  

All of the changes are localized to the file cmd1.c  However, the way
that the Mail program handles output piped to "more" (or some other
paging program) makes the patch about twice as complicated as it
otherwise would be.

At any rate, there are four changes:

1.  In cmd1.c, there is a routine called "type1" -- in my version it
starts on line 321.  At the end of the declarations at the top of this
routine, there is a line that says:

	FILE *ibuf, *obuf;

(In some versions, the "*ibuf" is omitted.  That's fine.)  Immediately
after that line, add the following declaration:

#ifndef NOMETAMAIL
	int PipeToMore = 0;
#endif

2.  In the same routine, about 18 lines down from the previous patch,
there is a code fragment that says:

			cp = value("PAGER");
			if (cp == NULL || *cp == '\0')
				cp = MORE;
			obuf = popen(cp, "w");
			if (obuf == NULL) {
				perror(cp);
				obuf = stdout;
			}
			else {
				pipef = obuf;
				sigset(SIGPIPE, brokpipe);
			}

You need to put three new lines before this code fragment and one new
line after it.  The end result should look like this:

#ifndef NOMETAMAIL
		    PipeToMore = 1;
#else
			cp = value("PAGER");
			if (cp == NULL || *cp == '\0')
				cp = MORE;
			obuf = popen(cp, "w");
			if (obuf == NULL) {
				perror(cp);
				obuf = stdout;
			}
			else {
				pipef = obuf;
				sigset(SIGPIPE, brokpipe);
			}
#endif

Note that in some versions of UNIX, the code will say "signal" instead
of "sigset" -- you should use whichever is used in your original.  In
some other variants (notably Ultrix) this code is significantly
different.  What matters is that the code that sets up the pager be
preceded by the #ifndef ... #else lines, and followed by the #endif line.

3.  About 8 lines further down in that routine, you'll find something
that looks like this:

	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		dot = mp;
		print(mp, obuf, doign);

In the Tahoe version, there is another line before the "print" line:
		if (value("quiet") == NOSTR)
			fprintf(obuf, "Message %d:\n", mesg);

THIS LINE SHOULD BE ADDED IF IT DOES NOT ALREADY EXIST!  Otherwise xmail
may behave badly with the modified mail program.

The "print" line is, in my version of Berkeley mail, line 367, and in my
version of Tahoe mail, line 343.  REPLACE the "print" line with the
following code:

#ifndef NOMETAMAIL
#include <sgtty.h>
		if (!getenv("NOMETAMAIL") && nontext(mp)) {
		    char Fname[100], Cmd[120];
		    FILE *fp;
		    int code;
		    struct sgttyb ttystatein, ttystateout;

#ifdef SVR4
		    sprintf(Fname, "/tmp/mail-metamail.%d.%d", getpid(), getuid());
#else
		    sprintf(Fname, "/tmp/metam.%05d", getpid());
#endif /* SVR4 */
		    fp = fopen(Fname, "w");
		    if (!fp) {
			perror(Fname);
		    } else {
			send(mp, fp, 0);
			fclose(fp);
			sprintf(Cmd, "metamail -z %s -m Mail %s", (PipeToMore) ? "-p" : "", Fname);
			if (obuf != stdout) {
			    pipef = NULL;
			    pclose(obuf);
			    obuf = stdout;
			}
			gtty(fileno(stdin), &ttystatein);
			gtty(fileno(stdout), &ttystateout);
			code = system(Cmd);
			stty(fileno(stdin), &ttystatein);
			stty(fileno(stdout), &ttystateout);
/*			The following line would cause the raw mail to print out if
metamail failed */
/*			if (code) print(mp, obuf, doign); */
		    }
		} else {
		    if (PipeToMore && stdout == obuf) {
			obuf = popen(MORE, "w");
			if (obuf == NULL) {
			    perror(MORE);
			    obuf = stdout;
			}
			else {
			    files[fileno(obuf)].filep = obuf;
			    files[fileno(obuf)].flags = PIPE_OPEN & ~KEEP_OPEN;
			    pipef = obuf;
			    sigset(SIGPIPE, brokpipe);
			}
		    }
		    print(mp, obuf, doign);
		}
#else
		print(mp, obuf, doign);
#endif

Note that in some versions, it will say "send" instead of "print" -- use
whichever routine name is used in your version.  Note also that on some
versions of UNIX, you will need to use "signal" instead of "sigset". 
Finally, in some versions of the sources, the "files" array does not
exist, and the two lines that refer to it can simply be eliminated.

IMPORTANT ADDITIONAL NOTE:  IN SOME VERSIONS OF MAIL, the "send"
procedure requires a fourth parameter.  This should be set to NOSTR, as
in

			send(mp, fp, 0, NOSTR);

YET ANOTHER NOTE.  On some System V UNIX systems, it may be necessary to
replace

                        gtty(fileno(stdin), &ttystatein);
                        gtty(fileno(stdout), &ttystateout);
                        code = system(Cmd);
                        stty(fileno(stdin), &ttystatein);
                        stty(fileno(stdout), &ttystateout);

with something like

            if(ioctl(fileno(stdin),  TCGETA, &ttystatein) < 0 ||
               ioctl(fileno(stdout), TCGETA, &ttystateout) < 0) {
                perror("pre-metamail-ioctl");
                code = 1;
            } else {
                code = system(Cmd);
                if(ioctl(fileno(stdin),  TCSETA, &ttystatein) < 0 ||
                   ioctl(fileno(stdout), TCSETA, &ttystateout) < 0)
                    perror("post-metamail-ioctl");
            }

4.  At the very end of cmd1.c, add the following new routines:

#ifndef NOMETAMAIL
#include <ctype.h>

nontext(mp) 
struct message *mp;
{
    long c;
    FILE *ibuf;
    char line[LINESIZE], *s, *t;

    ibuf = setinput(mp);
    c = mp->m_size;
    while (c > 0L) {
	fgets(line, LINESIZE, ibuf);
	c -= (long) strlen(line);
	if (line[0] == '\n') return(0);
	for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
	if (!strncmp(line, "content-type:", 13) && notplain(line+13)) return(1);
    }
    return(0);
}
notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (*s == '"') ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

These two changes should be all you need to do in order to make Berkeley
mail work with metamail, assuming that you already have the "metamail"
binary installed somewhere on your search path.
1.2	Sun Mail (another version of Berkeley mail)

Sun's version of Berkeley mail is sufficiently different from the others
to warrant a separate section.  Patching the "Mail" program properly
will almost automatically add metamail support to older versions of
mailtool and xmail, because they simply call Mail.  However, some
further changes are necessary for those programs, as noted in subsequent
sections.

All of the changes are localized to two files, cmd1.c and cmd2.c  In
particular, there are four changes:

1.  In cmd1.c, there is a routine called "type1" -- in my version it
starts on line 317.  At the end of the declarations at the top of this
routine, there is a pair of lines that say:

	FILE *ibuf, *obuf;
	void (*saveint)();

Immediately after that line, add the following declaration:

#ifndef NOMETAMAIL
	int PipeToMore = 0;
#endif

2.  In the same routine, about 18 lines down from the previous patch,
there is a code fragment that says:

			obuf = popen(MORE, "w");
			if (obuf == NULL) {
				perror(MORE);
				obuf = stdout;
			}
			else {
				pipef = obuf;
				sigset(SIGPIPE, brokpipe);
			}

You need to put three new lines before this code fragment and one new
line after it.  The end result should look like this:

#ifndef NOMETAMAIL
		    PipeToMore = 1;
#else
			obuf = popen(MORE, "w");
			if (obuf == NULL) {
				perror(MORE);
				obuf = stdout;
			}
			else {
				pipef = obuf;
				sigset(SIGPIPE, brokpipe);
			}
#endif

3.  About 8 lines further down in that routine, you'll find something
that looks like this:

	for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
		mesg = *ip;
		touch(mesg);
		mp = &message[mesg-1];
		dot = mp;
		print(mp, obuf, doign);


The "print;" line is, in my version, line 363.   REPLACE the "print"
line with the following code:

#ifndef NOMETAMAIL
#include <sgtty.h>
		if (!getenv("NOMETAMAIL") && nontext(mp)) {
		    char Fname[100], Cmd[120];
		    FILE *fp;
		    int code;
		    struct sgttyb ttystatein, ttystateout;

		    sprintf(Fname, "/tmp/mail-metamail.%d.%d", getpid(), getuid());
		    fp = fopen(Fname, "w");
		    if (!fp) {
			perror(Fname);
		    } else {
                     if (value("quiet") == NOSTR)
                            fprintf(obuf, "Message %2d:\n", mp -
&message[0] + 1);
			msend(mp, fp, 0, fputs);
			fclose(fp);
			sprintf(Cmd, "metamail -z %s -m Mail %s", (PipeToMore) ? "-p" : "", Fname);
			if (obuf != stdout) {
			    pipef = NULL;
			    pclose(obuf);
			    obuf = stdout;
			}
			gtty(fileno(stdin), &ttystatein);
			gtty(fileno(stdout), &ttystateout);
			code = shell(Cmd);
			stty(fileno(stdin), &ttystatein);
			stty(fileno(stdout), &ttystateout);
/*			if (code) print(mp, obuf, doign); */
		    }
		} else {
		    if (PipeToMore && obuf == stdout) {
			obuf = popen(MORE, "w");
			if (obuf == NULL) {
			    perror(MORE);
			    obuf = stdout;
			}
			else {
			    pipef = obuf;
			    sigset(SIGPIPE, brokpipe);
			}
		    }
		    print(mp, obuf, doign);
		}
#else
		    print(mp, obuf, doign);
#endif

2.  At the very end of cmd1.c, add the following new routines:

#ifndef NOMETAMAIL
#include <ctype.h>

nontext(mp) 
struct message *mp;
{
    long c;
    FILE *ibuf;
    char line[LINESIZE], *s, *t;

    ibuf = setinput(mp);
    c = mp->m_size;
    while (c > 0L) {
	fgets(line, LINESIZE, ibuf);
	c -= (long) strlen(line);
	if (line[0] == '\n') return(0);
	for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
	if (!strncmp(line, "content-type:", 13) && notplain(line+13)) return(1);
    }
    return(0);
}
notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (*s == '"') ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

3.  This step is required only if you want the changes to work right
with Sun's MailTool program.  In cmd2.c, there is a routine called
"savemsglist" -- in my version it starts on line 187.  About a page down
in that routine, you'll find a line that looks like this:

		mp = &message[mesg-1];

This line is, in my version, line 215.  Immediately following that line
(i.e. between line 215 and line 216, in my version) add the following
code:

#ifndef NOMETAMAIL
#include <sgtty.h>
		if (!mark && !getenv("NOMETAMAIL") && nontext(mp)) {
		    char Fname[100], Cmd[120];
		    FILE *fp;
		    int code;
		    struct sgttyb ttystatein, ttystateout;

		    sprintf(Fname, "/tmp/mail-metamail.%d.%d", getpid(), getuid());
		    fp = fopen(Fname, "w");
		    if (!fp) {
			perror(Fname);
		    } else {
			msend(mp, fp, 0, fputs);
			fclose(fp);
			fclose(obuf); /* To allow appending, sigh */
			sprintf(Cmd, "metamail -z -m Mail %s >> %s", Fname, file);
			gtty(fileno(stdin), &ttystatein);
			gtty(fileno(stdout), &ttystateout);
			code = shell(Cmd); /* ignore it if it fails */
			stty(fileno(stdin), &ttystatein);
			stty(fileno(stdout), &ttystateout);
			if ((obuf = fopen(file, "a")) == NULL) {
			    perror("");
			    return;
			}
			continue;
		    }
		}
#endif

These three changes should be all you need to do in order to make Sun's
version of Berkeley mail work with metamail, assuming that you already
have the metamail binary installed somewhere on your search path.
1.3	Xmail (X11 Interface to Berkeley Mail)

Xmail is a program that provides a graphical interface to Berkeley mail.
 It does most of its work by talking to the Berkeley mail program
itself.  Thus, if you want Xmail to work with metamail, you have to
first of all upgrade your version of the Berkeley Mail program as
described in the previous sections.

Once you have done this, everything would work fine automatically except
that there's no way for metamail to tell that it has been called
non-interactively.  You can tell it that this is the case (so that it
won't try to ask any questions) by setting the MM_NOTTTY environment
variable.  Thus, a simple way to fix xmail would be to rename the
"xmail" program to be "xmail.std" and then to install a new "xmail"
program that looked something like this:

    #!/bin/csh -f

    setenv MM_NOTTTY 1
    setenv MM_NOASK 1
    setenv MM_TRANSPARENT 1
    setenv XMAILER /usr/local/bin/Mail
    xmail.std $*

The MM_NOASK variable is also set to avoid lots of terminal windows
popping up to ask questions that are only being asked in the hope of
saving you time if you don't want to run a handling program.

This should make xmail work with metamail, although, unlike most of the
modified mailers described in this document, it will not ask the users
for confirmation before running metamail applications.

IMPORTANT NOTE:  Xmail is no longer known to be supported by ANYONE. 
People have reported some strange bugs with xmail, particularly in
conjunction with metamail, which the author of metamail will NOT assume
responsibility for fixing!  No data has been lost, but the xmail program
sometimes gets into a confused state and needs to be restarted.
1.4	Mailtool (SunTools Interface to Berkeley Mail)

There are TWO basic methods for modifying mailtool.  The easier method
simply uses mailtool's built in customization mechanism.  The harder
method involves modifying the code a bit.

1.4.a  Customizing mailtool to use metamail

This information was supplied by Ingo Dean <idean@warren.mentorg.com>:;

I don't know if anyone has mentioned this yet, but there's a really
cheap way to view MIME encoded messages from within Sun's Mailtool
program if you have metamail installed:

Just copy /usr/lib/text_extras_menu to ~/.text_extras_menu and add the
following line somewhere:

     "MIME"                  metamail -m textedit -p -x

Now you get a new submenu in any Xview-based textedit widget called
"MIME" under the "Text Pane" "Extras" menu.  If you triple-click the
text (selecting all text in the text window) and pull up the "MIME"
option in the "Extras" submenu, it will start up metamail in an xterm
for you.

1.4.b  Customizing the mailtool software

IMPORTANT NOTE:  The instructions here do NOT work for Mailtool 2.0,
Sun's latest version as of this writing.  A future version of Mailtool
will have MIME support built-in, but for now 2.0 users have no patch
available and must use the customization mechanism described in 1.4.a.

Mailtool is a program that provides a graphical interface to Berkeley
mail.  It does most of its work by talking to the Berkeley mail program
itself.  Thus, if you want mailtool to work with metamail, you have to
first of all upgrade your version of the Berkeley Mail program as
described in the previous sections.

Once you have done this, everything would work fine automatically except
that there's no way for metamail to tell that it has been called
non-interactively.  You can tell it that this is the case (so that it
won't try to ask any questions) by setting the MM_NOTTTY environment
variable.  Thus, a simple way to fix mailtool would be to rename the
"mailtool" program to be "mailtool.std" and then to install a new
"mailtool" program that looked something like this:

    #!/bin/csh -f

    setenv MM_NOTTTY 1
    setenv MM_NOASK 1
    mailtool.std $*

The MM_NOASK variable is also set to avoid lots of terminal windows
popping up to ask questions that are only being asked in the hope of
saving you time if you don't want to run a handling program.

This should make mailtool work with metamail, although, unlike most of
the modified mailers described in this document, it will not ask the
users for confirmation before running metamail applications.
1.5	Imail (Bellcore MICE mailer)

All of the changes are localized to the file util.c  In particular,
there are two changes to be made:

1.  In util.c, there is a routine called "listit" -- in my version it
starts on line 82.  The first line of that routine looks like this:

	debug("about to listit, %d lines\n",nlines);

Immediately following that line (i.e. between line 88 and line 89, in my
version) add the following code:

#ifndef NOMETAMAIL
#include <sgtty.h>
	if (!getenv("NOMETAMAIL") && nontext(fp)) { 
	    struct sgttyb ttystatein, ttystateout;
	    char Fname[100], Cmd[100], linebuf[1000];
	    FILE *fp2;
	    int chars = 0, code;

	    sprintf(Fname, "/tmp/imail-metamail.%d.%d", getpid(), getuid());
	    fp2 = fopen(Fname, "w");
	    if (!fp2) {
		perror(Fname);
	    } else {
		while( chars < p->len && fgets(linebuf,sizeof(linebuf),fp) != NULL) {
		    fputs(linebuf,fp2);
		    chars += strlen(linebuf);
		}
		fclose(fp2);
		sprintf(Cmd, "metamail -p -R -z -m imail %s", Fname);
		gtty(fileno(stdin), &ttystatein);
		gtty(fileno(stdout), &ttystateout);
		code = system(Cmd);
		stty(fileno(stdin), &ttystatein);
		stty(fileno(stdout), &ttystateout);
		/* if (!code) return(0); */
		return(0); /* Don't show raw datastream anyway! */
	    }
	}
#endif

2.  After the "listit" procedure, or at the very end of util.c,
whichever you prefer, add the following new routines, which are used by
the patch above:

#ifndef NOMETAMAIL
#include <ctype.h>

nontext(fp)
FILE *fp;
{
    char buf[1000], *s;
    int oldnl=nl, oldcnt=cnt, oldfptr, retval = 0;

    oldfptr = ftell(fp);
    while( cnt < nlines && fgets(buf,sizeof(buf),fp) != NULL) {
	if (buf[0] == '\n') break;
	for (s=buf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
       if (!strncmp(buf, "content-type:", 13) && notplain(buf+13)) {
           retval = 1;
           break;
       }
	nl++;
	cnt++;
    }
    nl = oldnl;
    cnt = oldcnt;
    fseek(fp, oldfptr, 0);
    return(retval);
}
notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

These two changes should be all you need to do in order to make imail
work with metamail, assuming that you already have the metamail binary
installed somewhere on your search path.
1.6	PCS readmail/rdmail

All of the changes are localized to the file display.c  In particular,
there are two changes to be made:

1.  In display.c, there is a routine called "display".  The first line
of that routine looks like this:

	messnum = messord[messnum];        /* convert to internal number */

Immediately following that line (i.e. between 13 and 14 in my version)
add the following code:

#ifndef NOMETAMAIL
{
#include <sgtty.h>
   if (!getenv("NOMETAMAIL") && nontext(messnum)) { 
       char Fname[100], Cmd[100], linebuf[1000];
       FILE *fp2;
       int code;
       struct sgttyb ttystatein, ttystateout; 

       sprintf(Fname, "/tmp/readmail-metamail.%d.%d.%d", getpid(), getuid());
       fp2 = fopen(Fname, "w");
       if (!fp2) {
	   perror(Fname);
       } else {
	   fseek(curr.fp,messbeg[messnum],0);
	   while (fgets (linebuf, sizeof(linebuf), curr.fp) != NULL &&
ftell(curr.fp) <= messend[messnum]) {
	       fputs(linebuf, fp2);
	   }
	   fclose(fp2);
	   sprintf(Cmd, "metamail -m readmail -z %s %s", profile("page") ? "-p"
: "", Fname);
	   gtty(fileno(stdin), &ttystatein);
	   gtty(fileno(stdout), &ttystateout);
	   code = system(Cmd);
	   stty(fileno(stdin), &ttystatein);
	   stty(fileno(stdout), &ttystateout);
	   /* if (!code)) */ return(0);
       }
   }
}
#endif

2.  At the very end of display.c, add the following new routines, which
are used by the patch above:

#ifndef NOMETAMAIL
#include <ctype.h>

nontext(messnum) 
int messnum;
{
    char buf[1000], *s;

    fseek(curr.fp,messbeg[messnum],0);
    while (fgets (buf, sizeof(buf), curr.fp) != NULL && ftell(curr.fp) <
messend[messnum]) {
	if (buf[0] == '\n') return(0);
	for (s=buf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
	if (!strncmp(buf, "content-type:", 13) && notplain(buf+13)) return(1);
    }
    return(0);
}
notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

These two changes should be all you need to do in order to make pcs
readmail/rdmail work with metamail, assuming that you already have the
metamail binary installed somewhere on your search path.
1.7	PCS sreadmail

All of the changes are localized to the file display.c  In particular,
there are two changes to be made:

1.  In display.c, there is a routine called "display".  The first line
of that routine looks like this:

	messnum = messord[messnum];        /* convert to internal number */

Immediately following that line (i.e. between line 14 and line 15, in my
version) add the following code:

#ifndef NOMETAMAIL
{
#include <sgtty.h>
   if (!getenv("NOMETAMAIL") && nontext(messnum)) { 
       char Fname[100], Cmd[100], linebuf[1000];
       FILE *fp2;
       int code;
       struct sgttyb ttystatein, ttystateout; 

       sprintf(Fname, "/tmp/sreadmail-metamail.%d.%d.%d", getpid(), getuid());
       fp2 = fopen(Fname, "w");
       if (!fp2) {
	   perror(Fname);
       } else {
	   fseek(curr.fp,messbeg[messnum],0);
	   while (fgets (linebuf, sizeof(linebuf), curr.fp) != NULL &&
ftell(curr.fp) <= messend[messnum]) {
	       fputs(linebuf, fp2);
	   }
	   fclose(fp2);
	   sprintf(Cmd, "metamail -R -m sreadmail -z -p %s", Fname);
	   gtty(fileno(stdin), &ttystatein);
	   gtty(fileno(stdout), &ttystateout);
	   code = system(Cmd);
	   stty(fileno(stdin), &ttystatein);
	   stty(fileno(stdout), &ttystateout);
	   clear();
	   redisplay=TRUE;
	   curr.pageno=0;
	   helppage=0;
	   /* if (!code)) */ return(0);
       }
   }
}
#endif

2.  At the very end of display.c, add the following new routines, which
are used by the patch above:

#ifndef NOMETAMAIL
#include <ctype.h>

nontext(messnum) 
int messnum;
{
    char buf[1000], *s;

    fseek(curr.fp,messbeg[messnum],0);
    while (fgets (buf, sizeof(buf), curr.fp) != NULL && ftell(curr.fp) <
messend[messnum]) {
	if (buf[0] == '\n') return(0);
	for (s=buf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
	if (!strncmp(buf, "content-type:", 13) && notplain(buf+13)) return(1);
    }
    return(0);
}
notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

These two changes should be all you need to do in order to make pcs
readmail/rdmail/sreadmail work with metamail, assuming that you already
have the metamail binary installed somewhere on your search path.
1.8	MH -- Rand Message Handling System

All of the changes are localized to the file uip/show.c  In particular,
there are two changes to be made:

1.  In uip/show.c, there is a label "go_to_it:".  Shortly after that
you'll find the following bit of code:

    if (nshow)
	proc = "/bin/cat";
    else {
	(void) putenv ("mhfolder", folder);
	if (strcmp (r1bindex (showproc, '/'), "mhl") == 0) {
	    vec[0] = "mhl";
	    (void) mhl (vecp, vec);
	    done (0);
	}
	proc = showproc;
    }

Immediately preceding that code fragment (i.e. between between 245 and
246 in my version of uip/show.c) add the following code:

#ifndef NOMETAMAIL
    if (!getenv("NOMETAMAIL") && nontext(--msgnum, mp)) {
#ifdef SYS5
#include <termio.h>
	struct termio ttystatein, ttystateout;
#else
#include <sgtty.h>
	struct sgttyb ttystatein, ttystateout;
#endif SYS5
	char Cmd[120];
	FILE *fp;
	int code;

	sprintf(Cmd, "metamail -e -p -m MH %s/%d", mp->foldpath, msgnum);
#ifdef SYS5
	ioctl (fileno(stdin), TCGETA, &ttystatein);
	ioctl (fileno(stdout), TCGETA, &ttystateout);
#else
	gtty(fileno(stdin), &ttystatein);
	gtty(fileno(stdout), &ttystateout);
#endif SYS5
	code = system(Cmd);
#ifdef SYS5
	ioctl (fileno(stdin), TCSETAW, &ttystatein);
	ioctl (fileno(stdout), TCSETAW, &ttystateout);
#else
	stty(fileno(stdin), &ttystatein);
	stty(fileno(stdout), &ttystateout);
#endif SYS5
	exit(code);
    }
#endif


2.  At the very end of uip/show.c, add the following new routine, which
is used by the patch above:

#ifndef NOMETAMAIL
#include <ctype.h>

nontext(msgnum, mp) 
int msgnum;
struct msgs *mp;
{
	FILE *fp;
	char line[1000], *s;

	sprintf(line, "%s/%d", mp->foldpath, msgnum);
	fp = fopen(line, "r");
	if (!fp) return(0);
	while (fgets(line, sizeof(line), fp)) {
	    if (line[0] == '\n') {fclose(fp);return(0);}
	    for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
	    if (!strncmp(line, "content-type:", 13) && notplain(line+13)) return(1);
	}
	fclose(fp);
	return(0);
}

notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
        
#endif

These two changes should be all you need to do in order to make MH work
with metamail, assuming that you already have the metamail binary
installed somewhere on your search path.
1.9	XMH -- X11 Interface to Rand Message Handling System

All of the changes are localized to the file msg.c  In particular, there
are only two change to be made:

1.  Near the beginning of the file (e.g. right after the last #include
line), add the following code:

#ifndef NOMETAMAIL
struct mmdata {
    Msg msg;
    Scrn scrn;
};
void RedisplayMsg();

void NoCallMetamail (widget, client_data, call_data)
Widget	widget;
XtPointer *client_data;
XtPointer	call_data;
{
}

void CallMetamail (widget, mmd, call_data)
Widget	widget;
struct mmdata *mmd;
XtPointer	call_data;
{
    char TmpFileName[200];
    char Cmd[200];

    sprintf(TmpFileName, "/tmp/xmh.%d.%d", getpid(), getuid());
    sprintf(Cmd, "csh -c \"metamail -e -m XMH -x -d %s >& %s \"",
MsgFileName(mmd->msg), TmpFileName);
    fprintf(stderr, "Executing %s, please wait...\n", Cmd);
    system(Cmd);
    mmd->msg->source = CreateFileSource(mmd->scrn->viewwidget,
TmpFileName, FALSE);
    RedisplayMsg(mmd->scrn);
    unlink(TmpFileName);
}
#endif


2.  In msg.c, there is a procedure "SetScrnNewMsg". In that procedure
you will find the following bit of code:

	msg->num_scrns++;
	msg->scrn = (Scrn *) XtRealloc((char *)msg->scrn,
				       (unsigned) sizeof(Scrn)*msg->num_scrns);
	msg->scrn[msg->num_scrns - 1] = scrn;
	if ((msg->source == NULL) || (msg->toc == DraftsFolder))
	    msg->source = CreateFileSource(scrn->viewwidget, MsgFileName(msg),
					   scrn->kind == STcomp);

Immediately following that code fragment (i.e. between between 312 and
313 in my version of msg.c) add the following code:

#ifndef NOMETAMAIL
	if (!getenv("NOMETAMAIL")) {
#include <ctype.h>
	    static XtCallbackRec yes_callbacks[] = {
		{CallMetamail, (XtPointer) NULL},
		{(XtCallbackProc) NULL,	(XtPointer) NULL}
	    };

	    static XtCallbackRec no_callbacks[] = {
		{NoCallMetamail, (XtPointer) NULL},
		{(XtCallbackProc) NULL,	(XtPointer) NULL}
	    };
	    FILE *fp;
	    char line[1000], *s, *t, Query[200];
	    static struct mmdata Mmdata;

	    fp = fopen(MsgFileName(msg), "r");
	    if (fp) {
		while (fgets(line, sizeof(line), fp)) {
		    if (line[0] == '\n') break;
		    for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
		    if (!strncmp(line, "content-type:", 13)) {
			s = &line[13];
			while (s && isspace(*s)) ++s;
			t = index(s, ';');
			if (t) {
			    *t = (char) NULL;
			} else {
			    t = index(s, '\n');
			    if (t) *t = (char) NULL;
			}
			if (t--) {
			    while (isspace(*t) && (t > s)) {
				*t-- = (char) NULL;
			    }
			}
			if (notplain(s)) {
			    Mmdata.msg = msg;
			    Mmdata.scrn = scrn;
			    yes_callbacks[0].closure = (XtPointer) &Mmdata;
			    no_callbacks[0].closure =  (XtPointer) NULL;
			    sprintf(Query, "This message is in %s format.\nDo you want to try
to run an external interpreter?", s);
			    if (getenv("MM_NOASK")) {
				CallMetamail(scrn->viewwidget, &Mmdata, NULL);
			    } else {
				PopupConfirm(scrn->tocwidget, Query, yes_callbacks, no_callbacks);
			    }
			    break;
			}
		    }
		}
		fclose(fp);
	    }
	}
#endif


Finally, add the following procedure to the end of the file:

notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}


These changes should be all you need to do in order to make XMH work
with metamail, assuming that you already have the metamail binary
installed somewhere on your search path.
1.10	Rmail -- GNU Emacs mail reading package

Emacs being what it is, there are several versions of the rmail package
floating around.  Therefore the patch is particularly hard to describe,
since your version of rmail may vary.  The patch is therefore described
in several steps.

1.  Make sure your rmail has an "rmail-show-message-hook" function.  If
you look in the source file rmail.el, you will (probably) find a
function called "rmail-show-message".  This function itself varies
significantly in different versions of rmail.   Near the end of it, you
might find a line of the form:

(run-hooks 'rmail-show-message-hook)

If there isn't such a line, you should add one -- probably right at the
end of the function, before it returns.

2.  You may now either make changes to the rmail source, or only to your
own personal hook function.

2a.  If you want to change the rmail source, put the following line
immediately before the aforementioned "run-hooks" line:

(rmail-check-content-type)

2a.  If you want to change only your own customized rmail behavior, put
the following lines in your "~/.emacs" file:

(setq
 rmail-show-message-hook
 '(lambda()
	 (rmail-check-content-type)))

(If you already had an rmail-show-message-hook function defined, you can
just put the rmail-check-content-type call at the beginning of it.)

3.  Make sure you have the GNU "transparent.el" package installed.  This
is not part of the standard distribution, but is widely available, and
should be part of most FTP archives, etc.

4.  Put the following LISP code somewhere that emacs will find it.  In
particular, if you modified rmail.el in step 2a, then it probably makes
sense to put this code at the end of that file.  If you just modified
your own .emacs file, it probably makes sense to put this code in your
.emacs file.  The code you want is:
;;; Functions added for METAMAIL support

(require 'transparent)

(defvar rmail-never-execute-automatically t 
        "*Prevent metamail from happening semi-automatically")

(define-key rmail-mode-map "!" 'rmail-execute-content-type)

(defun rmail-check-content-type ()
  "Check for certain Content Type headers in mail"
  (rmail-maybe-execute-content-type nil))

(defun rmail-execute-content-type ()
  "Check for certain Content Type headers in mail"
  (interactive)
  (rmail-maybe-execute-content-type t))

(defun rmail-handle-content-type (ctype override dotoggle)
  (let (oldpt
        (oldbuf (current-buffer))
        (fname (make-temp-name "/tmp/rmailct")))
    
    (cond
     ((and rmail-never-execute-automatically (not override))
      (progn
       (if dotoggle (rmail-toggle-header))
       (message (concat "You can use '!' to run an interpreter for this '"
                        ctype "' format mail."))))
     ((or override
          (getenv "MM_NOASK")
          (y-or-n-p (concat "Run an interpreter for this '"
                            ctype "' format mail? ")))
      (progn
       (save-restriction
        (goto-char (point-max))
        (setq oldpt (point))
        (goto-char 0)
        (widen)
        (write-region
         (point)
         oldpt
         fname
         'nil
         "silent"))
       (if dotoggle (rmail-toggle-header))
       (if
        (and window-system (getenv "DISPLAY"))
         (progn
          (switch-to-buffer-other-window "METAMAIL")
          (erase-buffer)
          (pop-to-buffer oldbuf)
          (start-process "metamail"  "METAMAIL" "metamail" "-m"
                         "rmail" "-p" "-x" "-d" "-z" "-q" fname)
          (message "Starting metamail.  Sending output to METAMAIL buffer."))
         (progn
          (switch-to-buffer "METAMAIL")
          (erase-buffer)
          (sit-for 0)
          (transparent-window
           "METAMAIL"
           "metamail"
           (list "-m" "rmail" "-p" "-d" "-z" "-q" fname)
           nil
          (concat
           "\n\r\n\r*****************************************"
           "*******************************\n\rPress any key "
           "to go back to EMACS\n\r\n\r***********************" 
           "*************************************************\n\r")
	  )))))
     (t (progn
	  (if dotoggle (rmail-toggle-header))
	  (message (concat "You can use the '!' keystroke to "
			   "execute the external viewing program.")))))))

(defun rmail-maybe-execute-content-type (dorun)
  "Check for certain Content Type headers in mail"
  (cond
   ((not (getenv "NOMETAMAIL"))
    (let* ((ct nil)
           (needs-toggled nil))
      (save-excursion
       (save-restriction
        (widen)
        (goto-char (rmail-msgbeg rmail-current-message))
        (forward-line 1)
        (if (and dorun (= (following-char) ?1)) (setq needs-toggled t))
        (if (= (following-char) ?0)
          (narrow-to-region
           (progn (forward-line 2)
                  (point))
           (progn (search-forward "\n\n" (rmail-msgend rmail-current-message)
           'move)
           (point)))
         (narrow-to-region (point)
                           (progn (search-forward "\n*** EOOH ***\n")
                  (beginning-of-line) (point))))
        (setq ct (mail-fetch-field "content-type" t))))
      (cond
	(ct
	(cond ((and (not (string= (downcase ct) "text"))
       	     (not (string= (downcase ct) "text/plain"))
	            (not (string= (downcase ct)
       	                   "text/plain; charset=us-ascii")))
              (progn
               (if needs-toggled (rmail-toggle-header))
               (rmail-handle-content-type
                ct dorun needs-toggled)))
	     (needs-toggled
	      (rmail-toggle-header)))))))))


The above changes should be all you need to do in order to make rmail
work with metamail, assuming that you already have the metamail binary
installed somewhere on your search path.

IMPORTANT NOTE:  For some users and some versions of Emacs,
terminal-oriented programs work poorly in the "transparent-window"
(non-X11) case with the above patch.  If you have problems in this case,
you might get better behavior by adding "-R" to the list of options that
are given to metamail.
1.11	VM -- Another GNU Emacs mail reading package

Emacs being what it is, there are several mail readers besides rmail
floating around.  The'yre all similar, however.  The following patch for
VM is extremely similar to the patch for rmail -- in fact, the contents
of the hook functions are identical.  However, there are two versions of
this patch given, for older and newer versions of VM

1.11.a	VM Version 5.32

Patches for the current beta release of the VM GNU Emacs mail reading package:

The following set of patches works for VM beta 5.32.

1.  Modify the variable of "vm-visible-headers" to force the inclusion
of the "Content-Type" and "Content-Transfer-Encoding" headers.  In
vm-vars.el, you will find a definition that looks something like this:

(defvar vm-visible-headers
  '("From:" "Sender:" "Resent-From"
    "To:" "Apparently-To:" "Cc:"
    "Subject:"
    "Date:" "Resent-Date:")
  "*List of headers that should be visible when VM first displays a message.
These should be listed in the order you wish them presented.
Regular expressions are allowed.")

You should alter this definition so that it looks like this instead:

(defvar vm-visible-headers
  '("From:" "Sender:" "Resent-From"
    "To:" "Apparently-To:" "Cc:"
    "Subject:"
    "Date:" "Resent-Date:"
    "Content-Type:" "Content-Transfer-Encoding:")
  "*List of headers that should be visible when VM first displays a message.
These should be listed in the order you wish them presented.
Regular expressions are allowed.")

(defvar vm-never-execute-automatically t 
  "*Prevent metamail from happening semi-automatically")

2. Further down in vm-vars.el you will find a long variable definition
that begins with

(defvar vm-mode-map

inside of this definition is this line

    (define-key map "!" 'shell-command)

Exchange this for the line below

    (define-key map "!" 'vm-execute-content-type)

3.  If you look in the source file vm-page.el, you will find a function
called "vm-show-current-message". Near the end of it, you might find a
line of the form:

  (vm-update-summary-and-mode-line)

Immediately after that, you should add the line:

  (run-hooks 'vm-show-message-hook)

4.  You may now either make changes to the vm source, or only to your
own personal hook function.

4a.  If you want to change the vm source, put the following line
immediately before the aforementioned "run-hooks" line:

(vm-check-content-type)

4b.  If you want to change only your own customized vm behavior, put the
following lines in your "~/.emacs" file:

(setq
 vm-show-message-hook
 '(lambda()
	 (vm-check-content-type)))

(If you already had an vm-show-message-hook function defined, you can
just put the vm-check-content-type call at the beginning of it.)

5.  Make sure you have the GNU "transparent.el" package installed.  This
is not part of the standard distribution, but is widely available, and
should be part of most FTP archives, etc.

6.  Put the following LISP code somewhere that emacs will find it.  At
the end of vm-page.el or in your .emacs file are probably both good
choices, depending perhaps on if you did 4a or 4b. The code you want is:

;;; Functions added for METAMAIL support

(require 'transparent)

(defun vm-check-content-type ()
  "Check for certain Content Type headers in mail"
  (vm-maybe-execute-content-type nil))

(defun vm-execute-content-type ()
  "Check for certain Content Type headers in mail"
  (interactive)
  (vm-maybe-execute-content-type t))

(defun vm-handle-content-type (ctype override)
  (let (oldpt
        (fname (make-temp-name "/tmp/rmailct")))
    
    (cond
     ((and vm-never-execute-automatically (not override))
      (progn
       (message (concat "You can use '!' to run an interpreter for this '"
                        ctype "' format mail."))))
     ((or override
          (getenv "MM_NOASK")
          (y-or-n-p (concat "Run an interpreter for this '"
                            ctype "' format mail? ")))
      (progn
       (save-restriction
        (goto-char (point-max))
        (setq oldpt (point))
        (goto-char 0)
        (widen)
        (write-region
         (point)
         oldpt
         fname
         'nil
         "silent"))
       (if
        (and window-system (getenv "DISPLAY"))
         (progn
          (switch-to-buffer-other-window "METAMAIL")
          (erase-buffer)
          (start-process "metamail"  "METAMAIL" "metamail" "-m"
                         "vm" "-x" "-d" "-z" "-q" fname)
          (if vm-mail-buffer (pop-to-buffer vm-mail-buffer))
          (message "Starting metamail.  Sending output to METAMAIL buffer."))
         (progn
          (switch-to-buffer "METAMAIL")
          (erase-buffer)
          (sit-for 0)
          (transparent-window
           "METAMAIL"
           "metamail"
           (list "-m" "vm" "-d" "-z" "-q" fname)
           nil
          (concat
           "\n\r\n\r*****************************************"
           "*******************************\n\rPress any key "
           "to go back to EMACS\n\r\n\r***********************" 
           "*************************************************\n\r")
	  )))))
     (t (progn
	  (message (concat "You can use the '!' keystroke to "
			   "execute the external viewing program.")))))))

(defun vm-maybe-execute-content-type (dorun)
  "Check for certain Content Type headers in mail"
  (cond
   ((not (getenv "NOMETAMAIL"))
    (save-restriction
      (let ((saved-buffer (current-buffer))
	    (saved-window (selected-window)))
	(set-buffer (if vm-mail-buffer vm-mail-buffer (current-buffer)))
	(setq buffer-read-only 'nil)
	(let ((headend 0)
	      (ctype "text")
	      (old-min (point-min))
	      (old-max (point-max))
	      (opoint 0))
	  (goto-char (point-min))
	  (forward-line 1)
	  (goto-char (point-min))
	  (search-forward "\n\n")
	  (setq headend (point))
	  (setq opoint (- headend 1))
	  (goto-char (point-min))    
	  (setq case-fold-search 'T)
	  (cond
	   ((search-forward "\ncontent-type:" opoint 't)
	    (progn
	      (forward-word 1)	(backward-word 1)  ; Took care of white space
	      (setq opoint (point))
	      (re-search-forward "[;\n]" (point-max) t)
	      (setq ctype (downcase (buffer-substring opoint (- (point) 1))))
	      (goto-char (point-min)))))
	  (cond ((and (not (string= (downcase ctype) "text"))
		      (not (string= (downcase ctype) "text/plain"))
		      (not (string= (downcase ctype)
				    "text/plain; charset=us-ascii")))
		 (vm-handle-content-type
		  ctype dorun))
		)) 
	(set-buffer saved-buffer)
	(select-window saved-window)
	)))))


An older version of the last function, vm-maybe-execute-content-type, is
given below for anyone who has problems with the version given above:


(defun vm-maybe-execute-content-type (dorun)
  "Check for certain Content Type headers in mail"
  (cond
   ((not (getenv "NOMETAMAIL"))
    (save-restriction
     (setq buffer-read-only 'nil)
     (let ((headend 0)
	   (ctype "text")
           (old-min (point-min))
           (old-max (point-max))
	   (opoint 0))
       (goto-char (point-min))
       (forward-line 1)
       (goto-char (point-min))
       (search-forward "\n\n")
       (setq headend (point))
       (setq opoint (- headend 1))
       (goto-char (point-min))    
       (setq case-fold-search 'T)
       (cond
	((search-forward "\ncontent-type:" opoint 't)
	 (progn
          (forward-word 1)	(backward-word 1)  ; Took care of white space
	  (setq opoint (point))
	  (re-search-forward "[;\n]" (point-max) t)
	  (setq ctype (downcase (buffer-substring opoint (- (point) 1))))
          (goto-char (point-min)))))
	(cond ((and (not (string= (downcase ctype) "text"))
                    (not (string= (downcase ctype) "text/plain"))
                    (not (string= (downcase ctype)
                                  "text/plain; charset=us-ascii")))
	      (vm-handle-content-type
	       ctype dorun))
             ))))))


7.  Watch out for an oddity in the terminal emulator package.  If you
get the error message

Key sequence \277 uses invalid prefix characters

or something like that,  this probably means that you have set your
global variable "help-char" to a META key.  The terminal emulator
package doesn't like that at all, and you'll need to change it in order
for the terminal emulator package, and these extensions to vm, to work
properly.

The above changes should be all you need to do in order to make vm 5.32
work with metamail, assuming that you already have the metamail binary
installed somewhere on your search path.

IMPORTANT NOTE:  For some users and some versions of Emacs,
terminal-oriented programs work poorly in the "transparent-window"
(non-X11) case with the above patch.  If you have problems in this case,
you might get better behavior by adding "-R" to the list of options that
are given to metamail.

Important note for Emacs "Hyperbole" users:  Hyperbole, in its "hvm.el"
file, overloads the definition of some VM functions.  Therefore, any VM
functions that need to be modified for metamail usage that exist in
"hvm.el" should be patched there rather than in the VM code itself.


1.11.b	Older Versions of VM

1.  Modify the definition of "vm-build-visible-header-alist" to force
the inclusion of the "Content-Type" and "Content-Transfer-Encoding"
headers.  In vm.el, you will find a definition that looks something like
this:

(defun vm-build-visible-header-alist ()
  (let ((header-alist (cons nil nil))
  (vheaders vm-visible-headers)
   list)
    (setq list header-alist)
    (while vheaders
      (setcdr list (cons (cons (car vheaders) nil) nil))
      (setq list (cdr list) vheaders (cdr vheaders)))
    (setq vm-visible-header-alist (cdr header-alist))))

You should alter the declaration of "vheaders", so that it looks like
this instead:

(defun vm-build-visible-header-alist ()
  (let ((header-alist (cons nil nil))
        (vheaders (append vm-visible-headers
                          '("Content-Type:" "Content-Transfer-Encoding:")))
	list)
    (setq list header-alist)
    (while vheaders
      (setcdr list (cons (cons (car vheaders) nil) nil))
      (setq list (cdr list) vheaders (cdr vheaders)))
    (setq vm-visible-header-alist (cdr header-alist))))

2.  Make sure your version of VM has a "vmail-show-message-hook"
function.  It probably doesn't.  However, if you look in the source file
vm.el, you will (probably) find a function called
"vm-show-current-message".    Near the end of it, you might find a line
of the form:

  (vm-update-summary-and-mode-line)

Immediately after that, you should add the line:

  (run-hooks 'vm-show-message-hook)

3.  You may now either make changes to the vm source, or only to your
own personal hook function.

3a.  If you want to change the vm source, put the following line
immediately before the aforementioned "run-hooks" line:

(vm-check-content-type)

3a.  If you want to change only your own customized vm behavior, put the
following lines in your "~/.emacs" file:

(setq
 vm-show-message-hook
 '(lambda()
	 (vm-check-content-type)))

(If you already had an vm-show-message-hook function defined, you can
just put the vm-check-content-type call at the beginning of it.)

4.  Make sure you have the GNU "transparent.el" package installed.  This
is not part of the standard distribution, but is widely available, and
should be part of most FTP archives, etc.

5.  Put the following LISP code somewhere that emacs will find it.  In
particular, if you modified vm.el in step 2a, then it probably makes
sense to put this code at the end of that file.  If you just modified
your own .emacs file, it probably makes sense to put this code in your
.emacs file.  The code you want is:


;;; Functions added for METAMAIL support

(require 'transparent)

(defvar vm-never-execute-automatically t "*Prevent metamail from
happening semi-automatically")

(define-key vm-mode-map "!" 'vm-execute-content-type)

(defun vm-check-content-type ()
  "Check for certain Content Type headers in mail"
  (vm-maybe-execute-content-type nil))

(defun vm-execute-content-type ()
  "Check for certain Content Type headers in mail"
  (interactive)
  (vm-maybe-execute-content-type t))

(defun vm-handle-content-type (ctype override)
  (let (oldpt
        (fname (make-temp-name "/tmp/rmailct")))
    
    (cond
     ((and vm-never-execute-automatically (not override))
      (progn
       (message (concat "You can use '!' to run an interpreter for this '"
                        ctype "' format mail."))))
     ((or override
          (getenv "MM_NOASK")
          (y-or-n-p (concat "Run an interpreter for this '"
                            ctype "' format mail? ")))
      (progn
       (save-restriction
        (goto-char (point-max))
        (setq oldpt (point))
        (goto-char 0)
        (widen)
        (write-region
         (point)
         oldpt
         fname
         'nil
         "silent"))
       (if
        (and window-system (getenv "DISPLAY"))
         (progn
          (switch-to-buffer-other-window "METAMAIL")
          (erase-buffer)
          (start-process "metamail"  "METAMAIL" "metamail" "-m"
                         "vm" "-x" "-d" "-z" "-q" fname)
          (if vm-mail-buffer (pop-to-buffer vm-mail-buffer))
          (message "Starting metamail.  Sending output to METAMAIL buffer."))
         (progn
          (switch-to-buffer "METAMAIL")
          (erase-buffer)
          (sit-for 0)
          (transparent-window
           "METAMAIL"
           "metamail"
           (list "-m" "vm" "-d" "-z" "-q" fname)
           nil
          (concat
           "\n\r\n\r*****************************************"
           "*******************************\n\rPress any key "
           "to go back to EMACS\n\r\n\r***********************" 
           "*************************************************\n\r")
	  )))))
     (t (progn
	  (message (concat "You can use the '!' keystroke to "
			   "execute the external viewing program.")))))))

(defun vm-maybe-execute-content-type (dorun)
  "Check for certain Content Type headers in mail"
  (cond
   ((not (getenv "NOMETAMAIL"))
    (save-restriction
     (setq buffer-read-only 'nil)
     (let ((headend 0)
	   (ctype "text")
           (old-min (point-min))
           (old-max (point-max))
	   (opoint 0))
       (goto-char (point-min))
       (forward-line 1)
       (goto-char (point-min))
       (search-forward "\n\n")
       (setq headend (point))
       (setq opoint (- headend 1))
       (goto-char (point-min))    
       (setq case-fold-search 'T)
       (cond
	((search-forward "\ncontent-type:" opoint 't)
	 (progn
          (forward-word 1)	(backward-word 1)  ; Took care of white space
	  (setq opoint (point))
	  (re-search-forward "[;\n]" (point-max) t)
	  (setq ctype (downcase (buffer-substring opoint (- (point) 1))))
          (goto-char (point-min)))))
	(cond ((and (not (string= (downcase ctype) "text"))
                    (not (string= (downcase ctype) "text/plain"))
                    (not (string= (downcase ctype)
                                  "text/plain; charset=us-ascii")))
	      (vm-handle-content-type
	       ctype dorun))
             ))))))




6.  Watch out for an oddity in the terminal emulator package.  If you
get the error message

Key sequence \277 uses invalid prefix characters

or something like that,  this probably means that you have set your
global variable "help-char" to a META key.  The terminal emulator
package doesn't like that at all, and you'll need to change it in order
for the terminal emulator package, and these extensions to vm, to work
properly.


The above changes should be all you need to do in order to make vm work
with metamail, assuming that you already have the metamail binary
installed somewhere on your search path.

IMPORTANT NOTE:  For some users and some versions of Emacs,
terminal-oriented programs work poorly in the "transparent-window"
(non-X11) case with the above patch.  If you have problems in this case,
you might get better behavior by adding "-R" to the list of options that
are given to metamail.

Important note for Emacs "Hyperbole" users:  Hyperbole, in its "hvm.el"
file, overloads the definition of some VM functions.  Therefore, any VM
functions that need to be modified for metamail usage that exist in
"hvm.el" should be patched there rather than in the VM code itself.

1.12	MH-E -- GNU Emacs interface to MH mail 

This patch was contributed by Dave Cohrs.

Alter the definition of the function mh-display-msg.  In that function,
near the very end (line 1223 in my version) you will find the following
lines:

        (setq mode-line-buffer-identification
  	    (list (format mh-show-buffer-mode-line-buffer-id
 			  folder msg-num))))))

You should REPLACE these lines with the following ones:

        (setq mode-line-buffer-identification
  	    (list (format mh-show-buffer-mode-line-buffer-id
 			  folder msg-num)))
       (mh-check-content-type)	; Metamail extension
       )))

3.  Add the following code to the end of the source file:

;;; Functions added for METAMAIL support

(require 'transparent)

(defvar mh-never-execute-automatically t "*Prevent metamail from
happening semi-automatically")

(define-key mh-folder-mode-map "@" 'mh-execute-content-type)

(defun mh-check-content-type ()
  "Check for certain Content Type headers in mail"
  (mh-maybe-execute-content-type nil))

(defun mh-execute-content-type ()
  "Check for certain Content Type headers in mail"
  (interactive)
  (mh-maybe-execute-content-type t))

(defun mh-exec-metamail-cmd-output (command &rest args)
  ;; Execute MH library command COMMAND with ARGS.
  ;; Put the output into buffer after point.  Set mark after inserted text.
  (push-mark (point) t)
  (erase-buffer)
  (apply 'call-process
	 command nil t nil
	 (mh-list-to-string args))
  (exchange-point-and-mark)
  (set-buffer-modified-p nil)
  (other-window -1))

(defun mh-handle-content-type (ctype override)
  (let (oldpt
        (fname (make-temp-name "/tmp/rmailct")))
    
    (cond
     ((and mh-never-execute-automatically (not override))
      (progn
       (message (concat "You can use '@' to run an interpreter for this '"
                        ctype "' format mail."))))
     ((or override
          (getenv "MM_NOASK")
          (y-or-n-p (concat "Run an interpreter for this '"
                            ctype "' format mail? ")))
      (progn
       (save-restriction
        (goto-char (point-max))
        (setq oldpt (point))
        (goto-char 0)
        (widen)
        (write-region
         (point)
         oldpt
         fname
         'nil
         "silent"))
       (if 
	   (and window-system (getenv "DISPLAY"))
	   (mh-exec-metamail-cmd-output "metamail" "-m" "mh-e" "-x" "-d"
					"-q" fname)
	 (progn
	   (other-window -1)
	   (switch-to-buffer "METAMAIL")
	   (erase-buffer)
	   (sit-for 0)
	   (transparent-window
	    "METAMAIL"
	    "metamail"
	    (list "-m" "mh-e" "-p" "-d" "-q" fname)
	    nil
	    (concat
	     "\n\r\n\r*****************************************"
	     "*******************************\n\rPress any key "
	     "to go back to EMACS\n\r\n\r***********************" 
	     "*************************************************\n\r"))))))
     (t (progn
	  (message (concat "You can use the '@' keystroke to "
			   "execute the external viewing program.")))))))

(defun mh-maybe-execute-content-type (dorun)
  "Check for certain Content Type headers in mail"
  (cond
   ((not (getenv "NOMETAMAIL"))
    (save-restriction
     (setq buffer-read-only 'nil)
     (let ((headend 0)
	   (ctype "text/plain")
           (old-min (point-min))
           (old-max (point-max))
	   (opoint 0))
       (if mh-show-buffer (pop-to-buffer mh-show-buffer))
       (goto-char (point-min))
       (forward-line 1)
       (goto-char (point-min))
       (search-forward "\n\n" nil 1)
       (setq headend (point))
       (setq opoint (- headend 1))
       (goto-char (point-min))    
       (setq case-fold-search 'T)
       (cond
	((search-forward "\ncontent-type:" opoint 't)
	 (progn
          (forward-word 1)	(backward-word 1)  ; Took care of white space
	  (setq opoint (point))
	  (re-search-forward "[;\n]" (point-max) t)
	  (setq ctype (downcase (buffer-substring opoint (- (point) 1))))
          (goto-char (point-min)))))
	(cond ((and (not (string= (downcase ctype) "text"))
                    (not (string= (downcase ctype) "text/plain"))
                    (not (string= (downcase ctype)
                                  "text/plain; charset=us-ascii")))
	      (mh-handle-content-type
	       ctype dorun))
             ))))))

3.  Make sure you have the GNU "transparent.el" package installed.  This
is not part of the standard distribution, but is widely available, and
should be part of most FTP archives, etc.


The above changes should be all you need to do in order to make mh-e
work with metamail, assuming that you already have the metamail binary
installed somewhere on your search path.  If MH-E hackers contribute
improvements, they will be incorporated into a future version of this
document.

IMPORTANT NOTE:  For some users and some versions of Emacs,
terminal-oriented programs work poorly in the "transparent-window"
(non-X11) case with the above patch.  If you have problems in this case,
you might get better behavior by adding "-R" to the list of options that
are given to metamail.

1.13	CUI (Simplest Andrew Mail Reader)

NOTE:  If you have the version of CUI that corresponds to Messages
version 8.0 or later, you do not need this patch.  If you have the
version that corresponds to Messages 7.15 or later, you probably do not
need this patch, either.  If you have an earlier version, you need this
patch.

In the file ams/msclients/cui/cui.c, there is a routine called
"GetBodyFromCUID"  The first line of that routine looks like this:

    debug(1,("GetBodyFromCUID %d\n", cuid));

Immediately after that line, which is between lines 1228 and 1229 in my
version, add the following lines:

#ifndef NOMETAMAIL
    {
#include <sgtty.h>
	struct sgttyb ttystatein, ttystateout;
	char ctype[100], TmpFileName[1+MAXPATHLEN], Cmd[1+MAXPATHLEN];
	int ShouldDelete, code;
	extern int LinesOnTerminal;

	if (CUI_GetHeaderContents(cuid,(char *) NULL, HP_CONTENTTYPE, ctype,
sizeof(ctype) - 1) != NULL) {
	    /* error already reported */
	    return(-1);
	}
	if (!getenv("NOMETAMAIL") && ctype[0] && strnicmp(ctype, "x-be2", 5) &&
nontext(ctype)) {
	    if (CUI_GetBodyToLocalFile(cuid, TmpFileName, &ShouldDelete)) {
		return(-1); /* error reported */
	    }
	    sprintf(Cmd, "metamail -m cui %s %s %s", (LinesOnTerminal > 0) ?
"-p" : "", ShouldDelete ? "-z" : "", TmpFileName);
	    gtty(fileno(stdin), &ttystatein);
	    gtty(fileno(stdout), &ttystateout);
	    code = system(Cmd);
	    stty(fileno(stdin), &ttystatein);
	    stty(fileno(stdout), &ttystateout);
	    /* if (code) */ return(0);
	}
    }
#endif

Then, at the end of the file, add the following code:

#ifndef NOMETAMAIL

nontext(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

This simple change should be all you need to do in order to make CUI
work with metamail, assuming that you already have the "metamail"
executable installed somewhere on your search path.
1.14	VUI (Terminal-oriented Andrew Mail Reader)

NOTE:  If you have the version of VUI that corresponds to Messages
version 8.0 or later, you do not need this patch.  If you have the
version that corresponds to Messages 7.15 or later, you probably do not
need this patch, either.  If you have an earlier version, you need this
patch.

In the file ams/msclients/vui/vuipnl.c, there is a routine called
"InitBodyData"  That routine starts out with the following line of code:

    char filename[MAXPATHLEN+1];

Immediately after that code, which is between lines 2386 and 2387 in my
version, add the following lines:

#ifndef NOMETAMAIL
#include <sgtty.h>
	struct sgttyb ttystatein, ttystateout;
	char ctype[100], TmpFileName[1+MAXPATHLEN], Cmd[1+MAXPATHLEN];
	int ShouldDelete, cuid = CuidFromMsgno(msgno), code;

	if (CUI_GetHeaderContents(cuid,(char *) NULL, HP_CONTENTTYPE, ctype,
sizeof(ctype) - 1) != NULL) {
	    /* error already reported */
	    return(-1);
	}
	if (!getenv("NOMETAMAIL") && ctype[0] && ULstrncmp(ctype, "x-be2", 5)
&& nontext(ctype)) {
	    if (CUI_GetBodyToLocalFile(cuid, TmpFileName, &ShouldDelete)) {
		return(-1); /* error reported */
	    }
	    sprintf(Cmd, "metamail -R -m vui -p %s %s", ShouldDelete ? "-z" :
"", TmpFileName);
	    gtty(fileno(stdin), &ttystatein);
	    gtty(fileno(stdout), &ttystateout);
	    code = system(Cmd);
	    stty(fileno(stdin), &ttystatein);
	    stty(fileno(stdout), &ttystateout);
	    RedrawScreen(0); 
	    UpdateMsgData (msgno, 1); 
	    return(MENU_EMPTY);
	}
#endif

Then, at the end of the file, add the following code:

#ifndef NOMETAMAIL

nontext(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

This simple change should be all you need to do in order to make VUI
work with metamail, assuming that you already have the "metamail"
executable installed somewhere on your search path.
1.15	Messages (Andrew Multimedia Mail Reader)

NOTE:  If you have Messages version 8.0 or later, you do not need this
patch.  If you have Messages 7.15 or later, you probably do not need
this patch, either.  If you have an earlier version, you need this patch.

There are two changes, both localized to the file
atkams/messages/lib/text822.c:

1.  Near the beginning of atkams/messages/lib/text822.c, there are the
following #include lines:

#include <stylesht.ih>
#include <environ.ih>
#include <fnote.ih>
#include <ams.h>

Immediately after those lines, insert the following lines:

#ifndef NOMETAMAIL
#include <sys/param.h>
#include <fdphack.h>
#undef popen /* BOGUS -- should be handled by fdphack */
#undef pclose /* ditto */
#include <ams.ih>
#include <message.ih>
#include <im.ih>

MetaOutput(fp, self)
FILE *fp;
struct text *self;
{
    char buf[1000];

    if (fgets(buf, sizeof(buf), fp) != NULL) {
	text_AlwaysInsertCharacters(self, text_GetLength(self), buf, strlen(buf));
	text_NotifyObservers(self, 0);
	return;
    }
    if (errno != EWOULDBLOCK) {
	im_RemoveFileHandler(fp);
	pclose(fp);
	strcpy(buf, "\n--- Command execution terminated ---\n");
	text_AlwaysInsertCharacters(self, text_GetLength(self), buf, strlen(buf));
    }
    text_NotifyObservers(self, 0);
}
#endif

2.  In atkams/messages/lib/text822.c, there is a routine called
"text822__ReadIntoText"  Near the end of that very long routine, you
will find some code that looks like this:

	} else if (!amsutil_lc2strncmp("troff", sfmttype, strlen(sfmttype))) {
	    char **resources = (char **)
amsutil_BreakDownResourcesIntoArray(fmtresources);

	    rofftext_ReadRoffIntoText(d, fp, ShowPos, resources);
	    if (resources) free(resources);
	    ReadRaw = FALSE;
	}

The patch needs to be added AFTER the line that says "ReadRaw = FALSE"
but before the next line, which is just a closing brace.  (In my
version, this means the patch goes between lines 353 and 354.)  The
patch to insert is as follows:

#ifndef NOMETAMAIL
	} else if (!environ_Get("NOMETAMAIL") && IsReallyTextObject &&
amsutil_lc2strncmp("text", sfmttype, strlen(sfmttype))) {
	    /* IsReallyTextObject test ensures we don't run metamail when
printing!!! */
	    char TmpFileName[1+MAXPATHLEN], LineBuf[1000], Cmd[1+MAXPATHLEN],
Msg[50+MAXPATHLEN], TmpFile2[1+MAXPATHLEN];
	    FILE *fp2;

	    sprintf(Msg, "Do you want to run an interpreter for this '%s'
format mail", sfmttype);
	    if (environ_Get("MM_NOASK")
		|| ams_GetBooleanFromUser(ams_GetAMS(), Msg, TRUE)) {
		ams_CUI_GenLocalTmpFileName(ams_GetAMS(), TmpFileName);
		ams_CUI_GenLocalTmpFileName(ams_GetAMS(), TmpFile2);
		fp2 = (FILE *) fopen (TmpFileName, "w");
		if (fp2) {
		    fseek(fp, 0, 0);
		    while (fgets(LineBuf, sizeof(LineBuf), fp)) {
			fputs(LineBuf, fp2);
		    }
		    fclose(fp2);
		    sprintf(Cmd, "metamail -m messages -z -x -d -q %s 2>&1", TmpFileName);
		    sprintf(Msg, "Executing: %s\n", Cmd);
		    linelen = strlen(Msg);
		    text822_AlwaysInsertCharacters(d, ShowPos, Msg, linelen);
		    ShowPos += strlen(Msg);
		    fp2 = (FILE *) popen(Cmd, "r");
		    im_AddFileHandler(fp2, MetaOutput, d, 0);
		}
	    }
#endif

This simple change should be all you need to do in order to make
Messages work with metamail, assuming that you already have the
"metamail" executable installed somewhere on your search path.
1.16	BatMail (Emacs interface to Andrew)

Patching BatMail is quite simple.  This patch was provided by Bob
Glickstein, and has not been tested by the author of this document.

Batmail already provides for a hook called "bat-body-hook" which gets
called when a new message body is displayed.  You can hook metamail into
batmail by simply defining that hook to be the Lisp code below. (The one
catch is that the user must have "mime-version" and "content-type" as
headers which are normally displayed, specified by the bat-headers
variable.  The reason is that the hook searches the body buffer for
these headers [rather than the time-consuming option of grepping the
body file ("robin" does not have a GetHeaderContents stub)].)

The following is the code that you need to add to BatMail:

(setq bat-headers
      (concat "from:resent-from:resent-to:date:subject:to"
	      ":cc:newsgroups:mime-version:content-type:content-transfer-encoding"))


(setq bat-body-hook 'bat-body-hook-fn)


(defun bat-body-hook-fn ()
  (save-excursion
    (set-buffer bat-display-buf)
    (goto-char (point-min))
    (let ((end-of-headers (if (search-forward "\n\n" (point-max) t)
			      (point))))
      (goto-char (point-min))
      (if (re-search-forward "^[Mm][Ii][Mm][Ee]-[Vv]ersion:[ \\t]*\\(.+\\)"
			     end-of-headers t)
	  (let ((mime-version (buffer-substring (match-beginning 1)
						(match-end 1))))
	    (goto-char (point-min))
	    (if (re-search-forward "^[Cc]ontent-[Tt]ype:[ \\t]*\\(.+\\)"
				   end-of-headers t)
		(let ((content-type (buffer-substring (match-beginning 1)
						      (match-end 1))))
		  (if (not (string-match "^text/plain" content-type))
		      (if (get-tty-bool "Run metamail" t)
			  (let ((tmp (concat "/tmp/" 
					     (make-temp-name "bat-meta-")))
				(cuid (bat-current-cuid))
				(process-connection-type nil)
				metamail-process)
			    (bat-command "a" cuid ":" tmp "\n")
			    (while (not (file-exists-p tmp))
			      (sit-for 1))
			    (setq metamail-process
				  (start-process "metamail" bat-display-buf
						 "metamail" "-d" "-m" "batmail"
						 "-x" "-z" tmp))
			    (if metamail-process
				(progn
				  (process-kill-without-query
				   metamail-process nil))
			      (error "Couldn't start metamail"))))))))))))

1.17	Elm (Mail system from HP)

NOTE:  Elm version 2.4 (and, presumably later versions) have metamail
built-in, so no patching of the Elm code is necessary if you have a
recent enough version.

All of the changes are localized to the file src/showmsg.c  In
particular, there are two changes to be made:

1.  In src/showmsg.c, there is a routine called "show_msg" my version it
starts on line 49.  About a page down in that routine, you'll find
something that looks like this:


	memory_lock = FALSE;

	/* some explanation for that last one - We COULD use memory locking
	   to speed up the paging, but the action of "ClearScreen" on a screen
	   with memory lock turned on seems to vary considerably (amazingly so)
	   so it's safer to only allow memory lock to be a viable bit of
	   trickery when dumping text to the screen in scroll mode.
	   Philosophical arguments should be forwarded to Bruce at the 
	   University of Walamazoo, Australia, via ACSNet  *wry chuckle* */

Immediately following that fragment (i.e. between line 114 and line 115,
in my version) add the following code:

#ifndef NOMETAMAIL
	if (!getenv("NOMETAMAIL") && nontext(current_header)) {
	    char fname[100], Cmd[200], line[VERY_LONG_STRING];
	    int code;
	    long lines = current_header->lines;
	    FILE *fpout;

	    if (fseek(mailfile, current_header->offset, 0) != -1) {
		sprintf(fname, "/tmp/elm-metamail.%d.%d", getpid(), getuid());
		fpout = fopen(fname, "w");
		if (fpout) {
		    while (lines > 0L) {
			fgets(line, VERY_LONG_STRING, mailfile);
			fputs(line, fpout);
			--lines;
		    }
		    fclose(fpout);
		    sprintf(Cmd, "metamail -R -P -z -m Elm %s", fname);
                       Raw(OFF);
		    code = system(Cmd);
                       Raw(ON);
		    /* if (code) */ return(0);
		}
	    }
	}
#endif

2.  At the very end of src/showmsg.c, add the following new routines,
which are used by the patch above:

#ifndef NOMETAMAIL
nontext(ch) 
struct header_rec *ch;
{
    long lines = ch->lines;
    char line[VERY_LONG_STRING], *s, *t;

    if (fseek(mailfile, ch->offset, 0) == -1) return(-1);
    while (lines > 0L) {
	fgets(line, VERY_LONG_STRING, mailfile);
	--lines;
	if (line[0] == '\n') return(0);
	for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
	if (!strncmp(line, "content-type:", 13)) {
	    s = &line[13];
	    while (s && isspace(*s)) ++s;
	    t = index(s, '\n');
	    if (!t) t = index(s, ';');
	    if (t) *t-- = NULL;
	    while (t && *t && t > s && isspace(*t)) *t-- = NULL;
	    if (notplain(s)) return(1);
	}
    }
    return(0);
}
notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}
#endif

These two changes should be all you need to do in order to make Elm work
with metamail, assuming that you already have the "metamail" executable
installed somewhere on your search path.

NOTE ABOUT XTERM AND ELM:  Apparently, sometimes the combination of Elm,
xterm, and richtext can cause problems.  The following note from the Elm
documentation describes the problem and how to fix it:

> Limitations/Problems you might encounter in compiling and installing Elm:
> 	
> >From comp.mail.elm, dws@ssec.wisc.edu (DaviD W. Sanderson) writes:
> >... whoever wrote the default termcap
> >and/or terminfo descriptions for xterm included in the ti/te strings
> >the special escape sequences to make xterm switch between the normal
> >and alternate screen buffers.  These sequences are:
> >
> >	\E[?47h		- use alternate screen buffer
> >	\E[?47l		- use normal screen buffer
> >...
> >The elm code is just fine as it is.  If you change it so that it
> >doesn't ever send ti/te, you'll just break elm for somebody else.  Fix
> >your termcap/terminfo definition instead.

Making this change allows richtext messages to be properly displayed.

1.18	Mush mail reader

The following patch was provided by Bart Schaefer.  Her reports that
this patch should work with versions of mush as far back as 6.5,
although 6.5 doesn't have M_PRIORITY()
either, so you'd have to apply by hand and omit that part.

He also reports that mush version  7.2.5 will have metamail support
built in, so no patches will be necessary if you are using that version
or later.

There are two files to patch, msgs.c and mush.h.  I haven't included the
non-essential third patch that shows an "M" in the "headers" summary if
the message is a MIME-format message.

The first two hunks of this patch, to msgs.c, modify display_msg() to
have it send the "raw" message to the program named in the $metamail
variable when the message's METAMAIL flag bit is set.  If $metamail is
not set, it ignores the METAMAIL bit and pages the message normally. The
third hunk modifies load_folder() to recognize the Content-Type: header
and turn the METAMAIL bit on.  (There's some other slop in there   about
checking Resent-Date: for a date which can safely be ignored if patching
by hand.)

*** 7.2.4/msgs.c	Sun Feb  2 13:59:14 1992
--- 7.2.5/msgs.c	Mon Mar  9 23:38:34 1992
***************
*** 8,13 ****
--- 8,14 ----
  u_long flg;
  {
      char buf[32], *pager = NULL;
+     int intro = TRUE;
  
      if (ison(msg[n].m_flags, DELETE) && !do_set(set_options,
"show_deleted")) {
  	print("Message %d deleted; ", n+1);
***************
*** 31,37 ****
  #ifdef MSG_SEPARATOR
      turnon(flg, NO_SEPARATOR);
  #endif /* MMDF */
!     if (!istool && isoff(flg, NO_PAGE) &&
  	    crt < msg[n].m_lines && isoff(flg, M_TOP)) {
  	if (!(pager = do_set(set_options, "pager")))
  	    pager = DEF_PAGER;
--- 32,44 ----
  #ifdef MSG_SEPARATOR
      turnon(flg, NO_SEPARATOR);
  #endif /* MMDF */
!     if (ison(msg[n].m_flags, METAMAIL) && isoff(flg, NO_PAGE) &&
! 	    (pager = do_set(set_options, "metamail"))) {
! 	intro = FALSE;
! 	turnoff(flg, NO_HEADER);
! 	turnoff(flg, M_TOP);
! 	turnon(flg, NO_IGNORE);
!     } else if (!istool && isoff(flg, NO_PAGE) &&
  	    crt < msg[n].m_lines && isoff(flg, M_TOP)) {
  	if (!(pager = do_set(set_options, "pager")))
  	    pager = DEF_PAGER;
***************
*** 38,46 ****
  	if (!*pager || !strcmp(pager, "internal"))
  	    pager = NULL; /* default to internal pager if pager set to "" */
      }
!     (void) do_pager(pager, TRUE); /* start pager */
!     (void) do_pager(sprintf(buf, "Message #%d (%d lines)\n",
! 		     n+1, msg[n].m_lines), FALSE);
      (void) copy_msg(n, NULL_FILE, flg, NULL);
      (void) do_pager(NULL, FALSE); /* end pager */
  }
--- 45,54 ----
  	if (!*pager || !strcmp(pager, "internal"))
  	    pager = NULL; /* default to internal pager if pager set to "" */
      }
!     (void) do_pager(pager, intro? 1 : -1); /* start pager */
!     if (intro)
! 	(void) do_pager(sprintf(buf, "Message #%d (%d lines)\n",
! 				 n+1, msg[n].m_lines), FALSE);
      (void) copy_msg(n, NULL_FILE, flg, NULL);
      (void) do_pager(NULL, FALSE); /* end pager */
  }
***************
*** 897,905 ****
  		 */
  		while (fgets(buf, sizeof (buf), fp) && (*buf != '\n')) {
  		    p = buf;
  		    if (!strncmp(buf, "Date:", 5))
  			strdup(msg[cnt].m_date_sent, parse_date(p+5));
! 		    if (!strncmp(buf, "Priority:", 9)) {
  			for (p += 9 ; *p != '\n'; p++) {
  			    if (!isalpha(*p) || upper(*p) > 'A' + MAX_PRIORITY)
  				continue;
--- 910,923 ----
  		 */
  		while (fgets(buf, sizeof (buf), fp) && (*buf != '\n')) {
  		    p = buf;
  		    if (!strncmp(buf, "Date:", 5))
  			strdup(msg[cnt].m_date_sent, parse_date(p+5));
! 		    else if (!msg[cnt].m_date_sent &&
! 			    !strncmp(buf, "Resent-Date:", 12))
! 			msg[cnt].m_date_sent = savestr(parse_date(p+12));
! 		    else if (!lcase_strncmp(buf, "Content-Type:", 13))
! 			turnon(msg[cnt].m_flags, METAMAIL);
! 		    else if (!strncmp(buf, "Priority:", 9)) {
  			for (p += 9 ; *p != '\n'; p++) {
  			    if (!isalpha(*p) || upper(*p) > 'A' + MAX_PRIORITY)
  				continue;

This patch to mush.h defines the METAMAIL bit.  I hope nobody is counting
on being able to redefine MAX_PRIORITY as 10. :-}

*** 7.2.4/mush.h	Sun Feb  2 13:50:50 1992
--- 7.2.5/mush.h	Mon Mar  2 21:04:59 1992
***************
*** 430,441 ****
  #define REPLIED		ULBIT(17) /* Messages that have been replied to */
  #define NEW_SUBJECT	ULBIT(18) /* new subject regardless of $ask (mail -s) */
  #define SAVED		ULBIT(19) /* when message has been saved */
- #ifdef MSG_SEPARATOR
  #define NO_SEPARATOR	ULBIT(20) /* don't include message separator lines */
! #endif /* MSG_SEPARATOR */
  
! #define M_PRIORITY(n)	ULBIT(21+(n))
! /* It is possible to reset MAX_PRIORITY to as high as 10 */
  #define MAX_PRIORITY	5

  #define MAXMSGS_BITS	MAXMSGS/sizeof(char)	/* number of bits for bitmap */
--- 430,440 ----
  #define REPLIED		ULBIT(17) /* Messages that have been replied to */
  #define NEW_SUBJECT	ULBIT(18) /* new subject regardless of $ask (mail -s) */
  #define SAVED		ULBIT(19) /* when message has been saved */
  #define NO_SEPARATOR	ULBIT(20) /* don't include message separator lines */
! #define METAMAIL	ULBIT(21) /* message can display with "metamail" */
  
! #define M_PRIORITY(n)	ULBIT(22+(n))
! /* It is possible to reset MAX_PRIORITY to as high as 9 */
  #define MAX_PRIORITY	5

  #define MAXMSGS_BITS	MAXMSGS/sizeof(char)	/* number of bits for bitmap */

1.19	Msgs (Berkeley Bulletin Board system)

All of the changes are localized to the file msgs.c  In particular,
there are four changes to be made:

1.  In msgs.c, there are lots of global declarations near the top.  In
my version, the last of them (around line 114) looks like this:

jmp_buf	tstpbuf;

Immediately after that line,  add the following code:

#ifndef NOMETAMAIL
int NonTextMessage;
#endif

2.  Further down in msgs.c, there is a routine named "prmesg" (around
line 580) which starts with the following declarations:

FILE *outf, *inf;
int c;

Immediately after these declaration, add the following code:

#ifndef NOMETAMAIL
	if (!getenv("NOMETAMAIL") && NonTextMessage) {
	    char Fname[100], Cmd[120];
	    FILE *fp;
	    int code;
	    struct sgttyb ttystatein, ttystateout;

	    sprintf(Cmd, "metamail %s -m Mail %s", pause ? "-p" : "", fname);
	    gtty(fileno(stdin), &ttystatein);
	    gtty(fileno(stdout), &ttystateout);
	    code = system(Cmd);
	    stty(fileno(stdin), &ttystatein);
	    stty(fileno(stdout), &ttystateout);
	    return;
	  }
#endif

3.  Still further down in msgs.c, there is a routine named "gfrsub"
(around line 775).  A page or so down in that routine, there is code
that looks like this:

	while (fgets(inbuf, sizeof inbuf, infile)
	    && !(blankline = (inbuf[0] == '\n'))) {
		/*
		 * extract Subject line
		 */
		if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
			seensubj = YES;
			frompos = ftell(infile);
			strncpy(subj, nxtfld(inbuf), sizeof subj);
		}

Immediately before this code fragment, add the following three lines:

#ifndef NOMETAMAIL
        NonTextMessage = 0;
#endif

Immediately after this code fragment (around line 840 in my version) add
the following code:

#ifndef NOMETAMAIL
	  {		
	    char *s, *t;
	    for (s=inbuf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
	    if (!strncmp(inbuf, "content-type:", 13)) {
	      s = inbuf + 13;
	      while (s && isspace(*s)) ++s;
	      t = (char *) index(s, ';');
	      if (t) *t = NULL; else t = s+strlen(s);
	      while (--t > s && isspace(*t)) *t = NULL;
	      NonTextMessage = nontext(s);
	    }
	  }
#endif

Finally, add this routine to the end of the file:

nontext(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}        

These four changes should be all you need to do in order to make msgs
work with metamail, assuming that you already have the "metamail"
executable installed somewhere on your search path.
1.20	UUPC (MS-DOS Mail-Reading Program)

UUPC is an Internet mail-reading program for MS-DOS.  It turns out that
if you build metamail for DOS, you don't need to patch the UUPC program
at all.  Instead, you can simply set your PAGER to metamail.

In particular, you need to put a line like the following in your
personal configuration file:

Pager=metamail %s

This should be all that it takes to make UUPC work with metamail. 
However, you should note that the metamail "-p" feature is not being
used (and probably doesn't yet work on DOS), which means that your
mailcap entries should take paging into account.  In particular, you
probably want to have an entry for "text/richtext" that passes "-p" to
the richtext program, and a followup general entry for "text/*" that
uses a normal text paging program to view the mail.

1.21	TRN (Threaded News Reader)

TRN version 3.x or later has the code for MIME support supplied - no
patches are necessary.  It IS necessary to hand edit common.h to turn on
the METAMAIL define.  There is also a problem
in trn's 3.x distribution in that there is only code to call
/usr/local/bin/mh/mhn to extract a MIME article.  Someone needs to
identify what other options one has to using mhn -store -auto -file ...

If you have an earlier version of TRN, you might be interested in the
following metamail patch for TRN which was contributed byJohn Schmitz:

*** ../trn2.2/common.h	Fri Nov 20 11:10:25 1992
--- common.h	Mon Nov 23 15:51:15 1992
***************
*** 521,526 ****
--- 521,542 ----
  #   define UNSHAR "/bin/sh"
  #endif
  
+ #ifndef NOMETAMAIL
+ /* default MIME extraction program */
+ #  ifndef MIMESTORE
+ #    define MIMESTORE "/usr/local/bin/mh/mhn -store -auto -file "
+ #  endif
+ 
+ /* default MIME show program */
+ #  ifndef MIMESHOW
+ #    ifdef SERVER
+ #       define MIMESHOW "metamail -e -p -m trn %P/rrn%a.%$"
+ #    else
+ #       define MIMESHOW "metamail -e -p -m trn %s %A"
+ #    endif
+ #  endif
+ #endif
+ 
  /* path to default editor */
  #ifndef DEFEDITOR
  #   define DEFEDITOR "/usr/ucb/vi"
***************
*** 679,684 ****
--- 695,710 ----
  #    endif
  #endif
  
+ #ifndef NOMETAMAIL
+ #  ifndef EXMIMESAVER
+ #    ifdef SERVER
+ #	define EXMIMESAVER "%e %P/rrn%a.%$"
+ #    else
+ #	define EXMIMESAVER "%e %A"
+ #    endif
+ #  endif
+ #endif
+ 
  #ifndef NORMSAVER		/* % and ~ */
  #    ifdef SERVER
  #	define NORMSAVER "%X/norm.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\""
***************
*** 940,945 ****
--- 966,975 ----
      EXT char nocd[] INIT("Can't chdir to directory %s\n");
  #else
      EXT char nocd[] INIT("Can't find %s\n");
+ #endif
+ 
+ #ifndef NOMETAMAIL
+ EXT bool mime_article INIT(FALSE);
  #endif
  
  #ifdef NOLINEBUF
*** ../trn2.2/head.h	Mon Nov 25 09:49:12 1991
--- head.h	Fri Nov 20 11:12:06 1992
***************
*** 34,39 ****
--- 34,72 ----
  #define ACAT_LINE	4	/* ACategory (ClariNet) */
  #define ANPA_LINE	5	/* ANPA (ClariNet) */
  #define CODES_LINE	6	/* Codes (ClariNet) */
+ #ifndef NOMETAMAIL
+ #define CONTENT_LINE	7	/* MIME */
+ #define DIST_LINE	8	/* distribution */
+ #define DATE_LINE	9	/* date */
+ #define RECEIVED_LINE	10	/* date-received */
+ #define EXPIR_LINE	11	/* expires */
+ #define FOLLOW_LINE	12	/* followup-to */
+ #define FROM_LINE	13	/* from */
+ #define FORM_LINE	14	/* Format (ClariNet) */
+ #define KEYW_LINE	15	/* keywords */
+ #define LINES_LINE	16	/* lines */
+ #define MESSID_LINE	17	/* message-id */
+ #define NFFR_LINE	18	/* nf-from */
+ #define NFID_LINE	19	/* nf-id */
+ #define NGS_LINE	20	/* newsgroups */
+ #define NNTP_LINE	21	/* nntp-posting-host */
+ #define NOTE_LINE	22	/* Note (ClariNet) */
+ #define ORG_LINE	23	/* organization */
+ #define PATH_LINE	24	/* path */
+ #define POSTED_LINE	25	/* posted */
+ #define PVER_LINE	26	/* posting-version */
+ #define PRI_LINE	27	/* Priority (ClariNet) */
+ #define REPLY_LINE	28	/* reply-to */
+ #define REFS_LINE	29	/* references */
+ #define RVER_LINE	30	/* relay-version */
+ #define SENDER_LINE	31	/* sender */
+ #define SUMRY_LINE	32	/* summary */
+ #define SUBJ_LINE	33	/* subject */
+ #define SLUG_LINE	34	/* Slugword (ClariNet) */
+ #define XREF_LINE	35	/* xref */
+ #define XSUP_LINE	36	/* X-Supersedes (ClariNet) */
+ #define HEAD_LAST	37	/* one more than the last one above */
+ #else /* NOMETAMAIL
  #define DIST_LINE	7	/* distribution */
  #define DATE_LINE	8	/* date */
  #define RECEIVED_LINE	9	/* date-received */
***************
*** 63,68 ****
--- 96,102 ----
  #define XREF_LINE	33	/* xref */
  #define XSUP_LINE	34	/* X-Supersedes (ClariNet) */
  #define HEAD_LAST	35	/* one more than the last one above */
+ #endif NOMETAMAIL
  
  struct headtype {
      char *ht_name;		/* header line identifier */
***************
*** 94,99 ****
--- 128,136 ----
      {"acategory",	0,	0,	9,	HT_HIDE		},
      {"anpa",		0,	0,	4,	HT_HIDE		},
      {"codes",		0,	0,	5,	HT_HIDE		},
+ #ifndef NOMETAMAIL
+     {"content-type",	0,	0,	12,	HT_MAGIC	},
+ #endif
      {"distribution",	0,	0,	12,	0		},
  #ifdef USETHREADS
      {"date",		0,	0,	4,	HT_MAGIC	},
***************
*** 111,116 ****
--- 148,156 ----
      {"nf-from",		0,	0,	7,	HT_HIDE		},
      {"nf-id",		0,	0,	5,	HT_HIDE		},
      {"newsgroups",	0,	0,	10,	HT_MAGIC|HT_HIDE},
+ #ifndef NOMETAMAIL
+     {"nntp-posting-host",	0,	0,	17,	HT_HIDE},
+ #endif
      {"note",		0,	0,	4,	0,		},
      {"organization",	0,	0,	12,	0		},
      {"path",		0,	0,	4,	HT_HIDE		},
*** ../trn2.2/respond.c	Fri Nov 20 11:10:31 1992
--- respond.c	Mon Nov 23 15:55:50 1992
***************
*** 66,72 ****
--- 66,76 ----
  #ifdef ASYNC_PARSE
      parse_maybe(art);
  #endif
+ #ifndef NOMETAMAIL
+     savefrom = (!mime_article && (cmd == 'w' || cmd == 'e') ?
htype[PAST_HEADER].ht_minpos : 0);
+ #else
      savefrom = (cmd == 'w' || cmd == 'e' ? htype[PAST_HEADER].ht_minpos : 0);
+ #endif
      if (artopen(art) == Nullfp) {
  #ifdef VERBOSE
  	IF(verbose)
***************
*** 173,178 ****
--- 177,202 ----
  	    while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
  		if (*art_buf <= ' ')
  		    continue;	/* Ignore empty or initially-whitespace lines */
+ #ifndef NOMETAMAIL
+ 		if (mime_article) {
+ 		    if (!custom_extract) {
+ 		      printf("Extracting MIME article into %s:\n", s) FLUSH;
+ 		      extractprog = savestr(filexp(getval("MIMESTORE",MIMESTORE)));
+ 		    }
+ 		    else
+ 		      printf("Extracting MIME article into %s using %s\n",
+ 		      s, extractprog) FLUSH;
+ 		    cnt = 0;
+ 		    interp(cmd_buf,(sizeof cmd_buf),getval("EXMIMESAVER",EXMIMESAVER));
+ 		    termlib_reset();
+ 		    resetty();		/* restore tty state */
+ 		    doshell(SH,cmd_buf);
+ 		    noecho();		/* revert to cbreaking */
+ 		    crmode();
+ 		    termlib_init();
+ 		    break;
+ 		}
+ #endif
  		if (found_cut && custom_extract) {
  		    printf("Extracting data into %s using %s:\n",
  			s, extractprog) FLUSH;
*** ../trn2.2/art.c	Fri Nov 20 11:10:23 1992
--- art.c	Mon Nov 23 14:24:27 1992
***************
*** 78,84 ****
--- 78,106 ----
      ;
  }
  
+ #ifndef NOMETAMAIL
+ #define VERY_LONG_STRING        200
  int
+ display_mime()
+ {
+     if (!getenv("NOMETAMAIL")) {
+ 	int code;
+ 
+ 	interp(cmd_buf,(sizeof cmd_buf),getval("MIMESHOW",MIMESHOW));
+ 	termlib_reset();
+ 	resetty();
+ 	code = doshell(SH,cmd_buf);
+ 	noecho();
+ 	crmode();
+ 	termlib_init();
+ 	return(code);
+     }
+     else return(1);
+ }
+ #endif
+ 
+ 
+ int
  do_article()
  {
      register char *s;
***************
*** 96,105 ****
--- 118,134 ----
      bool notesfiles = FALSE;		/* might there be notesfiles junk? */
      char oldmode = mode;
      char *ctime();
+ #ifndef NOMETAMAIL
+     bool tried_display_mime = FALSE;
+ #endif
  
+ 
  #ifdef INNERSEARCH
      register int outputok;
  #endif
+ #ifndef NOMETAMAIL
+     mime_article = FALSE;
+ #endif
  
      if (fstat(fileno(artfp),&filestat))
  			    /* get article file stats */
***************
*** 287,292 ****
--- 316,326 ----
  			localtime(&curr_p_art->date));
  		}
  #endif
+ #ifndef NOMETAMAIL
+                 else if (in_header == CONTENT_LINE) {
+                     mime_article = nontext(art_buf+14);
+                 }
+ #endif
  	    }
  	    if (in_header == SUBJ_LINE &&
  		htype[SUBJ_LINE].ht_flags & HT_MAGIC) {
***************
*** 360,365 ****
--- 394,405 ----
  	    }
  #endif
  	    else {			/* just a normal line */
+ #ifndef NOMETAMAIL
+                 if (mime_article && ! tried_display_mime)
+                     if (display_mime() == 0)
+                         return DA_NORM;
+                     else tried_display_mime = TRUE;
+ #endif
  		if (highlight==artline) {	/* this line to be highlit? */
  		    if (marking == STANDOUT) {
  #ifdef NOFIREWORKS
***************
*** 1021,1025 ****
--- 1061,1109 ----
  	return TRUE;
      }
      return FALSE;
+ }
+ #endif
+ #ifndef NOMETAMAIL
+ nontext(content_type)
+ char *content_type;
+ {
+     char *t;
+ 
+     if (content_type[0] == '\n') return(0);
+     while (content_type && isspace(*content_type)) ++content_type;
+     t = index(content_type, ';');
+     if (!t) t = index(content_type, '\n');
+     if (t) *t-- = NULL;
+     while (t && *t && t > content_type && isspace(*t)) *t-- = NULL;
+     if (notplain(content_type)) return(1);
+     return(0);
+ }
+ 
+ notplain(s)
+ char *s;
+ {
+     char *t;
+     if (!s) return(1);
+     while (*s && isspace(*s)) ++s;
+     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
+     while (t > s && isspace(*--t)) {;}
+     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
+     if (strncmp(s, "text/plain", 10)) return(1);
+     t = (char *) index(s, ';');
+     while (t) {
+         ++t;
+         while (*t && isspace(*t)) ++t;
+         if (!strncmp(t, "charset", 7)) {
+             s = (char *) index(t, '=');
+             if (s) {
+                 ++s;
+                 while (*s && isspace(*s)) ++s;
+                 if (!strncmp(s, "us-ascii", 8)) return(0);
+             }
+             return(1);
+         }
+         t = (char *) index(t, ';');
+     }
+     return(0); /* no charset, was text/plain */
  }
  #endif
1.22	RN News Reader

A metamail patch for RN was contributed by Rudy Maceyko as follows.

The patch is almost word-for-word the same as the patch for TRN.  As
such, it contains a dependency on the program 'mhn'.

[Note 1: this patch is available from
 ftp.pitt.edu:/users/r/m/rm55/rn/rn-4.4-metamail-2.6-patch]

[Note 2: Larry Virden has promised to send me a substitute for mhn.
 Perhaps *it* should be used instead of mhn.]

*** art.c.orig	Tue Mar  3 08:09:45 1992
--- art.c	Wed Nov 24 16:48:04 1993
***************
*** 74,80 ****
--- 74,102 ----
      ;
  }
  
+ #ifndef NOMETAMAIL
+ #define VERY_LONG_STRING        200
  int
+ display_mime()
+ {
+     if (!getenv("NOMETAMAIL")) {
+ 	int code;
+ 
+ 	interp(cmd_buf,(sizeof cmd_buf),getval("MIMESHOW",MIMESHOW));
+ 	termlib_reset();
+ 	resetty();
+ 	code = doshell(SH,cmd_buf);
+ 	noecho();
+ 	crmode();
+ 	termlib_init();
+ 	return(code);
+     }
+     else return(1);
+ }
+ #endif
+ 
+ 
+ int
  do_article()
  {
      register char *s;
***************
*** 92,101 ****
--- 114,130 ----
      bool notesfiles = FALSE;		/* might there be notesfiles junk? */
      char oldmode = mode;
      char *ctime();
+ #ifndef NOMETAMAIL
+     bool tried_display_mime = FALSE;
+ #endif
  
+ 
  #ifdef INNERSEARCH
      register int outputok;
  #endif
+ #ifndef NOMETAMAIL
+     mime_article = FALSE;
+ #endif
  
      if (fstat(fileno(artfp),&filestat))
  			    /* get article file stats */
***************
*** 242,247 ****
--- 271,281 ----
  			    *s = '\0';
  		    }
  		}
+ #ifndef NOMETAMAIL
+                 else if (in_header == CONTENT_LINE) {
+                     mime_article = nontext(art_buf+14);
+                 }
+ #endif
  	    }
  	    if (in_header == SUBJ_LINE &&
  		htype[SUBJ_LINE].ht_flags & HT_MAGIC) {
***************
*** 302,307 ****
--- 336,347 ----
  		    hide_this_line = FALSE;
  	    }
  	    else {			/* just a normal line */
+ #ifndef NOMETAMAIL
+                 if (mime_article && ! tried_display_mime)
+                     if (display_mime() == 0)
+                         return DA_NORM;
+                     else tried_display_mime = TRUE;
+ #endif
  		if (highlight==artline) {	/* this line to be highlit? */
  		    if (marking == STANDOUT) {
  #ifdef NOFIREWORKS
***************
*** 943,947 ****
--- 983,1031 ----
  	return TRUE;
      }
      return FALSE;
+ }
+ #endif
+ #ifndef NOMETAMAIL
+ nontext(content_type)
+ char *content_type;
+ {
+     char *t;
+ 
+     if (content_type[0] == '\n') return(0);
+     while (content_type && isspace(*content_type)) ++content_type;
+     t = index(content_type, ';');
+     if (!t) t = index(content_type, '\n');
+     if (t) *t-- = NULL;
+     while (t && *t && t > content_type && isspace(*t)) *t-- = NULL;
+     if (notplain(content_type)) return(1);
+     return(0);
+ }
+ 
+ notplain(s)
+ char *s;
+ {
+     char *t;
+     if (!s) return(1);
+     while (*s && isspace(*s)) ++s;
+     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
+     while (t > s && isspace(*--t)) {;}
+     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
+     if (strncmp(s, "text/plain", 10)) return(1);
+     t = (char *) index(s, ';');
+     while (t) {
+         ++t;
+         while (*t && isspace(*t)) ++t;
+         if (!strncmp(t, "charset", 7)) {
+             s = (char *) index(t, '=');
+             if (s) {
+                 ++s;
+                 while (*s && isspace(*s)) ++s;
+                 if (!strncmp(s, "us-ascii", 8)) return(0);
+             }
+             return(1);
+         }
+         t = (char *) index(t, ';');
+     }
+     return(0); /* no charset, was text/plain */
  }
  #endif
*** common.h.orig	Tue Mar  3 08:09:37 1992
--- common.h	Wed Nov 24 17:15:06 1993
***************
*** 469,474 ****
--- 469,490 ----
  #   define UNSHAR "/bin/sh"
  #endif
  
+ #ifndef NOMETAMAIL
+ /* default MIME extraction program */
+ #  ifndef MIMESTORE
+ #    define MIMESTORE "/usr/local/bin/mh/mhn -store -auto -file "
+ #  endif
+ 
+ /* default MIME show program */
+ #  ifndef MIMESHOW
+ #    ifdef SERVER
+ #       define MIMESHOW "metamail -e -p -m rn %P/rrn%a.%$"
+ #    else
+ #       define MIMESHOW "metamail -e -p -m rn %s %A"
+ #    endif
+ #  endif
+ #endif
+ 
  /* path to default editor */
  #ifndef DEFEDITOR
  #   define DEFEDITOR "/usr/ucb/vi"
***************
*** 627,632 ****
--- 643,658 ----
  #    endif
  #endif
  
+ #ifndef NOMETAMAIL
+ #  ifndef EXMIMESAVER
+ #    ifdef SERVER
+ #	define EXMIMESAVER "%e %P/rrn%a.%$"
+ #    else
+ #	define EXMIMESAVER "%e %A"
+ #    endif
+ #  endif
+ #endif
+ 
  #ifndef NORMSAVER		/* % and ~ */
  #    ifdef SERVER
  #	define NORMSAVER "%X/norm.saver %P/rrn%a.%$ %P %c %a %B %C \"%b\""
***************
*** 875,880 ****
--- 901,910 ----
      EXT char nocd[] INIT("Can't find %s\n");
  #endif
  
+ #ifndef NOMETAMAIL
+ EXT bool mime_article INIT(FALSE);
+ #endif
+  
  #ifdef NOLINEBUF
  #define FLUSH ,fflush(stdout)
  #else
*** head.h.orig	Tue Mar  3 08:10:12 1992
--- head.h	Wed Nov 24 17:04:02 1993
***************
*** 37,42 ****
--- 37,75 ----
  #define ACAT_LINE	4	/* ACategory (ClariNet) */
  #define ANPA_LINE	5	/* ANPA (ClariNet) */
  #define CODES_LINE	6	/* Codes (ClariNet) */
+ #ifndef NOMETAMAIL
+ #define CONTENT_LINE	7	/* MIME */
+ #define DIST_LINE	8	/* distribution */
+ #define DATE_LINE	9	/* date */
+ #define RECEIVED_LINE	10	/* date-received */
+ #define EXPIR_LINE	11	/* expires */
+ #define FOLLOW_LINE	12	/* followup-to */
+ #define FROM_LINE	13	/* from */
+ #define FORM_LINE	14	/* Format (ClariNet) */
+ #define KEYW_LINE	15	/* keywords */
+ #define LINES_LINE	16	/* lines */
+ #define MESSID_LINE	17	/* message-id */
+ #define NFFR_LINE	18	/* nf-from */
+ #define NFID_LINE	19	/* nf-id */
+ #define NGS_LINE	20	/* newsgroups */
+ #define NNTP_LINE	21	/* nntp-posting-host */
+ #define NOTE_LINE	22	/* Note (ClariNet) */
+ #define ORG_LINE	23	/* organization */
+ #define PATH_LINE	24	/* path */
+ #define POSTED_LINE	25	/* posted */
+ #define PVER_LINE	26	/* posting-version */
+ #define PRI_LINE	27	/* Priority (ClariNet) */
+ #define REPLY_LINE	28	/* reply-to */
+ #define REFS_LINE	29	/* references */
+ #define RVER_LINE	30	/* relay-version */
+ #define SENDER_LINE	31	/* sender */
+ #define SUMRY_LINE	32	/* summary */
+ #define SUBJ_LINE	33	/* subject */
+ #define SLUG_LINE	34	/* Slugword (ClariNet) */
+ #define XREF_LINE	35	/* xref */
+ #define XSUP_LINE	36	/* X-Supersedes (ClariNet) */
+ #define HEAD_LAST	37	/* one more than the last one above */
+ #else /* NOMETAMAIL
  #define DIST_LINE	7	/* distribution */
  #define DATE_LINE	8	/* date */
  #define RECEIVED_LINE	9	/* date-received */
***************
*** 66,71 ****
--- 99,105 ----
  #define XREF_LINE	33	/* xref */
  #define XSUP_LINE	34	/* X-Supersedes (ClariNet) */
  #define HEAD_LAST	35	/* one more than the last one above */
+ #endif NOMETAMAIL
  
  struct headtype {
      char *ht_name;		/* header line identifier */
***************
*** 97,102 ****
--- 131,139 ----
      {"acategory",	0,	0,	9,	HT_HIDE		},
      {"anpa",		0,	0,	4,	HT_HIDE		},
      {"codes",		0,	0,	5,	HT_HIDE		},
+ #ifndef NOMETAMAIL
+     {"content-type",	0,	0,	12,	HT_MAGIC	},
+ #endif
      {"distribution",	0,	0,	12,	0		},
      {"date",		0,	0,	4,	0		},
      {"date-received",	0,	0,	13,	0		},
***************
*** 110,115 ****
--- 147,155 ----
      {"nf-from",		0,	0,	7,	HT_HIDE		},
      {"nf-id",		0,	0,	5,	HT_HIDE		},
      {"newsgroups",	0,	0,	10,	HT_MAGIC|HT_HIDE},
+ #ifndef NOMETAMAIL
+     {"nntp-posting-host",	0,	0,	17,	HT_HIDE},
+ #endif
      {"note",		0,	0,	4,	0,		},
      {"organization",	0,	0,	12,	0		},
      {"path",		0,	0,	4,	HT_HIDE		},
*** respond.c.orig	Tue Mar  3 08:09:51 1992
--- respond.c	Wed Nov 24 16:40:04 1993
***************
*** 70,76 ****
--- 70,80 ----
  #ifdef ASYNC_PARSE
      parse_maybe(art);
  #endif
+ #ifndef NOMETAMAIL
+     savefrom = (!mime_article && (cmd == 'w' || cmd == 'e') ?
htype[PAST_HEADER].ht_minpos : 0);
+ #else
      savefrom = (cmd == 'w' || cmd == 'e' ? htype[PAST_HEADER].ht_minpos : 0);
+ #endif
      if (artopen(art) == Nullfp) {
  #ifdef VERBOSE
  	IF(verbose)
***************
*** 174,179 ****
--- 178,203 ----
  	    while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
  		if (*art_buf <= ' ')
  		    continue;	/* Ignore empty or initially-whitespace lines */
+ #ifndef NOMETAMAIL
+ 		if (mime_article) {
+ 		    if (!custom_extract) {
+ 		      printf("Extracting MIME article into %s:\n", s) FLUSH;
+ 		      extractprog = savestr(filexp(getval("MIMESTORE",MIMESTORE)));
+ 		    }
+ 		    else
+ 		      printf("Extracting MIME article into %s using %s\n",
+ 		      s, extractprog) FLUSH;
+ 		    cnt = 0;
+ 		    interp(cmd_buf,(sizeof cmd_buf),getval("EXMIMESAVER",EXMIMESAVER));
+ 		    termlib_reset();
+ 		    resetty();		/* restore tty state */
+ 		    doshell(SH,cmd_buf);
+ 		    noecho();		/* revert to cbreaking */
+ 		    crmode();
+ 		    termlib_init();
+ 		    break;
+ 		}
+ #endif
  		if (found_cut && custom_extract) {
  		    printf("Extracting data into %s using %s:\n",
  			s, extractprog) FLUSH;
1.23	MM (Mail system from Columbia University)

All of the changes are localized to the file more.c.  In particular,
there are two changes to be made:

1.  In more.c, there is a routine called "display_message".  In my
version it starts on line 151.  A few lines down in that routine, you'll
find something that looks like this:


    if (use_crt_filter_always ||
	(logical_lines (msg, cmcsb._cmrmx) +1 >= cmcsb._cmrmx)) {
	fp = more_pipe_open(out);
    }

Immediately BEFORE that fragment (i.e. between line 167 and line 168, in
my version) insert the following code:

#ifndef NOMETAMAIL
    if (!nontext(msg) || (fp = mm_popen ("metamail -m MM -p -R", "w")) == NULL)
#endif

2.  At the very end of more.c, add the following new routines, which are
used by the patch above:

#ifndef NOMETAMAIL
nontext(msg) 
char *msg;
{
    char *s, *ct=NULL, *ctend;
    int LineStart=1;

    for (s=msg; *s; ++s) {
        if (LineStart) {
            if (*s == '\n') break; /* end of headers */
            LineStart = 0;
            if (!lc2strncmp(s, "content-type:", 13)) {
                ct=s + 13;
                break; /* found it */
            }
        } else if (*s == '\n') {
            LineStart = 1;
        }
    }
    if (!ct) return 0;
    ctend = ct;
    while (1) {
        ctend = index(ct, '\n');
        if (!ctend) break;/* use whole thing */
        if (*(ctend+1) == ' ' || *(ctend+1) == '\t') {
            ++ctend; /* keep looking */
        } else {
            break;
        }
    }
    *ctend = NULL;
    if (notplain(ct)) {
        *ctend = '\n';
        return(1);
    }
    *ctend = '\n';
    return(0);
}

notplain(s)
char *s;
{
    char *t;
    if (!s) return(1);
    while (*s && isspace(*s)) ++s;
    for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
    while (t > s && isspace(*--t)) {;}
    if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
    if (strncmp(s, "text/plain", 10)) return(1);
    t = (char *) index(s, ';');
    while (t) {
        ++t;
        while (*t && isspace(*t)) ++t;
        if (!strncmp(t, "charset", 7)) {
            s = (char *) index(t, '=');
            if (s) {
                ++s;
                while (*s && isspace(*s)) ++s;
                if (!strncmp(s, "us-ascii", 8)) return(0);
            }
            return(1);
        }
        t = (char *) index(t, ';');
    }
    return(0); /* no charset, was text/plain */
}

lc2strncmp(s1, s2, len)
char *s1, *s2;
int len;
{
    if (!s1 || !s2) return (-1);
    while (*s1 && *s2 && len > 0) {
	if (*s1 != *s2 && (tolower(*s1) != *s2)) return(-1);
	++s1; ++s2; --len;
    }
    if (len <= 0) return(0);
    return((*s1 == *s2) ? 0 : -1);
}
#endif

These two changes should be all you need to do in order to make MM work
with metamail, assuming that you already have the "metamail" executable
installed somewhere on your search path.
