/*
 * xmail - X window system interface to the mail program
 *
 * Copyright 1990,1991,1992 by National Semiconductor Corporation
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of National Semiconductor Corporation not
 * be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * NATIONAL SEMICONDUCTOR CORPORATION MAKES NO REPRESENTATIONS ABOUT THE
 * SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS"
 * WITHOUT EXPRESS OR IMPLIED WARRANTY.  NATIONAL SEMICONDUCTOR CORPORATION
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  IN NO
 * EVENT SHALL NATIONAL SEMICONDUCTOR CORPORATION BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author:  Michael C. Wagnitz - National Semiconductor Corporation
 *
 */


#include "global.h"
#include "MailwatchP.h"
#include "xmailregexp.h"
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/signal.h>
#include <errno.h>
#include <pwd.h>

#ifdef vms
extern int	noshare errno;
extern int	noshare sys_nerr;
extern char	noshare *sys_errlist[];
#else
#if !defined(__FreeBSD__)
extern int	errno;
extern int	sys_nerr;
extern char	*sys_errlist[];
#endif
#endif

extern	char	otherBuf[BUFSIZ];

#if ! defined(SIGCHLD) && defined(SIGCLD)
#define	SIGCHLD		SIGCLD
#endif

/*
** @(#)Autograph() - Add user's Sign or sign autograph to outgoing mail
**                   Then make button insensitive, to prevent multiple calls.
**                   If requested autograph signature does not exist, tell user.
**		     Because we can now be called by a translation table entry,
**		     check to see if already insensitive before proceeding.
*/
/* ARGSUSED */
void
Autograph(w, closure, call_data)
Widget	w;
XtPointer	closure;
XtPointer	call_data;		/* unused */
{
 int		n;
 String		cp, autograph;
 char		tmp[BUFSIZ], *C = (char *) closure;
 FILE		*fp;
 struct stat	st_buf;


 if (XtIsSensitive(w)) {	 /* if okay to proceed... */
    (void) strcpy(tmp, "Sign");  /* Default action is to use Sign autograph */
    if (*C == 'a') tmp[0] = 's';
    autograph = GetMailEnv(tmp); /* First, see if an autograph entry exists */

    if (! autograph) {
       /*
       ** If no Sign or sign, see if the user has created a .signature file
       */
       st_buf.st_size = 0;
       (void) sprintf(tmp, "%s/.Signature", HOME);
       if (*C == 'a')
          (void) sprintf(tmp, "%s/.signature", HOME);
       if (stat(tmp, &st_buf) != -1 &&
           st_buf.st_size < BUFSIZ &&
          (fp = fopen(tmp, "r")) != NULL) {
          autograph = (String) XtMalloc((unsigned) st_buf.st_size + 1);
          n = fread(autograph, sizeof(char), (int) st_buf.st_size, fp);
          autograph[n] = '\0';
          (void) fclose(fp);
         } else {
          (void) strcpy(tmp, "Cannot find a 'Sign'ature to append\n");
          if (*C == 'a') tmp[15] = 's';
          if (st_buf.st_size) {
             (void) sprintf(tmp,"Cannot append your .Signature (exceeds %d characters)\n",
                    BUFSIZ - 1);
             if (*C == 'a') tmp[20] = 's';
            }
          Bell(tmp);
          return;
         }
      } else {
       if ((int)strlen(autograph) >= BUFSIZ) {
          (void) sprintf(tmp,"Cannot append your Signature (exceeds %d characters)\n",
                    BUFSIZ - 1);
          if (*C == 'a') tmp[19] = 's';
          Bell(tmp);
          XtFree((String) autograph);
          return;
         }
      }

    XtSetSensitive(w, False);	/* Don't let us be pressed more than once */
    if (*C == 'A')		/* also make other sign button inoperative */
       XtSetSensitive(XtNameToWidget(XtParent(w), "autograph"), False);
    else			/* if this was a request for lowercase sign */
       XtSetSensitive(XtNameToWidget(XtParent(w), "Autograph"), False);

    for (cp = autograph, n = 0; n < BUFSIZ - 1 && *cp; cp++)
        if (*cp == '\\' && *(cp + 1) == 'n') {
           tmp[n++] = '\n';	/* Replace newline strings with a character */
           cp++;
          } else tmp[n++] = *cp;

    if (tmp[n - 1] != '\n')	/* make sure msg ends with a newline */
       tmp[n++] = '\n';
    tmp[n] = '\0';

    if ((fp = fopen(tmpName, "a")) != NULL) {
       if (*tmp != '-' || *(tmp + 1) != '-')	/* add a signature separator */
          (void) fwrite("-- \n", sizeof(char), 4, fp);
       (void) fwrite(tmp, sizeof(* tmp), strlen(tmp), fp);
       (void) fclose(fp);
      }
    XtFree((String) autograph);
   } /* end - if we were currently sensitive */
} /* Autograph */


/*
** @(#)endDelivery() - catch the signal when the delivery child finishes
*/
#if defined(sun) 
void
#else
int
#endif
endDelivery(signum)
int	signum;
{
 int	status;


 if (signum == SIGCHLD &&
     mailpid != wait(&status))		/* in case mail child was what died */
 (void) signal(SIGCHLD, SIG_DFL);	/* turn off the endDelivery hook */

#if !defined(sun) && !defined(SVR4)
 return 1;
#endif

} /* endDelivery */


