wsh/
wsh/binsrc/
wsh/docs/help/
wsh/docs/old/
wsh/etc/
wsh/src/util/
/*  Define this if you have a shell script like wsh/binsrc/mud
 *  Then, make a link to splitvt called splitmud.  This will run $SHELL
 *  in the top window and BOTTOM_EXEC in the bottom if invoked as splitmud
 *       -Addition for the WizPort by David Ljung
 */
#define BOTTOM_EXEC "mud"

/* 
	splitvt

  A fun utility designed to split your screen into twin shells.

				-Sam Lantinga  10/5/93
*/

static char *version="@(#)splitvt 1.4.0   10/7/93  -Sam Lantinga\n";

#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/wait.h>
#include	<fcntl.h>
#include	<signal.h>
#include	<stdio.h>
#include	<errno.h>
#include	<ctype.h>
#include	<pwd.h>

#ifdef NEED_SELECT_H
#include	<sys/select.h>
#endif

#ifdef NEED_INET_H
#include	<sys/inet.h>
#endif

#ifndef SIGCLD	/* BSD */
#define SIGCLD SIGCHLD
#endif

#define toctrl(X)	(X-'@')		/* uppercase-to-control macro */

#define SWITCH	toctrl('W')	/* Switch window key */
#define QUOTE	toctrl('V')	/* Quote next character key */
#define QUIT	toctrl('X')	/* Quick exit */

#define UPPER	0		/* For the upper window */
#define LOWER	1		/* For the lower window */


extern char *init_vt100();	/* Initialize the vt100 screen */
extern int   vt_write();	/* Write to the vt100 screen */
extern void  end_vt100();	/* Quit the vt100 screen */
extern void set_win();		/* Set the cursor in the proper window */
extern int UU_lines;		/* The user requested lines for the top win */


/* A Macro to set the window in the proper place.   */
#define SET_WIN()	set_win((thisfd == topfd) ? UPPER : LOWER)


extern char tty_name[];		/* From misc.c about get_master_pty() */
static char upper_tty[64];	/* tty_name of the upper window */
static char lower_tty[64];	/* tty_name of the lower window */	
static struct passwd *pw=NULL;	/* Our passwd entry pointer */

static void finish(), reap(), winch();
static int  isalive();

static int topok=1, bottomok=1;		/* I/O flags */
static int toppid=0, bottompid=0;	/* Children */
static int ttyfd=0, thisfd;		/* I/O file descriptors */
static int topfd, bottomfd;		/* Master file descriptors */

