No error-checking for error-checking alloc functions
[mmh] / uip / repl.c
index e5fa527..f5df9ec 100644 (file)
-
 /*
- * repl.c -- reply to a message
- *
- * $Id$
- *
- * This code is Copyright (c) 2002, by the authors of nmh.  See the
- * COPYRIGHT file in the root directory of the nmh distribution for
- * complete copyright information.
- */
+** repl.c -- reply to a message
+**
+** This code is Copyright (c) 2002, by the authors of nmh.  See the
+** COPYRIGHT file in the root directory of the nmh distribution for
+** complete copyright information.
+*/
 
 #include <h/mh.h>
 #include <h/utils.h>
-
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+#include <sys/file.h>  /* L_SET */
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <sysexits.h>
 
 static struct swit switches[] = {
-#define GROUPSW                0
-    { "group", 0 },
-#define NGROUPSW               1
-    { "nogroup", 0 },
-#define        ANNOSW                 2
-    { "annotate", 0 },
-#define        NANNOSW                3
-    { "noannotate", 0 },
-#define        CCSW                   4
-    { "cc all|to|cc|me", 0 },
-#define        NCCSW                  5
-    { "nocc type", 0 },
-#define        DFOLDSW                6
-    { "draftfolder +folder", 0 },
-#define        DMSGSW                 7
-    { "draftmessage msg", 0 },
-#define        NDFLDSW                8
-    { "nodraftfolder", 0 },
-#define        EDITRSW                9
-    { "editor editor", 0 },
-#define        NEDITSW               10
-    { "noedit", 0 },
-#define        FCCSW                 11
-    { "fcc folder", 0 },
-#define        FILTSW                12
-    { "filter filterfile", 0 },
-#define        FORMSW                13
-    { "form formfile", 0 },
-#define        FRMTSW                14
-    { "format", 5 },
-#define        NFRMTSW               15
-    { "noformat", 7 },
-#define        INPLSW                16
-    { "inplace", 0 },
-#define        NINPLSW               17
-    { "noinplace", 0 },
-#define MIMESW                18
-    { "mime", 0 },
-#define NMIMESW               19
-    { "nomime", 0 },
-#define        QURYSW                20
-    { "query", 0 },
-#define        NQURYSW               21
-    { "noquery", 0 },
-#define        WHATSW                22
-    { "whatnowproc program", 0 },
-#define        NWHATSW               23
-    { "nowhatnowproc", 0 },
-#define        WIDTHSW               24
-    { "width columns", 0 },
-#define VERSIONSW             25
-    { "version", 0 },
-#define        HELPSW                26
-    { "help", 0 },
-#define        FILESW                27
-    { "file file", 4 },                        /* interface from msh */
-
-#ifdef MHE
-#define        BILDSW                28
-    { "build", 5 },                    /* interface from mhe */
-#endif
-
-    { NULL, 0 }
+#define GROUPSW  0
+       { "group", 0 },
+#define NGROUPSW  1
+       { "nogroup", 2 },
+#define ANNOSW  2
+       { "annotate", 0 },
+#define NANNOSW  3
+       { "noannotate", 2 },
+#define CCSW  4
+       { "cc all|to|cc|me", 0 },
+#define NCCSW  5
+       { "nocc type", 2 },
+#define EDITRSW  6
+       { "editor editor", 0 },
+#define FILTSW  7
+       { "filter filterfile", 0 },
+#define NFILTSW  8
+       { "nofilter", 2 },
+#define FORMSW  9
+       { "form formfile", 0 },
+#define MIMESW  10
+       { "mime", 0 },
+#define NMIMESW  11
+       { "nomime", 2 },
+#define QURYSW  12
+       { "query", 0 },
+#define NQURYSW  13
+       { "noquery", 2 },
+#define WHATSW  14
+       { "whatnowproc program", 0 },
+#define VERSIONSW  15
+       { "Version", 0 },
+#define HELPSW  16
+       { "help", 0 },
+#define FILESW  17
+       { "file file", 4 },  /* interface from msh */
+#define BILDSW  18
+       { "build", 5 },  /* interface from mhe */
+       { NULL, 0 }
 };
 
 static struct swit ccswitches[] = {
-#define        CTOSW   0
-    { "to", 0 },
-#define        CCCSW   1
-    { "cc", 0 },
-#define        CMESW   2
-    { "me", 0 },
-#define        CALSW   3
-    { "all", 0 },
-    { NULL, 0 }
+#define CTOSW  0
+       { "to", 0 },
+#define CCCSW  1
+       { "cc", 0 },
+#define CMESW  2
+       { "me", 0 },
+#define CALSW  3
+       { "all", 0 },
+       { NULL, 0 }
 };
 