/*
** @(#)DoCopy() - send a copy of the message to the indicated folder file
*/
DoCopy(file, to, subject, inreply, cc, bcc, outfolder, edits, add_face)
char	*file, *to, *subject, *inreply, *cc, *bcc;
int	outfolder, edits, add_face;
{
 int		len, n;
 time_t		clock;
 char		*m, tmp[BUFSIZ], From[BUFSIZ], Copy[BUFSIZ];
 MailwatchWidget mb = (MailwatchWidget) XtNameToWidget(toplevel,"icon.mailbox");
 FILE		*fp, *xf;
 struct stat	st_buf;


 if (file[0]) {
    st_buf.st_size = 0;			/* (in case msg file does not exist) */
    (void) stat(tmpName, &st_buf);

    (void) time(&clock);
    (void) sprintf(From, "From %s %24.24s\n", mb->mailbox.username, (char *) ctime(&clock));

    switch (file[0]) {
       case '|':			/* accomodate alias with Sun pipe cmd */
       (void) sprintf(Copy, "%s.pipe", tmpName);
                break;
       case '~':			/* file with a tilde expansion name */
          if (file[1] == '/')
             (void) sprintf(Copy, "%s%s", HOME, &file[1]);
          else {
             struct passwd *pw;

             for (len = 0, n = 1; file[n] && file[n] != '/';)
                 tmp[len++] = file[n++];
             tmp[len] = '\0';

             if (pw = getpwnam(tmp))
                (void) sprintf(Copy, "%s%s", pw->pw_dir, &file[n]);
             else (void) strcpy(Copy, file);
            }
                break;
       case '/':			/* file with an absolute path name */
                (void) strcpy(Copy, file);
                break;
       case '+':			/* file with a folder relative name */
                if (! foldir[0])
                   (void) strcpy(Copy, file);
                else
                   (void) sprintf(Copy, "%s%s", foldir, &file[1]);
                break;

        default:
                if (file[0] == '.' && file[1] == '/')
                   (void) strcpy(Copy, file);
                else if (outfolder == 0 || ! foldir[0]) {
                   if (strchr(file, '/') == NULL)	/* no dir reference */
                      (void) strcpy(Copy, file);
                   else		/* assume file with dir is relative to home */
                      (void) sprintf(Copy, "%s/%s", HOME, file);
                  } else {	/* if outfolder (and folder name is included) */
                      (void) sprintf(Copy, "%s%s", foldir, file);
                  }
                break;
      }

    if ((fp = fopen(Copy, "a")) == NULL) {
       /*
       ** Can't use the Bell routine internally, because we're a child.
       ** So, just pump the message to stderr and let user deal with it.
       */
       (void) sprintf(tmp, "Could not write file %s - %s\n", Copy,
              (errno < sys_nerr)? sys_errlist[errno]:"Unknown error");
       (void) fprintf(stderr, tmp);
      } else {
                     (void) fprintf(fp, From);
       if (*to)      (void) fprintf(fp, "To: %s\n", to);
       if (*subject) (void) fprintf(fp, "Subject: %s\n", subject);
       if (*inreply) (void) fprintf(fp, "%s\n", inreply);
       /*
       ** Add support for custom header information as a resource
       ** and for editheaders mail option.  If editheaders, find
       ** custom header information in message text.
       */
       if (edits) {
          int	skip;


          if (st_buf.st_size) {
             xf = fopen(tmpName, "r");
             while (fgets(tmp, BUFSIZ, xf) != NULL) {
                if (tmp[0] == '\n') break;
                if (tmp[0] != ' ' && tmp[0] != '\t') skip = 0;
                if (strncmp(tmp,"To: ", 4) == 0) { skip = 1; continue; }
                if (strncmp(tmp,"Subject: ", 9) == 0) { skip = 1; continue; }
                if (strncmp(tmp,"In-Reply-To: ",13)==0) { skip = 1; continue; }
                if (strncmp(tmp,"Forwarding: ",12)==0) { skip = 1; continue; }
                if (strncmp(tmp,"Cc: ", 4) == 0) { skip = 1; continue; }
                if (strncmp(tmp,"Bcc: ", 5) == 0) { skip = 1; continue; }
                if (! skip) (void) fprintf(fp, "%s", tmp);
               }
             (void) fclose(xf);
            } else
             if (m = XGetDefault(XtDisplay(toplevel), "XMail", "customHeader"))
               {
                if (m[0] == '"' || m[0] == "'"[0]) {
                   bcopy(m + 1, m, strlen(m) - 1);
                   while (LASTCH(m) == '"' || LASTCH(m) == "'"[0])
                         LASTCH(m) = '\0';
                  }
                if (LASTCH(m) != '\n')
                   (void) fprintf(fp, "%s\n", m);
                else
                   (void) fprintf(fp, "%s", m);
               }
         } else
       if (m = XGetDefault(XtDisplay(toplevel), "XMail", "customHeader")) {
           if (m[0] == '"' || m[0] == "'"[0]) {
              bcopy(m + 1, m, strlen(m) - 1);
              while (LASTCH(m) == '"' || LASTCH(m) == "'"[0])
                    LASTCH(m) = '\0';
             }
          if (LASTCH(m) != '\n')
                     (void) fprintf(fp, "%s\n", m);
          else
                     (void) fprintf(fp, "%s", m);
         }
       if (*cc)      (void) fprintf(fp, "Cc: %s\n", cc);
       if (*bcc)     (void) fprintf(fp, "Bcc: %s\n", bcc);
#ifdef X_FACE
       if (add_face) {
          (void) sprintf(tmp, "%s/.face", HOME);
          if (xf = fopen(tmp, "r")) {
             n = 1;
             while (fgets(tmp, BUFSIZ, xf) != NULL) {
                   if (n) {
                      n = 0;
                      if (strncmp(tmp, "X-Face:", 7) != 0)
                           (void) fprintf(fp, "X-Face:");
                     }
                   (void) fprintf(fp, "%s", tmp);
                  }
             (void) fclose(xf);
            }
         }
#endif /* X_FACE */
       (void) fprintf(fp, "\n");
       if (st_buf.st_size) {
          xf = fopen(tmpName, "r");
          while (fgets(tmp, BUFSIZ, xf) != NULL) {
                if (edits) {	/* if headers edited, drop them now */
                   if (tmp[0] == '\n') edits = 0;
                   continue;
                  }
                if (strncmp(tmp,"From ",5) == 0) (void)fprintf(fp,">");
                (void) fprintf(fp, "%s", tmp);
               }
          (void) fclose(xf);
          if (*tmp != '\n')
             (void) fprintf(fp, "\n");
         }
       (void) fclose(fp);
       if (file[0] == '|') {
          (void) sprintf(tmp, "/bin/cat %s %s", Copy, file);
          (void) system(tmp);
          (void) unlink(Copy);
         }
      } /* end - if we opened the target file successfully */
   } /* end - if passed file variable is not null */
} /* end - DoCopy */