static char extract(arg)	/* get a char from x/^x format */
char *arg;
{
	if ( *arg == '^' )
	{
		++arg;
		if ( islower(*arg) )
			*arg=toupper(*arg);
		return(*arg-'@');
	}
	return(*arg);
}

 
main(argc, argv)
int argc;
char *argv[];
{
	extern int errno, optind;
	extern char *optarg;

	int i, len, maxfds, numready;
	char buffer[BUFSIZ], *args[256], *ptr;
        struct timeval tv, *tvptr;
	char 	quit_c=QUIT, 
		switch_c=SWITCH, 
		quote_c=QUOTE, 
		suspend_c='\0';
	fd_set read_mask;
	static struct passwd pwdata;	/* Our passwd entry */

#ifdef NEED_INET_H
        /* There is a bug in the Wallabong Group's implementation
           of select().  It will not work properly with fd 0 */

        if ( (ttyfd=dup(0)) < 0 )
        {
                perror("dup() error");
                exit(2);
        }
#endif

	/* Parse command line options */
	while ( (i=getopt(argc, argv, "hl:q:k:s:")) != EOF )
	{
		switch (i)
		{
			case 'q': quote_c=extract(optarg);
				  break;
			case 'k': quit_c=extract(optarg);
				  break;
			case 's': switch_c=extract(optarg);
				  break;
			case 'z': suspend_c=extract(optarg);
				  break;
			case 'l': UU_lines=atoi(optarg);
				  break;
			case 'h':
			default:  fprintf(stderr, 
"Usage: %s [-k kill_c] [-q quote_c] [-s switch_c] [-l lines_in_top_win]\n", 
								argv[0]);
				  exit(1);
				  break;
		}
	}
	argv+=(optind-1);

	/* Retrieve and save our passwd entry */
	if ( (pw=getpwuid(getuid())) == NULL ) {
		fprintf(stderr, 
		"Warning: Can't find your passwd entry; no utmp logging.\n");
		sleep(2);
	} else { /* Save the passwd entry for future reference */
		d_copy((char *)pw, (char *)&pwdata, sizeof(pwdata));
		pw=(&pwdata);
	}

	if ( (ptr=init_vt100()) != NULL )
	{
		fprintf(stderr, "Can't initialize screen: %s\n", ptr);
		exit(12);
	}
	if ( tty_getmode(ttyfd) < 0 ) 
	{
		end_vt100();
		fprintf(stderr, "Can't get terminal settings.\n");
		exit(2);
	}

	if ( (args[0]=(char *)getenv("SHELL")) == NULL ) {
		fprintf(stderr,"FATAL!  SHELL VARIABLE NOT SET!\n");
		exit(-1);
	}

#ifdef NEED_INET_H
	signal(SIGCLD, SIG_IGN);
#endif

	(void) remove_me();
	if ( (topfd=pty_open(args, &toppid, UPPER)) < 0 )
	{
		end_vt100();
		switch (errno) {
		   case EIO:
		   case EPERM:
		   case ENOENT:
			fprintf(stderr, "No available pseudo terminals.\n");
			break;
		   default:
			perror("pty_open() error");
		}
		(void) replace_me();
		exit(2);
	} else if ( pw ) {
		(void) strcpy(upper_tty, tty_name);
		(void) addutmp(pw->pw_name, pw->pw_uid, upper_tty);
	}

#ifdef BOTTOM_EXEC
        if(!strcmp(argv[0],"splitmud")) args[0]=BOTTOM_EXEC;
#endif
	if ( (bottomfd=pty_open(args, &bottompid, LOWER)) < 0 )
	{
		end_vt100();
		switch (errno) {
		   case EIO:
		   case EPERM:
		   case ENOENT:
			fprintf(stderr, "No available pseudo terminals.\n");
			break;
		   default:
			perror("pty_open() error");
		}
		(void) replace_me();
		exit(2);
	} else if ( pw ) {
		(void) strcpy(lower_tty, tty_name);
		(void) addutmp(pw->pw_name, pw->pw_uid, lower_tty); 
	}
	thisfd=topfd;

#if defined(SOLARIS) || defined(HAVE_BSDTTY_H) || defined(HP_UX)
        maxfds=32;              /* Any comments?  This is a WAG */
#else
        maxfds=getdtablesize();
#endif
        /* Set select() timeout, and zero out the read mask */
#ifdef NEED_INET_H
        tv.tv_sec=3;
        tv.tv_usec=0;
        tvptr=&tv;
#else
        tvptr=NULL;
#endif
	signal(SIGHUP, finish);
	signal(SIGINT, finish);
	signal(SIGQUIT, finish);
	signal(SIGTERM, finish);
	signal(SIGBUS, finish);
#ifdef SIGWINCH
	signal(SIGWINCH, winch);
#endif
	(void) tty_raw(0);

	for ( SET_WIN(); (topok || bottomok); )
	{
		FD_ZERO(&read_mask);
		FD_SET(ttyfd, &read_mask);

		/* Make sure the children are still alive */
		if ( ! isalive() )
			break;

		if ( topok )
			FD_SET(topfd, &read_mask);

		if ( bottomok )
			FD_SET(bottomfd, &read_mask);

                if ( (numready=select(maxfds, &read_mask, NULL, NULL, tvptr))
                                                                        <= 0 )
                {
#ifndef NEED_INET_H                     /* Wallabong select() is buggy */
                        switch (errno)
                        {
                                case EIO:    /* The program is finished. */
                                                break;
                                case EINTR:  /* Probably SIGWINCH */
                                                break;
                                default:        perror("select() error");
                                                fprintf(stderr, "\r");
                                                break;
                        }
                        if ( errno != EINTR )
                                finish(0);
			else
				continue;
#endif
                }


		if ( FD_ISSET(ttyfd, &read_mask) )
		{
			read(ttyfd, buffer, 1);

			if ( buffer[0] == quote_c )
			{
				read(ttyfd, buffer, 1);
				goto writit;
			}
			else if ( buffer[0] == switch_c )
			{
				if ( (thisfd == topfd) && bottomok )
					thisfd=bottomfd;
				else if ( topok )
					thisfd=topfd;
				SET_WIN();
			}
			else if ( buffer[0] == quit_c )
				finish(0);
			else
			{
		writit:
				write(thisfd, buffer, 1);
			}
		}

		if  ( FD_ISSET(bottomfd, &read_mask) )
		{
			if ( (len=read(bottomfd, buffer, BUFSIZ)) <= 0 )
			{
				switch (errno)
				{
					case EIO: /*break;*/
					default:  if ( isalive() < 0 )
							finish(0);
				}
			}
			else {
				(void) vt_write(LOWER, buffer, len);
				if ( thisfd == topfd )
					SET_WIN();
			}
		}

		if  ( FD_ISSET(topfd, &read_mask) )
		{
			if ( (len=read(topfd, buffer, BUFSIZ)) <= 0 )
			{
				switch (errno)
				{
					case EIO: /*break;*/
					default:  if ( isalive() < 0 )
							finish(0);
				}
			}
			else {
				(void) vt_write(UPPER, buffer, len);
				if ( thisfd == bottomfd ) 
					SET_WIN();
			}
		}
	}
	finish(0);
}


