home *** CD-ROM | disk | FTP | other *** search
/ The AGA Experience 2 / agavol2.iso / software / utilities / comms / metamail-2.3a / mailers.txt < prev    next >
Text File  |  1992-05-21  |  86KB  |  2,653 lines

  1.  
  2.  
  3. Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
  4.  
  5. Permission to use, copy, modify, and distribute this material  for any
  6. purpose and without fee is hereby granted, provided  that the above
  7. copyright notice and this permission notice  appear in all copies, and
  8. that the name of Bellcore not be  used in advertising or publicity
  9. pertaining to this  material without the specific, prior written
  10. permission  of an authorized representative of Bellcore.  BELLCORE 
  11. MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY  OF THIS
  12. MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",  WITHOUT ANY EXPRESS
  13. OR IMPLIED WARRANTIES.
  14.  
  15.  
  16. 1    Adding Diverse Multimedia Format Support to Established RFC822 Mail
  17. and Bulletin Board Readers
  18.  
  19.                          Nathaniel S. Borenstein
  20.                        <nsb@thumper.bellcore.com>
  21.                                 Bellcore
  22.  
  23. Abstract
  24.  
  25. It is surprisingly easy to use the RFC1049 "Content-type" header to turn
  26. virtually any mail reading interface into a multi-media mail reading
  27. interface.  Mail readers are simply modified to use the new "metamail"
  28. program whenever they receive non-text mail.  The metamail program is
  29. itself easily customizable by the use of a "mailcap" file that specifies
  30. the media types supported by a given site or user.  Given the existence
  31. of the metamail program,  this document explains how to add multimedia
  32. support to sixteen very different mail reading programs, including all
  33. of the most popular UNIX mail reading programs and (so far) one DOS mail
  34. reading program.  
  35.  
  36. Motivation
  37.  
  38. Multimedia mail has been a long time coming.  The biggest impediments to
  39. multimedia mail have been the absence of standards and the pain users
  40. experience when they convert to a new mail system.   Thus, for example,
  41. to receive the benefits of the rich multimedia capabilities of the
  42. Andrew mail format, you have, in the past, had to convert from whatever
  43. other mailer you're using to one of the Andrew-based mailers.
  44.  
  45. In the context of a new research project (the MAGICMAIL language for
  46. active messaging) the author of this document has discovered that it is
  47. remarkably easy to extend nearly any mail reading interface so that it
  48. recognizes certain "Content-type" headers and, when it receives mail
  49. with such headers, calls an appropriate external program to display or
  50. interpret the mail body.  Because this functionality seems so generally
  51. useful, I have taken the time to create this document, in the hope that
  52. it will help to make various kinds of multimedia mail functionality more
  53. widely available.
  54.  
  55. The ideas and code in this system apply equally well to both mail and
  56. bulletin board programs.  Although this document talks mostly about
  57. mail, it applies equally to bulletin board readers, and in fact one
  58. bulletin board reading program (the Berkeley "msgs" program) is included
  59. in the set of programs discussed here.
  60. The Basic Idea
  61.  
  62. Basically, there are only two things you have to do to each mail reading
  63. program:
  64.  
  65. 1.  Make the mail reader notice the special header ("Content-type") that
  66. marks a message as a non-text message.  (In the case of mail readers
  67. that already understand certain content-types, such as Andrew, the mail
  68. reader must be modified only to deal with the content-types it does not
  69. already know how to handle.
  70.  
  71. 2.  When the special header appears, instead of (or, if it's much
  72. easier, in addition to) showing the user the body of the message, the
  73. mail reader must send that body off to the metamail interpreter.  The
  74. metamail interpreter includes features that deal with the diverse
  75. situations of terminal-oriented and window-oriented mail readers.
  76.  
  77. Beyond this, of course, you have to make sure that the appropriate
  78. interpreters are available on your users' search paths.  In particular,
  79. you'll need the metamail binary, an appropriately-configured mailcap
  80. file, and interpreters for whatever mail formats you support locally.  
  81.  
  82. For information on how to use the metamail program or how to customize a
  83. mailcap file, see the metamail program's manual entry.  This document
  84. will describe the specific patches that can be used to make certain
  85. established mail-reading programs work with metamail.
  86. A Variety of Mail and Bulletin Board Reading Interfaces 
  87.  
  88. With this document, you can patch all of your site's mail reading
  89. interfaces to support whatever multimedia formats are deemed useful at
  90. your site.  This means that those who regularly use the multimedia tools
  91. can begin to send mail in those formats freely, without worrying about
  92. the ability of any local user to interpret the mail.  It is my intent to
  93. make this document exhaustive; as time goes on, I hope it will grow to
  94. include an ever widening set of mail reading interfaces.  Currently it
  95. includes all of the mail reading interfaces that I know to be in use
  96. anywhere in Bellcore's research laboratories.
  97.  
  98. Currently this document describes how to add support for the following
  99. mail readers:
  100.  
  101.       Berkeley Mail (/usr/ucb/Mail, /usr/ucb/mail, and Tahoe mail)
  102.       SunMail (another version of Berkeley mail, but rather different)
  103.       Xmail (an X11 interface to Berkeley mail)
  104.       Mailtool (a SunTools interface to Berkeley mail)
  105.       Imail (Bellcore MICE mailer)
  106.       PCS readmail/rdmail/sreadmail (another Bellcore mailer)
  107.       MH -- Rand Message Handling System
  108.       XMH -- X11 Interface to Rand Message Handling System
  109.       Rmail -- GNU Emacs mail reading package
  110.       VM -- Another GNU Emacs mail reading package
  111.       MH-E -- Yet another GNU Emacs mail reading package (GNU interface to MH)
  112.       CUI -- Andrew low-end mail reader 
  113.       VUI -- Andrew termcap-based mail reader
  114.       Messages -- Andrew multimedia mail reader
  115.       BatMail -- Andrew Emacs mail-reading interface
  116.       Elm -- Mail reader from HP.
  117.       Mush -- Yet another popular mail reader
  118.       Msgs -- simple Berkeley bulletin board reader
  119.       UUPC --a mail reading program for MS-DOS
  120.  
  121. If you have mail readers that are not dicussed here, you will still
  122. probably find some of this code useful as a model.  If you develop a
  123. patch for some other mail reader, and you send it back to me, I'll
  124. include it in future versions of this document.
  125.  
  126. NOTE:  For the programs that were written in C, all of the patches are
  127. surrounded by "#ifndef NOMETAMAIL/#endif".  This was done so that a
  128. single source could easily be maintained even for use at those sites
  129. that for some reason desire to inhibit the metamail functionality.  Such
  130. sites need only compile with "-DNOMETAMAIL" to have the modified mailers
  131. behave exactly like their unmodified predecessors.
  132.  
  133. ADDITIONAL NOTE:  Most of these patches send a message to metamail if it
  134. has ANY content-type header.  Technically, this is not necessary with a
  135. content-type such as
  136.  
  137. Content-type: text/plain
  138.  
  139. or
  140.  
  141. Content-type: text/plain; charset=US-ASCII
  142.  
  143. However, this can't just be a literal string comparison -- the full MIME
  144. content-type syntax must be parsed and the "charset" value checked.  For
  145. example, you WOULD want to pass the following to metamail:
  146.  
  147. Content-type: text/plain; charset=iso-8859-8
  148.  
  149. However, there is not really a need to pass the following to metamail:
  150.  
  151. Content-type: text/plain; something-else=foobar; charset=us-ascii
  152.  
  153. Instead of adding the content-type parsing to each mailer, most of the
  154. patches below simply call metamail for any message with a content-type
  155. header.  This is a bit inefficient for plain ascii text, but most such
  156. messages probably won't have content-type headers anyway, and it makes
  157. the patches much simpler.  Programmers incorporating these patches into
  158. "production" versions of mail readers to be released to the world might
  159. consider parsing the content-type header in full.
  160.  
  161. 1.1    Berkeley Mail (/usr/ucb/[Mm]ail, and Tahoe mail)
  162.  
  163. NOTE:  If you don't have the sources for Berkeley mail, you can get
  164. about 95% of metamail functionality to work by setting your PAGER
  165. environment to "metamail" and reading everything with the pager (e.g. by
  166. using "more" instead of "type".)   Alternately, you can just put the
  167. following two lines in your .mailrc file:
  168.  
  169.     set PAGER=metamail
  170.     set crt=1
  171.  
  172. Alternately, you can modify NOTHING AT ALL, and can still read non-text
  173. message by simply typing  "| metamail" in response to the "&" prompt.
  174.  
  175. However, things will work slightly better if you can modify the source files.
  176.  
  177. There are several versions of Berkeley mail; this describes the patch to
  178. the versions I happened to have.  
  179.  
  180. All of the changes are localized to the file cmd1.c  However, the way
  181. that the Mail program handles output piped to "more" (or some other
  182. paging program) makes the patch about twice as complicated as it
  183. otherwise would be.
  184.  
  185. At any rate, there are four changes:
  186.  
  187. 1.  In cmd1.c, there is a routine called "type1" -- in my version it
  188. starts on line 321.  At the end of the declarations at the top of this
  189. routine, there is a line that says:
  190.  
  191.     FILE *ibuf, *obuf;
  192.  
  193. (In some versions, the "*ibuf" is omitted.  That's fine.)  Immediately
  194. after that line, add the following declaration:
  195.  
  196. #ifndef NOMETAMAIL
  197.     int PipeToMore = 0;
  198. #endif
  199.  
  200. 2.  In the same routine, about 18 lines down from the previous patch,
  201. there is a code fragment that says:
  202.  
  203.             cp = value("PAGER");
  204.             if (cp == NULL || *cp == '\0')
  205.                 cp = MORE;
  206.             obuf = popen(cp, "w");
  207.             if (obuf == NULL) {
  208.                 perror(cp);
  209.                 obuf = stdout;
  210.             }
  211.             else {
  212.                 pipef = obuf;
  213.                 sigset(SIGPIPE, brokpipe);
  214.             }
  215.  
  216. You need to put three new lines before this code fragment and one new
  217. line after it.  The end result should look like this:
  218.  
  219. #ifndef NOMETAMAIL
  220.             PipeToMore = 1;
  221. #else
  222.             cp = value("PAGER");
  223.             if (cp == NULL || *cp == '\0')
  224.                 cp = MORE;
  225.             obuf = popen(cp, "w");
  226.             if (obuf == NULL) {
  227.                 perror(cp);
  228.                 obuf = stdout;
  229.             }
  230.             else {
  231.                 pipef = obuf;
  232.                 sigset(SIGPIPE, brokpipe);
  233.             }
  234. #endif
  235.  
  236. Note that in some versions of UNIX, the code will say "signal" instead
  237. of "sigset" -- you should use whichever is used in your original.  In
  238. some other variants (notably Ultrix) this code is significantly
  239. different.  What matters is that the code that sets up the pager be
  240. preceded by the #ifndef ... #else lines, and followed by the #endif line.
  241.  
  242. 3.  About 8 lines further down in that routine, you'll find something
  243. that looks like this:
  244.  
  245.     for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
  246.         mesg = *ip;
  247.         touch(mesg);
  248.         mp = &message[mesg-1];
  249.         dot = mp;
  250.         print(mp, obuf, doign);
  251.  
  252. In the Tahoe version, there is another line before the "print" line:
  253.         if (value("quiet") == NOSTR)
  254.             fprintf(obuf, "Message %d:\n", mesg);
  255.  
  256. THIS LINE SHOULD BE ADDED IF IT DOES NOT ALREADY EXIST!  Otherwise xmail
  257. may behave badly with the modified mail program.
  258.  
  259. The "print" line is, in my version of Berkeley mail, line 367, and in my
  260. version of Tahoe mail, line 343.  REPLACE the "print" line with the
  261. following code:
  262.  
  263. #ifndef NOMETAMAIL
  264. #include <sgtty.h>
  265.         if (!getenv("NOMETAMAIL") && nontext(mp)) {
  266.             char Fname[100], Cmd[120];
  267.             FILE *fp;
  268.             int code;
  269.             struct sgttyb ttystatein, ttystateout;
  270.  
  271.             sprintf(Fname, "/tmp/mail-metamail.%d.%d", getpid(), getuid());
  272.             fp = fopen(Fname, "w");
  273.             if (!fp) {
  274.             perror(Fname);
  275.             } else {
  276.             send(mp, fp, 0);
  277.             fclose(fp);
  278.             sprintf(Cmd, "metamail -z %s -m Mail %s", (PipeToMore) ? "-p" : "", Fname);
  279.             if (obuf != stdout) {
  280.                 pipef = NULL;
  281.                 pclose(obuf);
  282.                 obuf = stdout;
  283.             }
  284.             gtty(fileno(stdin), &ttystatein);
  285.             gtty(fileno(stdout), &ttystateout);
  286.             code = system(Cmd);
  287.             stty(fileno(stdin), &ttystatein);
  288.             stty(fileno(stdout), &ttystateout);
  289. /*            The following line would cause the raw mail to print out if
  290. metamail failed */
  291. /*            if (code) print(mp, obuf, doign); */
  292.             }
  293.         } else {
  294.             if (PipeToMore && stdout == obuf) {
  295.             obuf = popen(MORE, "w");
  296.             if (obuf == NULL) {
  297.                 perror(MORE);
  298.                 obuf = stdout;
  299.             }
  300.             else {
  301.                 files[fileno(obuf)].filep = obuf;
  302.                 files[fileno(obuf)].flags = PIPE_OPEN & ~KEEP_OPEN;
  303.                 pipef = obuf;
  304.                 sigset(SIGPIPE, brokpipe);
  305.             }
  306.             }
  307.             print(mp, obuf, doign);
  308.         }
  309. #else
  310.         print(mp, obuf, doign);
  311. #endif
  312.  
  313. Note that in some versions, it will say "send" instead of "print" -- use
  314. whichever routine name is used in your version.  Note also that on some
  315. versions of UNIX, you will need to use "signal" instead of "sigset". 
  316. Finally, in some versions of the sources, the "files" array does not
  317. exist, and the two lines that refer to it can simply be eliminated.
  318.  
  319. IMPORTANT ADDITIONAL NOTE:  IN SOME VERSIONS OF MAIL, the "send"
  320. procedure requires a fourth parameter.  This should be set to NOSTR, as
  321. in
  322.  
  323.             send(mp, fp, 0, NOSTR);
  324.  
  325. 4.  At the very end of cmd1.c, add the following new routines:
  326.  
  327. #ifndef NOMETAMAIL
  328. #include <ctype.h>
  329.  
  330. nontext(mp) 
  331. struct message *mp;
  332. {
  333.     long c;
  334.     FILE *ibuf;
  335.     char line[LINESIZE], *s, *t;
  336.  
  337.     ibuf = setinput(mp);
  338.     c = mp->m_size;
  339.     while (c > 0L) {
  340.     fgets(line, LINESIZE, ibuf);
  341.     c -= (long) strlen(line);
  342.     if (line[0] == '\n') return(0);
  343.     for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  344.     if (!strncmp(line, "content-type:", 13) && notplain(line+13)) return(1);
  345.     }
  346.     return(0);
  347. }
  348. notplain(s)
  349. char *s;
  350. {
  351.     char *t;
  352.     if (!s) return(1);
  353.     while (*s && isspace(*s)) ++s;
  354.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  355.     while (t > s && isspace(*--t)) {;}
  356.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  357.     if (strncmp(s, "text/plain", 10)) return(1);
  358.     t = (char *) index(s, ';');
  359.     while (t) {
  360.         ++t;
  361.         while (*t && isspace(*t)) ++t;
  362.         if (!strncmp(t, "charset", 7)) {
  363.             s = (char *) index(t, '=');
  364.             if (s) {
  365.                 ++s;
  366.                 while (*s && isspace(*s)) ++s;
  367.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  368.             }
  369.             return(1);
  370.         }
  371.         t = (char *) index(t, ';');
  372.     }
  373.     return(0); /* no charset, was text/plain */
  374. }
  375. #endif
  376.  
  377. These two changes should be all you need to do in order to make Berkeley
  378. mail work with metamail, assuming that you already have the "metamail"
  379. binary installed somewhere on your search path.
  380. 1.2    Sun Mail (another version of Berkeley mail)
  381.  
  382. Sun's version of Berkeley mail is sufficiently different from the others
  383. to warrant a separate section.  Patching the "Mail" program properly
  384. will almost automatically add metamail support to mailtool and xmail,
  385. because they simply call Mail.  However, some further changes are
  386. necessary for those programs, as noted in subsequent sections.
  387.  
  388. All of the changes are localized to two files, cmd1.c and cmd2.c  In
  389. particular, there are four changes:
  390.  
  391. 1.  In cmd1.c, there is a routine called "type1" -- in my version it
  392. starts on line 317.  At the end of the declarations at the top of this
  393. routine, there is a pair of lines that say:
  394.  
  395.     FILE *ibuf, *obuf;
  396.     void (*saveint)();
  397.  
  398. Immediately after that line, add the following declaration:
  399.  
  400. #ifndef NOMETAMAIL
  401.     int PipeToMore = 0;
  402. #endif
  403.  
  404. 2.  In the same routine, about 18 lines down from the previous patch,
  405. there is a code fragment that says:
  406.  
  407.             obuf = popen(MORE, "w");
  408.             if (obuf == NULL) {
  409.                 perror(MORE);
  410.                 obuf = stdout;
  411.             }
  412.             else {
  413.                 pipef = obuf;
  414.                 sigset(SIGPIPE, brokpipe);
  415.             }
  416.  
  417. You need to put three new lines before this code fragment and one new
  418. line after it.  The end result should look like this:
  419.  
  420. #ifndef NOMETAMAIL
  421.             PipeToMore = 1;
  422. #else
  423.             obuf = popen(MORE, "w");
  424.             if (obuf == NULL) {
  425.                 perror(MORE);
  426.                 obuf = stdout;
  427.             }
  428.             else {
  429.                 pipef = obuf;
  430.                 sigset(SIGPIPE, brokpipe);
  431.             }
  432. #endif
  433.  
  434. 3.  About 8 lines further down in that routine, you'll find something
  435. that looks like this:
  436.  
  437.     for (ip = msgvec; *ip && ip-msgvec < msgCount; ip++) {
  438.         mesg = *ip;
  439.         touch(mesg);
  440.         mp = &message[mesg-1];
  441.         dot = mp;
  442.         print(mp, obuf, doign);
  443.  
  444.  
  445. The "print;" line is, in my version, line 363.   REPLACE the "print"
  446. line with the following code:
  447.  
  448. #ifndef NOMETAMAIL
  449. #include <sgtty.h>
  450.         if (!getenv("NOMETAMAIL") && nontext(mp)) {
  451.             char Fname[100], Cmd[120];
  452.             FILE *fp;
  453.             int code;
  454.             struct sgttyb ttystatein, ttystateout;
  455.  
  456.             sprintf(Fname, "/tmp/mail-metamail.%d.%d", getpid(), getuid());
  457.             fp = fopen(Fname, "w");
  458.             if (!fp) {
  459.             perror(Fname);
  460.             } else {
  461.                      if (value("quiet") == NOSTR)
  462.                             fprintf(obuf, "Message %2d:\n", mp -
  463. &message[0] + 1);
  464.             msend(mp, fp, 0, fputs);
  465.             fclose(fp);
  466.             sprintf(Cmd, "metamail -z %s -m Mail %s", (PipeToMore) ? "-p" : "", Fname);
  467.             if (obuf != stdout) {
  468.                 pipef = NULL;
  469.                 pclose(obuf);
  470.                 obuf = stdout;
  471.             }
  472.             gtty(fileno(stdin), &ttystatein);
  473.             gtty(fileno(stdout), &ttystateout);
  474.             code = system(Cmd);
  475.             stty(fileno(stdin), &ttystatein);
  476.             stty(fileno(stdout), &ttystateout);
  477. /*            if (code) print(mp, obuf, doign); */
  478.             }
  479.         } else {
  480.             if (PipeToMore && obuf == stdout) {
  481.             obuf = popen(MORE, "w");
  482.             if (obuf == NULL) {
  483.                 perror(MORE);
  484.                 obuf = stdout;
  485.             }
  486.             else {
  487.                 pipef = obuf;
  488.                 sigset(SIGPIPE, brokpipe);
  489.             }
  490.             }
  491.             print(mp, obuf, doign);
  492.         }
  493. #else
  494.             print(mp, obuf, doign);
  495. #endif
  496.  
  497. 2.  At the very end of cmd1.c, add the following new routines:
  498.  
  499. #ifndef NOMETAMAIL
  500. #include <ctype.h>
  501.  
  502. nontext(mp) 
  503. struct message *mp;
  504. {
  505.     long c;
  506.     FILE *ibuf;
  507.     char line[LINESIZE], *s, *t;
  508.  
  509.     ibuf = setinput(mp);
  510.     c = mp->m_size;
  511.     while (c > 0L) {
  512.     fgets(line, LINESIZE, ibuf);
  513.     c -= (long) strlen(line);
  514.     if (line[0] == '\n') return(0);
  515.     for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  516.     if (!strncmp(line, "content-type:", 13) && notplain(line+13)) return(1);
  517.     }
  518.     return(0);
  519. }
  520. notplain(s)
  521. char *s;
  522. {
  523.     char *t;
  524.     if (!s) return(1);
  525.     while (*s && isspace(*s)) ++s;
  526.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  527.     while (t > s && isspace(*--t)) {;}
  528.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  529.     if (strncmp(s, "text/plain", 10)) return(1);
  530.     t = (char *) index(s, ';');
  531.     while (t) {
  532.         ++t;
  533.         while (*t && isspace(*t)) ++t;
  534.         if (!strncmp(t, "charset", 7)) {
  535.             s = (char *) index(t, '=');
  536.             if (s) {
  537.                 ++s;
  538.                 while (*s && isspace(*s)) ++s;
  539.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  540.             }
  541.             return(1);
  542.         }
  543.         t = (char *) index(t, ';');
  544.     }
  545.     return(0); /* no charset, was text/plain */
  546. }
  547. #endif
  548.  
  549. 3.  This step is required only if you want the changes to work right
  550. with Sun's MailTool program.  In cmd2.c, there is a routine called
  551. "savemsglist" -- in my version it starts on line 187.  About a page down
  552. in that routine, you'll find a line that looks like this:
  553.  
  554.         mp = &message[mesg-1];
  555.  
  556. This line is, in my version, line 215.  Immediately following that line
  557. (i.e. between line 215 and line 216, in my version) add the following
  558. code:
  559.  
  560. #ifndef NOMETAMAIL
  561. #include <sgtty.h>
  562.         if (!mark && !getenv("NOMETAMAIL") && nontext(mp)) {
  563.             char Fname[100], Cmd[120];
  564.             FILE *fp;
  565.             int code;
  566.             struct sgttyb ttystatein, ttystateout;
  567.  
  568.             sprintf(Fname, "/tmp/mail-metamail.%d.%d", getpid(), getuid());
  569.             fp = fopen(Fname, "w");
  570.             if (!fp) {
  571.             perror(Fname);
  572.             } else {
  573.             msend(mp, fp, 0, fputs);
  574.             fclose(fp);
  575.             fclose(obuf); /* To allow appending, sigh */
  576.             sprintf(Cmd, "metamail -z -m Mail %s >> %s", Fname, file);
  577.             gtty(fileno(stdin), &ttystatein);
  578.             gtty(fileno(stdout), &ttystateout);
  579.             code = system(Cmd); /* ignore it if it fails */
  580.             stty(fileno(stdin), &ttystatein);
  581.             stty(fileno(stdout), &ttystateout);
  582.             if ((obuf = fopen(file, "a")) == NULL) {
  583.                 perror("");
  584.                 return;
  585.             }
  586.             continue;
  587.             }
  588.         }
  589. #endif
  590.  
  591. These three changes should be all you need to do in order to make Sun's
  592. version of Berkeley mail work with metamail, assuming that you already
  593. have the metamail binary installed somewhere on your search path.
  594. 1.3    Xmail (X11 Interface to Berkeley Mail)
  595.  
  596. Xmail is a program that provides a graphical interface to Berkeley mail.
  597.  It does most of its work by talking to the Berkeley mail program
  598. itself.  Thus, if you want Xmail to work with metamail, you have to
  599. first of all upgrade your version of the Berkeley Mail program as
  600. described in the previous sections.
  601.  
  602. Once you have done this, everything would work fine automatically except
  603. that there's no way for metamail to tell that it has been called
  604. non-interactively.  You can tell it that this is the case (so that it
  605. won't try to ask any questions) by setting the MM_NOTTTY environment
  606. variable.  Thus, a simple way to fix xmail would be to rename the
  607. "xmail" program to be "xmail.std" and then to install a new "xmail"
  608. program that looked something like this:
  609.  
  610.     #!/bin/csh -f
  611.  
  612.     setenv MM_NOTTTY 1
  613.     setenv MM_NOASK 1
  614.     setenv MM_TRANSPARENT 1
  615.     setenv XMAILER /usr/local/bin/Mail
  616.     xmail.std $*
  617.  
  618. The MM_NOASK variable is also set to avoid lots of terminal windows
  619. popping up to ask questions that are only being asked in the hope of
  620. saving you time if you don't want to run a handling program.
  621.  
  622. This should make xmail work with metamail, although, unlike most of the
  623. modified mailers described in this document, it will not ask the users
  624. for confirmation before running metamail applications.
  625. 1.4    Mailtool (SunTools Interface to Berkeley Mail)
  626.  
  627. Mailtool is a program that provides a graphical interface to Berkeley
  628. mail.  It does most of its work by talking to the Berkeley mail program
  629. itself.  Thus, if you want mailtool to work with metamail, you have to
  630. first of all upgrade your version of the Berkeley Mail program as
  631. described in the previous sections.
  632.  
  633. Once you have done this, everything would work fine automatically except
  634. that there's no way for metamail to tell that it has been called
  635. non-interactively.  You can tell it that this is the case (so that it
  636. won't try to ask any questions) by setting the MM_NOTTTY environment
  637. variable.  Thus, a simple way to fix mailtool would be to rename the
  638. "mailtool" program to be "mailtool.std" and then to install a new
  639. "mailtool" program that looked something like this:
  640.  
  641.     #!/bin/csh -f
  642.  
  643.     setenv MM_NOTTTY 1
  644.     setenv MM_NOASK 1
  645.     mailtool.std $*
  646.  
  647. The MM_NOASK variable is also set to avoid lots of terminal windows
  648. popping up to ask questions that are only being asked in the hope of
  649. saving you time if you don't want to run a handling program.
  650.  
  651. This should make mailtool work with metamail, although, unlike most of
  652. the modified mailers described in this document, it will not ask the
  653. users for confirmation before running metamail applications.
  654. 1.5    Imail (Bellcore MICE mailer)
  655.  
  656. All of the changes are localized to the file util.c  In particular,
  657. there are two changes to be made:
  658.  
  659. 1.  In util.c, there is a routine called "listit" -- in my version it
  660. starts on line 82.  The first line of that routine looks like this:
  661.  
  662.     debug("about to listit, %d lines\n",nlines);
  663.  
  664. Immediately following that line (i.e. between line 88 and line 89, in my
  665. version) add the following code:
  666.  
  667. #ifndef NOMETAMAIL
  668. #include <sgtty.h>
  669.     if (!getenv("NOMETAMAIL") && nontext(fp)) { 
  670.         struct sgttyb ttystatein, ttystateout;
  671.         char Fname[100], Cmd[100], linebuf[1000];
  672.         FILE *fp2;
  673.         int chars = 0, code;
  674.  
  675.         sprintf(Fname, "/tmp/imail-metamail.%d.%d", getpid(), getuid());
  676.         fp2 = fopen(Fname, "w");
  677.         if (!fp2) {
  678.         perror(Fname);
  679.         } else {
  680.         while( chars < p->len && fgets(linebuf,sizeof(linebuf),fp) != NULL) {
  681.             fputs(linebuf,fp2);
  682.             chars += strlen(linebuf);
  683.         }
  684.         fclose(fp2);
  685.         sprintf(Cmd, "metamail -p -R -z -m imail %s", Fname);
  686.         gtty(fileno(stdin), &ttystatein);
  687.         gtty(fileno(stdout), &ttystateout);
  688.         code = system(Cmd);
  689.         stty(fileno(stdin), &ttystatein);
  690.         stty(fileno(stdout), &ttystateout);
  691.         /* if (!code) return(0); */
  692.         return(0); /* Don't show raw datastream anyway! */
  693.         }
  694.     }
  695. #endif
  696.  
  697. 2.  After the "listit" procedure, or at the very end of util.c,
  698. whichever you prefer, add the following new routines, which are used by
  699. the patch above:
  700.  
  701. #ifndef NOMETAMAIL
  702. #include <ctype.h>
  703.  
  704. nontext(fp)
  705. FILE *fp;
  706. {
  707.     char buf[1000], *s;
  708.     int oldnl=nl, oldcnt=cnt, oldfptr, retval = 0;
  709.  
  710.     oldfptr = ftell(fp);
  711.     while( cnt < nlines && fgets(buf,sizeof(buf),fp) != NULL) {
  712.     if (buf[0] == '\n') break;
  713.     for (s=buf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  714.        if (!strncmp(buf, "content-type:", 13) && notplain(buf+13)) {
  715.            retval = 1;
  716.            break;
  717.        }
  718.     nl++;
  719.     cnt++;
  720.     }
  721.     nl = oldnl;
  722.     cnt = oldcnt;
  723.     fseek(fp, oldfptr, 0);
  724.     return(retval);
  725. }
  726. notplain(s)
  727. char *s;
  728. {
  729.     char *t;
  730.     if (!s) return(1);
  731.     while (*s && isspace(*s)) ++s;
  732.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  733.     while (t > s && isspace(*--t)) {;}
  734.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  735.     if (strncmp(s, "text/plain", 10)) return(1);
  736.     t = (char *) index(s, ';');
  737.     while (t) {
  738.         ++t;
  739.         while (*t && isspace(*t)) ++t;
  740.         if (!strncmp(t, "charset", 7)) {
  741.             s = (char *) index(t, '=');
  742.             if (s) {
  743.                 ++s;
  744.                 while (*s && isspace(*s)) ++s;
  745.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  746.             }
  747.             return(1);
  748.         }
  749.         t = (char *) index(t, ';');
  750.     }
  751.     return(0); /* no charset, was text/plain */
  752. }
  753. #endif
  754.  
  755. These two changes should be all you need to do in order to make imail
  756. work with metamail, assuming that you already have the metamail binary
  757. installed somewhere on your search path.
  758. 1.6    PCS readmail/rdmail
  759.  
  760. All of the changes are localized to the file display.c  In particular,
  761. there are two changes to be made:
  762.  
  763. 1.  In display.c, there is a routine called "display".  The first line
  764. of that routine looks like this:
  765.  
  766.     messnum = messord[messnum];        /* convert to internal number */
  767.  
  768. Immediately following that line (i.e. between 13 and 14 in my version)
  769. add the following code:
  770.  
  771. #ifndef NOMETAMAIL
  772. {
  773. #include <sgtty.h>
  774.    if (!getenv("NOMETAMAIL") && nontext(messnum)) { 
  775.        char Fname[100], Cmd[100], linebuf[1000];
  776.        FILE *fp2;
  777.        int code;
  778.        struct sgttyb ttystatein, ttystateout; 
  779.  
  780.        sprintf(Fname, "/tmp/readmail-metamail.%d.%d.%d", getpid(), getuid());
  781.        fp2 = fopen(Fname, "w");
  782.        if (!fp2) {
  783.        perror(Fname);
  784.        } else {
  785.        fseek(curr.fp,messbeg[messnum],0);
  786.        while (fgets (linebuf, sizeof(linebuf), curr.fp) != NULL &&
  787. ftell(curr.fp) <= messend[messnum]) {
  788.            fputs(linebuf, fp2);
  789.        }
  790.        fclose(fp2);
  791.        sprintf(Cmd, "metamail -m readmail -z %s %s", profile("page") ? "-p"
  792. : "", Fname);
  793.        gtty(fileno(stdin), &ttystatein);
  794.        gtty(fileno(stdout), &ttystateout);
  795.        code = system(Cmd);
  796.        stty(fileno(stdin), &ttystatein);
  797.        stty(fileno(stdout), &ttystateout);
  798.        /* if (!code)) */ return(0);
  799.        }
  800.    }
  801. }
  802. #endif
  803.  
  804. 2.  At the very end of display.c, add the following new routines, which
  805. are used by the patch above:
  806.  
  807. #ifndef NOMETAMAIL
  808. #include <ctype.h>
  809.  
  810. nontext(messnum) 
  811. int messnum;
  812. {
  813.     char buf[1000], *s;
  814.  
  815.     fseek(curr.fp,messbeg[messnum],0);
  816.     while (fgets (buf, sizeof(buf), curr.fp) != NULL && ftell(curr.fp) <
  817. messend[messnum]) {
  818.     if (buf[0] == '\n') return(0);
  819.     for (s=buf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  820.     if (!strncmp(buf, "content-type:", 13) && notplain(buf+13)) return(1);
  821.     }
  822.     return(0);
  823. }
  824. notplain(s)
  825. char *s;
  826. {
  827.     char *t;
  828.     if (!s) return(1);
  829.     while (*s && isspace(*s)) ++s;
  830.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  831.     while (t > s && isspace(*--t)) {;}
  832.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  833.     if (strncmp(s, "text/plain", 10)) return(1);
  834.     t = (char *) index(s, ';');
  835.     while (t) {
  836.         ++t;
  837.         while (*t && isspace(*t)) ++t;
  838.         if (!strncmp(t, "charset", 7)) {
  839.             s = (char *) index(t, '=');
  840.             if (s) {
  841.                 ++s;
  842.                 while (*s && isspace(*s)) ++s;
  843.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  844.             }
  845.             return(1);
  846.         }
  847.         t = (char *) index(t, ';');
  848.     }
  849.     return(0); /* no charset, was text/plain */
  850. }
  851. #endif
  852.  
  853. These two changes should be all you need to do in order to make pcs
  854. readmail/rdmail work with metamail, assuming that you already have the
  855. metamail binary installed somewhere on your search path.
  856. 1.7    PCS sreadmail
  857.  
  858. All of the changes are localized to the file display.c  In particular,
  859. there are two changes to be made:
  860.  
  861. 1.  In display.c, there is a routine called "display".  The first line
  862. of that routine looks like this:
  863.  
  864.     messnum = messord[messnum];        /* convert to internal number */
  865.  
  866. Immediately following that line (i.e. between line 14 and line 15, in my
  867. version) add the following code:
  868.  
  869. #ifndef NOMETAMAIL
  870. {
  871. #include <sgtty.h>
  872.    if (!getenv("NOMETAMAIL") && nontext(messnum)) { 
  873.        char Fname[100], Cmd[100], linebuf[1000];
  874.        FILE *fp2;
  875.        int code;
  876.        struct sgttyb ttystatein, ttystateout; 
  877.  
  878.        sprintf(Fname, "/tmp/sreadmail-metamail.%d.%d.%d", getpid(), getuid());
  879.        fp2 = fopen(Fname, "w");
  880.        if (!fp2) {
  881.        perror(Fname);
  882.        } else {
  883.        fseek(curr.fp,messbeg[messnum],0);
  884.        while (fgets (linebuf, sizeof(linebuf), curr.fp) != NULL &&
  885. ftell(curr.fp) <= messend[messnum]) {
  886.            fputs(linebuf, fp2);
  887.        }
  888.        fclose(fp2);
  889.        sprintf(Cmd, "metamail -R -m sreadmail -z -p %s", Fname);
  890.        gtty(fileno(stdin), &ttystatein);
  891.        gtty(fileno(stdout), &ttystateout);
  892.        code = system(Cmd);
  893.        stty(fileno(stdin), &ttystatein);
  894.        stty(fileno(stdout), &ttystateout);
  895.        clear();
  896.        redisplay=TRUE;
  897.        curr.pageno=0;
  898.        helppage=0;
  899.        /* if (!code)) */ return(0);
  900.        }
  901.    }
  902. }
  903. #endif
  904.  
  905. 2.  At the very end of display.c, add the following new routines, which
  906. are used by the patch above:
  907.  
  908. #ifndef NOMETAMAIL
  909. #include <ctype.h>
  910.  
  911. nontext(messnum) 
  912. int messnum;
  913. {
  914.     char buf[1000], *s;
  915.  
  916.     fseek(curr.fp,messbeg[messnum],0);
  917.     while (fgets (buf, sizeof(buf), curr.fp) != NULL && ftell(curr.fp) <
  918. messend[messnum]) {
  919.     if (buf[0] == '\n') return(0);
  920.     for (s=buf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  921.     if (!strncmp(buf, "content-type:", 13) && notplain(buf+13)) return(1);
  922.     }
  923.     return(0);
  924. }
  925. notplain(s)
  926. char *s;
  927. {
  928.     char *t;
  929.     if (!s) return(1);
  930.     while (*s && isspace(*s)) ++s;
  931.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  932.     while (t > s && isspace(*--t)) {;}
  933.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  934.     if (strncmp(s, "text/plain", 10)) return(1);
  935.     t = (char *) index(s, ';');
  936.     while (t) {
  937.         ++t;
  938.         while (*t && isspace(*t)) ++t;
  939.         if (!strncmp(t, "charset", 7)) {
  940.             s = (char *) index(t, '=');
  941.             if (s) {
  942.                 ++s;
  943.                 while (*s && isspace(*s)) ++s;
  944.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  945.             }
  946.             return(1);
  947.         }
  948.         t = (char *) index(t, ';');
  949.     }
  950.     return(0); /* no charset, was text/plain */
  951. }
  952. #endif
  953.  
  954. These two changes should be all you need to do in order to make pcs
  955. readmail/rdmail/sreadmail work with metamail, assuming that you already
  956. have the metamail binary installed somewhere on your search path.
  957. 1.8    MH -- Rand Message Handling System
  958.  
  959. All of the changes are localized to the file uip/show.c  In particular,
  960. there are two changes to be made:
  961.  
  962. 1.  In uip/show.c, there is a label "go_to_it:".  Shortly after that
  963. you'll find the following bit of code:
  964.  
  965.     if (nshow)
  966.     proc = "/bin/cat";
  967.     else {
  968.     (void) putenv ("mhfolder", folder);
  969.     if (strcmp (r1bindex (showproc, '/'), "mhl") == 0) {
  970.         vec[0] = "mhl";
  971.         (void) mhl (vecp, vec);
  972.         done (0);
  973.     }
  974.     proc = showproc;
  975.     }
  976.  
  977. Immediately preceding that code fragment (i.e. between between 245 and
  978. 246 in my version of uip/show.c) add the following code:
  979.  
  980. #ifndef NOMETAMAIL
  981.     if (!getenv("NOMETAMAIL") && nontext(--msgnum, mp)) { 
  982. #include <sgtty.h>
  983.     struct sgttyb ttystatein, ttystateout; 
  984.     char Cmd[120];
  985.     FILE *fp;
  986.     int code;
  987.  
  988.     sprintf(Cmd, "metamail -e -p -m MH %s/%d", mp->foldpath, msgnum);
  989.     gtty(fileno(stdin), &ttystatein);
  990.     gtty(fileno(stdout), &ttystateout);
  991.     code = system(Cmd);
  992.     stty(fileno(stdin), &ttystatein);
  993.     stty(fileno(stdout), &ttystateout);
  994.     exit(code);
  995.     }
  996. #endif
  997.  
  998. 2.  At the very end of uip/show.c, add the following new routine, which
  999. is used by the patch above:
  1000.  
  1001. #ifndef NOMETAMAIL
  1002. #include <ctype.h>
  1003.  
  1004. nontext(msgnum, mp) 
  1005. int msgnum;
  1006. struct msgs *mp;
  1007. {
  1008.     FILE *fp;
  1009.     char line[1000], *s;
  1010.  
  1011.     sprintf(line, "%s/%d", mp->foldpath, msgnum);
  1012.     fp = fopen(line, "r");
  1013.     if (!fp) return(0);
  1014.     while (fgets(line, sizeof(line), fp)) {
  1015.         if (line[0] == '\n') {fclose(fp);return(0);}
  1016.         for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  1017.         if (!strncmp(line, "content-type:", 13) && notplain(line+13)) return(1);
  1018.     }
  1019.     fclose(fp);
  1020.     return(0);
  1021. }
  1022.  
  1023. notplain(s)
  1024. char *s;
  1025. {
  1026.     char *t;
  1027.     if (!s) return(1);
  1028.     while (*s && isspace(*s)) ++s;
  1029.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  1030.     while (t > s && isspace(*--t)) {;}
  1031.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  1032.     if (strncmp(s, "text/plain", 10)) return(1);
  1033.     t = (char *) index(s, ';');
  1034.     while (t) {
  1035.         ++t;
  1036.         while (*t && isspace(*t)) ++t;
  1037.         if (!strncmp(t, "charset", 7)) {
  1038.             s = (char *) index(t, '=');
  1039.             if (s) {
  1040.                 ++s;
  1041.                 while (*s && isspace(*s)) ++s;
  1042.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  1043.             }
  1044.             return(1);
  1045.         }
  1046.         t = (char *) index(t, ';');
  1047.     }
  1048.     return(0); /* no charset, was text/plain */
  1049. }
  1050.         
  1051. #endif
  1052.  
  1053. These two changes should be all you need to do in order to make MH work
  1054. with metamail, assuming that you already have the metamail binary
  1055. installed somewhere on your search path.
  1056. 1.9    XMH -- X11 Interface to Rand Message Handling System
  1057.  
  1058. All of the changes are localized to the file msg.c  In particular, there
  1059. are only two change to be made:
  1060.  
  1061. 1.  Near the beginning of the file (e.g. right after the last #include
  1062. line), add the following code:
  1063.  
  1064. #ifndef NOMETAMAIL
  1065. struct mmdata {
  1066.     Msg msg;
  1067.     Scrn scrn;
  1068. };
  1069. void RedisplayMsg();
  1070.  
  1071. void NoCallMetamail (widget, client_data, call_data)
  1072. Widget    widget;
  1073. XtPointer *client_data;
  1074. XtPointer    call_data;
  1075. {
  1076. }
  1077.  
  1078. void CallMetamail (widget, mmd, call_data)
  1079. Widget    widget;
  1080. struct mmdata *mmd;
  1081. XtPointer    call_data;
  1082. {
  1083.     char TmpFileName[200];
  1084.     char Cmd[200];
  1085.  
  1086.     sprintf(TmpFileName, "/tmp/xmh.%d.%d", getpid(), getuid());
  1087.     sprintf(Cmd, "csh -c \"metamail -e -m XMH -x -d %s >& %s \"",
  1088. MsgFileName(mmd->msg), TmpFileName);
  1089.     fprintf(stderr, "Executing %s, please wait...\n", Cmd);
  1090.     system(Cmd);
  1091.     mmd->msg->source = CreateFileSource(mmd->scrn->viewwidget,
  1092. TmpFileName, FALSE);
  1093.     RedisplayMsg(mmd->scrn);
  1094.     unlink(TmpFileName);
  1095. }
  1096. #endif
  1097.  
  1098.  
  1099. 2.  In msg.c, there is a procedure "SetScrnNewMsg". In that procedure
  1100. you will find the following bit of code:
  1101.  
  1102.     msg->num_scrns++;
  1103.     msg->scrn = (Scrn *) XtRealloc((char *)msg->scrn,
  1104.                        (unsigned) sizeof(Scrn)*msg->num_scrns);
  1105.     msg->scrn[msg->num_scrns - 1] = scrn;
  1106.     if ((msg->source == NULL) || (msg->toc == DraftsFolder))
  1107.         msg->source = CreateFileSource(scrn->viewwidget, MsgFileName(msg),
  1108.                        scrn->kind == STcomp);
  1109.  
  1110. Immediately following that code fragment (i.e. between between 312 and
  1111. 313 in my version of msg.c) add the following code:
  1112.  
  1113. #ifndef NOMETAMAIL
  1114.     if (!getenv("NOMETAMAIL")) {
  1115. #include <ctype.h>
  1116.         static XtCallbackRec yes_callbacks[] = {
  1117.         {CallMetamail, (XtPointer) NULL},
  1118.         {(XtCallbackProc) NULL,    (XtPointer) NULL}
  1119.         };
  1120.  
  1121.         static XtCallbackRec no_callbacks[] = {
  1122.         {NoCallMetamail, (XtPointer) NULL},
  1123.         {(XtCallbackProc) NULL,    (XtPointer) NULL}
  1124.         };
  1125.         FILE *fp;
  1126.         char line[1000], *s, *t, Query[200];
  1127.         static struct mmdata Mmdata;
  1128.  
  1129.         fp = fopen(MsgFileName(msg), "r");
  1130.         if (fp) {
  1131.         while (fgets(line, sizeof(line), fp)) {
  1132.             if (line[0] == '\n') break;
  1133.             for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  1134.             if (!strncmp(line, "content-type:", 13)) {
  1135.             s = &line[13];
  1136.             while (s && isspace(*s)) ++s;
  1137.             t = index(s, ';');
  1138.             if (t) {
  1139.                 *t = (char) NULL;
  1140.             } else {
  1141.                 t = index(s, '\n');
  1142.                 if (t) *t = (char) NULL;
  1143.             }
  1144.             if (t--) {
  1145.                 while (isspace(*t) && (t > s)) {
  1146.                 *t-- = (char) NULL;
  1147.                 }
  1148.             }
  1149.             if (notplain(s)) {
  1150.                 Mmdata.msg = msg;
  1151.                 Mmdata.scrn = scrn;
  1152.                 yes_callbacks[0].closure = (XtPointer) &Mmdata;
  1153.                 no_callbacks[0].closure =  (XtPointer) NULL;
  1154.                 sprintf(Query, "This message is in %s format.\nDo you want to try
  1155. to run an external interpreter?", s);
  1156.                 if (getenv("MM_NOASK")) {
  1157.                 CallMetamail(scrn->viewwidget, &Mmdata, NULL);
  1158.                 } else {
  1159.                 PopupConfirm(scrn->tocwidget, Query, yes_callbacks, no_callbacks);
  1160.                 }
  1161.                 break;
  1162.             }
  1163.             }
  1164.         }
  1165.         fclose(fp);
  1166.         }
  1167.     }
  1168. #endif
  1169.  
  1170.  
  1171. Finally, add the following procedure to the end of the file:
  1172.  
  1173. notplain(s)
  1174. char *s;
  1175. {
  1176.     char *t;
  1177.     if (!s) return(1);
  1178.     while (*s && isspace(*s)) ++s;
  1179.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  1180.     while (t > s && isspace(*--t)) {;}
  1181.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  1182.     if (strncmp(s, "text/plain", 10)) return(1);
  1183.     t = (char *) index(s, ';');
  1184.     while (t) {
  1185.         ++t;
  1186.         while (*t && isspace(*t)) ++t;
  1187.         if (!strncmp(t, "charset", 7)) {
  1188.             s = (char *) index(t, '=');
  1189.             if (s) {
  1190.                 ++s;
  1191.                 while (*s && isspace(*s)) ++s;
  1192.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  1193.             }
  1194.             return(1);
  1195.         }
  1196.         t = (char *) index(t, ';');
  1197.     }
  1198.     return(0); /* no charset, was text/plain */
  1199. }
  1200.  
  1201.  
  1202. These changes should be all you need to do in order to make XMH work
  1203. with metamail, assuming that you already have the metamail binary
  1204. installed somewhere on your search path.
  1205. 1.10    Rmail -- GNU Emacs mail reading package
  1206.  
  1207. Emacs being what it is, there are several versions of the rmail package
  1208. floating around.  Therefore the patch is particularly hard to describe,
  1209. since your version of rmail may vary.  The patch is therefore described
  1210. in several steps.
  1211.  
  1212. 1.  Make sure your rmail has an "rmail-show-message-hook" function.  If
  1213. you look in the source file rmail.el, you will (probably) find a
  1214. function called "rmail-show-message".  This function itself varies
  1215. significantly in different versions of rmail.   Near the end of it, you
  1216. might find a line of the form:
  1217.  
  1218. (run-hooks 'rmail-show-message-hook)
  1219.  
  1220. If there isn't such a line, you should add one -- probably right at the
  1221. end of the function, before it returns.
  1222.  
  1223. 2.  You may now either make changes to the rmail source, or only to your
  1224. own personal hook function.
  1225.  
  1226. 2a.  If you want to change the rmail source, put the following line
  1227. immediately before the aforementioned "run-hooks" line:
  1228.  
  1229. (rmail-check-content-type)
  1230.  
  1231. 2a.  If you want to change only your own customized rmail behavior, put
  1232. the following lines in your "~/.emacs" file:
  1233.  
  1234. (setq
  1235.  rmail-show-message-hook
  1236.  '(lambda()
  1237.      (rmail-check-content-type)))
  1238.  
  1239. (If you already had an rmail-show-message-hook function defined, you can
  1240. just put the rmail-check-content-type call at the beginning of it.)
  1241.  
  1242. 3.  Make sure you have the GNU "transparent.el" package installed.  This
  1243. is not part of the standard distribution, but is widely available, and
  1244. should be part of most FTP archives, etc.
  1245.  
  1246. 4.  Put the following LISP code somewhere that emacs will find it.  In
  1247. particular, if you modified rmail.el in step 2a, then it probably makes
  1248. sense to put this code at the end of that file.  If you just modified
  1249. your own .emacs file, it probably makes sense to put this code in your
  1250. .emacs file.  The code you want is:
  1251. ;;; Functions added for METAMAIL support
  1252.  
  1253. (require 'transparent)
  1254.  
  1255. (defvar rmail-never-execute-automatically t 
  1256.         "*Prevent metamail from happening semi-automatically")
  1257.  
  1258. (define-key rmail-mode-map "!" 'rmail-execute-content-type)
  1259.  
  1260. (defun rmail-check-content-type ()
  1261.   "Check for certain Content Type headers in mail"
  1262.   (rmail-maybe-execute-content-type nil))
  1263.  
  1264. (defun rmail-execute-content-type ()
  1265.   "Check for certain Content Type headers in mail"
  1266.   (interactive)
  1267.   (rmail-maybe-execute-content-type t))
  1268.  
  1269. (defun rmail-handle-content-type (ctype override dotoggle)
  1270.   (let (oldpt
  1271.         (oldbuf (current-buffer))
  1272.         (fname (make-temp-name "/tmp/rmailct")))
  1273.     
  1274.     (cond
  1275.      ((and rmail-never-execute-automatically (not override))
  1276.       (progn
  1277.        (if dotoggle (rmail-toggle-header))
  1278.        (message (concat "You can use '!' to run an interpreter for this '"
  1279.                         ctype "' format mail."))))
  1280.      ((or override
  1281.           (getenv "MM_NOASK")
  1282.           (y-or-n-p (concat "Run an interpreter for this '"
  1283.                             ctype "' format mail? ")))
  1284.       (progn
  1285.        (save-restriction
  1286.         (goto-char (point-max))
  1287.         (setq oldpt (point))
  1288.         (goto-char 0)
  1289.         (widen)
  1290.         (write-region
  1291.          (point)
  1292.          oldpt
  1293.          fname
  1294.          'nil
  1295.          "silent"))
  1296.        (if dotoggle (rmail-toggle-header))
  1297.        (if
  1298.         (and window-system (getenv "DISPLAY"))
  1299.          (progn
  1300.           (switch-to-buffer-other-window "METAMAIL")
  1301.           (erase-buffer)
  1302.           (pop-to-buffer oldbuf)
  1303.           (start-process "metamail"  "METAMAIL" "metamail" "-m"
  1304.                          "rmail" "-p" "-x" "-d" "-z" "-q" fname)
  1305.           (message "Starting metamail.  Sending output to METAMAIL buffer."))
  1306.          (progn
  1307.           (switch-to-buffer "METAMAIL")
  1308.           (erase-buffer)
  1309.           (sit-for 0)
  1310.           (transparent-window
  1311.            "METAMAIL"
  1312.            "metamail"
  1313.            (list "-m" "rmail" "-p" "-d" "-z" "-q" fname)
  1314.            nil
  1315.           (concat
  1316.            "\n\r\n\r*****************************************"
  1317.            "*******************************\n\rPress any key "
  1318.            "to go back to EMACS\n\r\n\r***********************" 
  1319.            "*************************************************\n\r")
  1320.       )))))
  1321.      (t (progn
  1322.       (if dotoggle (rmail-toggle-header))
  1323.       (message (concat "You can use the '!' keystroke to "
  1324.                "execute the external viewing program.")))))))
  1325.  
  1326. (defun rmail-maybe-execute-content-type (dorun)
  1327.   "Check for certain Content Type headers in mail"
  1328.   (cond
  1329.    ((not (getenv "NOMETAMAIL"))
  1330.     (let* ((ct nil)
  1331.            (needs-toggled nil))
  1332.       (save-excursion
  1333.        (save-restriction
  1334.         (widen)
  1335.         (goto-char (rmail-msgbeg rmail-current-message))
  1336.         (forward-line 1)
  1337.         (if (and dorun (= (following-char) ?1)) (setq needs-toggled t))
  1338.         (if (= (following-char) ?0)
  1339.           (narrow-to-region
  1340.            (progn (forward-line 2)
  1341.                   (point))
  1342.            (progn (search-forward "\n\n" (rmail-msgend rmail-current-message)
  1343.            'move)
  1344.            (point)))
  1345.          (narrow-to-region (point)
  1346.                            (progn (search-forward "\n*** EOOH ***\n")
  1347.                   (beginning-of-line) (point))))
  1348.         (setq ct (mail-fetch-field "content-type" t))))
  1349.       (cond
  1350.     (ct
  1351.        (cond ((not (string= ct "text"))
  1352.               (progn
  1353.                (if needs-toggled (rmail-toggle-header))
  1354.                (rmail-handle-content-type
  1355.                 ct dorun needs-toggled)))
  1356.          (needs-toggled
  1357.           (rmail-toggle-header)))))))))
  1358.  
  1359.  
  1360. The above changes should be all you need to do in order to make rmail
  1361. work with metamail, assuming that you already have the metamail binary
  1362. installed somewhere on your search path.
  1363.  
  1364. IMPORTANT NOTE:  For some users and some versions of Emacs,
  1365. terminal-oriented programs work poorly in the "transparent-window"
  1366. (non-X11) case with the above patch.  If you have problems in this case,
  1367. you might get better behavior by adding "-R" to the list of options that
  1368. are given to metamail.
  1369. 1.11    VM -- Another GNU Emacs mail reading package
  1370.  
  1371. Emacs being what it is, there are several mail readers besides rmail
  1372. floating around.  The'yre all similar, however.  The following patch for
  1373. VM is extremely similar to the patch for rmail -- in fact, the contents
  1374. of the hook functions are identical.  However, there are two versions of
  1375. this patch given, for older and newer versions of VM
  1376.  
  1377. 1.11.a    VM Version 5.32
  1378.  
  1379. Patches for the current beta release of the VM GNU Emacs mail reading package:
  1380.  
  1381. The following set of patches works for VM beta 5.32.
  1382.  
  1383. 1.  Modify the variable of "vm-visible-headers" to force the inclusion
  1384. of the "Content-Type" and "Content-Transfer-Encoding" headers.  In
  1385. vm-vars.el, you will find a definition that looks something like this:
  1386.  
  1387. (defvar vm-visible-headers
  1388.   '("From:" "Sender:" "Resent-From"
  1389.     "To:" "Apparently-To:" "Cc:"
  1390.     "Subject:"
  1391.     "Date:" "Resent-Date:")
  1392.   "*List of headers that should be visible when VM first displays a message.
  1393. These should be listed in the order you wish them presented.
  1394. Regular expressions are allowed.")
  1395.  
  1396. You should alter this definition so that it looks like this instead:
  1397.  
  1398. (defvar vm-visible-headers
  1399.   '("From:" "Sender:" "Resent-From"
  1400.     "To:" "Apparently-To:" "Cc:"
  1401.     "Subject:"
  1402.     "Date:" "Resent-Date:"
  1403.     "Content-Type:" "Content-Transfer-Encoding:")
  1404.   "*List of headers that should be visible when VM first displays a message.
  1405. These should be listed in the order you wish them presented.
  1406. Regular expressions are allowed.")
  1407.  
  1408. (defvar vm-never-execute-automatically t 
  1409.   "*Prevent metamail from happening semi-automatically")
  1410.  
  1411. 2. Further down in vm-vars.el you will find a long variable definition
  1412. that begins with
  1413.  
  1414. (defvar vm-mode-map
  1415.  
  1416. inside of this definition is this line
  1417.  
  1418.     (define-key map "!" 'shell-command)
  1419.  
  1420. Exchange this for the line below
  1421.  
  1422.     (define-key map "!" 'vm-execute-content-type)
  1423.  
  1424. 3.  If you look in the source file vm-page.el, you will find a function
  1425. called "vm-show-current-message". Near the end of it, you might find a
  1426. line of the form:
  1427.  
  1428.   (vm-update-summary-and-mode-line)
  1429.  
  1430. Immediately after that, you should add the line:
  1431.  
  1432.   (run-hooks 'vm-show-message-hook)
  1433.  
  1434. 4.  You may now either make changes to the vm source, or only to your
  1435. own personal hook function.
  1436.  
  1437. 4a.  If you want to change the vm source, put the following line
  1438. immediately before the aforementioned "run-hooks" line:
  1439.  
  1440. (vm-check-content-type)
  1441.  
  1442. 4b.  If you want to change only your own customized vm behavior, put the
  1443. following lines in your "~/.emacs" file:
  1444.  
  1445. (setq
  1446.  vm-show-message-hook
  1447.  '(lambda()
  1448.      (vm-check-content-type)))
  1449.  
  1450. (If you already had an vm-show-message-hook function defined, you can
  1451. just put the vm-check-content-type call at the beginning of it.)
  1452.  
  1453. 5.  Make sure you have the GNU "transparent.el" package installed.  This
  1454. is not part of the standard distribution, but is widely available, and
  1455. should be part of most FTP archives, etc.
  1456.  
  1457. 6.  Put the following LISP code somewhere that emacs will find it.  At
  1458. the end of vm-page.el or in your .emacs file are probably both good
  1459. choices, depending perhaps on if you did 4a or 4b. The code you want is:
  1460.  
  1461. ;;; Functions added for METAMAIL support
  1462.  
  1463. (require 'transparent)
  1464.  
  1465. (defun vm-check-content-type ()
  1466.   "Check for certain Content Type headers in mail"
  1467.   (vm-maybe-execute-content-type nil))
  1468.  
  1469. (defun vm-execute-content-type ()
  1470.   "Check for certain Content Type headers in mail"
  1471.   (interactive)
  1472.   (vm-maybe-execute-content-type t))
  1473.  
  1474. (defun vm-handle-content-type (ctype override)
  1475.   (let (oldpt
  1476.         (fname (make-temp-name "/tmp/rmailct")))
  1477.     
  1478.     (cond
  1479.      ((and vm-never-execute-automatically (not override))
  1480.       (progn
  1481.        (message (concat "You can use '!' to run an interpreter for this '"
  1482.                         ctype "' format mail."))))
  1483.      ((or override
  1484.           (getenv "MM_NOASK")
  1485.           (y-or-n-p (concat "Run an interpreter for this '"
  1486.                             ctype "' format mail? ")))
  1487.       (progn
  1488.        (save-restriction
  1489.         (goto-char (point-max))
  1490.         (setq oldpt (point))
  1491.         (goto-char 0)
  1492.         (widen)
  1493.         (write-region
  1494.          (point)
  1495.          oldpt
  1496.          fname
  1497.          'nil
  1498.          "silent"))
  1499.        (if
  1500.         (and window-system (getenv "DISPLAY"))
  1501.          (progn
  1502.           (switch-to-buffer-other-window "METAMAIL")
  1503.           (erase-buffer)
  1504.           (start-process "metamail"  "METAMAIL" "metamail" "-m"
  1505.                          "vm" "-x" "-d" "-z" "-q" fname)
  1506.           (if vm-mail-buffer (pop-to-buffer vm-mail-buffer))
  1507.           (message "Starting metamail.  Sending output to METAMAIL buffer."))
  1508.          (progn
  1509.           (switch-to-buffer "METAMAIL")
  1510.           (erase-buffer)
  1511.           (sit-for 0)
  1512.           (transparent-window
  1513.            "METAMAIL"
  1514.            "metamail"
  1515.            (list "-m" "vm" "-d" "-z" "-q" fname)
  1516.            nil
  1517.           (concat
  1518.            "\n\r\n\r*****************************************"
  1519.            "*******************************\n\rPress any key "
  1520.            "to go back to EMACS\n\r\n\r***********************" 
  1521.            "*************************************************\n\r")
  1522.       )))))
  1523.      (t (progn
  1524.       (message (concat "You can use the '!' keystroke to "
  1525.                "execute the external viewing program.")))))))
  1526.  
  1527. (defun vm-maybe-execute-content-type (dorun)
  1528.   "Check for certain Content Type headers in mail"
  1529.   (cond
  1530.    ((not (getenv "NOMETAMAIL"))
  1531.     (save-restriction
  1532.      (setq buffer-read-only 'nil)
  1533.      (let ((headend 0)
  1534.        (ctype "text")
  1535.            (old-min (point-min))
  1536.            (old-max (point-max))
  1537.        (opoint 0))
  1538.        (goto-char (point-min))
  1539.        (forward-line 1)
  1540.        (goto-char (point-min))
  1541.        (search-forward "\n\n")
  1542.        (setq headend (point))
  1543.        (setq opoint (- headend 1))
  1544.        (goto-char (point-min))    
  1545.        (setq case-fold-search 'T)
  1546.        (cond
  1547.     ((search-forward "\ncontent-type:" opoint 't)
  1548.      (progn
  1549.           (forward-word 1)    (backward-word 1)  ; Took care of white space
  1550.       (setq opoint (point))
  1551.       (re-search-forward "[;\n]" (point-max) t)
  1552.       (setq ctype (downcase (buffer-substring opoint (- (point) 1))))
  1553.           (goto-char (point-min)))))
  1554.        (cond ((not (string= ctype "text"))
  1555.           (vm-handle-content-type
  1556.            ctype dorun))
  1557.              ))))))
  1558.  
  1559.  
  1560. 7.  Watch out for an oddity in the terminal emulator package.  If you
  1561. get the error message
  1562.  
  1563. Key sequence \277 uses invalid prefix characters
  1564.  
  1565. or something like that,  this probably means that you have set your
  1566. global variable "help-char" to a META key.  The terminal emulator
  1567. package doesn't like that at all, and you'll need to change it in order
  1568. for the terminal emulator package, and these extensions to vm, to work
  1569. properly.
  1570.  
  1571. The above changes should be all you need to do in order to make vm 5.32
  1572. work with metamail, assuming that you already have the metamail binary
  1573. installed somewhere on your search path.
  1574.  
  1575. IMPORTANT NOTE:  For some users and some versions of Emacs,
  1576. terminal-oriented programs work poorly in the "transparent-window"
  1577. (non-X11) case with the above patch.  If you have problems in this case,
  1578. you might get better behavior by adding "-R" to the list of options that
  1579. are given to metamail.
  1580.  
  1581. Important note for Emacs "Hyperbole" users:  Hyperbole, in its "hvm.el"
  1582. file, overloads the definition of some VM functions.  Therefore, any VM
  1583. functions that need to be modified for metamail usage that exist in
  1584. "hvm.el" should be patched there rather than in the VM code itself.
  1585.  
  1586.  
  1587. 1.11.b    Older Versions of VM
  1588.  
  1589. 1.  Modify the definition of "vm-build-visible-header-alist" to force
  1590. the inclusion of the "Content-Type" and "Content-Transfer-Encoding"
  1591. headers.  In vm.el, you will find a definition that looks something like
  1592. this:
  1593.  
  1594. (defun vm-build-visible-header-alist ()
  1595.   (let ((header-alist (cons nil nil))
  1596.   (vheaders vm-visible-headers)
  1597.    list)
  1598.     (setq list header-alist)
  1599.     (while vheaders
  1600.       (setcdr list (cons (cons (car vheaders) nil) nil))
  1601.       (setq list (cdr list) vheaders (cdr vheaders)))
  1602.     (setq vm-visible-header-alist (cdr header-alist))))
  1603.  
  1604. You should alter the declaration of "vheaders", so that it looks like
  1605. this instead:
  1606.  
  1607. (defun vm-build-visible-header-alist ()
  1608.   (let ((header-alist (cons nil nil))
  1609.         (vheaders (append vm-visible-headers
  1610.                           '("Content-Type:" "Content-Transfer-Encoding:")))
  1611.     list)
  1612.     (setq list header-alist)
  1613.     (while vheaders
  1614.       (setcdr list (cons (cons (car vheaders) nil) nil))
  1615.       (setq list (cdr list) vheaders (cdr vheaders)))
  1616.     (setq vm-visible-header-alist (cdr header-alist))))
  1617.  
  1618. 2.  Make sure your version of VM has a "vmail-show-message-hook"
  1619. function.  It probably doesn't.  However, if you look in the source file
  1620. vm.el, you will (probably) find a function called
  1621. "vm-show-current-message".    Near the end of it, you might find a line
  1622. of the form:
  1623.  
  1624.   (vm-update-summary-and-mode-line)
  1625.  
  1626. Immediately after that, you should add the line:
  1627.  
  1628.   (run-hooks 'vm-show-message-hook)
  1629.  
  1630. 3.  You may now either make changes to the vm source, or only to your
  1631. own personal hook function.
  1632.  
  1633. 3a.  If you want to change the vm source, put the following line
  1634. immediately before the aforementioned "run-hooks" line:
  1635.  
  1636. (vm-check-content-type)
  1637.  
  1638. 3a.  If you want to change only your own customized vm behavior, put the
  1639. following lines in your "~/.emacs" file:
  1640.  
  1641. (setq
  1642.  vm-show-message-hook
  1643.  '(lambda()
  1644.      (vm-check-content-type)))
  1645.  
  1646. (If you already had an vm-show-message-hook function defined, you can
  1647. just put the vm-check-content-type call at the beginning of it.)
  1648.  
  1649. 4.  Make sure you have the GNU "transparent.el" package installed.  This
  1650. is not part of the standard distribution, but is widely available, and
  1651. should be part of most FTP archives, etc.
  1652.  
  1653. 5.  Put the following LISP code somewhere that emacs will find it.  In
  1654. particular, if you modified vm.el in step 2a, then it probably makes
  1655. sense to put this code at the end of that file.  If you just modified
  1656. your own .emacs file, it probably makes sense to put this code in your
  1657. .emacs file.  The code you want is:
  1658.  
  1659.  
  1660. ;;; Functions added for METAMAIL support
  1661.  
  1662. (require 'transparent)
  1663.  
  1664. (defvar vm-never-execute-automatically t "*Prevent metamail from
  1665. happening semi-automatically")
  1666.  
  1667. (define-key vm-mode-map "!" 'vm-execute-content-type)
  1668.  
  1669. (defun vm-check-content-type ()
  1670.   "Check for certain Content Type headers in mail"
  1671.   (vm-maybe-execute-content-type nil))
  1672.  
  1673. (defun vm-execute-content-type ()
  1674.   "Check for certain Content Type headers in mail"
  1675.   (interactive)
  1676.   (vm-maybe-execute-content-type t))
  1677.  
  1678. (defun vm-handle-content-type (ctype override)
  1679.   (let (oldpt
  1680.         (fname (make-temp-name "/tmp/rmailct")))
  1681.     
  1682.     (cond
  1683.      ((and vm-never-execute-automatically (not override))
  1684.       (progn
  1685.        (message (concat "You can use '!' to run an interpreter for this '"
  1686.                         ctype "' format mail."))))
  1687.      ((or override
  1688.           (getenv "MM_NOASK")
  1689.           (y-or-n-p (concat "Run an interpreter for this '"
  1690.                             ctype "' format mail? ")))
  1691.       (progn
  1692.        (save-restriction
  1693.         (goto-char (point-max))
  1694.         (setq oldpt (point))
  1695.         (goto-char 0)
  1696.         (widen)
  1697.         (write-region
  1698.          (point)
  1699.          oldpt
  1700.          fname
  1701.          'nil
  1702.          "silent"))
  1703.        (if
  1704.         (and window-system (getenv "DISPLAY"))
  1705.          (progn
  1706.           (switch-to-buffer-other-window "METAMAIL")
  1707.           (erase-buffer)
  1708.           (start-process "metamail"  "METAMAIL" "metamail" "-m"
  1709.                          "vm" "-x" "-d" "-z" "-q" fname)
  1710.           (if vm-mail-buffer (pop-to-buffer vm-mail-buffer))
  1711.           (message "Starting metamail.  Sending output to METAMAIL buffer."))
  1712.          (progn
  1713.           (switch-to-buffer "METAMAIL")
  1714.           (erase-buffer)
  1715.           (sit-for 0)
  1716.           (transparent-window
  1717.            "METAMAIL"
  1718.            "metamail"
  1719.            (list "-m" "vm" "-d" "-z" "-q" fname)
  1720.            nil
  1721.           (concat
  1722.            "\n\r\n\r*****************************************"
  1723.            "*******************************\n\rPress any key "
  1724.            "to go back to EMACS\n\r\n\r***********************" 
  1725.            "*************************************************\n\r")
  1726.       )))))
  1727.      (t (progn
  1728.       (message (concat "You can use the '!' keystroke to "
  1729.                "execute the external viewing program.")))))))
  1730.  
  1731. (defun vm-maybe-execute-content-type (dorun)
  1732.   "Check for certain Content Type headers in mail"
  1733.   (cond
  1734.    ((not (getenv "NOMETAMAIL"))
  1735.     (save-restriction
  1736.      (setq buffer-read-only 'nil)
  1737.      (let ((headend 0)
  1738.        (ctype "text")
  1739.            (old-min (point-min))
  1740.            (old-max (point-max))
  1741.        (opoint 0))
  1742.        (goto-char (point-min))
  1743.        (forward-line 1)
  1744.        (goto-char (point-min))
  1745.        (search-forward "\n\n")
  1746.        (setq headend (point))
  1747.        (setq opoint (- headend 1))
  1748.        (goto-char (point-min))    
  1749.        (setq case-fold-search 'T)
  1750.        (cond
  1751.     ((search-forward "\ncontent-type:" opoint 't)
  1752.      (progn
  1753.           (forward-word 1)    (backward-word 1)  ; Took care of white space
  1754.       (setq opoint (point))
  1755.       (re-search-forward "[;\n]" (point-max) t)
  1756.       (setq ctype (downcase (buffer-substring opoint (- (point) 1))))
  1757.           (goto-char (point-min)))))
  1758.        (cond ((not (string= ctype "text"))
  1759.           (vm-handle-content-type
  1760.            ctype dorun))
  1761.              ))))))
  1762.  
  1763.  
  1764.  
  1765.  
  1766. 6.  Watch out for an oddity in the terminal emulator package.  If you
  1767. get the error message
  1768.  
  1769. Key sequence \277 uses invalid prefix characters
  1770.  
  1771. or something like that,  this probably means that you have set your
  1772. global variable "help-char" to a META key.  The terminal emulator
  1773. package doesn't like that at all, and you'll need to change it in order
  1774. for the terminal emulator package, and these extensions to vm, to work
  1775. properly.
  1776.  
  1777.  
  1778. The above changes should be all you need to do in order to make vm work
  1779. with metamail, assuming that you already have the metamail binary
  1780. installed somewhere on your search path.
  1781.  
  1782. IMPORTANT NOTE:  For some users and some versions of Emacs,
  1783. terminal-oriented programs work poorly in the "transparent-window"
  1784. (non-X11) case with the above patch.  If you have problems in this case,
  1785. you might get better behavior by adding "-R" to the list of options that
  1786. are given to metamail.
  1787.  
  1788. Important note for Emacs "Hyperbole" users:  Hyperbole, in its "hvm.el"
  1789. file, overloads the definition of some VM functions.  Therefore, any VM
  1790. functions that need to be modified for metamail usage that exist in
  1791. "hvm.el" should be patched there rather than in the VM code itself.
  1792.  
  1793. 1.12    MH-E -- GNU Emacs interface to MH mail 
  1794.  
  1795. This patch was contributed by Dave Cohrs.
  1796.  
  1797. Alter the definition of the function mh-display-msg.  In that function,
  1798. near the very end (line 1223 in my version) you will find the following
  1799. lines:
  1800.  
  1801.         (setq mode-line-buffer-identification
  1802.           (list (format mh-show-buffer-mode-line-buffer-id
  1803.                folder msg-num))))))
  1804.  
  1805. You should REPLACE these lines with the following ones:
  1806.  
  1807.         (setq mode-line-buffer-identification
  1808.           (list (format mh-show-buffer-mode-line-buffer-id
  1809.                folder msg-num)))
  1810.        (mh-check-content-type)    ; Metamail extension
  1811.        )))
  1812.  
  1813. 3.  Add the following code to the end of the source file:
  1814.  
  1815. ;;; Functions added for METAMAIL support
  1816.  
  1817. (require 'transparent)
  1818.  
  1819. (defvar mh-never-execute-automatically t "*Prevent metamail from
  1820. happening semi-automatically")
  1821.  
  1822. (define-key mh-folder-mode-map "@" 'mh-execute-content-type)
  1823.  
  1824. (defun mh-check-content-type ()
  1825.   "Check for certain Content Type headers in mail"
  1826.   (mh-maybe-execute-content-type nil))
  1827.  
  1828. (defun mh-execute-content-type ()
  1829.   "Check for certain Content Type headers in mail"
  1830.   (interactive)
  1831.   (mh-maybe-execute-content-type t))
  1832.  
  1833. (defun mh-exec-metamail-cmd-output (command &rest args)
  1834.   ;; Execute MH library command COMMAND with ARGS.
  1835.   ;; Put the output into buffer after point.  Set mark after inserted text.
  1836.   (push-mark (point) t)
  1837.   (erase-buffer)
  1838.   (apply 'call-process
  1839.      command nil t nil
  1840.      (mh-list-to-string args))
  1841.   (exchange-point-and-mark)
  1842.   (set-buffer-modified-p nil)
  1843.   (other-window -1))
  1844.  
  1845. (defun mh-handle-content-type (ctype override)
  1846.   (let (oldpt
  1847.         (fname (make-temp-name "/tmp/rmailct")))
  1848.     
  1849.     (cond
  1850.      ((and mh-never-execute-automatically (not override))
  1851.       (progn
  1852.        (message (concat "You can use '@' to run an interpreter for this '"
  1853.                         ctype "' format mail."))))
  1854.      ((or override
  1855.           (getenv "MM_NOASK")
  1856.           (y-or-n-p (concat "Run an interpreter for this '"
  1857.                             ctype "' format mail? ")))
  1858.       (progn
  1859.        (save-restriction
  1860.         (goto-char (point-max))
  1861.         (setq oldpt (point))
  1862.         (goto-char 0)
  1863.         (widen)
  1864.         (write-region
  1865.          (point)
  1866.          oldpt
  1867.          fname
  1868.          'nil
  1869.          "silent"))
  1870.        (if 
  1871.        (and window-system (getenv "DISPLAY"))
  1872.        (mh-exec-metamail-cmd-output "metamail" "-m" "mh-e" "-x" "-d"
  1873.                     "-q" fname)
  1874.      (progn
  1875.        (other-window -1)
  1876.        (switch-to-buffer "METAMAIL")
  1877.        (erase-buffer)
  1878.        (sit-for 0)
  1879.        (transparent-window
  1880.         "METAMAIL"
  1881.         "metamail"
  1882.         (list "-m" "mh-e" "-p" "-d" "-q" fname)
  1883.         nil
  1884.         (concat
  1885.          "\n\r\n\r*****************************************"
  1886.          "*******************************\n\rPress any key "
  1887.          "to go back to EMACS\n\r\n\r***********************" 
  1888.          "*************************************************\n\r"))))))
  1889.      (t (progn
  1890.       (message (concat "You can use the '@' keystroke to "
  1891.                "execute the external viewing program.")))))))
  1892.  
  1893. (defun mh-maybe-execute-content-type (dorun)
  1894.   "Check for certain Content Type headers in mail"
  1895.   (cond
  1896.    ((not (getenv "NOMETAMAIL"))
  1897.     (save-restriction
  1898.      (setq buffer-read-only 'nil)
  1899.      (let ((headend 0)
  1900.        (ctype "text/plain")
  1901.            (old-min (point-min))
  1902.            (old-max (point-max))
  1903.        (opoint 0))
  1904.        (if mh-show-buffer (pop-to-buffer mh-show-buffer))
  1905.        (goto-char (point-min))
  1906.        (forward-line 1)
  1907.        (goto-char (point-min))
  1908.        (search-forward "\n\n" nil 1)
  1909.        (setq headend (point))
  1910.        (setq opoint (- headend 1))
  1911.        (goto-char (point-min))    
  1912.        (setq case-fold-search 'T)
  1913.        (cond
  1914.     ((search-forward "\ncontent-type:" opoint 't)
  1915.      (progn
  1916.           (forward-word 1)    (backward-word 1)  ; Took care of white space
  1917.       (setq opoint (point))
  1918.       (re-search-forward "[;\n]" (point-max) t)
  1919.       (setq ctype (downcase (buffer-substring opoint (- (point) 1))))
  1920.           (goto-char (point-min)))))
  1921.        (cond
  1922.     ((not (string= ctype "text/plain"))
  1923.           (mh-handle-content-type
  1924.            ctype dorun))
  1925.              ))))))
  1926.  
  1927. 3.  Make sure you have the GNU "transparent.el" package installed.  This
  1928. is not part of the standard distribution, but is widely available, and
  1929. should be part of most FTP archives, etc.
  1930.  
  1931.  
  1932. The above changes should be all you need to do in order to make mh-e
  1933. work with metamail, assuming that you already have the metamail binary
  1934. installed somewhere on your search path.  If MH-E hackers contribute
  1935. improvements, they will be incorporated into a future version of this
  1936. document.
  1937.  
  1938. IMPORTANT NOTE:  For some users and some versions of Emacs,
  1939. terminal-oriented programs work poorly in the "transparent-window"
  1940. (non-X11) case with the above patch.  If you have problems in this case,
  1941. you might get better behavior by adding "-R" to the list of options that
  1942. are given to metamail.
  1943.  
  1944. 1.13    CUI (Simplest Andrew Mail Reader)
  1945.  
  1946. NOTE:  If you have the version of CUI that corresponds to Messages
  1947. version 8.0 or later, you do not need this patch.  If you have the
  1948. version that corresponds to Messages 7.15 or later, you probably do not
  1949. need this patch, either.  If you have an earlier version, you need this
  1950. patch.
  1951.  
  1952. In the file ams/msclients/cui/cui.c, there is a routine called
  1953. "GetBodyFromCUID"  The first line of that routine looks like this:
  1954.  
  1955.     debug(1,("GetBodyFromCUID %d\n", cuid));
  1956.  
  1957. Immediately after that line, which is between lines 1228 and 1229 in my
  1958. version, add the following lines:
  1959.  
  1960. #ifndef NOMETAMAIL
  1961.     {
  1962. #include <sgtty.h>
  1963.     struct sgttyb ttystatein, ttystateout;
  1964.     char ctype[100], TmpFileName[1+MAXPATHLEN], Cmd[1+MAXPATHLEN];
  1965.     int ShouldDelete, code;
  1966.     extern int LinesOnTerminal;
  1967.  
  1968.     if (CUI_GetHeaderContents(cuid,(char *) NULL, HP_CONTENTTYPE, ctype,
  1969. sizeof(ctype) - 1) != NULL) {
  1970.         /* error already reported */
  1971.         return(-1);
  1972.     }
  1973.     if (!getenv("NOMETAMAIL") && ctype[0] && strnicmp(ctype, "x-be2", 5) &&
  1974. nontext(ctype)) {
  1975.         if (CUI_GetBodyToLocalFile(cuid, TmpFileName, &ShouldDelete)) {
  1976.         return(-1); /* error reported */
  1977.         }
  1978.         sprintf(Cmd, "metamail -m cui %s %s %s", (LinesOnTerminal > 0) ?
  1979. "-p" : "", ShouldDelete ? "-z" : "", TmpFileName);
  1980.         gtty(fileno(stdin), &ttystatein);
  1981.         gtty(fileno(stdout), &ttystateout);
  1982.         code = system(Cmd);
  1983.         stty(fileno(stdin), &ttystatein);
  1984.         stty(fileno(stdout), &ttystateout);
  1985.         /* if (code) */ return(0);
  1986.     }
  1987.     }
  1988. #endif
  1989.  
  1990. Then, at the end of the file, add the following code:
  1991.  
  1992. #ifndef NOMETAMAIL
  1993.  
  1994. nontext(s)
  1995. char *s;
  1996. {
  1997.     char *t;
  1998.     if (!s) return(1);
  1999.     while (*s && isspace(*s)) ++s;
  2000.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  2001.     while (t > s && isspace(*--t)) {;}
  2002.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  2003.     if (strncmp(s, "text/plain", 10)) return(1);
  2004.     t = (char *) index(s, ';');
  2005.     while (t) {
  2006.         ++t;
  2007.         while (*t && isspace(*t)) ++t;
  2008.         if (!strncmp(t, "charset", 7)) {
  2009.             s = (char *) index(t, '=');
  2010.             if (s) {
  2011.                 ++s;
  2012.                 while (*s && isspace(*s)) ++s;
  2013.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  2014.             }
  2015.             return(1);
  2016.         }
  2017.         t = (char *) index(t, ';');
  2018.     }
  2019.     return(0); /* no charset, was text/plain */
  2020. }
  2021. #endif
  2022.  
  2023. This simple change should be all you need to do in order to make CUI
  2024. work with metamail, assuming that you already have the "metamail"
  2025. executable installed somewhere on your search path.
  2026. 1.14    VUI (Terminal-oriented Andrew Mail Reader)
  2027.  
  2028. NOTE:  If you have the version of VUI that corresponds to Messages
  2029. version 8.0 or later, you do not need this patch.  If you have the
  2030. version that corresponds to Messages 7.15 or later, you probably do not
  2031. need this patch, either.  If you have an earlier version, you need this
  2032. patch.
  2033.  
  2034. In the file ams/msclients/vui/vuipnl.c, there is a routine called
  2035. "InitBodyData"  That routine starts out with the following line of code:
  2036.  
  2037.     char filename[MAXPATHLEN+1];
  2038.  
  2039. Immediately after that code, which is between lines 2386 and 2387 in my
  2040. version, add the following lines:
  2041.  
  2042. #ifndef NOMETAMAIL
  2043. #include <sgtty.h>
  2044.     struct sgttyb ttystatein, ttystateout;
  2045.     char ctype[100], TmpFileName[1+MAXPATHLEN], Cmd[1+MAXPATHLEN];
  2046.     int ShouldDelete, cuid = CuidFromMsgno(msgno), code;
  2047.  
  2048.     if (CUI_GetHeaderContents(cuid,(char *) NULL, HP_CONTENTTYPE, ctype,
  2049. sizeof(ctype) - 1) != NULL) {
  2050.         /* error already reported */
  2051.         return(-1);
  2052.     }
  2053.     if (!getenv("NOMETAMAIL") && ctype[0] && ULstrncmp(ctype, "x-be2", 5)
  2054. && nontext(ctype)) {
  2055.         if (CUI_GetBodyToLocalFile(cuid, TmpFileName, &ShouldDelete)) {
  2056.         return(-1); /* error reported */
  2057.         }
  2058.         sprintf(Cmd, "metamail -R -m vui -p %s %s", ShouldDelete ? "-z" :
  2059. "", TmpFileName);
  2060.         gtty(fileno(stdin), &ttystatein);
  2061.         gtty(fileno(stdout), &ttystateout);
  2062.         code = system(Cmd);
  2063.         stty(fileno(stdin), &ttystatein);
  2064.         stty(fileno(stdout), &ttystateout);
  2065.         RedrawScreen(0); 
  2066.         UpdateMsgData (msgno, 1); 
  2067.         return(MENU_EMPTY);
  2068.     }
  2069. #endif
  2070.  
  2071. Then, at the end of the file, add the following code:
  2072.  
  2073. #ifndef NOMETAMAIL
  2074.  
  2075. nontext(s)
  2076. char *s;
  2077. {
  2078.     char *t;
  2079.     if (!s) return(1);
  2080.     while (*s && isspace(*s)) ++s;
  2081.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  2082.     while (t > s && isspace(*--t)) {;}
  2083.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  2084.     if (strncmp(s, "text/plain", 10)) return(1);
  2085.     t = (char *) index(s, ';');
  2086.     while (t) {
  2087.         ++t;
  2088.         while (*t && isspace(*t)) ++t;
  2089.         if (!strncmp(t, "charset", 7)) {
  2090.             s = (char *) index(t, '=');
  2091.             if (s) {
  2092.                 ++s;
  2093.                 while (*s && isspace(*s)) ++s;
  2094.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  2095.             }
  2096.             return(1);
  2097.         }
  2098.         t = (char *) index(t, ';');
  2099.     }
  2100.     return(0); /* no charset, was text/plain */
  2101. }
  2102. #endif
  2103.  
  2104. This simple change should be all you need to do in order to make VUI
  2105. work with metamail, assuming that you already have the "metamail"
  2106. executable installed somewhere on your search path.
  2107. 1.15    Messages (Andrew Multimedia Mail Reader)
  2108.  
  2109. NOTE:  If you have Messages version 8.0 or later, you do not need this
  2110. patch.  If you have Messages 7.15 or later, you probably do not need
  2111. this patch, either.  If you have an earlier version, you need this patch.
  2112.  
  2113. There are two changes, both localized to the file
  2114. atkams/messages/lib/text822.c:
  2115.  
  2116. 1.  Near the beginning of atkams/messages/lib/text822.c, there are the
  2117. following #include lines:
  2118.  
  2119. #include <stylesht.ih>
  2120. #include <environ.ih>
  2121. #include <fnote.ih>
  2122. #include <ams.h>
  2123.  
  2124. Immediately after those lines, insert the following lines:
  2125.  
  2126. #ifndef NOMETAMAIL
  2127. #include <sys/param.h>
  2128. #include <fdphack.h>
  2129. #undef popen /* BOGUS -- should be handled by fdphack */
  2130. #undef pclose /* ditto */
  2131. #include <ams.ih>
  2132. #include <message.ih>
  2133. #include <im.ih>
  2134.  
  2135. MetaOutput(fp, self)
  2136. FILE *fp;
  2137. struct text *self;
  2138. {
  2139.     char buf[1000];
  2140.  
  2141.     if (fgets(buf, sizeof(buf), fp) != NULL) {
  2142.     text_AlwaysInsertCharacters(self, text_GetLength(self), buf, strlen(buf));
  2143.     text_NotifyObservers(self, 0);
  2144.     return;
  2145.     }
  2146.     if (errno != EWOULDBLOCK) {
  2147.     im_RemoveFileHandler(fp);
  2148.     pclose(fp);
  2149.     strcpy(buf, "\n--- Command execution terminated ---\n");
  2150.     text_AlwaysInsertCharacters(self, text_GetLength(self), buf, strlen(buf));
  2151.     }
  2152.     text_NotifyObservers(self, 0);
  2153. }
  2154. #endif
  2155.  
  2156. 2.  In atkams/messages/lib/text822.c, there is a routine called
  2157. "text822__ReadIntoText"  Near the end of that very long routine, you
  2158. will find some code that looks like this:
  2159.  
  2160.     } else if (!amsutil_lc2strncmp("troff", sfmttype, strlen(sfmttype))) {
  2161.         char **resources = (char **)
  2162. amsutil_BreakDownResourcesIntoArray(fmtresources);
  2163.  
  2164.         rofftext_ReadRoffIntoText(d, fp, ShowPos, resources);
  2165.         if (resources) free(resources);
  2166.         ReadRaw = FALSE;
  2167.     }
  2168.  
  2169. The patch needs to be added AFTER the line that says "ReadRaw = FALSE"
  2170. but before the next line, which is just a closing brace.  (In my
  2171. version, this means the patch goes between lines 353 and 354.)  The
  2172. patch to insert is as follows:
  2173.  
  2174. #ifndef NOMETAMAIL
  2175.     } else if (!environ_Get("NOMETAMAIL") && IsReallyTextObject &&
  2176. amsutil_lc2strncmp("text", sfmttype, strlen(sfmttype))) {
  2177.         /* IsReallyTextObject test ensures we don't run metamail when
  2178. printing!!! */
  2179.         char TmpFileName[1+MAXPATHLEN], LineBuf[1000], Cmd[1+MAXPATHLEN],
  2180. Msg[50+MAXPATHLEN], TmpFile2[1+MAXPATHLEN];
  2181.         FILE *fp2;
  2182.  
  2183.         sprintf(Msg, "Do you want to run an interpreter for this '%s'
  2184. format mail", sfmttype);
  2185.         if (environ_Get("MM_NOASK")
  2186.         || ams_GetBooleanFromUser(ams_GetAMS(), Msg, TRUE)) {
  2187.         ams_CUI_GenLocalTmpFileName(ams_GetAMS(), TmpFileName);
  2188.         ams_CUI_GenLocalTmpFileName(ams_GetAMS(), TmpFile2);
  2189.         fp2 = (FILE *) fopen (TmpFileName, "w");
  2190.         if (fp2) {
  2191.             fseek(fp, 0, 0);
  2192.             while (fgets(LineBuf, sizeof(LineBuf), fp)) {
  2193.             fputs(LineBuf, fp2);
  2194.             }
  2195.             fclose(fp2);
  2196.             sprintf(Cmd, "metamail -m messages -z -x -d -q %s 2>&1", TmpFileName);
  2197.             sprintf(Msg, "Executing: %s\n", Cmd);
  2198.             linelen = strlen(Msg);
  2199.             text822_AlwaysInsertCharacters(d, ShowPos, Msg, linelen);
  2200.             ShowPos += strlen(Msg);
  2201.             fp2 = (FILE *) popen(Cmd, "r");
  2202.             im_AddFileHandler(fp2, MetaOutput, d, 0);
  2203.         }
  2204.         }
  2205. #endif
  2206.  
  2207. This simple change should be all you need to do in order to make
  2208. Messages work with metamail, assuming that you already have the
  2209. "metamail" executable installed somewhere on your search path.
  2210. 1.16    BatMail (Emacs interface to Andrew)
  2211.  
  2212. Patching BatMail is quite simple.  This patch was provided by Bob
  2213. Glickstein, and has not been tested by the author of this document.
  2214.  
  2215. Batmail already provides for a hook called "bat-body-hook" which gets
  2216. called when a new message body is displayed.  You can hook metamail into
  2217. batmail by simply defining that hook to be the Lisp code below. (The one
  2218. catch is that the user must have "mime-version" and "content-type" as
  2219. headers which are normally displayed, specified by the bat-headers
  2220. variable.  The reason is that the hook searches the body buffer for
  2221. these headers [rather than the time-consuming option of grepping the
  2222. body file ("robin" does not have a GetHeaderContents stub)].)
  2223.  
  2224. The following is the code that you need to add to BatMail:
  2225.  
  2226. (setq bat-headers
  2227.       (concat "from:resent-from:resent-to:date:subject:to"
  2228.           ":cc:newsgroups:mime-version:content-type:content-transfer-encoding"))
  2229.  
  2230.  
  2231. (setq bat-body-hook 'bat-body-hook-fn)
  2232.  
  2233.  
  2234. (defun bat-body-hook-fn ()
  2235.   (save-excursion
  2236.     (set-buffer bat-display-buf)
  2237.     (goto-char (point-min))
  2238.     (let ((end-of-headers (if (search-forward "\n\n" (point-max) t)
  2239.                   (point))))
  2240.       (goto-char (point-min))
  2241.       (if (re-search-forward "^[Mm][Ii][Mm][Ee]-[Vv]ersion:[ \\t]*\\(.+\\)"
  2242.                  end-of-headers t)
  2243.       (let ((mime-version (buffer-substring (match-beginning 1)
  2244.                         (match-end 1))))
  2245.         (goto-char (point-min))
  2246.         (if (re-search-forward "^[Cc]ontent-[Tt]ype:[ \\t]*\\(.+\\)"
  2247.                    end-of-headers t)
  2248.         (let ((content-type (buffer-substring (match-beginning 1)
  2249.                               (match-end 1))))
  2250.           (if (not (string-match "^text/plain" content-type))
  2251.               (if (get-tty-bool "Run metamail" t)
  2252.               (let ((tmp (concat "/tmp/" 
  2253.                          (make-temp-name "bat-meta-")))
  2254.                 (cuid (bat-current-cuid))
  2255.                 (process-connection-type nil)
  2256.                 metamail-process)
  2257.                 (bat-command "a" cuid ":" tmp "\n")
  2258.                 (while (not (file-exists-p tmp))
  2259.                   (sit-for 1))
  2260.                 (setq metamail-process
  2261.                   (start-process "metamail" bat-display-buf
  2262.                          "metamail" "-d" "-m" "batmail"
  2263.                          "-x" "-z" tmp))
  2264.                 (if metamail-process
  2265.                 (progn
  2266.                   (process-kill-without-query
  2267.                    metamail-process nil))
  2268.                   (error "Couldn't start metamail"))))))))))))
  2269.  
  2270. 1.17    Elm (Mail system from HP)
  2271.  
  2272. All of the changes are localized to the file src/showmsg.c  In
  2273. particular, there are two changes to be made:
  2274.  
  2275. 1.  In src/showmsg.c, there is a routine called "show_msg" my version it
  2276. starts on line 49.  About a page down in that routine, you'll find
  2277. something that looks like this:
  2278.  
  2279.  
  2280.     memory_lock = FALSE;
  2281.  
  2282.     /* some explanation for that last one - We COULD use memory locking
  2283.        to speed up the paging, but the action of "ClearScreen" on a screen
  2284.        with memory lock turned on seems to vary considerably (amazingly so)
  2285.        so it's safer to only allow memory lock to be a viable bit of
  2286.        trickery when dumping text to the screen in scroll mode.
  2287.        Philosophical arguments should be forwarded to Bruce at the 
  2288.        University of Walamazoo, Australia, via ACSNet  *wry chuckle* */
  2289.  
  2290. Immediately following that fragment (i.e. between line 114 and line 115,
  2291. in my version) add the following code:
  2292.  
  2293. #ifndef NOMETAMAIL
  2294.     if (!getenv("NOMETAMAIL") && nontext(current_header)) {
  2295.         char fname[100], Cmd[200], line[VERY_LONG_STRING];
  2296.         int code;
  2297.         long lines = current_header->lines;
  2298.         FILE *fpout;
  2299.  
  2300.         if (fseek(mailfile, current_header->offset, 0) != -1) {
  2301.         sprintf(fname, "/tmp/elm-metamail.%d.%d", getpid(), getuid());
  2302.         fpout = fopen(fname, "w");
  2303.         if (fpout) {
  2304.             while (lines > 0L) {
  2305.             fgets(line, VERY_LONG_STRING, mailfile);
  2306.             fputs(line, fpout);
  2307.             --lines;
  2308.             }
  2309.             fclose(fpout);
  2310.             sprintf(Cmd, "metamail -R -P -z -m Elm %s", fname);
  2311.                        Raw(OFF);
  2312.             code = system(Cmd);
  2313.                        Raw(ON);
  2314.             /* if (code) */ return(0);
  2315.         }
  2316.         }
  2317.     }
  2318. #endif
  2319.  
  2320. 2.  At the very end of src/showmsg.c, add the following new routines,
  2321. which are used by the patch above:
  2322.  
  2323. #ifndef NOMETAMAIL
  2324. nontext(ch) 
  2325. struct header_rec *ch;
  2326. {
  2327.     long lines = ch->lines;
  2328.     char line[VERY_LONG_STRING], *s, *t;
  2329.  
  2330.     if (fseek(mailfile, ch->offset, 0) == -1) return(-1);
  2331.     while (lines > 0L) {
  2332.     fgets(line, VERY_LONG_STRING, mailfile);
  2333.     --lines;
  2334.     if (line[0] == '\n') return(0);
  2335.     for (s=line; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  2336.     if (!strncmp(line, "content-type:", 13)) {
  2337.         s = &line[13];
  2338.         while (s && isspace(*s)) ++s;
  2339.         t = index(s, ';');
  2340.         if (!t) t = index(s, '\n');
  2341.         if (t) *t-- = NULL;
  2342.         while (t && *t && t > s && isspace(*t)) *t-- = NULL;
  2343.         if (notplain(s)) return(1);
  2344.     }
  2345.     }
  2346.     return(0);
  2347. }
  2348. notplain(s)
  2349. char *s;
  2350. {
  2351.     char *t;
  2352.     if (!s) return(1);
  2353.     while (*s && isspace(*s)) ++s;
  2354.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  2355.     while (t > s && isspace(*--t)) {;}
  2356.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  2357.     if (strncmp(s, "text/plain", 10)) return(1);
  2358.     t = (char *) index(s, ';');
  2359.     while (t) {
  2360.         ++t;
  2361.         while (*t && isspace(*t)) ++t;
  2362.         if (!strncmp(t, "charset", 7)) {
  2363.             s = (char *) index(t, '=');
  2364.             if (s) {
  2365.                 ++s;
  2366.                 while (*s && isspace(*s)) ++s;
  2367.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  2368.             }
  2369.             return(1);
  2370.         }
  2371.         t = (char *) index(t, ';');
  2372.     }
  2373.     return(0); /* no charset, was text/plain */
  2374. }
  2375. #endif
  2376.  
  2377. These two changes should be all you need to do in order to make Elm work
  2378. with metamail, assuming that you already have the "metamail" executable
  2379. installed somewhere on your search path.
  2380. 1.18    Mush mail reader
  2381.  
  2382. The following patch was provided by Bart Schaefer.  Her reports that
  2383. this patch should work with versions of mush as far back as 6.5,
  2384. although 6.5 doesn't have M_PRIORITY()
  2385. either, so you'd have to apply by hand and omit that part.
  2386.  
  2387. He also reports that mush version  7.2.5 will have metamail support
  2388. built in, so no patches will be necessary if you are using that version
  2389. or later.
  2390.  
  2391. There are two files to patch, msgs.c and mush.h.  I haven't included the
  2392. non-essential third patch that shows an "M" in the "headers" summary if
  2393. the message is a MIME-format message.
  2394.  
  2395. The first two hunks of this patch, to msgs.c, modify display_msg() to
  2396. have it send the "raw" message to the program named in the $metamail
  2397. variable when the message's METAMAIL flag bit is set.  If $metamail is
  2398. not set, it ignores the METAMAIL bit and pages the message normally. The
  2399. third hunk modifies load_folder() to recognize the Content-Type: header
  2400. and turn the METAMAIL bit on.  (There's some other slop in there   about
  2401. checking Resent-Date: for a date which can safely be ignored if patching
  2402. by hand.)
  2403.  
  2404. *** 7.2.4/msgs.c    Sun Feb  2 13:59:14 1992
  2405. --- 7.2.5/msgs.c    Mon Mar  9 23:38:34 1992
  2406. ***************
  2407. *** 8,13 ****
  2408. --- 8,14 ----
  2409.   u_long flg;
  2410.   {
  2411.       char buf[32], *pager = NULL;
  2412. +     int intro = TRUE;
  2413.   
  2414.       if (ison(msg[n].m_flags, DELETE) && !do_set(set_options,
  2415. "show_deleted")) {
  2416.       print("Message %d deleted; ", n+1);
  2417. ***************
  2418. *** 31,37 ****
  2419.   #ifdef MSG_SEPARATOR
  2420.       turnon(flg, NO_SEPARATOR);
  2421.   #endif /* MMDF */
  2422. !     if (!istool && isoff(flg, NO_PAGE) &&
  2423.           crt < msg[n].m_lines && isoff(flg, M_TOP)) {
  2424.       if (!(pager = do_set(set_options, "pager")))
  2425.           pager = DEF_PAGER;
  2426. --- 32,44 ----
  2427.   #ifdef MSG_SEPARATOR
  2428.       turnon(flg, NO_SEPARATOR);
  2429.   #endif /* MMDF */
  2430. !     if (ison(msg[n].m_flags, METAMAIL) && isoff(flg, NO_PAGE) &&
  2431. !         (pager = do_set(set_options, "metamail"))) {
  2432. !     intro = FALSE;
  2433. !     turnoff(flg, NO_HEADER);
  2434. !     turnoff(flg, M_TOP);
  2435. !     turnon(flg, NO_IGNORE);
  2436. !     } else if (!istool && isoff(flg, NO_PAGE) &&
  2437.           crt < msg[n].m_lines && isoff(flg, M_TOP)) {
  2438.       if (!(pager = do_set(set_options, "pager")))
  2439.           pager = DEF_PAGER;
  2440. ***************
  2441. *** 38,46 ****
  2442.       if (!*pager || !strcmp(pager, "internal"))
  2443.           pager = NULL; /* default to internal pager if pager set to "" */
  2444.       }
  2445. !     (void) do_pager(pager, TRUE); /* start pager */
  2446. !     (void) do_pager(sprintf(buf, "Message #%d (%d lines)\n",
  2447. !              n+1, msg[n].m_lines), FALSE);
  2448.       (void) copy_msg(n, NULL_FILE, flg, NULL);
  2449.       (void) do_pager(NULL, FALSE); /* end pager */
  2450.   }
  2451. --- 45,54 ----
  2452.       if (!*pager || !strcmp(pager, "internal"))
  2453.           pager = NULL; /* default to internal pager if pager set to "" */
  2454.       }
  2455. !     (void) do_pager(pager, intro? 1 : -1); /* start pager */
  2456. !     if (intro)
  2457. !     (void) do_pager(sprintf(buf, "Message #%d (%d lines)\n",
  2458. !                  n+1, msg[n].m_lines), FALSE);
  2459.       (void) copy_msg(n, NULL_FILE, flg, NULL);
  2460.       (void) do_pager(NULL, FALSE); /* end pager */
  2461.   }
  2462. ***************
  2463. *** 897,905 ****
  2464.            */
  2465.           while (fgets(buf, sizeof (buf), fp) && (*buf != '\n')) {
  2466.               p = buf;
  2467.               if (!strncmp(buf, "Date:", 5))
  2468.               strdup(msg[cnt].m_date_sent, parse_date(p+5));
  2469. !             if (!strncmp(buf, "Priority:", 9)) {
  2470.               for (p += 9 ; *p != '\n'; p++) {
  2471.                   if (!isalpha(*p) || upper(*p) > 'A' + MAX_PRIORITY)
  2472.                   continue;
  2473. --- 910,923 ----
  2474.            */
  2475.           while (fgets(buf, sizeof (buf), fp) && (*buf != '\n')) {
  2476.               p = buf;
  2477.               if (!strncmp(buf, "Date:", 5))
  2478.               strdup(msg[cnt].m_date_sent, parse_date(p+5));
  2479. !             else if (!msg[cnt].m_date_sent &&
  2480. !                 !strncmp(buf, "Resent-Date:", 12))
  2481. !             msg[cnt].m_date_sent = savestr(parse_date(p+12));
  2482. !             else if (!strncmp(buf, "Content-Type:", 13))
  2483. !             turnon(msg[cnt].m_flags, METAMAIL);
  2484. !             else if (!strncmp(buf, "Priority:", 9)) {
  2485.               for (p += 9 ; *p != '\n'; p++) {
  2486.                   if (!isalpha(*p) || upper(*p) > 'A' + MAX_PRIORITY)
  2487.                   continue;
  2488.  
  2489. This patch to mush.h defines the METAMAIL bit.  I hope nobody is counting
  2490. on being able to redefine MAX_PRIORITY as 10. :-}
  2491.  
  2492. *** 7.2.4/mush.h    Sun Feb  2 13:50:50 1992
  2493. --- 7.2.5/mush.h    Mon Mar  2 21:04:59 1992
  2494. ***************
  2495. *** 430,441 ****
  2496.   #define REPLIED        ULBIT(17) /* Messages that have been replied to */
  2497.   #define NEW_SUBJECT    ULBIT(18) /* new subject regardless of $ask (mail -s) */
  2498.   #define SAVED        ULBIT(19) /* when message has been saved */
  2499. - #ifdef MSG_SEPARATOR
  2500.   #define NO_SEPARATOR    ULBIT(20) /* don't include message separator lines */
  2501. ! #endif /* MSG_SEPARATOR */
  2502.   
  2503. ! #define M_PRIORITY(n)    ULBIT(21+(n))
  2504. ! /* It is possible to reset MAX_PRIORITY to as high as 10 */
  2505.   #define MAX_PRIORITY    5
  2506.  
  2507.   #define MAXMSGS_BITS    MAXMSGS/sizeof(char)    /* number of bits for bitmap */
  2508. --- 430,440 ----
  2509.   #define REPLIED        ULBIT(17) /* Messages that have been replied to */
  2510.   #define NEW_SUBJECT    ULBIT(18) /* new subject regardless of $ask (mail -s) */
  2511.   #define SAVED        ULBIT(19) /* when message has been saved */
  2512.   #define NO_SEPARATOR    ULBIT(20) /* don't include message separator lines */
  2513. ! #define METAMAIL    ULBIT(21) /* message can display with "metamail" */
  2514.   
  2515. ! #define M_PRIORITY(n)    ULBIT(22+(n))
  2516. ! /* It is possible to reset MAX_PRIORITY to as high as 9 */
  2517.   #define MAX_PRIORITY    5
  2518.  
  2519.   #define MAXMSGS_BITS    MAXMSGS/sizeof(char)    /* number of bits for bitmap */
  2520.  
  2521. 1.19    Msgs (Berkeley Bulletin Board system)
  2522.  
  2523. All of the changes are localized to the file msgs.c  In particular,
  2524. there are four changes to be made:
  2525.  
  2526. 1.  In msgs.c, there are lots of global declarations near the top.  In
  2527. my version, the last of them (around line 114) looks like this:
  2528.  
  2529. jmp_buf    tstpbuf;
  2530.  
  2531. Immediately after that line,  add the following code:
  2532.  
  2533. #ifndef NOMETAMAIL
  2534. int NonTextMessage;
  2535. #endif
  2536.  
  2537. 2.  Further down in msgs.c, there is a routine named "prmesg" (around
  2538. line 580) which starts with the following declarations:
  2539.  
  2540. FILE *outf, *inf;
  2541. int c;
  2542.  
  2543. Immediately after these declaration, add the following code:
  2544.  
  2545. #ifndef NOMETAMAIL
  2546.     if (!getenv("NOMETAMAIL") && NonTextMessage) {
  2547.         char Fname[100], Cmd[120];
  2548.         FILE *fp;
  2549.         int code;
  2550.         struct sgttyb ttystatein, ttystateout;
  2551.  
  2552.         sprintf(Cmd, "metamail %s -m Mail %s", pause ? "-p" : "", fname);
  2553.         gtty(fileno(stdin), &ttystatein);
  2554.         gtty(fileno(stdout), &ttystateout);
  2555.         code = system(Cmd);
  2556.         stty(fileno(stdin), &ttystatein);
  2557.         stty(fileno(stdout), &ttystateout);
  2558.         return;
  2559.       }
  2560. #endif
  2561.  
  2562. 3.  Still further down in msgs.c, there is a routine named "gfrsub"
  2563. (around line 775).  A page or so down in that routine, there is code
  2564. that looks like this:
  2565.  
  2566.     while (fgets(inbuf, sizeof inbuf, infile)
  2567.         && !(blankline = (inbuf[0] == '\n'))) {
  2568.         /*
  2569.          * extract Subject line
  2570.          */
  2571.         if (!seensubj && strncmp(inbuf, "Subj", 4)==0) {
  2572.             seensubj = YES;
  2573.             frompos = ftell(infile);
  2574.             strncpy(subj, nxtfld(inbuf), sizeof subj);
  2575.         }
  2576.  
  2577. Immediately before this code fragment, add the following three lines:
  2578.  
  2579. #ifndef NOMETAMAIL
  2580.         NonTextMessage = 0;
  2581. #endif
  2582.  
  2583. Immediately after this code fragment (around line 840 in my version) add
  2584. the following code:
  2585.  
  2586. #ifndef NOMETAMAIL
  2587.       {        
  2588.         char *s, *t;
  2589.         for (s=inbuf; *s; ++s) if (isupper(*s)) *s = tolower(*s);
  2590.         if (!strncmp(inbuf, "content-type:", 13)) {
  2591.           s = inbuf + 13;
  2592.           while (s && isspace(*s)) ++s;
  2593.           t = (char *) index(s, ';');
  2594.           if (t) *t = NULL; else t = s+strlen(s);
  2595.           while (--t > s && isspace(*t)) *t = NULL;
  2596.           NonTextMessage = nontext(s);
  2597.         }
  2598.       }
  2599. #endif
  2600.  
  2601. Finally, add this routine to the end of the file:
  2602.  
  2603. nontext(s)
  2604. char *s;
  2605. {
  2606.     char *t;
  2607.     if (!s) return(1);
  2608.     while (*s && isspace(*s)) ++s;
  2609.     for(t=s; *t; ++t) if (isupper(*t)) *t = tolower(*t);
  2610.     while (t > s && isspace(*--t)) {;}
  2611.     if (((t-s) == 3) && !strncmp(s, "text", 4)) return(0);
  2612.     if (strncmp(s, "text/plain", 10)) return(1);
  2613.     t = (char *) index(s, ';');
  2614.     while (t) {
  2615.         ++t;
  2616.         while (*t && isspace(*t)) ++t;
  2617.         if (!strncmp(t, "charset", 7)) {
  2618.             s = (char *) index(t, '=');
  2619.             if (s) {
  2620.                 ++s;
  2621.                 while (*s && isspace(*s)) ++s;
  2622.                 if (!strncmp(s, "us-ascii", 8)) return(0);
  2623.             }
  2624.             return(1);
  2625.         }
  2626.         t = (char *) index(t, ';');
  2627.     }
  2628.     return(0); /* no charset, was text/plain */
  2629. }        
  2630.  
  2631. These four changes should be all you need to do in order to make msgs
  2632. work with metamail, assuming that you already have the "metamail"
  2633. executable installed somewhere on your search path.
  2634. 1.20    UUPC (MS-DOS Mail-Reading Program)
  2635.  
  2636. UUPC is an Internet mail-reading program for MS-DOS.  It turns out that
  2637. if you build metamail for DOS, you don't need to patch the UUPC program
  2638. at all.  Instead, you can simply set your PAGER to metamail.
  2639.  
  2640. In particular, you need to put a line like the following in your
  2641. personal configuration file:
  2642.  
  2643. Pager=metamail %s
  2644.  
  2645. This should be all that it takes to make UUPC work with metamail. 
  2646. However, you should note that the metamail "-p" feature is not being
  2647. used (and probably doesn't yet work on DOS), which means that your
  2648. mailcap entries should take paging into account.  In particular, you
  2649. probably want to have an entry for "text/richtext" that passes "-p" to
  2650. the richtext program, and a followup general entry for "text/*" that
  2651. uses a normal text paging program to view the mail.
  2652.  
  2653.