/*
** @(#)Done() - Send composed message - if closure data says "Deliver"
*/
/* ARGSUSED */
void
Done(w, closure, call_data)
Widget w;
XtPointer closure;
XtPointer call_data;
{
 int		outfolder, n, len, edit_headers;
 int		add_face = 1;		/* Are we using faces.sendmail script */
 String		m, q, z ;
 char		record[BUFSIZ], Copy[BUFSIZ], tmp[BUFSIZ], addressees[BUFSIZ];
 char		To[BUFSIZ], Subject[BUFSIZ], Cc[BUFSIZ], Bcc[BUFSIZ];
 char		*c = (char *) closure;
 Widget		popup;
 Arg		args[1];
 FILE		*fp, *xf, *popen();
 struct stat	st_buf;
 struct passwd	*pw;


 Bell(Default_Status_Info);

 popup = XtNameToWidget(toplevel, "topBox.commandPanel.Send.popup");
 if (! popup) return;			/* SOMEthing would be VERY wrong here */

 XtPopdown(popup);			/* remove from view but don't destroy */
 XWarpPointer(XtDisplay(toplevel), None,
              XtWindow(XtNameToWidget(toplevel, "topBox.textWindow.text")),
              0,0,0,0, XMail.helpX * 2, XMail.helpY);

 st_buf.st_size = 0;			/* (in case msg file does not exist) */
 (void) stat(tmpName, &st_buf);
 record[0] = Copy[0] = tmp[0] = addressees[0] = '\0';
 To[0] = Subject[0] = Cc[0] = Bcc[0] = '\0';

 if (strcmp(c, "Deliver") != 0) {
    /*
    ** Restore sensitivity of the Autograph, Send, and reply for next time
    */
    XtSetSensitive(XtNameToWidget(XtParent(w), "Autograph"), True);
    XtSetSensitive(XtNameToWidget(XtParent(w), "autograph"), True);
    XtSetSensitive(XtParent(popup), True);
    XtSetSensitive(XtNameToWidget(toplevel, "topBox.commandPanel.reply"), True);
    n = st_buf.st_size;		/* the number of bytes in the message text */
    if (n == 0 && *c == 'c')
       Bell("No text to save in your dead letter file\n");
    else {
       if (n && (*c == 'c' || ! Confirm("REALLY discard this text"))) {
          if (! (m = GetMailEnv("DEAD")))
             (void) sprintf(Copy, "%s/dead.letter", HOME);
          else {
             (void) strcpy(record, m);
             XtFree((String) m);
             if (record[0] != '~')
                (void) strcpy(Copy, record);	/* take whatever is given */
             else {
                if (record[1] == '/')
                   (void) sprintf(Copy, "%s%s", HOME, &record[1]);
                else {
                   for (len = 0, n = 1; record[n] && record[n] != '/';)
                       tmp[len++] = record[n++];
                   tmp[len] = '\0';

                   if (pw = getpwnam(tmp))
                      (void) sprintf(Copy, "%s%s", pw->pw_dir, &record[n]);
                   else (void) strcpy(Copy, record);
                  }
               }
            }
          st_buf.st_size = -1;		/* see if our target exists */
          (void) stat(Copy, &st_buf);
          if (fp = fopen(Copy, "a")) {
             xf = fopen(tmpName, "r");
             while (fgets(tmp, BUFSIZ, xf) != NULL) {
                   if (strncmp(tmp, "From ", 5) == 0) (void) fprintf(fp, ">");
                   (void) fprintf(fp, "%s", tmp);
                  }
             (void) fclose(xf);
             if (*tmp != '\n')
                (void) fprintf(fp, "\n");
             (void) fclose(fp);

             (void) sprintf(tmp, "\"%s\" [%s] (%d bytes)\n", Copy,
                 (st_buf.st_size >= 0) ? "Appended" : "New file", n);
             Bell(tmp);
            } else {
             (void) sprintf(tmp, "Could not write file %s - %s\n", Copy,
                    (errno < sys_nerr)? sys_errlist[errno] : "Unknown error");
             Bell(tmp); 
            }
         }
      }					/* end - if some message text exists */
    (void) unlink(tmpName);		/* remove the message text file */
   } else {				/* Deliver this message text */
    /*
    ** Call the alias() routine recursively, to de-alias the user's To:, Cc:,
    ** and Bcc: fields.
    */
    To[0] = Subject[0] = Cc[0] = Bcc[0] = otherBuf[0] = '\0';
    /*
    ** Retrieve current values from the header buffers
    */
    m = NULL;
    XtSetArg(args[0], XtNstring, &m);
    XtGetValues(XtNameToWidget(popup, "*SubjCc*To"), args, 1);
    if (m && *m) {
       (void) strncpy(addressees, m, BUFSIZ);
       (void) strncpy(To, m, BUFSIZ);	/* in case there's no user recipient */
       /*
       ** alias() adds filenames to a list and returns just user addresses
       ** (it also automatically wraps long header lines)
       */
       for (m = alias(addressees); strcmp(m, addressees); m = alias(addressees))
           (void) strncpy(addressees, m, BUFSIZ);
       if (addressees[0])		/* if some real users are named... */
          (void) strncpy(To, addressees, BUFSIZ);
      }
    
    m = NULL;
    XtSetArg(args[0], XtNstring, &m);
    XtGetValues(XtNameToWidget(popup, "*SubjCc*Subject"), args, 1);

    if ((! addressees[0] && ! otherBuf[0]) || (! *m && st_buf.st_size == 0)) {
       if (! addressees[0])
          Bell("No recipient specified\n"); 
       else
          Bell("No subject and no message body\n"); 
       Waiting = TRUE;		/* so popup's EnterNotify won't overwrite msg */
       /*
       ** FORCE the user to either complete the message or abort delivery
       */
       XtPopup(popup, XtGrabNone);

       XSync(XtDisplay(toplevel), False);

       XtAddEventHandler(popup, StructureNotifyMask, False, warp_handler, (XtPointer)NULL);
      } else {			/* if there is a message to be delivered... */
       /*
       ** Restore the sensitivity of the Autograph, Send, and reply buttons
       */
       XtSetSensitive(XtNameToWidget(XtParent(w), "Autograph"), True);
       XtSetSensitive(XtNameToWidget(XtParent(w), "autograph"), True);
       XtSetSensitive(XtParent(popup), True);
       XtSetSensitive(XtNameToWidget(toplevel, "topBox.commandPanel.reply"), True);

       if (*m) {
          /*
          ** Format the subject line to wrap on ~70 characters.  In case the
          ** user didn't install the xmail default resources file, also look
          ** for imbedded newlines in the Subject text.
          */
          for (n = 0, len = 9; *m && n < BUFSIZ; n++, len++) {
              Subject[n] = *m++;
              if (Subject[n] == '\n') {
                 if (! *m) Subject[n] = '\0';
                 else if (len < 35) Subject[n] = ' ';
                      else { Subject[++n] = '\t'; len = 8; }
                } else if (len % 70 == 0)
                          if (Subject[n] == ' ') {
                             Subject[++n] = '\n';
                             Subject[++n] = '\t';
                             len = 8;
                            } else len--;	/* break at a word boundary */
             }
          Subject[n] = '\0';
         }
    
       m = NULL;
       XtSetArg(args[0], XtNstring, &m);
       XtGetValues(XtNameToWidget(popup, "*SubjCc*Cc"), args, 1);
       if (m && *m) {
          (void) strncpy(tmp, m, BUFSIZ);
          for (m = alias(tmp); strcmp(m, tmp); m = alias(tmp))
              (void) strncpy(tmp, m, BUFSIZ);

          if (*m) {
             if (addressees[0]) (void) strcat(addressees, ", ");
             (void) strcat(addressees, m);
             (void) strncpy(Cc, m, BUFSIZ);
            }
         }

       m = NULL;
       XtSetArg(args[0], XtNstring, &m);
       XtGetValues(XtNameToWidget(popup, "*SubjCc*Bcc"), args, 1);
       if (m && *m) {
          (void) strncpy(tmp, m, BUFSIZ);
          for (m = alias(tmp); strcmp(m, tmp); m = alias(tmp))
              (void) strncpy(tmp, m, BUFSIZ);

          if (*m) {
             if (addressees[0]) (void) strcat(addressees, ", ");
             (void) strcat(addressees, m);
             (void) strncpy(Bcc, m, BUFSIZ);
            }
         }
       /*
       ** To avoid a bug in Sun's sendmail (for occasionally not being able to
       ** find the name of the real author of the message), strip commas from
       ** the address list and pass the recipient names to sendmail on the
       ** command line, ala Mail.  Also, strip all but the actual address from
       ** any compound addresses present in the list.
       */
       for (q = addressees; *q; q++) {
           if (*q == ',') {		/* remove any commas */
              if (q[1] == ' ') {	/* if comma is followed by a space */
                 for (m = q, z = q + 1; *z;) *m++ = *z++;
                 *m = '\0';		/* then shift string left by one */
                }
              else *q = ' ';		/* else substitute space for comma */
              q++;
             }
           if (*q == '\n' && *(q+1) == '\t') {	/* drop any newline tabs */
              for (m = q, z = q + 2; *z;) *m++ = *z++;
              *m = '\0';
             }
           if (z = strchr(q, ',')) {	/* find the end of next alias in list */
              n = (int) (z - q);
              for (m = tmp, z = q; *z && n--;) *m++ = *z++;
              *m = '\0';		/* shorten our search to just this */
              if ((m = strchr(tmp, '<')) || (m = strchr(tmp, '('))) {
                 if (*m == '<') {	/* if its a chevroned address type */
                    (void) sscanf(m, "<%[^>]>", Copy);
                    len = strlen(Copy);	/* save the address part of this only */
                    for (m = Copy; *m && len--;) *q++ = *m++;
                    for (m = q; *z;) *m++ = *z++;
                    *m = '\0';		/* shift rest of addressees left */
                    for (; *q && *q != ','; q++);
                    if (*q) q--;
                   } else {		/* must be a parenthetical style */
                    for (n = 0; tmp[n] && strchr(" \n\t", tmp[n]); n++);
                    if (1 == sscanf(&tmp[n], "%*[^)]) %s", Copy)) {
                       bcopy(Copy, q, strlen(Copy));
                       m = q + strlen(Copy);
                       bcopy(z, m, strlen(z) + 1);
                       if (q = strchr(q, ',')) q--;
                      } else {
                       (void) sscanf(&tmp[n], "%s", Copy);	/* address preceeds */
                       m = q + strlen(Copy);
                       bcopy(z, m, strlen(z) + 1);
                       if (q = strchr(q, ',')) q--;
                      }
                   }
                } else q = z - 1;	/* skip to alias end if not compound */
             } else {			/* last (or only) alias in the list */
              if ((m = strchr(q, '<')) || (m = strchr(q, '('))) {
                 if (*m == '<') {
                    if (! sscanf(q, "%*[^<]<%[^>]>", Copy))
                         (void) sscanf(q, "<%[^>]>", Copy);
                    bcopy(Copy, q, strlen(Copy) + 1);
                   } else {
                    for (z = q; strchr(" \n\t", *z); z++);
                    if (1 == sscanf(z, "%*[^)]) %s", Copy)) {
                       bcopy(Copy, q, strlen(Copy) + 1);
                      } else {
                       (void) sscanf(z, "%s", Copy);
                       z[strlen(Copy) + 1] = '\0';
                      }
                   }
                }
              break;			/* no more commas to be replaced */
             }
          }

       if (! (m = GetMailEnv("sendmail")))
            m = XtNewString("/usr/sbin/sendmail");
#ifdef X_FACE
       else {
          add_face = (strcmp(&m[strlen(m)-14], "faces.sendmail") != 0) ? 1 : 0;
         }
#endif /* X_FACE */
       /*
       ** The following arguments are passed to the sendmail command:
       **
       ** -oi	don't accept a dot on a line by itself as message termination
       **
       ** -om	send to "me" too, if I am a member of an alias expansion
       */
       (void) sprintf(tmp, "exec %s -oi -om %s\n", m, addressees);
       XtFree((String) m);

       initfoldir();

       if (m = GetMailEnv("outfolder")) {
          outfolder = 1;
          XtFree((String) m);
         } else outfolder = 0;

       if (m = GetMailEnv("record")) {
          (void) strncpy(record, m, BUFSIZ);
          XtFree((String) m);
         } else record[0] = '\0';

       if (m = GetMailEnv("editheaders")) {
          XtFree((String) m);
          edit_headers = 1;
         } else edit_headers = 0;

       /*
       ** Fork a child process to effect the message delivery functions.
       */
       if ((n = fork()) != 0) {			/* if we are not the child */
          if (n == -1) {			/* delivery fork failed ... */
             if (errno == ENOMEM)
                Bell("Not enough core for message delivery child\n");
             else
                Bell("No more processes - no message delivery!?!\n");
            } else				/* parent sets delivery hook */
             (void) signal(SIGCHLD, endDelivery);

          return;
         }
       /*
       ** Use this child process to effect our message delivery function.
       */
       (void) close(ConnectionNumber(XtDisplay(toplevel)));

       if (addressees[0]) {		/* if there are primary addresses... */
          /*
          ** First, mail the header information and message text (in temporary
          ** file) using sendmail.  If we fail, we can't use the Bell routine
          ** because we are a child process, so write any complaints to stderr.
          */
          if (! (fp = popen(tmp, "w")))
             (void) fprintf(stderr, "xmail: Couldn't connect to sendmail.\n"); 
          else {
             if (To[0])      (void) fprintf(fp, "To: %s\n", To);
             if (Subject[0]) (void) fprintf(fp, "Subject: %s\n", Subject);
             if (InReply[0]) (void) fprintf(fp, "%s\n", InReply);
             /*
             ** Add support for custom header information as a resource
             ** and for editheaders mail option.  If editheaders, find
             ** custom header information in message text.
             */
             if (edit_headers) {
                int	skip;

                if (st_buf.st_size) {
                   xf = fopen(tmpName, "r");
                   while (fgets(tmp, BUFSIZ, xf) != NULL) {
                      if (tmp[0] == '\n') break;
                      if (tmp[0] != ' ' && tmp[0] != '\t') skip = 0;
                      if (strncmp(tmp,"To: ", 4) == 0) {skip = 1; continue;}
                      if (strncmp(tmp,"Subject: ", 9) == 0) {skip=1;continue;}
                      if (strncmp(tmp,"In-Reply-To: ",13)==0) {skip=1;continue;}
                      if (strncmp(tmp,"Forwarding: ",12)==0) {skip=1;continue;}
                      if (strncmp(tmp,"Cc: ", 4) == 0) {skip = 1; continue;}
                      if (strncmp(tmp,"Bcc: ", 5) == 0) {skip = 1; continue;}
                      if (! skip) (void) fprintf(fp, "%s", tmp);
                     }
                   (void) fclose(xf);
                  } else
                   if (m = XGetDefault(XtDisplay(toplevel), "XMail", "customHeader"))
                     {
                      if (m[0] == '"' || m[0] == "'"[0]) {
                         bcopy(m + 1, m, strlen(m) - 1);
                         while (LASTCH(m) == '"' || LASTCH(m) == "'"[0])
                               LASTCH(m) = '\0';
                        }
                      if (LASTCH(m) != '\n')
                         (void) fprintf(fp, "%s\n", m);
                      else
                         (void) fprintf(fp, "%s", m);
                     }
               } else
             if (m = XGetDefault(XtDisplay(toplevel), "XMail", "customHeader"))
               {
                if (m[0] == '"' || m[0] == "'"[0]) {
                   bcopy(m + 1, m, strlen(m) - 1);
                   while (LASTCH(m) == '"' || LASTCH(m) == "'"[0])
                         LASTCH(m) = '\0';
                  }
                if (LASTCH(m) != '\n')
                   (void) fprintf(fp, "%s\n", m);
                else
                   (void) fprintf(fp, "%s", m);
               }

             if (Cc[0])      (void) fprintf(fp, "Cc: %s\n", Cc);
             if (Bcc[0])     (void) fprintf(fp, "Bcc: %s\n", Bcc);
#ifdef X_FACE
             /*
             ** Look for, and add contents of, user's ~/.face file unless
             ** user is already invoking the faces.sendmail script, which
             ** does the job itself.  In that case, don't add info here.
             ** To accomodate both cases, look for and add header if none. 
             */
             if (add_face) {
                (void) sprintf(tmp, "%s/.face", HOME);
                if (xf = fopen(tmp, "r")) {
                   n = 1;
                   while (fgets(tmp, BUFSIZ, xf) != NULL) {
                         if (n) {
                            n = 0;
                            if (strncmp(tmp, "X-Face:", 7) != 0)
                                 (void) fprintf(fp, "X-Face:");
                           }
                         (void) fprintf(fp, "%s", tmp);
                        }
                   (void) fclose(xf);
                  }
               }
#endif /* X_FACE */
             (void) fprintf(fp, "\n");	/* separate the header from any text */
             /*
             ** Now write message text (if any) to the sendmail process.  ANY
             ** line which begins with the keyword ``From '' gets prepended
             ** with a greater than (>) symbol.
             */
             if (st_buf.st_size) {
                xf = fopen(tmpName, "r");
                len = edit_headers;
                while (fgets(tmp, BUFSIZ, xf) != NULL) {
                      if (len) {	/* if headers edited, drop them now */
                         if (tmp[0] == '\n') len = 0;
                         continue;
                        }
                      if (strncmp(tmp,"From ",5) == 0) (void) fprintf(fp, ">");
                      (void) fprintf(fp, "%s", tmp);
                     }
                (void) fclose(xf);

                if (tmp[0] != '\n')
                   (void) fprintf(fp, "\n");	/* ensure a blank last line */
               }
             (void) pclose(fp);		/* this concludes sendmail delivery */
            }
          /*
          ** If user has set ``record'' in their .mailrc,
          ** add a message copy to that record file.
          */
          (void) DoCopy(record, To, Subject, InReply, Cc, Bcc,
                        outfolder, edit_headers, add_face);

         }			/* end - if there are any primary addresses */
       /*
       ** If other addresses exist, send copies to those files or folders.
       */
       if (*otherBuf) {
          for (m = otherBuf; *m;) {
              for (q = m; *q && *q != ','; q++);
              if (*q == ',') {
                 *q = '\0';
                 n = 1;
                } else n = 0;

              (void) DoCopy(m, To, Subject, InReply, Cc, Bcc,
                            outfolder, edit_headers, add_face);

              q += n;
              m = q;
             }			/* end - for each record in otherBuf */
         }			/* end - if there were records in otherBuf */
       (void) unlink(tmpName);	/* remove any message text */
       exit(1);			/* the delivery child sub-process */
       /* NOTREACHED */
      }				/* end - if there was something to deliver */
   }				/* end - if we wanted to deliver a message */
} /* Done */