-static struct swit aqrnl[] = {
-#define        NOSW         0
-    { "quit", 0 },
-#define        YESW         1
-    { "replace", 0 },
-#define        LISTDSW      2
-    { "list", 0 },
-#define        REFILSW      3
-    { "refile +folder", 0 },
-#define NEWSW        4
-    { "new", 0 },
-    { NULL, 0 }
+/*
+** Buffer size for content part of header fields.
+** We want this to be large enough so that we don't
+** do a lot of extra FLDPLUS calls on m_getfld but
+** small enough so that we don't snarf the entire
+** message body when we're not going to use any of it.
+*/
+#define SBUFSIZ 256
+
+static short ccto = -1;
+static short cccc = -1;
+static short ccme = -1;
+static short querysw = 0;
+
+static short groupreply = 0;  /* Is this a group reply? */
+
+static int mime = 0;  /* include original as MIME part */
+static char *form   = NULL;  /* form (components) file */
+static char *filter = NULL;  /* message filter file */
+
+static int dftype=0;
+
+static char *badaddrs = NULL;
+static char *dfhost = NULL;
+
+static struct mailname mq;
+
+static struct format *fmt;
+
+static int ncomps = 0;  /* # of interesting components */
+static char **compbuffers = NULL;  /* buffers for component text */
+static struct comp **used_buf = NULL;  /* stack for comp that use buffers */
+
+static int dat[5];  /* aux. data for format routine */
+
+static char *addrcomps[] = {
+       "from",
+       "sender",
+       "reply-to",
+       "to",
+       "cc",
+       "bcc",
+       "resent-from",
+       "resent-sender",
+       "resent-reply-to",
+       "resent-to",
+       "resent-cc",
+       "resent-bcc",
+       NULL
 };
 
-static struct swit aqrl[] = {
-    { "quit", 0 },
-    { "replace", 0 },
-    { "list", 0 },
-    { "refile +folder", 0 },
-    { NULL, 0 }
-};
+/*
+** static prototypes
+*/
+static void docc(char *, int);
+static int insert(struct mailname *);
+static void replfilter(FILE *, FILE *, char *);
+static void replout(FILE *, char *, struct msgs *, int,
+               char *, char *);
 
-short ccto = -1;               /* global for replsbr */
-short cccc = -1;
-short ccme = -1;
-short querysw = 0;
 
-short outputlinelen = OUTPUTLINELEN;
-short groupreply = 0;          /* Is this a group reply?        */
+int
+main(int argc, char **argv)
+{
+       int anot = 0;
+       char *cp, *cwd, *maildir, *file = NULL;
+       char *folder = NULL, *msg = NULL;
+       char *ed = NULL, drft[BUFSIZ], buf[BUFSIZ];
+       char **argp, **arguments;
+       struct msgs *mp = NULL;
+       FILE *in;
+       int buildsw = 0;
+
+       filter = getcpy(etcpath(mhlreply));
+
+       setlocale(LC_ALL, "");
+       invo_name = mhbasename(argv[0]);
+
+       /* read user profile/context */
+       context_read();
+
+       arguments = getarguments(invo_name, argc, argv, 1);
+       argp = arguments;
+
+       while ((cp = *argp++)) {
+               if (*cp == '-') {
+                       switch (smatch(++cp, switches)) {
+                       case AMBIGSW:
+                               ambigsw(cp, switches);
+                               exit(EX_USAGE);
+                       case UNKWNSW:
+                               adios(EX_USAGE, NULL, "-%s unknown", cp);
+
+                       case HELPSW:
+                               snprintf(buf, sizeof(buf), "%s: [+folder] [msg] [switches]", invo_name);
+                               print_help(buf, switches, 1);
+                               exit(argc == 2 ? EX_OK : EX_USAGE);
+                       case VERSIONSW:
+                               print_version(invo_name);
+                               exit(argc == 2 ? EX_OK : EX_USAGE);
+
+                       case GROUPSW:
+                               groupreply++;
+                               continue;
+                       case NGROUPSW:
+                               groupreply = 0;
+                               continue;
+
+                       case ANNOSW:
+                               anot++;
+                               continue;
+                       case NANNOSW:
+                               anot = 0;
+                               continue;
+
+                       case CCSW:
+                               if (!(cp = *argp++) || *cp == '-')
+                                       adios(EX_USAGE, NULL, "missing argument to %s",
+                                                       argp[-2]);
+                               docc(cp, 1);
+                               continue;
+                       case NCCSW:
+                               if (!(cp = *argp++) || *cp == '-')
+                                       adios(EX_USAGE, NULL, "missing argument to %s",
+                                                       argp[-2]);
+                               docc(cp, 0);
+                               continue;
+
+                       case EDITRSW:
+                               if (!(ed = *argp++) || *ed == '-')
+                                       adios(EX_USAGE, NULL, "missing argument to %s",
+                                                       argp[-2]);
+                               continue;
+
+                       case WHATSW:
+                               if (!(whatnowproc = *argp++) ||
+                                               *whatnowproc == '-')
+                                       adios(EX_USAGE, NULL, "missing argument to %s",
+                                                       argp[-2]);
+                               continue;
+
+                       case BILDSW:
+                               buildsw++;
+                               continue;
+
+                       case FILESW:
+                               if (file)
+                                       adios(EX_USAGE, NULL, "only one file at a time!");
+                               if (!(cp = *argp++) || *cp == '-')
+                                       adios(EX_USAGE, NULL, "missing argument to %s",
+                                                       argp[-2]);
+                               file = getcpy(expanddir(cp));
+                               continue;
+                       case FORMSW:
+                               if (!(form = *argp++) || *form == '-')
+                                       adios(EX_USAGE, NULL, "missing argument to %s",
+                                                       argp[-2]);
+                               continue;
+
+                       case FILTSW:
+                               if (!(cp = *argp++) || *cp == '-')
+                                       adios(EX_USAGE, NULL, "missing argument to %s",
+                                                       argp[-2]);
+                               filter = getcpy(etcpath(cp));
+                               continue;
+                       case NFILTSW:
+                               filter = NULL;
+                               continue;
+
+                       case MIMESW:
+                               mime++;
+                               continue;
+                       case NMIMESW:
+                               mime = 0;
+                               continue;
+
+                       case QURYSW:
+                               querysw++;
+                               continue;
+                       case NQURYSW:
+                               querysw = 0;
+                               continue;
+
+                       }
+               }
+               if (*cp == '+' || *cp == '@') {
+                       if (folder)
+                               adios(EX_USAGE, NULL, "only one folder at a time!");
+                       else
+                               folder = getcpy(expandfol(cp));
+               } else {
+                       if (msg)
+                               adios(EX_USAGE, NULL, "only one message at a time!");
+                       else
+                               msg = cp;
+               }
+       }
 
-int mime = 0;                  /* include original as MIME part */
-char *form   = NULL;           /* form (components) file        */
-char *filter = NULL;           /* message filter file           */
-char *fcc    = NULL;           /* folders to add to Fcc: header */
+       if (ccto == -1)
+               ccto = groupreply;
+       if (cccc == -1)
+               cccc = groupreply;
+       if (ccme == -1)
+               ccme = groupreply;
 
+       cwd = getcpy(pwd());
 
-/*
- * prototypes
- */
-void docc (char *, int);
+       if (file && (msg || folder))
+               adios(EX_USAGE, NULL, "can't mix files and folders/msgs");
 
+       strncpy(drft, buildsw ? toabsdir("reply") : m_draft(seq_beyond),
+                       sizeof(drft));
+       /*
+       ** FIXME: (concerning MHE support (buildsw) only)
+       ** There's no check if the draft already exists. mmh has removed
+       ** this case by having the draft folder. I won't add code only to
+       ** handle this legacy issue for MHE. -- meillo@marmaro.de 2012-05
+       */
+
+       if (file) {
+               /*
+               ** We are replying to a file.
+               */
+               anot = 0;  /* we don't want to annotate a file */
+       } else {
+               /*
+               ** We are replying to a message.
+               */
+               if (!msg)
+                       msg = seq_cur;
+               if (!folder)
+                       folder = getcurfol();
+               maildir = toabsdir(folder);
+
+               if (chdir(maildir) == NOTOK)
+                       adios(EX_OSERR, maildir, "unable to change directory to");
+
+               /* read folder and create message structure */
+               if (!(mp = folder_read(folder)))
+                       adios(EX_IOERR, NULL, "unable to read folder %s", folder);
+
+               /* check for empty folder */
+               if (mp->nummsg == 0)
+                       adios(EX_DATAERR, NULL, "no messages in %s", folder);
+
+               /* parse the message range/sequence/name and set SELECTED */
+               if (!m_convert(mp, msg))
+                       exit(EX_SOFTWARE);
+               seq_setprev(mp);  /* set the previous-sequence */
+
+               if (mp->numsel > 1)
+                       adios(EX_USAGE, NULL, "only one message at a time!");
+
+               context_replace(curfolder, folder); /* update current folder */
+               seq_setcur(mp, mp->lowsel);  /* update current message  */
+               seq_save(mp);  /* synchronize sequences   */
+               context_save();  /* save the context file   */
+       }
 
-int
-main (int argc, char **argv)
+       msg = file ? file : getcpy(m_name(mp->lowsel));
+
+       if ((in = fopen(msg, "r")) == NULL)
+               adios(EX_IOERR, msg, "unable to open");
+
+       /* find form (components) file */
+       if (!form) {
+               if (groupreply)
+                       form = etcpath(replgroupcomps);
+               else
+                       form = etcpath(replcomps);
+       }
+
+       replout(in, drft, mp, mime, form, filter);
+       fclose(in);
+
+       if (buildsw)
+               exit(EX_OK);
+       what_now(ed, NOUSE, drft, msg, 0, mp, anot ? "Replied" : NULL, cwd);
+       return EX_OSERR;
+}
+
+static void
+docc(char *cp, int ccflag)
 {
-    int        i, isdf = 0;
-    int anot = 0, inplace = 1;
-    int nedit = 0, nwhat = 0;
-    char *cp, *cwd, *dp, *maildir, *file = NULL;
-    char *folder = NULL, *msg = NULL, *dfolder = NULL;
-    char *dmsg = NULL, *ed = NULL, drft[BUFSIZ], buf[BUFSIZ];
-    char **argp, **arguments;
-    struct msgs *mp = NULL;
-    struct stat st;
-    FILE *in;
-
-#ifdef MHE
-    int buildsw = 0;
-#endif /* MHE */
-
-#ifdef LOCALE
-    setlocale(LC_ALL, "");
-#endif
-    invo_name = r1bindex (argv[0], '/');
-
-    /* read user profile/context */
-    context_read();
-
-    arguments = getarguments (invo_name, argc, argv, 1);
-    argp = arguments;
-
-    while ((cp = *argp++)) {
-       if (*cp == '-') {
-           switch (smatch (++cp, switches)) {
-               case AMBIGSW: 
-                   ambigsw (cp, switches);
-                   done (1);
-               case UNKWNSW: 
-                   adios (NULL, "-%s unknown", cp);
-
-               case HELPSW: 
-                   snprintf (buf, sizeof(buf), "%s: [+folder] [msg] [switches]",
-                       invo_name);
-                   print_help (buf, switches, 1);
-                   done (0);
-               case VERSIONSW:
-                   print_version(invo_name);
-                   done (1);
-
-               case GROUPSW:
-                   groupreply++;
-                   continue;
-               case NGROUPSW:
-                   groupreply = 0;
-                   continue;
-
-               case ANNOSW: 
-                   anot++;
-                   continue;
-               case NANNOSW: 
-                   anot = 0;
-                   continue;
-
-               case CCSW: 
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   docc (cp, 1);
-                   continue;
-               case NCCSW: 
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   docc (cp, 0);
-                   continue;
-
-               case EDITRSW: 
-                   if (!(ed = *argp++) || *ed == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   nedit = 0;
-                   continue;
-               case NEDITSW:
-                   nedit++;
-                   continue;
-                   
-               case WHATSW: 
-                   if (!(whatnowproc = *argp++) || *whatnowproc == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   nwhat = 0;
-                   continue;
-#ifdef MHE
-               case BILDSW: 
-                   buildsw++;  /* fall... */
-#endif /* MHE */
-               case NWHATSW: 
-                   nwhat++;
-                   continue;
-
-               case FCCSW: 
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   dp = NULL;
-                   if (*cp == '@')
-                       cp = dp = path (cp + 1, TSUBCWF);
-                   if (fcc)
-                       fcc = add (", ", fcc);
-                   fcc = add (cp, fcc);
-                   if (dp)
-                       free (dp);
-                   continue;
-
-               case FILESW: 
-                   if (file)
-                       adios (NULL, "only one file at a time!");
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   file = path (cp, TFILE);
-                   continue;
-               case FILTSW:
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   filter = getcpy (etcpath (cp));
-                   mime = 0;
-                   continue;
-               case FORMSW: 
-                   if (!(form = *argp++) || *form == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   continue;
-
-               case FRMTSW: 
-                   filter = getcpy (etcpath (mhlreply));
-                   mime = 0;
-                   continue;
-               case NFRMTSW: 
-                   filter = NULL;
-                   continue;
-
-               case INPLSW: 
-                   inplace++;
-                   continue;
-               case NINPLSW: 
-                   inplace = 0;
-                   continue;
-
-               case MIMESW:
-                   mime++;
-                   filter = NULL;
-                   continue;
-               case NMIMESW:
-                   mime = 0;
-                   continue;
-
-               case QURYSW: 
-                   querysw++;
-                   continue;
-               case NQURYSW: 
-                   querysw = 0;
-                   continue;
-
-               case WIDTHSW: 
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   if ((outputlinelen = atoi (cp)) < 10)
-                       adios (NULL, "impossible width %d", outputlinelen);
-                   continue;
-
-               case DFOLDSW: 
-                   if (dfolder)
-                       adios (NULL, "only one draft folder at a time!");
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
-                                   *cp != '@' ? TFOLDER : TSUBCWF);
-                   continue;
-               case DMSGSW:
-                   if (dmsg)
-                       adios (NULL, "only one draft message at a time!");
-                   if (!(dmsg = *argp++) || *dmsg == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   continue;
-               case NDFLDSW: 
-                   dfolder = NULL;
-                   isdf = NOTOK;
-                   continue;
-           }
+       switch (smatch(cp, ccswitches)) {
+       case AMBIGSW:
+               ambigsw(cp, ccswitches);
+               exit(EX_USAGE);
+       case UNKWNSW:
+               adios(EX_USAGE, NULL, "-%scc %s unknown", ccflag ? "" : "no", cp);
+
+       case CTOSW:
+               ccto = ccflag;
+               break;
+
+       case CCCSW:
+               cccc = ccflag;
+               break;
+
+       case CMESW:
+               ccme = ccflag;
+               break;
+
+       case CALSW:
+               ccto = cccc = ccme = ccflag;
+               break;
        }
-       if (*cp == '+' || *cp == '@') {
-           if (folder)
-               adios (NULL, "only one folder at a time!");
-           else
-               folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
-       } else {
-           if (msg)
-               adios (NULL, "only one message at a time!");
-           else
-               msg = cp;
+}
+
+
+
+
+static void
+replout(FILE *inb, char *drft, struct msgs *mp,
+       int mime, char *form, char *filter)
+{
+       int state, i;
+       struct comp *cptr;
+       char *tmpbuf;
+       char **nxtbuf;
+       char **ap;
+       struct comp **savecomp;
+       int char_read = 0, format_len, mask;
+       char name[NAMESZ], *scanl;
+       unsigned char *cp;
+       FILE *out;
+
+       mask = umask(~m_gmprot());
+       if ((out = fopen(drft, "w")) == NULL)
+               adios(EX_CANTCREAT, drft, "unable to create");
+
+       umask(mask);
+
+       /* get new format string */
+       cp = new_fs(form, NULL);
+       format_len = strlen(cp);
+
+       /* compile format string */
+       ncomps = fmt_compile(cp, &fmt) + 1;
+
+       nxtbuf = compbuffers = (char **)
+                       mh_xcalloc((size_t) ncomps, sizeof(char *));
+       savecomp = used_buf = (struct comp **)
+                       mh_xcalloc((size_t) (ncomps+1), sizeof(struct comp *));
+       savecomp += ncomps + 1;
+       *--savecomp = NULL;  /* point at zero'd end minus 1 */
+
+       for (i = ncomps; i--; )
+               *nxtbuf++ = mh_xmalloc(SBUFSIZ);
+
+       nxtbuf = compbuffers;  /* point at start */
+       tmpbuf = *nxtbuf++;
+
+       for (ap = addrcomps; *ap; ap++) {
+               FINDCOMP(cptr, *ap);
+               if (cptr)
+                       cptr->c_type |= CT_ADDR;
+       }
+
+       /*
+       ** ignore any components killed by command line switches
+       */
+       if (!ccto) {
+               FINDCOMP(cptr, "to");
+               if (cptr)
+                       cptr->c_name = "";
+       }
+       if (!cccc) {
+               FINDCOMP(cptr, "cc");
+               if (cptr)
+                       cptr->c_name = "";
        }
-    }
-
-    if (ccto == -1) 
-       ccto = groupreply;
-    if (cccc == -1)
-       cccc = groupreply;
-    if (ccme == -1)
-       ccme = groupreply;
-
-    cwd = getcpy (pwd ());
-
-    if (!context_find ("path"))
-       free (path ("./", TFOLDER));
-    if (file && (msg || folder))
-       adios (NULL, "can't mix files and folders/msgs");
-
-try_it_again:
-
-#ifdef MHE
-    strncpy (drft, buildsw ? m_maildir ("reply")
-                         : m_draft (dfolder, NULL, NOUSE, &isdf), sizeof(drft));
-
-    /* Check if a draft exists */
-    if (!buildsw && stat (drft, &st) != NOTOK) {
-#else
-    strncpy (drft, m_draft (dfolder, dmsg, NOUSE, &isdf), sizeof(drft));
-
-    /* Check if a draft exists */
-    if (stat (drft, &st) != NOTOK) {
-#endif /* MHE */
-       printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
-       for (i = LISTDSW; i != YESW;) {
-           if (!(argp = getans ("\nDisposition? ", isdf ? aqrnl : aqrl)))
-               done (1);
-           switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
-               case NOSW: 
-                   done (0);
-               case NEWSW: 
-                   dmsg = NULL;
-                   goto try_it_again;
-               case YESW: 
-                   break;
-               case LISTDSW: 
-                   showfile (++argp, drft);
-                   break;
-               case REFILSW: 
-                   if (refile (++argp, drft) == 0)
-                       i = YESW;
-                   break;
-               default: 
-                   advise (NULL, "say what?");
-                   break;
-           }
+       if ((cp = getenv("USER"))) {
+               FINDCOMP(cptr, "user");
+               if (cptr)
+                       cptr->c_text = getcpy(cp);
        }
-    }
+       if (!ccme)
+               ismymbox(NULL);
 
-    if (file) {
        /*
-        * We are replying to a file.
-        */
-       anot = 0;       /* we don't want to annotate a file */
-    } else {
+       ** pick any interesting stuff out of msg "inb"
+       */
+       for (state = FLD;;) {
+               state = m_getfld(state, name, tmpbuf, SBUFSIZ, inb);
+               switch (state) {
+               case FLD:
+               case FLDPLUS:
+                       /*
+                       ** if we're interested in this component, save
+                       ** a pointer to the component text, then start
+                       ** using our next free buffer as the component
+                       ** temp buffer (buffer switching saves an extra
+                       ** copy of the component text).
+                       */
+                       if ((cptr = wantcomp[CHASH(name)]))
+                               do {
+                                       if (!mh_strcasecmp(name, cptr->c_name)) {
+                                               char_read += msg_count;
+                                               if (! cptr->c_text) {
+                                                       i = strlen(cptr->c_text = tmpbuf) - 1;
+                                                       if (tmpbuf[i] == '\n')
+                                                               tmpbuf[i] = '\0';
+                                                       *--savecomp = cptr;
+                                                       tmpbuf = *nxtbuf++;
+                                               } else {
+                                                       i = strlen(cp = cptr->c_text) - 1;
+                                                       if (cp[i] == '\n') {
+                                                               if (cptr->c_type & CT_ADDR) {
+                                                                       cp[i] = '\0';
+                                                                       cp = add(",\n\t", cp);
+                                                               } else {
+                                                                       cp = add("\t", cp);
+                                                               }
+                                                       }
+                                                       cptr->c_text = add(tmpbuf, cp);
+                                               }
+                                               while (state == FLDPLUS) {
+                                                       state = m_getfld(state, name, tmpbuf,
+                                                                                         SBUFSIZ, inb);
+                                                       cptr->c_text = add(tmpbuf, cptr->c_text);
+                                                       char_read += msg_count;
+                                               }
+                                               break;
+                                       }
+                               } while ((cptr = cptr->c_next));
+
+                       while (state == FLDPLUS)
+                               state = m_getfld(state, name, tmpbuf,
+                                               SBUFSIZ, inb);
+                       break;
+
+               case LENERR:
+               case FMTERR:
+               case BODY:
+               case FILEEOF:
+                       goto finished;
+
+               default:
+                       adios(EX_SOFTWARE, NULL, "m_getfld() returned %d", state);
+               }
+       }
+
        /*
-        * We are replying to a message.
-        */
-       if (!msg)
-           msg = "cur";
-       if (!folder)
-           folder = getfolder (1);
-       maildir = m_maildir (folder);
-
-       if (chdir (maildir) == NOTOK)
-           adios (maildir, "unable to change directory to");
-
-       /* read folder and create message structure */
-       if (!(mp = folder_read (folder)))
-           adios (NULL, "unable to read folder %s", folder);
-
-       /* check for empty folder */
-       if (mp->nummsg == 0)
-           adios (NULL, "no messages in %s", folder);
-
-       /* parse the message range/sequence/name and set SELECTED */
-       if (!m_convert (mp, msg))
-           done (1);
-       seq_setprev (mp);       /* set the previous-sequence */
-
-       if (mp->numsel > 1)
-           adios (NULL, "only one message at a time!");
-
-       context_replace (pfolder, folder);      /* update current folder   */
-       seq_setcur (mp, mp->lowsel);    /* update current message  */
-       seq_save (mp);                  /* synchronize sequences   */
-       context_save ();                        /* save the context file   */
-    }
-
-    msg = file ? file : getcpy (m_name (mp->lowsel));
-
-    if ((in = fopen (msg, "r")) == NULL)
-       adios (msg, "unable to open");
-
-    /* find form (components) file */
-    if (!form) {
-       if (groupreply)
-           form = etcpath (replgroupcomps);
-       else
-           form = etcpath (replcomps);
-    }
-
-    replout (in, msg, drft, mp, outputlinelen, mime, form, filter, fcc);
-    fclose (in);
-
-    if (nwhat)
-       done (0);
-    what_now (ed, nedit, NOUSE, drft, msg, 0, mp,
-           anot ? "Replied" : NULL, inplace, cwd);
-    return done (1);
+       ** format and output the header lines.
+       */
+finished:
+
+       /*
+       ** if there's a "Subject" component, strip any "Re:"s off it
+       */
+       FINDCOMP(cptr, "subject")
+       if (cptr && (cp = cptr->c_text)) {
+               char *sp = cp;
+
+               for (;;) {
+                       while (isspace(*cp))
+                               cp++;
+                       if(uprf(cp, "re:"))
+                               cp += 3;
+                       else
+                               break;
+                       sp = cp;
+               }
+               if (sp != cptr->c_text) {
+                       cp = cptr->c_text;
+                       cptr->c_text = getcpy(sp);
+                       free(cp);
+               }
+       }
+       i = format_len + char_read + 256;
+       scanl = mh_xmalloc((size_t) i + 2);
+       dat[0] = 0;
+       dat[1] = 0;
+       dat[2] = 0;
+       dat[3] = OUTPUTLINELEN;
+       dat[4] = 0;
+       fmt_scan(fmt, scanl, i, dat);
+       fputs(scanl, out);
+       if (badaddrs) {
+               fputs("\nrepl: bad addresses:\n", out);
+               fputs( badaddrs, out);
+       }
+
+       /* Check if we should filter the message */
+       if (filter) {
+               fflush(out);
+               if (ferror(out))
+                       adios(EX_IOERR, drft, "error writing");
+
+               replfilter(inb, out, filter);
+       }
+
+       fflush(out);
+       if (ferror(out))
+               adios(EX_IOERR, drft, "error writing");
+       fclose(out);
+
+       if (mime && mp) {
+               /* add an attachment header */
+               char buffer[BUFSIZ];
+
+               snprintf(buffer, sizeof buffer, "+%s %s",
+                               mp->foldpath, m_name(mp->lowsel));
+               if (execprogl("anno", "anno", "-append", "-nodate",
+                               drft, "-comp", attach_hdr, "-text", buffer,
+                               (char *)NULL) != 0) {
+                       advise(NULL, "unable to add attachment header");
+               }
+       }
+
+       /* return dynamically allocated buffers */
+       free(scanl);
+       for (nxtbuf = compbuffers, i = ncomps; (cptr = *savecomp++);
+                       nxtbuf++, i--)
+               free(cptr->c_text);  /* if not nxtbuf, nxtbuf already freed */
+       while ( i-- > 0)
+               free(*nxtbuf++);  /* free unused nxtbufs */
+       free((char *) compbuffers);
+       free((char *) used_buf);
+}
+
+static char *buf;  /* our current working buffer */
+static char *bufend;  /* end of working buffer */
+static char *last_dst;  /* buf ptr at end of last call */
+static unsigned int bufsiz=0;  /* current size of buf */
+
+#define BUFINCR 512  /* how much to expand buf when if fills */
+
+#define CPY(s) { cp = (s); while ((*dst++ = *cp++)) ; --dst; }
+
+/*
+** check if there's enough room in buf for str.
+** add more mem if needed
+*/
+#define CHECKMEM(str) \
+       if ((len = strlen(str)) >= bufend - dst) {\
+               int i = dst - buf;\
+               int n = last_dst - buf;\
+               bufsiz += ((dst + len - bufend) / BUFINCR + 1) * BUFINCR;\
+               buf = mh_xrealloc(buf, bufsiz);\
+               dst = buf + i;\
+               last_dst = buf + n;\
+               bufend = buf + bufsiz;\
+       }
+
+
+/*
+** fmt_scan will call this routine if the user includes the function
+** "(formataddr {component})" in a format string.  "orig" is the
+** original contents of the string register.  "str" is the address
+** string to be formatted and concatenated onto orig.  This routine
+** returns a pointer to the concatenated address string.
+**
+** We try to not do a lot of malloc/copy/free's (which is why we
+** don't call "getcpy") but still place no upper limit on the
+** length of the result string.
+**
+** This routine is an override for the equally named one in sbr/fmt_addr.c.
+** Don't delete it!
+*/
+char *
+formataddr(char *orig, char *str)
+{
+       int len;
+       char baddr[BUFSIZ], error[BUFSIZ];
+       int isgroup;
+       char *dst;
+       char *cp;
+       char *sp;
+       struct mailname *mp = NULL;
+
+       /* if we don't have a buffer yet, get one */
+       if (bufsiz == 0) {
+               buf = mh_xmalloc(BUFINCR);
+               last_dst = buf;  /* XXX */
+               bufsiz = BUFINCR - 6;  /* leave some slop */
+               bufend = buf + bufsiz;
+       }
+       /*
+       ** If "orig" points to our buffer we can just pick up where we
+       ** left off.  Otherwise we have to copy orig into our buffer.
+       */
+       if (orig == buf)
+               dst = last_dst;
+       else if (!orig || !*orig) {
+               dst = buf;
+               *dst = '\0';
+       } else {
+               dst = last_dst;  /* XXX */
+               CHECKMEM(orig);
+               CPY(orig);
+       }
+
+       /* concatenate all the new addresses onto 'buf' */
+       for (isgroup = 0; (cp = getname(str)); ) {
+               if ((mp = getm(cp, dfhost, dftype, AD_NAME, error)) == NULL) {
+                       snprintf(baddr, sizeof(baddr), "\t%s -- %s\n",
+                                       cp, error);
+                       badaddrs = add(baddr, badaddrs);
+                       continue;
+               }
+               if (isgroup && (mp->m_gname || !mp->m_ingrp)) {
+                       *dst++ = ';';
+                       isgroup = 0;
+               }
+               if (insert(mp)) {
+                       /* if we get here we're going to add an address */
+                       if (dst != buf) {
+                               *dst++ = ',';
+                               *dst++ = ' ';
+                       }
+                       if (mp->m_gname) {
+                               CHECKMEM(mp->m_gname);
+                               CPY(mp->m_gname);
+                               isgroup++;
+                       }
+                       sp = adrformat(mp);
+                       CHECKMEM(sp);
+                       CPY(sp);
+               }
+       }
+
+       if (isgroup)
+               *dst++ = ';';
+
+       *dst = '\0';
+       last_dst = dst;
+       return (buf);
+}
+
+
+static int
+insert(struct mailname *np)
+{
+       char buffer[BUFSIZ];
+       struct mailname *mp;
+
+       if (np->m_mbox == NULL)
+               return 0;
+
+       for (mp = &mq; mp->m_next; mp = mp->m_next) {
+               if (!mh_strcasecmp(np->m_host, mp->m_next->m_host) &&
+                               !mh_strcasecmp(np->m_mbox, mp->m_next->m_mbox))
+                       return 0;
+       }
+       if (!ccme && ismymbox(np))
+               return 0;
+
+       if (querysw) {
+               snprintf(buffer, sizeof(buffer), "Reply to %s? ",
+                               adrformat(np));
+               if (!gans(buffer, anoyes))
+                       return 0;
+       }
+       mp->m_next = np;
+       return 1;
 }
 
-void
-docc (char *cp, int ccflag)
+
+/*
+** Call mhl
+**
+** This function expects that argument out has been fflushed by the caller.
+*/
+static void
+replfilter(FILE *in, FILE *out, char *filter)
 {
-    switch (smatch (cp, ccswitches)) {
-       case AMBIGSW: 
-           ambigsw (cp, ccswitches);
-           done (1);
-       case UNKWNSW: 
-           adios (NULL, "-%scc %s unknown", ccflag ? "" : "no", cp);
-
-       case CTOSW: 
-           ccto = ccflag;
-           break;
-
-       case CCCSW: 
-           cccc = ccflag;
-           break;
-
-       case CMESW: 
-           ccme = ccflag;
-           break;
-
-       case CALSW: 
-           ccto = cccc = ccme = ccflag;
-           break;
-    }
+       int pid, n;
+       char *errstr;
+
+       if (filter == NULL)
+               return;
+
+       if (access(filter, R_OK) == NOTOK)
+               adios(EX_IOERR, filter, "unable to read");
+
+       rewind(in);
+       lseek(fileno(in), (off_t) 0, SEEK_SET);
+
+       switch (pid = fork()) {
+       case NOTOK:
+               adios(EX_OSERR, "fork", "unable to");
+
+       case OK:
+               dup2(fileno(in), fileno(stdin));
+               dup2(fileno(out), fileno(stdout));
+               for (n=3; n<OPEN_MAX; n++) {
+                       close(n);
+               }
+
+               execlp("mhl", "mhl", "-form", filter, NULL);
+               errstr = strerror(errno);
+               write(2, "unable to exec mhl: ", 20);
+               write(2, errstr, strlen(errstr));
+               write(2, "\n", 1);
+               _exit(EX_OSERR);
+
+       default:
+               if (pidXwait(pid, "mhl"))
+                       exit(EX_SOFTWARE);
+               fseek(out, 0L, SEEK_END);
+               break;
+       }
 }