/* A better child checker. :)  It gathers the status of the child,
   rendering it free and un-zombied. :) */
static int isalive()
{
	int status;

#if ! defined(WNOHANG) || defined(NEED_INET_H)
	if ( topok )
		if (  kill(toppid, 0) < 0 )
		{
			if ( pw ) 
				(void) delutmp(pw->pw_name, upper_tty);
			if ( thisfd == topfd )
				thisfd=bottomfd;
			(void) close(topfd);
			topok=0;
		}

	if ( bottomok )
		if ( kill(bottompid, 0) < 0 )
		{
			if ( pw ) 
				(void) delutmp(pw->pw_name, lower_tty);
			if ( thisfd == bottomfd )
				thisfd=topfd;
			(void) close(bottomfd);
			bottomok=0;
		}

#else
	if ( topok )
		if ( waitpid(toppid, &status, WNOHANG) != 0 )
		{
			if ( pw ) 
				(void) delutmp(pw->pw_name, upper_tty);
			if ( thisfd == topfd )
				thisfd=bottomfd;
			(void) close(topfd);
			topok=0;
		}

	if ( bottomok )
		if ( waitpid(bottompid, &status, WNOHANG) != 0 )
		{
			if ( pw ) 
				(void) delutmp(pw->pw_name, lower_tty);
			if ( thisfd == bottomfd )
				thisfd=topfd;
			(void) close(bottomfd);
			bottomok=0;
		}
#endif
	if ( ! toppid && ! bottompid )
		return(0);
	return(1);
}

	
/* Cleanup routine */
static void finish(sig)
int sig;
{
	time_t now;

	(void) time(&now);

	/* Reset the vt100 window */
	end_vt100();

	/* Reset our utmp entries */
	if ( pw && topok )
		(void) delutmp(pw->pw_name, upper_tty);
	if ( pw && bottomok )
		(void) delutmp(pw->pw_name, lower_tty);
	(void) replace_me();

	/* Only call this routine after tty_getmode() has been called */
	if ( tty_reset(0) < 0 )	
		(void) tty_sane(0);
	if ( sig != 0 )
		printf("Exiting due to signal: %d\n", sig);
	exit(sig);
}


/* Resize the window... unfortunately, this means that the
   current window is erased.  Oh well.. :) */

static void winch(sig)
int sig;
{
	char *ptr;

	signal(sig, winch);

	if ( (ptr=init_vt100()) != NULL )
	{
		fprintf(stderr, "Can't resize window: %s. (exiting)\n", ptr);
		finish(0);
	}
	else
		SET_WIN();
	if ( topfd )
		pty_setwin(topfd, UPPER);
	if ( bottomfd )
		pty_setwin(bottomfd, LOWER);
}