/*
** @(#)DoIt() - send command - passed via client_data argument - to mail
*/
/* ARGSUSED */
void
DoIt(w, closure, call_data)
Widget		w;
XtPointer		closure;
XtPointer		call_data;
{
 int		n;
 Arg		args[1];
 String		c, buf;


 SetCursor(NORMAL);
 (void) sprintf(Command, "%s\n", closure);
 if (mailpid) {				/* If connections are okay,... */
    if ((n = match(command_pattern, Command)) != XM_C_FILE && n != XM_C_NEWMAIL) {
       SetCursor(WATCH);		/* will be reset by parser routine */
       writeMail(Command);
      } else {				/* check for commit of any changes */
       buf = NULL;
       XtSetArg(args[0], XtNlabel, &buf);
       XtGetValues(XtNameToWidget(toplevel, "topBox.titleBar.title"), args, 1);

       c = strrchr(buf, 'l');  if (c) c -= 2;
       if (! c || strncmp(c, "deleted", 7) != 0 ||
           strcmp(closure, "inc") == 0 ||
           Confirm("COMMIT all changes to this folder")) {
          writeMail(Command);
          SetCursor(WATCH);
          (void) strcpy(lastFolder, buf);	/* save for utimes update */
         }
      }
   } else if (XM_C_NEWMAIL != match(command_pattern, Command))
         Bell("No current mail connection\n");		/* if not 'Newmail' */
     else {
         SetCursor(WATCH);		/* will be reset by parser routine */
         if (mailargc > 2 && strcmp(mailargv[mailargc - 2], "-f") == 0) {
            mailargc -= 2;		/* throw away any folder argument */
            mailargv[mailargc] = NULL;	/* and NULL end of argument list */
           }
         callMail(mailargv);		/* restart the mail connections */
         (void) strcpy(Command, "Start"); /* Let em know we've re-started */
         UnsetNewmail(w, (XtPointer) NULL, (XtPointer) NULL);
         In_Bogus_Mail_File = False;	/* reset this so titleBar will chg */
        }
} /* DoIt */


