/*
 * 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
 *
**  Xmail talks to mail through a pseudo terminal which is a pair of master
**  and slave devices: /dev/pty?? and /dev/tty??, where ?? goes from p0 to
**  zf (system dependent).  The terminal is opened for both read and write.
**
*/

#include	"global.h"
#include	<sys/stat.h>
#include	<signal.h>

#if	defined(AIXV3) || defined(_IBMR2)
#include	<sys/select.h>
#endif

#if	!(defined(SYSV) || defined(linux)) || defined(clipper)
#include	<sgtty.h>
#else
#include	<sys/termio.h>
#include	<sys/sysmacros.h>
#include	<fcntl.h>
#if	defined(att)
#include	<sys/stropts.h>
#endif
#endif

#if defined(sun) && defined(SVR4)
#include <sys/file.h>
#endif

#ifndef TTYDEV
#ifdef		sco
#define TTYDEV		"/dev/ptypxx"
#else	/* !sco */
#ifdef		hpux
#define TTYDEV		"/dev/pty/ptyxx"
#else	/* !hpux */
#define	TTYDEV		"/dev/ptyxx"
#endif	/* !sco */
#endif	/* !hpux */
#endif	/* !TTYDEV */

#ifndef PTYCHAR1
#ifdef		sco
#define PTYCHAR1	"0123456789abcdef"
#else	/* !sco */
#ifdef		hpux
#define PTYCHAR1	"zyxwvutsrqp"
#else	/* !hpux */
#define	PTYCHAR1	"pqrstuvwxyz"
#endif	/* !sco */
#endif	/* !hpux */
#endif	/* !PTYCHAR1 */

#ifndef PTYCHAR2
#ifdef		hpux
#define	PTYCHAR2	"fedcba9876543210"
#else	/* !hpux */
#define	PTYCHAR2	"0123456789abcdef"
#endif	/* !hpux */
#endif	/* !PTYCHAR2 */

int    	    	mail_fd;		/* mail process master tty id */
int    	    	mailpid;		/* mail process id */
int    	    	mailInputId;		/* mail input id */
char		pseudo_tty[20];


/*
** @(#) openMaster - searches for and opens a pty master.  If it finds one,
**		     it returns the value of the file descriptor.  If not,
**		     it reports an error and terminates.  Portions of this
**		     routine were stolen from X11R4 xterm get_pty() sources.
*/
int
openMaster()
{
 static int	devindex = 0;
 static int	letter = 0;
 struct	stat	st_buf;
 int		master;

#ifdef att
 (void) strcpy(pseudo_tty, "/dev/ptmx");
 if ((master = open(pseudo_tty, O_RDWR)) >= 0)
    return(master);
#else /* !att, need lots of code */
#if defined(umips) || defined(sgi) && defined (SYSV)
 int		c;

 (void) strcpy(pseudo_tty, "/dev/ptc");
 master = open(pseudo_tty, O_RDWR);
 if (master >= 0 && (fstat(master, &st_buf)) == 0) {
    (void) sprintf(pseudo_tty, "/dev/ttyq%d", minor(st_buf.st_rdev));
#if ! defined(sgi)
    if ((c = open(pseudo_tty, O_RDWR)) < 0) {
       (void) close (master);
      } else {				/* got one! */
       (void) close (c);
       return(master);
      }
#else
    return(master);
#endif
   }
#else /* not (umips && SYSV) */
 (void) strcpy(pseudo_tty, TTYDEV);
 while (PTYCHAR1[letter]) {
       pseudo_tty[strlen(pseudo_tty) - 2] = PTYCHAR1[letter];
       pseudo_tty[strlen(pseudo_tty) - 1] = PTYCHAR2[0];

       if (stat(pseudo_tty, &st_buf) == 0)
          while (PTYCHAR2[devindex]) {
                pseudo_tty[strlen(pseudo_tty) - 1] = PTYCHAR2[devindex];
                if ((master = open(pseudo_tty, O_RDWR)) >= 0)
                   return(master);

                devindex++;
               }
       devindex = 0;
       (void) letter++;
      }
#endif /* umips || sgi && SYSV */
#endif /* att */
/*
** We were unable to allocate a pty master!  Report the error and terminate.
*/
 XtError("xmail cannot open master/slave pipe connection");
 /* NOTREACHED */
} /* end - openMaster */


