/* 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); }