/*
** @(#)DoPrint() - Call the PrintMsg action routine from a callback
*/
/* ARGSUSED */
void
DoPrint(w, closure, call_data)
Widget	w;
XtPointer closure;
XtPointer	call_data;
{
 PrintMsg(w, NULL, NULL, NULL);
} /* DoPrint */


/*
** @(#)DoQuit() - Terminate xmail after first closing mail connection
*/
/* ARGSUSED */
void
DoQuit(w, closure, call_data)
Widget w;
XtPointer closure;
XtPointer call_data;
{
 Arg		args[1];
 String		c, buf, C = (char *) closure;


 if (mailpid) {				/* check for commit of any changes */
    buf = NULL;
    XtSetArg(args[0], XtNlabel, &buf);
    XtGetValues(XtNameToWidget(toplevel, "topBox.titleBar.title"), args, 1);

    c = strrchr(buf, 'l');  if (c) c -= 2;
    if (*C != 'q' || ! c || strncmp(c, "deleted", 7) != 0 ||
        Confirm("Changes in folder.  REALLY quit")) {
       (void) sprintf(Command, "%s\n", C);
       writeMail(Command);
      } else return;
   }

 XCloseDisplay(XtDisplay(toplevel));

 _exit(0);
} /* DoQuit */


/*
** @(#)DoSet() - send specified set request to mail and destroy current menu.
**		 To accommodate those systems where mail cannot handle the
**		 'set no' commands, convert 'set no's to unsets.
*/
/* ARGSUSED */
void
DoSet(w, closure, call_data)
Widget	w;
XtPointer	closure;
XtPointer	call_data;
{
 char	*c, buf[32];


 if (! mailpid)
    Bell("No current mail connection\n");
 else {
    SetCursor(WATCH);
    c = w->core.name;
    if ((int)strlen(c) > 5 && strcmp(&c[strlen(c) - 6], "expert") == 0)
       if (*c == 'n')
            XMail.expert = (Boolean) 0;
       else XMail.expert = (Boolean) 1;
    else {
       if (*c == 'n')
          (void) sprintf(buf, "unset %s", &c[2]);
       else
          (void) sprintf(buf, "set %s", c);

       c = QueryMail(buf);
      }

    XtDestroyWidget(XtParent(XtParent(w)));
    SetCursor(NORMAL);
   }
} /* DoSet */


/* ARGSUSED */
/*
** @(#)DoWith() - send client_data command to mail with selected msg number
*/
void
DoWith(w, client_data, call_data)
Widget	w;
XtPointer	client_data;
XtPointer	call_data;
{
 int	num = 1;
 char 	*c = (char *) client_data;


 Waiting = TRUE;		/* so popup's EnterNotify won't overwrite msg */
 if (*c == 'n' || *c == '-')
    DoSelected(w, (XEvent *)NULL, &c, (Cardinal *)&num);
 else if (! mailpid) Bell("No current mail connection\n");
 else {
    SetCursor(WATCH);
    num = SelectionNumber(*c == 'u');

    if (num) (void) sprintf(Command, "%s %d\n", c, num);
    else (void) sprintf(Command, "%s \n", c);

    writeMail(Command);
   }
} /* DoWith */