/*
** @(#)openSlave() - open slave side of pipe
*/
/* ARGSUSED */
int
openSlave(master)
int	master;
{
 int		slave;

#ifdef att
 char		*slaveName;
 int		grantpt(), unlockpt();
 char		*ptsname();

 if (grantpt(master) >= 0 &&
    unlockpt(master) >= 0 &&
    (slaveName = ptsname(master)) != NULL &&
    (slave = open(slaveName, O_RDWR)) >= 0 &&
    (void) ioctl(slave, I_PUSH, "ptem") >= 0 &&
    (void) ioctl(slave, I_PUSH, "ldterm") >= 0)
    return(slave);
#else /* !att */
#if defined(umips) || defined(sgi) && defined(SYSV)
 struct	stat	st_buf;

 fstat(master, &st_buf);
 (void) sprintf(pseudo_tty, "/dev/ttyq%d", minor(st_buf.st_rdev));
 if ((slave = open(pseudo_tty, O_RDWR)) >= 0)
    return(slave);				/* got one! */
#else /* not (umips || sgi && SYSV) */
#if defined(sco) || defined(hpux)
 pseudo_tty[strlen(pseudo_tty) - 6] = 't';
#else
 pseudo_tty[strlen(pseudo_tty) - 5] = 't';
#endif
 if ((slave = open(pseudo_tty, O_RDWR)) >= 0)
    return(slave);
#endif
#endif
 return(-1);				/* look for more master/slave pairs */
} /* openSlave */


/*
** @(#)callMail() - fork child to execute mail and attach to xmail input
*/
/* ARGSUSED */
void
callMail(argv)
char *argv[];
{
#if defined(linux) || (defined(SYSV)  && !defined(clipper))
 struct termio	tio;
#else	
 struct sgttyb	Sgtty;
#endif
 int		slave;			/* file descriptor to slave pty */


 for (;;) {				/* find a pair, or master fails */
     mail_fd = openMaster();
     if ((slave = openSlave(mail_fd)) != -1)
        break;
    }
/*
** Set minimal requirements for slave connection (no echo, no NL->CR, keep TABS)
*/
#if defined(linux) || (defined(SYSV) && !defined(clipper))
 (void) ioctl(slave, TCGETA, &tio);
 tio.c_oflag &= ~(OCRNL|ONLCR|ONLRET|TABDLY);
 tio.c_iflag &= ~IXOFF;
 tio.c_iflag |= ICRNL;
 tio.c_lflag &= ~(ISIG|ECHO);
 tio.c_lflag |= ICANON;
 (void) ioctl(slave, TCSETA, &tio);
#else	
 (void) ioctl(slave, TIOCGETP, &Sgtty);
 Sgtty.sg_flags &= ~(ECHO|CRMOD|XTABS);
 (void) ioctl(slave, TIOCSETP, &Sgtty);
#endif

 mailpid = fork();
 if (mailpid == -1) {
    XtError("callMail could not fork the child process");
   } else if (mailpid) { 
             /* 
              * Parent : close the slave side of pty
              *          set the mail file descriptor to append mode
              *          register mail input with X
              */
             (void) close(slave);
#ifndef linux
             (void) fcntl(mail_fd, F_SETFL, FAPPEND);
#else
             (void) fcntl(mail_fd, F_SETFL, O_APPEND);
#endif
             mailInputId = XtAddInput(mail_fd, (XtPointer)XtInputReadMask, readMail, (XtPointer)NULL);
            } else { 
             /* 
              * Child : close X connection and mail_fd side of pty
              *         redirect stdin, stdout, stderr of mail to pty
              *         unbuffer output data from mail
              *         exec mail with arguments
              *
              *         Use a select() call to delay starting the mail process
              *         until our parent can close its slave side of the pipe.
              *         Eliminates the parent hanging (for approximately 15
              *         seconds) on the close because the child terminated for
              *         lack of mail before the parent could issue its close.
              */
             int		readfds, width;
             struct timeval	timeout;

             (void) close(ConnectionNumber(XtDisplay(toplevel)));

             timeout.tv_sec = 0; timeout.tv_usec = 250000; /* 0.25 seconds */
             readfds = 1 << slave; width = slave + 1;

             (void) select(width, (fd_set *) &readfds, (fd_set *) NULL, (fd_set *) NULL, &timeout);

             (void) close(mail_fd);
             (void) dup2(slave, 0);
             (void) dup2(slave, 1);
             (void) dup2(slave, 2);
             if (slave > 2)
                 (void) close(slave);
#ifndef linux
             (void) fcntl(1, F_SETFL, FAPPEND);
#else
             (void) fcntl(1, F_SETFL, O_APPEND);
#endif
             setbuf(stdout, (char *) NULL);
#ifdef linux
	     /* ANSI highlighting loses for folder buttons. */
	     unsetenv("TERM");
#endif
             (void) execvp(argv[0], argv);
             /*
              * If we fail to make contact, we must re-establish access to
              * the terminal screen that started us for our error message,
              * because we don't want to send it up the xmail pipe.
              * Also terminate our parent.
              */
             if ((slave = open("/dev/tty", O_RDWR)) != -1) {
                (void) dup2(slave, 1);
                (void) dup2(slave, 2);
                perror(argv[0]);
               }
             (void) kill(getppid(), SIGKILL);		/* kill our parent */
             (void) exit(1);
             /* NOTREACHED */
            }
} /* callMail */