/* ARGSUSED */
/*
** @(#)DropIt() - callback to destroy the current folder popup list(s)
*/
void
DropIt(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
 Widget	popup = XtNameToWidget(toplevel,"topBox.commandPanel.Folder.popupList");
 if (popup)
    XtDestroyWidget(popup);
 SetCursor(NORMAL);
} /* DropIt */


/*
** @(#)GetAliasName() - retrieve alias name from button label
*/
/* ARGSUSED */
void
GetAliasName(w, shell, call_data)
Widget		w;
Widget		shell;
XtPointer		call_data;		/* unused */
{
 Arg		args[1];
 String		alias_name;


 XtSetArg(args[0], XtNlabel, &alias_name);
 XtGetValues(w, (ArgList) args, 1);	/* get this entry's label value */

 XtPopdown(XtParent(XtParent(w)));	/* aliasList<-table<-entry */

 if (TextGetLastPos(shell))		/* if some alias is already in there */
    writeTo(shell, ", ", APPEND);	/* add comma separator between names */

 writeTo(shell, alias_name, APPEND);
} /* GetAliasName */


/*
** @(#)GetFolderName() - retrieve full folder name from button labels
*/
/* ARGSUSED */
void
GetFolderName(w, client_data, call_data)
Widget	w;
XtPointer	client_data, call_data;
{
 Arg		args[1];
 int		k, n, x;
 String		folder_name;
 Widget		shell;
 char		tmp[BUFSIZ], buf[BUFSIZ];


 XtSetArg(args[0], XtNlabel, &folder_name);
 XtGetValues(w, (ArgList) args, 1);

 if (! call_data)				/* just a simple label name */
    (void) sprintf(buf, "File: %s", folder_name);
 else {						/* multiple stack of names */
    tmp[0] = '\0';
    shell = w;
    (void) sscanf(call_data, "%d", &n);		/* using the nesting depth */

    for (x = 1, k = (n * 2) + n - 1; k; k--) {
        shell = shell->core.parent;		/* travel up the widget tree */
        if (++x == 3) {				/* when we get to a label... */
           x = 0;
           (void) strcpy(buf, shell->core.name); /* stuff label name in */
           (void) strcat(buf, tmp);		/* front of previous labels */
           (void) strcpy(tmp, buf);		/* to build a complete path */
          }
       }
    (void) sprintf(buf, "File: +%s%s", tmp, folder_name);
   }
 writeTo(XtNameToWidget(toplevel, "topBox.commandPanel.fileWindow"),
         buf, REPLACE);
} /* GetFolderName */


/*
** @(#)ReEdit() - Call the editMail routine to re-edit a message
*/
/* ARGSUSED */
void
ReEdit(w, closure, call_data)
Widget	w;		/* unused */
XtPointer closure;	/* unused */
XtPointer	call_data;	/* unused */
{
 Widget	Popup = XtNameToWidget(toplevel, "topBox.commandPanel.Send.popup");


 if (! Popup) {
    XBell(XtDisplay(toplevel), 33);
    return;				/* SOMEthing must be VERY wrong here */
   }

 XtPopdown(Popup);			/* pop down the send popup */

 editMail();				/* re-edit the message file */
} /* ReEdit */


/*
** @(#)Reply() - send a reply to the author of the selected message
**               include its text and/or copy the other recipients, if asked.
*/
/* ARGSUSED */
void
Reply(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
 int		n, fd, alwaysIgnore, edit_headers, erasable = 0;
 String		p, q, r, txt;
 char		*us, *getlogin(), *C = (char *) client_data;
 char		oldFrom[BUFSIZ], author[BUFSIZ], returnPath[BUFSIZ];
 char		toList[BUFSIZ], date[BUFSIZ], subject[BUFSIZ], ccList[BUFSIZ];
 char		Recipient[BUFSIZ];
 Widget		sb = XtNameToWidget(toplevel, "topBox.commandPanel.Send");
 
#if defined(SIGACTION) || defined(SVR4) || defined(linux)
 struct sigaction outvec; 
 
 (void) sigaction(SIGCHLD, NULL, &outvec);
 if (outvec.sa_handler != SIG_DFL)
#else
 struct	sigvec	ovec;

 (void) sigvec(SIGCHLD, NULL, &ovec);
 if (ovec.sv_handler != SIG_DFL)
#endif
   {
    Bell("Still delivering your previous message.  Please try again later.\n");
    return;
   }

 XtSetSensitive(sb, False);	/* Don't start more than one composition */
 XtSetSensitive(XtNameToWidget(toplevel, "topBox.commandPanel.reply"), False);

 oldFrom[0] = author[0] = returnPath[0] = date[0] = subject[0] = '\0';
 ccList[0] = toList[0] = '\0';

 if (p = GetMailEnv("editheaders")) {
    XtFree((String) p);
    edit_headers = 1;
   } else edit_headers = 0;

 if (*C != 's' || edit_headers) {
    if ((fd = open(tmpName, O_WRONLY | O_CREAT, 0600)) == -1) {
       Bell("xmail: Cannot open the temp file\n");
       return;
      }

    if (p = GetMailEnv("alwaysignore")) {
       XtFree((String) p);
       alwaysIgnore = (strchr("SRA", *C)) ? 1 : 0;
      } else alwaysIgnore = 0;

    if (alwaysIgnore)		/* do we need to include a limited copy? */
       (void) sprintf(Command, "p %d", SelectionNumber(False));
    else
       (void) sprintf(Command, "P %d", SelectionNumber(False));

    if ((p = GetMailEnv("indentprefix")) != NULL)
       erasable = 1;
    else
       p = "\t";

    txt = QueryMail(Command);

    switch (*C) {
       case 'S':
            (void) write(fd, "---------- Begin Forwarded Message ----------\n", 46);
            /*
            ** Any lines that begin with keyword 'From ' get a leading symbol
            */
            for (q = r = txt; *r;) {
                for (; *r && *r != '\n'; r++);
                    if (*r == '\n') {		/* For each line of text */
                       if (strncmp(q, "From ", 5) == 0)
                          (void) write(fd, ">", 1);
                       (void) write(fd, q, r - q + 1);
                       q = ++r;
                      }
               }
            if (*q) {				/* should never happen, but. */
               if (strncmp(q, "From ", 5) == 0)
                  (void) write(fd, ">", 1);
               (void) write(fd, q, strlen(q));	/* write last line of text */
               if (LASTCH(q) != '\n')
                  (void) write(fd, "\n", 1);
              }
            (void) write(fd, "----------- End Forwarded Message -----------\n", 46);
            break;
       case 'R':
       case 'A':
            n = strlen(p);
            for (q = r = txt; *r;) {
                for (; *r && *r != '\n'; r++);
                    if (*r == '\n') {		/* For each line of insert */
                       (void) write(fd, p, n);	/* write indent prefix, */
                       (void) write(fd, q, r - q + 1);	/* followed by text */
                       q = ++r;
                      }
               }
            if (*q) {				/* should never happen, but. */
               (void) write(fd, p, n);		/* write the indent prefix, */
               (void) write(fd, q, strlen(q));	/* this last line of text, */
               if (LASTCH(q) != '\n')
                  (void) write(fd, "\n", 1);	/* and an ending newline. */
              }
            if (erasable)
               XtFree((String) p);
            break;
      } /* end - switch on client_data */

    (void) close(fd);

    if (alwaysIgnore) {		/* get full headers for data (top 100 lines) */
       txt = QueryMail("unset alwaysignore");
       (void) sprintf(Command, "top %d", SelectionNumber(False));
       txt = QueryMail(Command);
      }
    /*
    ** strip header information from the original message
    */
    for (p = txt; *p; p++) {
        if (*p == '\n' || strncmp(p, "Status:", 7) == 0) break;

        if (strncmp(p, "From ", 5) == 0 ) {
           bzero(oldFrom, BUFSIZ);
           p += 5;
           for (q = p; *q && *q != ' ' && *q != '\n'; q++);
           (void) strncpy(oldFrom, p, q - p);	/* drop the date portion now */
           for (; *q && *q != '\n'; q++);	/* skip to end of this line */
           p = q;
          }

        else if (strncmp(p, "Return-Path:", 12) == 0) {
           bzero(returnPath, BUFSIZ);
           p += 13;
           for (q = p; *q && *q != '\n'; q++);
           (void) strncpy(returnPath, p, q - p); /* drop trailing newline */	
           p = q;
          }

        else if (strncmp(p, "Date:", 5) == 0) {
           bzero(date, BUFSIZ);
           p += 6;
           for (q = p; *q && *q != '\n'; q++);
           (void) strncpy(date, p, q - p);
           p = q;
          }

        else if (strncmp(p, "From:", 5) == 0) {
           bzero(author, BUFSIZ);
           p += 6;
           for (q = p; *q && *q != '\n'; q++);
           while (*(q+1) && strchr(" \t", *(q+1))) {
                 *q = ' ';		/* change this newline to a space */
                 *(q+1) = ' ';		/* change possible tab to a space */
                 for (q++; *q && *q != '\n'; q++);
                }
           (void) strncpy(author, p, q - p);
           p = q;
          }

        else if (strncmp(p, "To:", 3) == 0) {
           bzero(toList, BUFSIZ);
           p += 4;
           for (q = p; *q && *q != '\n'; q++);
           while (*(q+1) && strchr(" \t", *(q+1))) {
                 *q = ' ';		/* change this newline to a space */
                 *(q+1) = ' ';		/* change possible tab to a space */
                 for (q++; *q && *q != '\n'; q++);
                }
           if (*C == 'a' || *C == 'A')
              (void) strncpy(toList, p, q - p);
           p = q;
          }

        else if (strncmp(p, "Subject:", 8) == 0) {
           bzero(subject, BUFSIZ);
           p += 9;
           for (q = p; *q && *q != '\n'; q++);
           while (*(q+1) && strchr(" \t", *(q+1))) {
                 *q = ' ';		/* change this newline to a space */
                 *(q+1) = ' ';		/* change possible tab to a space */
                 for (q++; *q && *q != '\n'; q++);
                }
           (void) strncpy(subject, p, q - p);
           p = q;
          }

        else if (strncmp(p, "Cc:", 3) == 0) {
           bzero(ccList, BUFSIZ);
           p += 4;
           for (q = p; *q && *q != '\n'; q++);
           while (*(q+1) && strchr(" \t", *(q+1))) {
                 *q = ' ';		/* change this newline to a space */
                 *(q+1) = ' ';		/* change possible tab to a space */
                 for (q++; *q && *q != '\n'; q++);
                }
           if (*C == 'a' || *C == 'A')
              (void) strncpy(ccList, p, q - p);
           p = q;
          }
        else for (; *p && *p != '\n'; p++);
       } /* end - for all of message body */

    if (alwaysIgnore)			/* restore this if originally set */
       p = QueryMail("set alwaysignore");

    if (toList[0] != '\0') {		/* remove ourself from the list */
       if (! (us = getlogin())) {
          struct passwd *pw = getpwuid((uid_t) getuid());

          if (pw)
             us = pw->pw_name;
          else
             us = "no_name";
         }
       for (p = toList; *us && *p; p++) {
           if (strncmp(p, us, strlen(us)) == 0) {
              for (q = p + strlen(us); *q && *q != ',' && *q != ' '; q++);
              for (; *q && (*q == ',' || *q == ' '); q++);
              bcopy(q, p, strlen(q) + 1);
              continue;
             }
           while (*p && *p != ',' && *p != ' ') p++;
           while (*p && (*p == ',' || *p == ' ')) p++;
           p--;
          }
      }
   } /* end - if client_data does not equal 's' */

 InReply[0] = Recipient[0] = '\0';
/*
** If message did not have a 'From:', use 'Return-Path:' for reply recipient.
** If message also did not have a 'Return-Path', use the older style 'From '.
*/
 if (*C != 'S' && *C != 's') {
    p = author;
    if (! *p && returnPath[0]) 
       p = returnPath;
    if (! *p && oldFrom[0])
       p = oldFrom;

    (void) strcpy(Recipient, p);
/*
** If author's name consists of a compound address (i.e. name <address>,
** (Name) address, or equivalents...) strip off the real address portion
** (i.e. that portion not in parens, but possibly between chevrons).
*/
    if ((p = strchr(Recipient, '<')) || (p = strchr(Recipient, '(')))
       switch (*p) {
          case '<': q = strchr(p++, '>');
                    *q = '\0';	/* '<address> Name' or 'Name <address>' */
                    bcopy(p, Recipient, strlen(p) + 1);
                    break;

          case '(': q = strchr(p, ')');		/* skipping past the parens */
                    if (p == Recipient) {	/* '(Name) address' format */
                       for (q++; *q && (*q == ' ' || *q == '\t'); q++);
                       bcopy(p, Recipient, strlen(p) + 1);
                      } else {			/* 'address (Name)' format */
                       for (; (p-1) > Recipient &&
                              (*(p-1) == ' ' || *(p-1) == '\t'); p--);
                       *p = '\0';
                      }
                    break;
         }
/*
** If this sender wishes to include all recipients of the original message in
** this response, include all others except this sender in that address list.
*/
    if (toList[0] && (*C == 'a' || *C == 'A')) {
       if (LASTCH(Recipient) && LASTCH(Recipient) != ',' &&
           (int)strlen(Recipient) + 2 < BUFSIZ)
          (void) strcat(Recipient, ", ");
       if ((int)strlen(Recipient) + (int)strlen(toList) < BUFSIZ)
          (void) strcat(Recipient, toList);
       for (p = Recipient + strlen(Recipient) - 1; *p == ' ' || *p == ','; p--);
       *++p = '\0';			/* drop any trailing ", " garbage */
      }
   }

 if (*C != 's' && author[0] && date[0]) {
    r = (*C == 'S') ? "Forwarding" : "In-Reply-To";
    (void) sprintf(InReply, "%s: Mail from '%s'\n%*sdated: %s", r, author,
                   strlen(r) - 5, " ", date);
   }

 sendMail(sb);

 fd = -1;
 if (edit_headers) {
    (void) sprintf(Command, "%s_", tmpName);
    if (rename(tmpName, Command))
       Bell("xmail: Cannot rename edit file for editheaders\n");
    else if ((fd = open(tmpName, O_WRONLY | O_CREAT, 0600)) < 0) {
       Bell("xmail: Cannot reopen the edit file for editheaders\n");
       return;
      }
   }

 if (! Recipient[0]) {
    writeTo(XtNameToWidget(sb, "*To"), "", REPLACE);
    if (fd >= 0) (void) write(fd, "To: \n", 5);
   } else {
    writeTo(XtNameToWidget(sb, "*To"), Recipient, REPLACE);
    if (fd >= 0) {
       if (*C == 's' && edit_headers)
          (void) write(fd, "To: \n", 5);
       else {
          (void) write(fd, "To: ", 4);
          (void) write(fd, Recipient, strlen(Recipient));
          (void) write(fd, "\n", 1);
         }
      }
   }

 if (! subject[0]) {
    writeTo(XtNameToWidget(sb, "*Subject"), "", REPLACE);
    if (fd >= 0) (void) write(fd, "Subject: \n", 10);
   } else {
    n = ((strchr("Rr", subject[0]) != NULL) &&
         (strchr("Ee", subject[1]) != NULL) &&
         (strchr(":;", subject[2]) != NULL));

    if (n)
       writeTo(XtNameToWidget(sb, "*Subject"), subject, REPLACE);
    else {
       writeTo(XtNameToWidget(sb, "*Subject"), "Re: ", REPLACE);
       writeTo(XtNameToWidget(sb, "*Subject"), subject, APPEND);
      }
    if (fd >= 0) {
       if (*C == 's' && edit_headers)
          (void) write(fd, "Subject: \n", 10);
       else {
          (void) write(fd, "Subject: ", 9);
          if (! n) (void) write(fd, "Re: ", 4);
          (void) write(fd, subject, strlen(subject));
          (void) write(fd, "\n", 1);

          if (InReply[0]) {
             (void) write(fd, InReply, strlen(InReply));
             (void) write(fd, "\n", 1);
            }
         }

       if (p = XGetDefault(XtDisplay(toplevel), "XMail", "customHeader")) {
          if (p[0] == '"' || p[0] == "'"[0]) {
             bcopy(p + 1, p, strlen(p) - 1);
             while (LASTCH(p) == '"' || LASTCH(p) == "'"[0])
                   LASTCH(p) = '\0';
            }
          (void) write(fd, p, strlen(p));
          if (LASTCH(p) != '\n')
             (void) write(fd, "\n", 1);
         }
      }
   }

 if (! ccList[0]) {
    writeTo(XtNameToWidget(sb, "*Cc"), "", REPLACE);
    if (fd >= 0 && edit_headers)
       (void) write(fd, "Cc: \n", 5);
   } else {
    writeTo(XtNameToWidget(sb, "*Cc"), ccList, REPLACE);
    if (fd >= 0) {
       (void) write(fd, "Cc: ", 4);
       (void) write(fd, ccList, strlen(ccList));
       (void) write(fd, "\n", 1);
      }
   }

    writeTo(XtNameToWidget(sb, "*Bcc"), "", REPLACE);
 if (fd >= 0 && edit_headers)
    (void) write(fd, "Bcc: \n", 6);

 if (fd >= 0) {
    (void) write(fd, "\n", 1);
    if ((erasable = open(Command, O_RDONLY)) < 0) {
       Bell("xmail: Cannot reread the edit file for editheaders\n");
       return;
      }
    (void) unlink(Command);
    while (n = read(erasable, Command, BUFSIZ))
          (void) write(fd, Command, n);
    (void) close(erasable);
    (void) close(fd);
   }

 editMail();
} /* Reply */


/*
** @(#)Save() - (or copy) a message to specified folder or mbox
*/
/* ARGSUSED */
void
Save(w, client_data, call_data)
Widget w;
XtPointer client_data;
XtPointer call_data;
{
 int			num;
 String			p, r, FBuf;
 XawTextPosition	pos;
 Arg			args[1];
 Widget			fileWindow;
 char			*cmd = (char *) client_data;


 if (! mailpid) Bell("No current mail connection\n");
 else {
    SetCursor(WATCH);			/* reset by parser routines */
    num = SelectionNumber(False);	/* no current message returns zero */
    if (*cmd == 'C' || *cmd == 'S' || num == 0) {
       if (num) {
          (void) sprintf(Command, "%s %d\n", cmd, num);
          Bell("");			/* Reset bell worthyness flag */
         } else {
          (void) sprintf(Command, "%s \n", cmd);
         }
      } else {
       fileWindow = XtNameToWidget(toplevel, "topBox.commandPanel.fileWindow");
       pos = TextGetLastPos(fileWindow);
       if (pos - StartPos > 0) {
          XtSetArg(args[0], XtNstring, &FBuf);
          XtGetValues(fileWindow, args, 1);

          if (FBuf[pos] != '\0') FBuf[pos] = '\0';
          p = XtNewString(&FBuf[StartPos]);
          (void) sprintf(Command, "%s %d %s\n", cmd, num, p);
          XtFree((String) p);
         } else {
          /*
          ** If no specified filename, use the mbox pointer.  We MUST include
          ** it here, because specifying the message number for the action
          ** would be interpreted as a filename, if we didn't append one.
          */
          if (! (p = GetMailEnv("MBOX")))
             (void) sprintf(Command, "%s %d %s/mbox\n", cmd, num, HOME);
          else {
             initfoldir();
             if (*p != '+' && (*p == '/' || (*p == '.' && *(p+1) == '/') ||
                  foldir[0] == '\0')) {
                (void) sprintf(Command, "%s %d %s\n", cmd, num, p);
               } else {
                (void) sprintf(Command, "%s %d %s%s\n", cmd, num, foldir, (*p == '+') ? &p[1] : p);
               }
             XtFree((String) p);
            }
         }
      }
    writeMail(Command);
   }
} /* Save */


/*
** @(#)SetNewmail - Highlight Newmail button to attract user attention
*/
/* ARGSUSED */
void
SetNewmail(w, client_data, call_data)
Widget	w;			/* unused */
XtPointer	client_data;		/* unused */
XtPointer	call_data;		/* unused */
{
 Widget	cw;

 if (! Highlighted) {
    cw = XtNameToWidget(toplevel, "topBox.commandPanel.Newmail");
    XSetWindowBackgroundPixmap(XtDisplay(toplevel), XtWindow(cw), hatch);
    XtUnmapWidget(cw);
    XtMapWidget(cw);
    Highlighted = 1;
    reset_mailbox(XtNameToWidget(toplevel, "icon.mailbox"), 0);
   }
} /* SetNewmail */


/*
** @(#)UnsetNewmail - Remove Newmail button highlighting
*/
/* ARGSUSED */
void
UnsetNewmail(w, client_data, call_data)
Widget	w;			/* unused */
XtPointer	client_data;		/* unused */
XtPointer	call_data;		/* unused */
{
 Widget	cw = XtNameToWidget(toplevel, "topBox.commandPanel.Newmail");

 if (Highlighted) {
    XSetWindowBackground(XtDisplay(toplevel), XtWindow(cw), cw->core.background_pixel);
    XtUnmapWidget(cw);
    XtMapWidget(cw);
    Highlighted = 0;
    reset_mailbox(XtNameToWidget(toplevel, "icon.mailbox"), 1);
   }
} /* UnsetNewmail */


syntax highlighted by Code2HTML, v. 0.9.1