/* Copyright (c) 1991
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 * Copyright (c) 1987 Oliver Laumann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 1, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Noteworthy contributors to screen's design and implementation:
 *	Wayne Davison (davison@borland.com)
 *	Patrick Wolfe (pat@kai.com, kailand!pat)
 *	Bart Schaefer (schaefer@cse.ogi.edu)
 *	Nathan Glasser (nathan@brokaw.lcs.mit.edu)
 *	Larry W. Virden (lwv27%cas.BITNET@CUNYVM.CUNY.Edu)
 *	Howard Chu (hyc@hanauma.jpl.nasa.gov)
 *	Tim MacKenzie (tym@dibbler.cs.monash.edu.au)
 *	Markku Jarvinen (mta@{cc,cs,ee}.tut.fi)
 *	Marc Boucher (marc@CAM.ORG)
 *
 ****************************************************************
 */

#ifndef lint
  static char rcs_id[] = "$Id: screen.c,v 1.2 92/02/03 02:28:05 jnweiger Exp $ FAU";
#endif


#include <sys/param.h>
/* #include <signal.h> */
#include <ctype.h>
#include <pwd.h>
#include <fcntl.h>
#ifdef sgi
# include <sys/sysmacros.h>
#endif /* sgi */
#if !defined(sun) && !defined(B43) && !defined(ISC)
# include <time.h>
#endif
/*
 * Gee!! We should reverse that #if! 
 */
#if defined(sun) || defined(_AIX) || defined(sysV68) || defined(MIPS) || defined(GOULD_NP1) || defined(B43) || defined(ISC) || defined(apollo) || defined(BSDI) || defined(sgi) || defined(linux)
# include <sys/time.h>
#endif
#if defined(M_XENIX) || defined(M_UNIX)
#include <sys/select.h> /* for timeval */
#endif
#include <sys/types.h>
#ifdef ISC
# include <sys/bsdtypes.h>
#endif
#if !defined(sysV68) && !defined(M_XENIX)
# include <sys/wait.h>
#endif
#include <sys/stat.h>
#ifndef sgi
# include <sys/file.h>
#endif /* sgi */
#ifndef sun
# include <sys/ioctl.h>
#endif /* sun */

#include <signal.h>
#if defined(SVR4) && !defined(NSIG)
#define NSIG 32
#endif

#include "config.h"

#ifdef SHADOWPW
# include <shadow.h>
#endif /* SHADOWPW */

#ifdef SVR4
# include <sys/stropts.h>
#endif

#ifdef SYSV
# include <sys/utsname.h>
#endif

#if defined(_SEQUENT_) 
/* for the FD.. stuff */
# include <sys/select.h>
#endif 

#if defined(sequent) || defined(SVR4)
# include <sys/resource.h>
#endif /* sequent || SVR4 */

#ifdef ISC
# include <sys/tty.h>
# include <sys/sioctl.h>
# include <sys/pty.h>
#endif

#include "screen.h"

#include "patchlevel.h"

#if defined(xelos) || defined(sysV68) || defined(M_XENIX)
 struct passwd *getpwuid __P((uid_t));
 struct passwd *getpwnam __P((char *));
#endif

#ifdef USEVARARGS
# if defined(__STDC__)
#  include <stdarg.h>
# else
#  include <varargs.h>
# endif
#endif

#ifdef DEBUG
FILE *dfp;
#endif


#ifdef COPY_PASTE
extern char *copybuffer;	/* def in mark.c jw. */
extern copylen;
#endif /* COPY_PASTE */

extern char *blank, *null, Term[], screenterm[], **environ, *Termcap;
int force_vt = 1, assume_LP = 0;
extern int in_ovl;
extern int ovl_blockfore;
extern void (*ovl_process)();
extern int help_page;
extern int screenwidth, screenheight;
extern char display_tty[];
extern int default_width, default_height;
extern int Z0width, Z1width;
extern int ISO2022;
extern int status, HS;
extern char *Z0, *WS, *LastMsg;
extern time_t TimeDisplayed;
int BellDisplayed;
int VBellWait, MsgWait, MsgMinWait;

/* tputs uses that: jw */
extern short ospeed;

extern int flow, default_flow, wrap, visual_bell, default_monitor;
extern int errno;
extern sys_nerr;
extern char *sys_errlist[];
extern char mark_key_tab[];

#if defined(TIOCSWINSZ) || defined(TIOCGWINSZ)
extern struct winsize glwz;
#endif

static char *MakeWinMsg __P((char *, int));
static void MakeNewEnv __P((void));
static int Attach __P((int));
static void Attacher __P((void));
static void SigHandler __P((void));
static sig_t AttacherSigInt __P(SIGPROTOARG);
static sig_t SigChld __P(SIGPROTOARG);
static sig_t SigInt __P(SIGPROTOARG);
static sig_t CoreDump __P((int));
static void DoWait __P((void));
static sig_t Finit __P((int));
static void InitKeytab __P((void));
static void SetForeWindow __P((int));
static int NextWindow __P((void));
static int PreviousWindow __P((void));
static int MoreWindows __P((void));
static void FreeWindow __P((struct win *));
static void execvpe __P((char *, char **, char **));
static void LogToggle __P((void));
static void ShowWindows __P((void));
static void ShowTime __P((void));
static void ShowInfo __P((void));
static int OpenPTY __P((void));
#ifdef PASSWORD
static void trysend __P((int, struct msg *, char *));
#endif
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
static sig_t SigAttWinch __P(SIGPROTOARG);
#endif
static void fgtty __P((void));
static void freetty __P((void));
static void brktty __P((void));

#if defined(LOCK)
static sig_t DoLock __P(SIGPROTOARG);
static void LockTerminal __P((void));
#endif

#ifdef COPY_PASTE
static pastelen;
static char *pastebuffer;
#endif
#ifdef PASSWORD
extern char Password[];
#endif

static struct passwd *ppp;

/* used for opening a new pty-pair: */
static char PtyName[32], TtyName[32];

/* used for the attacher's tty: */
static char *attach_tty;

char *ShellProg;
char *ShellArgs[2];
static char inbuf[MAXWIN][IOSIZE];
static inlen[MAXWIN];
static inbuf_ct;
static ESCseen;
static GotSignal;

static char DefaultShell[] = "/bin/sh";
static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";

#ifdef hpux
char PtyProto[] = "/dev/ptym/ptyXY";
char TtyProto[] = "/dev/pty/ttyXY";
#else
# if !(defined(sequent) || defined(_SEQUENT_) || defined(SVR4))
static char PtyProto[] = "/dev/ptyXY";
static char TtyProto[] = "/dev/ttyXY";
# endif
#endif /* hpux */
int TtyMode = 0622;
#ifdef SOCKDIR
char *SockDir = SOCKDIR;
#else
char *SockDir = ".iscreen";
#endif
extern char SockPath[], *SockNamePtr, *SockName;
int ServerSocket = -1;
static char **NewEnv;

char *RcFileName = NULL;
char Esc = Ctrl('a');
char MetaEsc = 'a';
char *home;

int HasWindow;
char *LoginName;
char *BellString;
char *VisualBellString;
char *ActivityString;
char *BufferFile;
char *PowDetachString;
int auto_detach = 1;
int iflag, rflag, dflag, lsflag, quietflag, wipeflag;
int adaptflag, loginflag = -1, allflag;
static intrc, startc, stopc;
char HostName[MAXSTR];
int Detached, Suspended;
int DeadlyMsg = 1;
int AttacherPid;	/* Non-Zero in child if we have an attacher */
int MasterPid;
int real_uid, real_gid, eff_uid, eff_gid;
int default_histheight;
int default_startup;
int slowpaste;

#if defined(BSDJOBS) && !(defined(POSIX) || defined(SYSV))
int DevTty = -1;
#endif

#ifdef NETHACK
int nethackflag = 0;
#endif

struct mode OldMode, NewMode;

struct win *fore = NULL;
int WinList = -1;
int ForeNum;
struct win *wtab[MAXWIN];

struct key ktab[256];

#ifndef FD_SET
typedef struct fd_set
{
  int fd_bits[1];
}      fd_set;
# define FD_ZERO(fd) ((fd)->fd_bits[0] = 0)
# define FD_SET(b, fd) ((fd)->fd_bits[0] |= 1 << (b))
# define FD_ISSET(b, fd) ((fd)->fd_bits[0] & 1 << (b))
# define FD_SETSIZE 32
#endif


#ifndef WTERMSIG
# ifndef BSDWAIT /* if wait is NOT a union: */
#  define WTERMSIG(status) (status & 0177)
# else
#  define WTERMSIG(status) status.w_T.w_Termsig 
# endif
#endif

#ifndef WIFCORESIG
# ifndef BSDWAIT /* if wait is NOT a union: */
#  define WIFCORESIG(status) (status & 0200)
# else
#  define WIFCORESIG(status) status.w_T.w_Coredump
# endif
#endif

#ifndef WEXITSTATUS
# ifndef BSDWAIT /* if wait is NOT a union: */
#  define WEXITSTATUS(status) ((status >> 8) & 0377)
# else
#  define WEXITSTATUS(status) status.w_T.w_Retcode
# endif
#endif

char *shellaka = NULL;

/*
 * Do this last
 */
#include "extern.h"

/*
 * XXX: Missing system header files.
 */
#ifdef USEVARARGS
# ifndef VPRNT_DECLARED
int vsprintf __P((char *, char *, va_list));
# endif /* VPRNT_DECLARED */
#endif
int select __P((int, fd_set *, fd_set *, fd_set *, struct timeval *));

static void
brktty()
{
#if defined(POSIX)
  setsid();		/* will break terminal affiliation */
# ifdef BSD
  ioctl(0, TIOCSCTTY, 0);
# endif /* BSD */
#else
# if defined(SYSV)
  setpgrp();		/* will break terminal affiliation */
# else
#  ifdef BSDJOBS
  if (DevTty)
    if (ioctl(DevTty, TIOCNOTTY, (char *) 0) != 0)
      debug2("brktty: ioctl(DevTty=%d, TIOCNOTTY, 0) = %d\n", DevTty, errno);
#  endif
# endif
#endif
}

static void
freetty()
{
  brktty();
#if defined(BSDJOBS) && !(defined(POSIX) || defined(SYSV))
  if (DevTty >= 0)
    {
      close(DevTty);
      DevTty = -1;
    }
#endif
  close(0);
  close(1);
  close(2);
  debug("did freetty\n");
}

static void
fgtty()
{
#ifdef BSDJOBS
  int mypid;

  mypid = getpid();

# ifdef BSDI
  setsid();
  ioctl(0, TIOCSCTTY, 0);
# endif /* BSDI */

# if defined(POSIX)
  if (tcsetpgrp(0, mypid))
    {
      debug1("fgtty: tcsetpgrp: %d\n", errno);
      /* error is likely to have side-effects -- better to warn our user */
      SendErrorMsg("fgtty: Could not set process group id in tty");
    }
# else
  if (ioctl(0, TIOCSPGRP, &mypid) != 0)
    debug1("fgtty: TIOSETPGRP: %d\n", errno);
  /* posix setsid() in brktty() from freetty() already made us leader */
  if (setpgrp(0, mypid))
    debug1("fgtty: setpgrp: %d\n", errno);
# endif /* POSIX */
#endif /* BSDJOBS */
}

#ifdef hpux
/*
 * hpux has berkeley signal semantics if we use sigvector,
 * but not, if we use signal, so we define our own signal() routine.
 * (jw)
 */
void (*signal(sig, func)) ()
int sig;
void (*func) ();
{
  struct sigvec osv, sv;

  sv.sv_handler = func;
  sv.sv_mask = sigmask(sig);
  sv.sv_flags = SV_BSDSIG;
  if (sigvector(sig, &sv, &osv) < 0)
    return (BADSIG);
  return (osv.sv_handler);
}
#endif	/* hpux */

#ifndef USEBCOPY
#ifdef bcopy
#undef bcopy
#endif
void bcopy(s1, s2, len)
register char *s1, *s2;
register int len;
{
  if (s1 < s2 && s2 < s1 + len)
    {
      s1 += len;
      s2 += len;
      while (len-- > 0)
	*--s2 = *--s1;
    }
  else
    while (len-- > 0)
      *s2++ = *s1++;
}
#endif	/* USEBCOPY */

void bclear(p, n)
int n;
char *p;
{
  bcopy(blank, p, n);
}

static void
closeallfiles()
{
  int f;
#ifdef SVR4
  struct rlimit rl;
  
  if ((getrlimit(RLIMIT_NOFILE, &rl) == 0) && rl.rlim_max != RLIM_INFINITY)
    f = rl.rlim_max;
  else
#endif /* SVR4 */
#if defined(SYSV) && !defined(ISC)
  f = NOFILE;
#else /* SYSV && !ISC */
  f = getdtablesize();
#endif /* SYSV && !ISC */
  while (--f > 2)
    close(f);
}
  
static int InterruptPlease = 0;

void main(ac, av)
int ac;
char **av;
{
  register int n, len;
  register struct win *p;
  char *ap, *aka = NULL;
  char *av0;
  char socknamebuf[2 * MAXSTR];
  int s = 0;
  fd_set r, w, e;
  int mflag = 0;
  struct timeval tv;
  int nsel;
  char buf[IOSIZE], *bufp, *myname = (ac == 0) ? "screen" : av[0];
  struct stat st;
  int buflen, tmp;
#ifdef _MODE_T			/* (jw) */
  mode_t oumask;
#else
  int oumask;
#endif
#ifdef SYSV
  struct utsname utsnam;
#endif

  /*
   *  First, close all unused descriptors
   *  (otherwise, we might have problems with the select() call)
   */
  closeallfiles();
#ifdef DEBUG
  (void) mkdir("/tmp/debug", 0777);
  if ((dfp = fopen("/tmp/debug/screen.front", "w")) == NULL)
    dfp = stderr;
  else
    (void) chmod("/tmp/debug/screen.front", 0666);
#endif
  debug1("-- screen debug started %s\n", *av);
#ifdef POSIX
  debug("POSIX\n");
#endif
#ifdef TERMIO
  debug("TERMIO\n");
#endif
#ifdef SYSV
  debug("SYSV\n");
#endif
#ifdef NAMEDPIPE
  debug("NAMEDPIPE\n");
#endif
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
  debug("Window changing enabled\n");
#endif
#ifdef NOREUID
  debug("NOREUID\n");
#endif
#ifdef hpux
  debug("hpux\n");
#endif
#ifdef USEBCOPY
  debug("USEBCOPY\n");
#endif
#ifdef UTMPOK
  debug("UTMPOK\n");
#endif
#ifdef NETHACK
  debug("NETHACK\n");
#endif
#ifdef TERMINFO
  debug("TERMINFO\n");
#endif
#ifdef NAME_MAX
  debug1("NAME_MAX = %d\n", NAME_MAX);
#endif

  BellString = SaveStr("Bell in window %");
  VisualBellString = SaveStr("   Wuff,  Wuff!!  ");
  ActivityString = SaveStr("Activity in window %");
  BufferFile = SaveStr("/tmp/screen-exchange");
  PowDetachString = 0;
  default_histheight = DEFAULTHISTHEIGHT;
  default_startup = (ac > 1) ? 0 : 1;
  adaptflag = 0;
  slowpaste = 0;
  VBellWait = VBELLWAIT;
  MsgWait = MSGWAIT;
  MsgMinWait = MSGMINWAIT;
  CompileKeys((char *)NULL, mark_key_tab);

  av0 = *av;
  while (ac > 0)
    {
      ap = *++av;
      if (--ac > 0 && *ap == '-')
	{
	  switch (ap[1])
	    {
	    case 'a':
	      allflag = 1;
	      break;
	    case 'A':
	      adaptflag = 1;
	      break;
	    case 'c':
	      if (ap[2])
		RcFileName = ap + 2;
	      else
		{
		  if (--ac == 0)
		    exit_with_usage(myname);
		  RcFileName = *++av;
		}
	      break;
	    case 'e':
	      if (ap[2])
		ap += 2;
	      else
		{
		  if (--ac == 0)
		    exit_with_usage(myname);
		  ap = *++av;
		}
	      if (!ParseEscape(ap))
		Msg(0, "Two characters are required with -e option.");
	      break;
	    case 'f':
	      switch (ap[2])
		{
		case 'n':
		case '0':
		  default_flow = FLOW_NOW * 0;
		  break;
		case 'y':
		case '1':
		case '\0':
		  default_flow = FLOW_NOW * 1;
		  break;
		case 'a':
		  default_flow = FLOW_AUTOFLAG;
		  break;
		default:
		  exit_with_usage(myname);
		}
	      break;
            case 'h':
	      if (ap[2])
		default_histheight = atoi(ap + 2);
	      else
		{
		  if (--ac == 0)
		    exit_with_usage(myname);
		  default_histheight = atoi(*++av);
		}
	      if (default_histheight < 0)
		default_histheight = 0;
	      break;
	    case 'i':
	      iflag = 1;
	      break;
	    case 't': /* title is a synonym for AkA */
	    case 'k':
	      if (ap[2])
		aka = ap + 2;
	      else
		{
		  if (--ac == 0)
		    exit_with_usage(myname);
		  aka = *++av;
		}
	      break;
	    case 'l':
	      switch (ap[2])
		{
		case 'n':
		case '0':
		  loginflag = 0;
		  break;
		case 'y':
		case '1':
		case '\0':
		  loginflag = 1;
		  break;
		case 's':
		case 'i':
		  lsflag = 1;
		  break;
		default:
		  exit_with_usage(myname);
		}
	      break;
	    case 'w':
	      lsflag = 1;
	      wipeflag = 1;
	      break;
	    case 'L':
	      assume_LP = 1;
	      break;
	    case 'm':
	      mflag = 1;
	      break;
	    case 'O':
	      force_vt = 0;
	      break;
	    case 'T':
              if (ap[2])
		{
		  if (strlen(ap+2) < 20)
                    strcpy(screenterm, ap + 2);
		}
              else
                {
                  if (--ac == 0)
                    exit_with_usage(myname);
		  if (strlen(*++av) < 20)
                    strcpy(screenterm, *av);
                }
              break;
	    case 'q':
	      quietflag = 1;
	      break;
	    case 'r':
	    case 'R':
	      if (ap[2])
		{
		  SockName = ap + 2;
		  if (ac != 1)
		    exit_with_usage(myname);
		}
	      else if (ac > 1 && *av[1] != '-')
		{
		  SockName = *++av;
		  ac--;
		}
	      rflag = (ap[1] == 'r') ? 1 : 2;
	      break;
#ifdef REMOTE_DETACH
	    case 'd':
	      dflag = 1;
	      /* FALLTHRU */
	    case 'D':
	      if (!dflag)
		dflag = 2;
	      if (ap[2])
		SockName = ap + 2;
	      if (ac == 2)
		{
		  if (*av[1] != '-')
		    {
		      SockName = *++av;
		      ac--;
		    }
		}
	      break;
#endif
	    case 's':
	      if (ap[2])
		ShellProg = ap + 2;
	      else
		{
		  if (--ac == 0)
		    exit_with_usage(myname);
		  ShellProg = *++av;
		}
	      break;
	    default:
	      exit_with_usage(myname);
	    }
	}
      else
	break;
    }
  real_uid = getuid();
  real_gid = getgid();
  eff_uid = geteuid();
  eff_gid = getegid();
  if (eff_uid != real_uid)
    {		
      /* if running with s-bit, we must install a special signal
       * handler routine that resets the s-bit, so that we get a
       * core file anyway.
       */
#ifdef SIGBUS
      signal(SIGBUS, CoreDump);
#endif
      signal(SIGSEGV, CoreDump);
    }
  if (!ShellProg && (ShellProg = getenv("SHELL")) == 0)
    ShellProg = DefaultShell;
  ShellArgs[0] = ShellProg;
#ifdef NETHACK
  nethackflag = (getenv("NETHACKOPTIONS") != NULL);
#endif
  home = getenv("HOME");	/* may or may not return a result. jw. */
  if ((LoginName = getlogin()) && LoginName[0] != '\0')
    {
      if ((ppp = getpwnam(LoginName)) != (struct passwd *) 0)
	if (ppp->pw_uid != real_uid)
	  ppp = (struct passwd *) 0;
    }
  if (ppp == 0)
    {
      if ((ppp = getpwuid(real_uid)) == 0)
        {
#ifdef NETHACK
          if (nethackflag)
	    Msg(0, "An alarm sounds through the dungeon...\nWarning, the kops are coming.");
	  else
#endif
	  Msg(0, "getpwuid() can't identify your account!");
	  exit(1);
        }
      LoginName = ppp->pw_name;
    }
  if (home == 0 || *home == '\0')
    home = ppp->pw_dir;
  if (strlen(LoginName) > 20)
    Msg(0, "LoginName too long - sorry.");
  if (strlen(home) > MAXPATH - 25)
    Msg(0, "$HOME too long - sorry.");
#ifdef PASSWORD
  strcpy(Password, ppp->pw_passwd);
#endif

  /* ttyname implies isatty */
  if (!(attach_tty = ttyname(0)))
    {
#ifdef NETHACK
      if (nethackflag)
	Msg(0, "You must play from a terminal.");
      else
#endif
      Msg(0, "Must be connected to a terminal.");
      exit(1);
    }
  if (strlen(attach_tty) >= MAXPATH)
    Msg(0, "TtyName too long - sorry.");
  if ((n = secopen(attach_tty, O_RDWR, 0)) < 0)
    Msg(0, "Cannot open '%s' - please check.", attach_tty);
  close(n);
    
  debug1("attach_tty is %s\n", attach_tty);
  
#ifdef _MODE_T
  oumask = umask(0);		/* well, unsigned never fails? jw. */
#else
  if ((oumask = umask(0)) == -1)
    Msg(errno, "Cannot change umask to zero");
#endif
  if ((SockDir = getenv("ISCREENDIR")) == NULL)
    SockDir = getenv("SCREENDIR");
  if (SockDir && strlen(SockDir) >= MAXPATH - 1)
    Msg(0, "ridiculous long $(I)SCREENDIR - try again.");
#ifndef SOCKDIR
  if (SockDir == 0)
    {
      sprintf(SockPath, "%s/.iscreen", home);
      SockDir = SockPath;
    }
#endif
  if (SockDir)
    {
      if (access(SockDir, F_OK))
	{
	  if (UserContext() > 0)
	    {
	      if (mkdir(SockDir, 0700))
		UserReturn(0);
	      UserReturn(1);
	    }
	  if (UserStatus() <= 0)
	    Msg(0, "Cannot make directory '%s'", SockDir);
	}
      if (SockDir != SockPath)
        strcpy(SockPath, SockDir);
    }
#ifdef SOCKDIR
  else
    {
      SockDir = SOCKDIR;
      if (stat(SockDir, &st))
	{
	  if (mkdir(SockDir, eff_uid ? 0777 : 0755) == -1)
	    Msg(errno, "Cannot make directory '%s'", SockDir);
	}
      else
	{
          n = eff_uid ? 0777 : 0755;
	  if ((st.st_mode & 0777) != n)
	    Msg(0, "Directory '%s' must have mode %03o.", SockDir, n);
	}
      sprintf(SockPath, "%s/S-%s", SockDir, LoginName);
      if (access(SockPath, F_OK))
	{
	  if (mkdir(SockPath, 0700) == -1)
	    Msg(errno, "Cannot make directory '%s'", SockPath);
	  (void) chown(SockPath, real_uid, real_gid);
	}
    }
#endif
  if (stat(SockPath, &st) == -1)
    {
      Msg(errno, "Cannot access %s", SockPath);
    }
  else
    {
#ifdef _POSIX_SOURCE
      if (S_ISDIR(st.st_mode) == 0)
#else
      if ((st.st_mode & S_IFMT) != S_IFDIR)
#endif
	Msg(0, "%s is not a directory.", SockPath);
      if (st.st_uid != real_uid)
	Msg(0, "You are not the owner of %s.", SockPath);
      if ((st.st_mode & 0777) != 0700)
	Msg(0, "Directory %s must have mode 700.", SockPath);
    }
  strcat(SockPath, "/");
  SockNamePtr = SockPath + strlen(SockPath);
  (void) umask(oumask);
#if defined(SYSV) && !defined(ISC)
  if (uname(&utsnam) == -1)
    Msg(0, "uname() failed, errno = %d", errno);
  else
    {
      strncpy(HostName, utsnam.nodename, MAXSTR);
      HostName[(sizeof(utsnam.nodename) <= MAXSTR) ? 
               sizeof(utsnam.nodename) : MAXSTR] = '\0';
    }
#else
  (void) gethostname(HostName, MAXSTR);
#endif
  HostName[MAXSTR - 1] = '\0';
  if ((ap = index(HostName, '.')) != NULL)
    *ap = '\0';
  GetTTY(0, &OldMode);
#ifdef POSIX
  ospeed = (short) cfgetospeed(&OldMode.tio);
#else
# ifndef TERMIO
  ospeed = (short) OldMode.m_ttyb.sg_ospeed;
# endif
#endif
  debug1("...setting extern short ospeed = %d\n", ospeed);

  if (lsflag)
    {
      int i;
      i = FindSocket(0, (int *)NULL);
      /* MakeClientSocket appended the last (Sock)Name there: */
      *SockNamePtr = '\0';
      if (i == 0)
	{
#ifdef NETHACK
          if (nethackflag)
	    Msg(0, "This room is empty (%s)\n", SockPath);
          else
#endif /* NETHACK */
          Msg(0, "No Sockets found in %s\n", SockPath);
        }
      else
        Msg(0, "%d Socket%s in %s.\n", i, i > 1 ? "s" : "", SockPath);
        /* NOTREACHED */
    }
  if (rflag)
    {
      debug("screen -r: - is there anybody out there?\n");
#ifdef SHADOWPW
      setspent();  /* open shadow file while we are still root */
#endif /* SHADOWPW */
      if (Attach(MSG_ATTACH))
	{
	  Attacher();
	  /* NOTREACHED */
	}
      debug("screen -r: backend not responding -- still crying\n");
    }
  else if (dflag)
    {
      (void) Attach(MSG_DETACH);
      DeadlyMsg = 0;
      Msg(0, "[%s %sdetached.]\n", SockName, (dflag > 1 ? "power " : ""));
      eexit(0);
      /* NOTREACHED */
    }
  if (!mflag && (SockName = getenv("STY")) != 0 && *SockName != '\0')
    {
      setuid(real_uid);
      setgid(real_gid);
      s = MakeClientSocket(1, SockName);
      if (ac == 0)
	{
	  ac = 1;
	  av = ShellArgs;
	}
      av[ac] = aka;
      SendCreateMsg(s, ac, av, allflag, default_flow, loginflag, default_histheight,
		    screenterm);
      close(s);
      exit(0);
    }
#if defined(BSDJOBS) && !(defined(POSIX) || defined(SYSV))
  if ((DevTty = open("/dev/tty", O_RDWR | O_NDELAY)) == -1)
    Msg(errno, "/dev/tty");
#endif
  switch (MasterPid = fork())
    {
    case -1:
      Msg(errno, "fork");
      /* NOTREACHED */
    case 0:
      break;
    default:
      sprintf(socknamebuf, "%d.%s.%s", MasterPid, stripdev(attach_tty),
	      HostName);
      for (ap = socknamebuf; *ap; ap++)
	if (*ap == '/')
	  *ap = '-';
      SockName = socknamebuf;
#ifdef SHADOWPW
      setspent();  /* open shadow file while we are still root */
#endif /* SHADOWPW */
      Attacher();
      /* NOTREACHED */
    }
#ifdef DEBUG
  if (dfp != stderr)
    fclose(dfp);
  if ((dfp = fopen("/tmp/debug/screen.back", "w")) == NULL)
    dfp = stderr;
  else
    (void) chmod("/tmp/debug/screen.back", 0666);
#endif
  debug("-- screen.back debug started\n");
  ap = av0 + strlen(av0) - 1;
  while (ap >= av0)
    {
      if (!strncmp("screen", ap, 6))
	{
	  strncpy(ap, "SCREEN", 6); /* name this process "SCREEN-BACKEND" */
	  break;
	}
      ap--;
    }
  if (ap < av0)
    *av0 = 'S';

  AttacherPid = getppid();
  sprintf(socknamebuf, "%d.%s.%s", getpid(), stripdev(attach_tty), HostName);
  for (ap = socknamebuf; *ap; ap++)
    if (*ap == '/')
      *ap = '-';
  SockName = socknamebuf;
  ServerSocket = s = MakeServerSocket();
#ifdef ETCSCREENRC
  if ((ap = getenv("SYSSCREENRC")) == NULL)
    StartRc(ETCSCREENRC);
  else
    StartRc(ap);
#endif
  StartRc(RcFileName);
  InitTermcap();
  InitTerm(0);
  MakeNewEnv();
  strcpy(display_tty, attach_tty);
#ifdef UTMPOK
# ifdef apollo
  ReInitUtmp();
# else
  InitUtmp();
# endif /* apollo */
#endif /* UTMPOK */
  signal(SIGHUP, SigHup);
  signal(SIGINT, Finit);
  signal(SIGQUIT, Finit);
  signal(SIGTERM, Finit);
#ifdef BSDJOBS
  signal(SIGTTIN, SIG_IGN);
  signal(SIGTTOU, SIG_IGN);
#endif
  InitKeytab();
#ifdef ETCSCREENRC
  if ((ap = getenv("SYSSCREENRC")) == NULL)
    FinishRc(ETCSCREENRC);
  else
    FinishRc(ap);
#endif
  FinishRc(RcFileName);

  /* Note: SetMode must be called _after_ FinishRc (flow is set there).
   */
  SetMode(&OldMode, &NewMode);
  SetTTY(0, &NewMode);
  if (loginflag == -1)
      loginflag = LOGINDEFAULT;
  if (ac == 0)
    {
      ac = 1;
      av = ShellArgs;
      if (!aka)
	aka = shellaka;
    }
  if (!HasWindow)
    {
      debug("We open one default window, as screenrc did not specify one.\n");
      if (MakeWindow(aka, av, allflag, default_flow, 0, (char *)0, loginflag, -1, (char *)0) == -1)
	{
	  Finit(1);
	  /* NOTREACHED */
	}
    }
  if (default_startup)
    display_copyright();
#ifdef SYSV
  signal(SIGCLD, SigChld);
#else
  signal(SIGCHLD, SigChld);
#endif
  signal(SIGINT, SigInt);
  tv.tv_usec = 0;
  if (rflag == 2)
    {
#ifdef NETHACK
      if (nethackflag)
        Msg(0, "I can't seem to find a... Hey, wait a minute!  Here comes a screen now.");
      else
#endif
      Msg(0, "New screen...");
      rflag = 0;
    }
  brktty();
  for (;;)
    {
      /*
       * check to see if message line should be removed
       */
      if (status)
	{
	  int time_left;

	  debug("checking status...\n");
	  time_left = TimeDisplayed + (BellDisplayed ? VBellWait : MsgWait) - time((time_t *)0);
	  if (time_left > 0)
	    {
	      tv.tv_sec = time_left;
	      debug(" not yet.\n");
	    }
	  else
	    {
	      debug(" removing now.\n");
	      RemoveStatus();
	    }
	}
      /*
       * check for I/O on all available I/O descriptors
       */
      FD_ZERO(&r);
      FD_ZERO(&w);
      FD_ZERO(&e);
      if (inbuf_ct > 0)
	for (n = 0; n < MAXWIN; n++)
#ifdef COPY_PASTE		/* wrong here? jw. */
	  if (inlen[n] > 0 || (pastelen > 0 && n == ForeNum))
#else
	  if (inlen[n] > 0)
#endif
	    FD_SET(wtab[n]->ptyfd, &w);
      if (!Detached)
	FD_SET(0, &r);
      for (n = WinList; n != -1; n = p->WinLink)
	{
	  p = wtab[n];
	  if (p->active && status && !BellDisplayed && !HS)
	    continue;
	  if (p->outlen > 0)
	    continue;
	  if (in_ovl && ovl_blockfore && n == ForeNum)
	    continue;
	  FD_SET(p->ptyfd, &r);
	}
      FD_SET(s, &r);
      (void) fflush(stdout);
      if (GotSignal && !status)
	{
	  SigHandler();
	  continue;
	}
      if ((nsel = select(FD_SETSIZE, &r, &w, &e, (status) ? &tv : (struct timeval *) 0)) < 0)
	{
	  debug1("Bad select - errno %d\n", errno);
	  if (errno != EINTR)
	    {
	      perror("select");
	      Finit(1);
	    }
	  else
	    {
	      errno = 0;
	      if ((!GotSignal || status) && !InterruptPlease)
	        continue;
	    }
	}
      if (InterruptPlease)
	{
	  char buf[1];

	  debug("Backend received interrupt\n");
	  *buf = intrc;
	  write(wtab[ForeNum]->ptyfd, buf, 1);
	  debug1("Backend wrote interrupt to %d\n", ForeNum);
	  InterruptPlease = 0;

	  continue;
	}
      if (GotSignal && !status)
	{
	  SigHandler();
	  continue;
	}
      /* Process a client connect attempt and message */
      if (nsel && FD_ISSET(s, &r))
	{
          nsel--;
	  if (!HS)
	    RemoveStatus();
	  if (in_ovl)
	    {
	      SetOvlCurr();
	      (*ovl_process)(0, 0); /* We have to abort first!! */
	      CheckScreenSize(1); /* Change fore */
	      DeadlyMsg = 0;
#ifdef NETHACK
              if (nethackflag)
	        Msg(0, "KAABLAMM!!!  You triggered a land mine!");
              else
#endif
	      Msg(0, "Aborted because of window change or message.");
	    }
	  else
	    CheckScreenSize(1); /* Change fore */
	  ReceiveMsg(s);
	  continue;
	}
      /*
       * Write the stored user input to the window descriptors first.
       * We do not want to choke, if he types fast.
       */
      if (nsel && inbuf_ct > 0)
	{
	  for (n = 0; n < MAXWIN ; n++)
	    {
	      if (inlen[n] <= 0)
		continue;
	      tmp = wtab[n]->ptyfd;
              if (FD_ISSET(tmp, &w))
                {
		  if ((len = write(tmp, inbuf[n], inlen[n])) > 0)
		    {
		      if ((inlen[n] -= len) == 0)
		      inbuf_ct--;
		      bcopy(inbuf[n] + len, inbuf[n], inlen[n]);
		    }
		  if (--nsel == 0)
		    break;
		}
	    }
	}
      /* Read, process, and store the user input */
      if (nsel && FD_ISSET(0, &r))
	{
          nsel--;
	  if (!HS)
	    RemoveStatus();
	  if (ESCseen)
	    {
	      buf[0] = Esc;
	      buflen = read(0, buf + 1, IOSIZE - 1) + 1;
	      ESCseen = 0;
	    }
	  else
	    buflen = read(0, buf, IOSIZE);
	  if (buflen < 0)
	    {
	      debug1("Read error: %d - SigHup()ing!\n", errno);
	      SigHup(SIGARG);
	      continue;
	    }
	  if (buflen == 0)
	    {
	      debug("Found EOF - SigHup()ing!\n");
	      SigHup(SIGARG);
	      continue;
	    }
	  bufp = buf;
          if (in_ovl)
	    {
	      SetOvlCurr();
	      (*ovl_process)(&bufp, &buflen);
	    }
	  while (buflen > 0)
	    {
	      n = ForeNum;
	      len = inlen[n];
	      bufp = ProcessInput(bufp, &buflen, inbuf[n], &inlen[n],
				  sizeof *inbuf);
	      if (inlen[n] > 0 && len == 0)
		inbuf_ct++;
	    }
	  if (inbuf_ct > 0)
	    continue;
	}
      if (GotSignal && !status)
	{
	  SigHandler();
	  continue;
	}
#ifdef COPY_PASTE
      /* Write the copybuffer contents first, if any. jw. */
      if (pastelen > 0)
	{
	  n = ForeNum;
	  debug1("writing pastebuffer (%d)\n", pastelen);
	  tmp = wtab[n]->ptyfd;
	  if (			/* FD_ISSET(tmp, &w) && */
	      (len = write(tmp, pastebuffer,
			   pastelen > IOSIZE ? IOSIZE : pastelen)) > 0)
	    {
	      pastebuffer += len;
	      pastelen -= len;
	      debug1("%d bytes pasted\n", len);
	      if (slowpaste > 0)
		{
		  struct timeval t;

                  debug1("slowpaste %d\n", slowpaste);
		  t.tv_usec = (long) (slowpaste * 1000);
		  t.tv_sec = 0;
		  select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t);
		}
	      else
	        continue;
	    }
	  /* 
	   * We could not paste? Let's see if the pty did echo the lot.
	   * Then continue by processing some pty output.
	   */
	}
#endif
      if (GotSignal && !status)
	{
	  SigHandler();
	  continue;
	}
      /* Read and process the output from the window descriptors */
      for (n = WinList; n != -1; n = p->WinLink)
	{
	  p = wtab[n];
	  if (in_ovl && ovl_blockfore && n == ForeNum)
	    continue;
	  if (p->outlen)
	    WriteString(p, p->outbuf, p->outlen);
	  else if (nsel && FD_ISSET(p->ptyfd, &r))
	    {
	      nsel--;
	      if ((len = read(p->ptyfd, buf, IOSIZE)) == -1)
		{
#ifdef EWOULDBLOCK
		  if (errno == EWOULDBLOCK)
		    len = 0;
#endif
		}
#if defined(TIOCPKT) && !defined(sgi)
	      if (buf[0])
		{
		  debug1("PAKET %x\n", buf[0]);
		  if (buf[0] & TIOCPKT_NOSTOP)
		    {
		      NewAutoFlow(p, 0);
		    }
		  if (buf[0] & TIOCPKT_DOSTOP)
		    {
		      NewAutoFlow(p, 1);
		    }
		}
	      if (len > 1)
		WriteString(p, buf + 1, len - 1);
#else /* TIOCPKT && !sgi */
	      if (len > 0)
		WriteString(p, buf, len);
#endif /* TIOCPKT && !sgi */
	    }
	  if (p->bell == BELL_ON)
	    {
	      p->bell = BELL_DONE;
	      Msg(0, MakeWinMsg(BellString, n));
	      if (p->monitor == MON_FOUND)
		p->monitor = MON_DONE;
	    }
	  else if (p->bell == BELL_VISUAL)
	    {
	      if (!BellDisplayed)
		{
		  p->bell = BELL_DONE;
		  Msg(0, VisualBellString);
		  BellDisplayed = 1;
		}
	    }
	  else if (p->monitor == MON_FOUND)
	    {
	      p->monitor = MON_DONE;
	      Msg(0, MakeWinMsg(ActivityString, n));
	    }
	}
      if (GotSignal && !status)
	SigHandler();
#ifdef DEBUG
      if (nsel)
	debug1("Left over nsel: %d\n", nsel);
#endif
    }
  /* NOTREACHED */
}

static void SigHandler()
{
  struct stat st;
  while (GotSignal)
    {
      GotSignal = 0;
      DoWait();
#ifdef SYSV
      signal(SIGCLD, SigChld);
#endif
    }
  if (stat(SockPath, &st) == -1)
    {
      debug1("SigHandler: Yuck! cannot stat '%s'\n", SockPath);
      if (!RecoverSocket())
	{
	  debug("SCREEN cannot recover from corrupt Socket, bye\n");
	  Finit(1);
	}
      else
	debug1("'%s' reconstructed\n", SockPath);
    }
  else
    debug2("SigHandler: stat '%s' o.k. (%03o)\n", SockPath, st.st_mode);
}

#ifdef DEBUG
int FEpanic;

sig_t FEChld(SIGDEFARG)
{
  FEpanic=1;
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}
#endif

static sig_t SigChld(SIGDEFARG)
{
  debug("SigChld()\n");
  GotSignal = 1;
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}

sig_t SigHup(SIGDEFARG)
{
  debug("SigHup()\n");
  if (auto_detach)
    Detach(D_DETACH);
  else
    Finit(0);
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}

/*
 * the frontend's Interrupt handler
 * we forward SIGINT to the backend
 */
static sig_t 
AttacherSigInt(SIGDEFARG)
{
  Kill(MasterPid, SIGINT);
  signal(SIGINT, AttacherSigInt);
# ifndef SIGVOID
  return (sig_t) 0;
# endif
}


/* 
 * the backend's Interrupt handler
 * we cannot insert the intrc directly, as we never know
 * if fore and ForeNum are valid.
 */
static sig_t SigInt(SIGDEFARG)
{
#if HAZARDOUS
  char buf[1];

  debug("SigInt()\n");
  *buf = (char) intrc;
  inlen[ForeNum] = 0;
  if (fore && !in_ovl)
    write(fore->ptyfd, buf, 1);
#else
  debug("SigInt() careful\n");
  InterruptPlease = 1;
  signal(SIGINT, SigInt);
#endif
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}

static sig_t CoreDump(sig)
int sig;
{
  setgid(getgid());
  setuid(getuid());
  unlink("core");
  fprintf(stderr, "\r\n[screen caught signal %d.%s]\r\n", sig,
#ifdef SHADOWPW
          ""
#else /* SHADOWPW */
          " (core dumped)"
#endif /* SHADOWPW */
          );
  fflush(stderr);
  Kill(AttacherPid, SIG_BYE);
#ifdef SHADOWPW
  eexit(sig);
#else /* SHADOWPW */
  abort();
#endif /* SHADOWPW */
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}

static void DoWait()
{
  register int n, next, pid;
#ifdef BSDWAIT
  union wait wstat;
#else
  int wstat;
#endif

#ifdef BSDJOBS
# ifndef BSDWAIT
  while ((pid = waitpid(-1, &wstat, WNOHANG | WUNTRACED)) > 0)
# else
  while ((pid = wait3(&wstat, WNOHANG | WUNTRACED, (struct rusage *) 0)) > 0)
# endif
#else	/* BSDJOBS */
  while ((pid = wait(&wstat)) < 0)
    if (errno != EINTR)
      break;
  if (pid >= 0)
#endif	/* BSDJOBS */
    {
      for (n = WinList; n != -1; n = next)
	{
	  next = wtab[n]->WinLink;
	  if (pid == wtab[n]->wpid)
	    {
#ifdef BSDJOBS
	      if (WIFSTOPPED(wstat))
		{
# ifdef NETHACK	
                  if (nethackflag)
		    Msg(0, "You regain consciousness.");
		  else
# endif /* NETHACK */
		  Msg(0, "Child has been stopped, restarting.");
		  debug1("WIFSTOPPED: %d SIGCONT\n", wtab[n]->wpid);
		  if (killpg(wtab[n]->wpid, SIGCONT))
		    kill(wtab[n]->wpid, SIGCONT);
		}
	      else
#endif
		KillWindow(n);
	    }
	}
    }
}

void KillWindow(n)
int n;
{
  register int i;
  /*
   * Remove window from linked list.
   */
  if (n == WinList)	/* WinList = ForeNum */
    {
      RemoveStatus();
      WinList = fore->WinLink;
      fore = 0;
    }
  else
    {
      i = WinList;
      while (wtab[i]->WinLink != n)
	i = wtab[i]->WinLink;
      wtab[i]->WinLink = wtab[n]->WinLink;
    }
  FreeWindow(wtab[n]);
  wtab[n] = 0;
  if (inlen[n] > 0)
    {
      inlen[n] = 0;
      inbuf_ct--;
    }
  /*
   * If the foreground window disappeared check the head of the linked list
   * of windows for the most recently used window. If no window is alive at
   * all, exit.
   */
  if (WinList == -1)
    Finit(0);
  if (!fore)
    SwitchWindow(WinList);
}

static sig_t Finit(i)
int i;
{
  register int n, next;

#ifdef SYSV
  signal(SIGCLD, SIG_IGN);
#else
  signal(SIGCHLD, SIG_IGN);
#endif
  signal(SIGHUP, SIG_IGN);
  debug1("Finit(%d);\n", i);
  for (n = WinList; n != -1; n = next)
    {
      next = wtab[n]->WinLink;
      FreeWindow(wtab[n]);
    }
  FinitTerm();
  SetTTY(0, &OldMode);
#ifdef UTMPOK
  RestoreLoginSlot();
#endif
  printf("\n[screen is terminating]\n");
  freetty();
  if (ServerSocket != -1)
    {
      debug1("we unlink(%s)\n", SockPath);
      (void) unlink(SockPath);
    }
  Kill(AttacherPid, SIG_BYE);
  exit(i);
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}

void
eexit(e)
int e;
{
  if (ServerSocket != -1)
    {
      debug1("we unlink(%s)\n", SockPath);
      (void) unlink(SockPath);
    }
  exit(e);
}

static void InitKeytab()
{
  register unsigned int i;

  for (i = 0; i < sizeof(ktab)/sizeof(*ktab); i++)
    ktab[i].type = KEY_IGNORE;

  ktab['h'].type = ktab[Ctrl('h')].type = KEY_HARDCOPY;
#ifdef BSDJOBS
  ktab['z'].type = ktab[Ctrl('z')].type = KEY_SUSPEND;
#endif
  ktab['c'].type = ktab[Ctrl('c')].type = KEY_SHELL;
  ktab[' '].type = ktab[Ctrl(' ')].type =
    ktab['n'].type = ktab[Ctrl('n')].type = KEY_NEXT;
  ktab['-'].type = ktab['p'].type = ktab[Ctrl('p')].type = KEY_PREV;
  ktab['k'].type = ktab[Ctrl('k')].type = KEY_KILL;
  ktab['l'].type = ktab[Ctrl('l')].type = KEY_REDISPLAY;
  ktab['w'].type = ktab[Ctrl('w')].type = KEY_WINDOWS;
  ktab['v'].type = ktab[Ctrl('v')].type = KEY_VERSION;
  ktab['q'].type = ktab[Ctrl('q')].type = KEY_XON;
  ktab['s'].type = ktab[Ctrl('s')].type = KEY_XOFF;
  ktab['t'].type = ktab[Ctrl('t')].type = KEY_TIME;
  ktab['i'].type = ktab[Ctrl('i')].type = KEY_INFO;
  ktab['m'].type = ktab[Ctrl('m')].type = KEY_LASTMSG;
  ktab['A'].type = KEY_AKA, ktab['A'].args = NULL;
  ktab['L'].type = KEY_LOGIN;
  ktab[','].type = KEY_LICENSE;
  ktab['W'].type = KEY_WIDTH;
  ktab['.'].type = KEY_TERMCAP;
  ktab[Ctrl('\\')].type = KEY_QUIT;
  ktab['d'].type = ktab[Ctrl('d')].type = KEY_DETACH;
  ktab['r'].type = ktab[Ctrl('r')].type = KEY_WRAP;
  ktab['f'].type = ktab[Ctrl('f')].type = KEY_FLOW;
  ktab['C'].type = KEY_CLEAR;
  ktab['Z'].type = KEY_RESET;
  ktab['H'].type = KEY_LOGTOGGLE;
  if (Esc != MetaEsc)
    ktab[Esc].type = KEY_OTHER;
  else
    ktab[Esc].type = KEY_IGNORE;
  ktab['M'].type = KEY_MONITOR;
  ktab['?'].type = KEY_HELP;
  for (i = 0; i <= 9; i++)
    ktab['0' + i].type = (enum keytype) (i + (int)KEY_0);
  ktab[Ctrl('G')].type = KEY_VBELL;
  ktab[':'].type = KEY_COLON;
#ifdef COPY_PASTE
  ktab['['].type = ktab[Ctrl('[')].type = KEY_COPY;
  ktab[']'].type = ktab[Ctrl(']')].type = KEY_PASTE;
  ktab['{'].type = KEY_HISTORY;
  ktab['}'].type = KEY_HISTNEXT;
  ktab['>'].type = KEY_WRITE_BUFFER;
  ktab['<'].type = KEY_READ_BUFFER;
  ktab['='].type = KEY_REMOVE_BUFFERS;
#endif
#ifdef POW_DETACH
  ktab['D'].type = KEY_POW_DETACH;
#endif
#ifdef LOCK
  ktab['x'].type = ktab[Ctrl('x')].type = KEY_LOCK;
#endif
}

/*
 * this is a braindamaged hack: if (obuf == NULL) then we provided
 * a key_type as a second char in ibuf. not a key.
 */
char *ProcessInput(ibuf, pilen, obuf, polen, obuf_size)
char *ibuf, *obuf;
register int *pilen, *polen, obuf_size;
{
  register int n;
  register enum keytype k;
  register char *s, *p;
  char buf[2];
  int newwidth;

  if (!obuf)
    obuf_size = 0;

  for (s = ibuf, p = obuf + *polen; *pilen > 0; --*pilen, s++)
    {
      if (*s == Esc)
	{
	  debug2("'%c %c ", s[0], s[1]);
	  debug2("%c %c' ", s[2], s[3]);
	  if (*pilen > 1)
	    {
	      --*pilen;
	      s++;
#if defined(GOULD_NP1)
	      k = (obuf)?(ktab[*s].type):(enum keytype)(int)(*s);
#else
	      k = (obuf)?(ktab[*s].type):(enum keytype)(*s);
#endif
	      debug2("Processinput C-A %02x '%c' ", k, k);
	      debug1("%s\n", (obuf)?"std":"NOOBUF");
	      if (*s == MetaEsc)
		{
		  if (*polen < obuf_size)
		    {
		      *p++ = Esc;
		      ++*polen;
		    }
		}
	      else if ((int)k >= (int)KEY_0 && (int)k <= (int)KEY_9)
		SwitchWindow((int)k - (int)KEY_0);
	      else
		switch (k)
		  {
		  case KEY_TERMCAP:
		    WriteFile(DUMP_TERMCAP);
		    break;
		  case KEY_HARDCOPY:
		    WriteFile(DUMP_HARDCOPY);
		    break;
		  case KEY_LOGTOGGLE:
		    LogToggle();
		    break;
#ifdef BSDJOBS
		  case KEY_SUSPEND:
		    *pilen = 0;
		    Detach(D_STOP);
		    break;
#endif
		  case KEY_SHELL:
		    debug("calling MakeWindow with shell\n");
		    MakeWindow(shellaka, ShellArgs, allflag, default_flow,
			       0, (char *) 0, loginflag, -1, (char *)0);
		    break;
		  case KEY_NEXT:
		    if (MoreWindows())
		      SwitchWindow(NextWindow());
		    break;
		  case KEY_PREV:
		    if (MoreWindows())
		      SwitchWindow(PreviousWindow());
		    break;
		  case KEY_KILL:
		    KillWindow(n = ForeNum);
#ifdef NETHACK
      		    if (nethackflag)
		      Msg(0, "You destroy poor window %d", n);
#endif
		    break;
		  case KEY_QUIT:
		    Finit(0);
		    /* NOTREACHED */
		  case KEY_DETACH:
		    *pilen = 0;
		    Detach(D_DETACH);
		    break;
#ifdef POW_DETACH
		  case KEY_POW_DETACH:
		    *pilen = 0;
		    if (obuf)
		      {
			buf[0] = *s;
			buf[1] = '\0';
			Msg(0, buf);
			read(0, buf, 1);
			if (*buf != *s)
			  {
			    write(1, "\007", 1);
			    RemoveStatus();
#ifdef NETHACK
			    if (nethackflag)
			       Msg(0, "The blast of disintegration whizzes by you!");
#endif
			    break;
			  }
		      }
		    Detach(D_POWER); /* detach and kill Attacher's
				      * parent	 */
		    break;
#endif
		  case KEY_REDISPLAY:
		    Activate(0);
		    break;
		  case KEY_WINDOWS:
		    ShowWindows();
		    break;
		  case KEY_VERSION:
		    Msg(0, "screen %d.%.2d.%.2d%s (%s) %s", REV, VERS,
		        PATCHLEVEL, STATE, ORIGIN, DATE);
		    break;
		  case KEY_TIME:
		    ShowTime();
		    break;
		  case KEY_INFO:
		    ShowInfo();
		    break;
		  case KEY_OTHER:
		    if (MoreWindows())
		      SwitchWindow(fore->WinLink);
		    break;
		  case KEY_XON:
		    if (*polen < obuf_size)
		      {
			*p++ = Ctrl('q');
			++*polen;
		      }
		    break;
		  case KEY_XOFF:
		    if (*polen < obuf_size)
		      {
			*p++ = Ctrl('s');
			++*polen;
		      }
		    break;
#ifdef LOCK
		  case KEY_LOCK:
		    Detach(D_LOCK); /* do it micha's way */
		    break;
#endif
		  case KEY_WIDTH:
		    if (Z0 || WS)
		      {
			if (fore->width == Z0width)
			  newwidth = Z1width;
			else if (fore->width == Z1width)
			  newwidth = Z0width;
			else if (fore->width > (Z0width+Z1width)/2)
			  newwidth = Z0width;
			else
			  newwidth = Z1width;
			ChangeWindowSize(fore, newwidth, fore->height);
			Activate(fore->norefresh);
		      }
		    else
		      Msg(0, "Your termcap does not specify how to change the terminal's width.");
		    break;
		  case KEY_LOGIN:
		    SlotToggle(0);
		    break;
		  case KEY_AKA:
		    if (!ktab[*s].args)
		      InputAKA();
		    else
		      strncpy(fore->cmd + fore->akapos, ktab[*s].args[0], 20);
		    break;
		  case KEY_COLON:
		    InputColon();
		    break;
		  case KEY_LASTMSG:
		    Msg(0, "%s", LastMsg);
		    break;
		  case KEY_SET:
		    DoSet(ktab[*s].args);
		    break;
		  case KEY_SCREEN:
		    debug3("KEY_SCREEN DoSc(, ktab[%d].args(='%s','%s')...)\n",
			   *s, ktab[*s].args[0], ktab[*s].args[1]);
		    DoScreen("key", ktab[*s].args);
		    break;
		  case KEY_CREATE:
		    debug2("KEY_CREATE MaWi(0, ktab[%d].args(='%s')...)\n",
			   *s, ktab[*s].args);
		    MakeWindow((char *) 0, ktab[*s].args, allflag, default_flow, 0, (char *) 0, loginflag, -1, (char *)0);
		    break;
		  case KEY_WRAP:
		    fore->wrap = !fore->wrap;
		    Msg(0, "%cwrap", fore->wrap ? '+' : '-');
		    break;
		  case KEY_FLOW:
		    if (fore->flow & FLOW_AUTOFLAG)
		      fore->flow = (fore->flow & FLOW_AUTO) | FLOW_NOW;
		    else if (fore->flow & FLOW_NOW)
		      fore->flow &= ~FLOW_NOW;
		    else
		      fore->flow = fore->flow ? FLOW_AUTOFLAG|FLOW_AUTO|FLOW_NOW : FLOW_AUTOFLAG;
		    SetFlow(fore->flow & FLOW_NOW);
		    Msg(0, "%cflow%s", (fore->flow & FLOW_NOW) ? '+' : '-',
			(fore->flow & FLOW_AUTOFLAG) ? "(auto)" : "");
		    break;
		  case KEY_CLEAR:
		    if (fore->state == LIT)
		      WriteString(fore, "\033[H\033[J", 6);
		    break;
		  case KEY_RESET:
		    if (fore->state == LIT)
		      WriteString(fore, "\033c", 2);
		    break;
		  case KEY_MONITOR:
		    if (fore->monitor == MON_OFF)
		      {
			fore->monitor = MON_ON;
			Msg(0,
			    "Window %d is now being monitored for all activity.",
			    ForeNum);
		      }
		    else
		      {
			fore->monitor = MON_OFF;
			Msg(0,
			    "Window %d is no longer being monitored for activity.",
			    ForeNum);
		      }
		    break;
		  case KEY_HELP:
		    display_help();
		    break;
		  case KEY_LICENSE:
		    display_copyright();
		    break;
#ifdef COPY_PASTE
		  case KEY_COPY:
		    (void) MarkRoutine(PLAIN);
		    break;
		  case KEY_HISTNEXT:
		    if (MarkRoutine(CRAZY))
		      if (copybuffer != NULL)
		        {
		  	  pastelen = copylen;
		  	  pastebuffer = copybuffer;
			  debug("histnext\n");
		        }
		    break;
		  case KEY_HISTORY:
		    if (MarkRoutine(TRICKY))
		      if (copybuffer != NULL)
			{
			  pastelen = copylen;
			  pastebuffer = copybuffer;
			  debug1("history new copylen: %d\n", pastelen);
			}
		    break;
		  case KEY_PASTE:
		    if (copybuffer == NULL)
		      {
#ifdef NETHACK
      			if (nethackflag)
			  Msg(0, "Nothing happens.");
      			else
#endif
			Msg(0, "empty buffer");
			copylen = 0;
			break;
		      }
		    pastelen = copylen;
		    pastebuffer = copybuffer;
		    break;
		  case KEY_WRITE_BUFFER:
		    if (copybuffer == NULL)
		      {
#ifdef NETHACK
      			if (nethackflag)
			  Msg(0, "Nothing happens.");
      			else
#endif
			Msg(0, "empty buffer");
			copylen = 0;
			break;
		      }
		    WriteFile(DUMP_EXCHANGE);
		    break;
		  case KEY_READ_BUFFER:
		    ReadFile();
		    break;
		  case KEY_REMOVE_BUFFERS:
		    KillBuffers();
		    break;
#endif				/* COPY_PASTE */
		  case KEY_VBELL:
		    if (visual_bell)
		      {
			visual_bell = 0;
			Msg(0, "switched to audible bell");
		      }
		    else
		      {
			visual_bell = 1;
			Msg(0, "switched to visual bell");
		      }
		    break;
		   default:
		    break;
		  }
	    }
	  else
	    ESCseen = 1;
	  --*pilen;
	  s++;
	  break;
	}
      else if (*polen < obuf_size)
	{
	  *p++ = *s;
	  ++*polen;
	}
    }
  return (s);
}

/* Send a terminal report as if it were typed. */ 
void
Report(wp, fmt, n1, n2)
struct win *wp;
char *fmt;
int n1, n2;
{
  register int n, len;
  char rbuf[40];

  sprintf(rbuf, fmt, n1, n2);
  len = strlen(rbuf);

  for (n = 0; n < MAXWIN; n++)
    {
      if (wp == wtab[n])
	{
	  if ((unsigned)(inlen[n] + len) <= sizeof *inbuf)
	    {
	      bcopy(rbuf, inbuf[n] + inlen[n], len);
	      if (inlen[n] == 0)
		inbuf_ct++;
	      inlen[n] += len;
	    }
	  break;
	}
    }/* for */
}

void
SwitchWindow(n)
int n;
{
  debug1("SwitchWindow %d\n", n);
  if (!wtab[n])
    {
      ShowWindows();
      return;
    }
  if (wtab[n] == fore)
    {
      Msg(0, "This IS window %d.", n);
      return;
    }
  SetForeWindow(n);
  if (!Detached && !in_ovl)
    Activate(fore->norefresh);
}

static void SetForeWindow(n)
int n;
{
  /*
   * If we come from another window, make it inactive.
   */
  if (fore)
    fore->active = 0;
  ForeNum = n;
  fore = wtab[n];
  if (!Detached && !in_ovl)
    fore->active = 1;
  /*
   * Place the window at the head of the most-recently-used list.
   */
  if ((n = WinList) != ForeNum)
    {
      /*
       * we had a bug here. we sometimes ran into n = -1; and crashed.
       * (this is not the perfect fix. "if (...) break;" inserted. jw.)
       */
      while (wtab[n]->WinLink != ForeNum)
	{
	  if (wtab[n]->WinLink == -1)
	    break;
	  n = wtab[n]->WinLink;
	}
      wtab[n]->WinLink = fore->WinLink;
      fore->WinLink = WinList;
      WinList = ForeNum;
    }
}

static int NextWindow()
{
  register struct win **pp;

  for (pp = wtab + ForeNum + 1; pp != wtab + ForeNum; ++pp)
    {
      if (pp == wtab + MAXWIN)
	pp = wtab;
      if (*pp)
	break;
    }
  return pp - wtab;
}

static int PreviousWindow()
{
  register struct win **pp;

  for (pp = wtab + ForeNum - 1; pp != wtab + ForeNum; --pp)
    {
      if (pp < wtab)
	pp = wtab + MAXWIN - 1;
      if (*pp)
	break;
    }
  return pp - wtab;
}

static int MoreWindows()
{
  if (fore->WinLink != -1)
    return 1;
#ifdef NETHACK
  if (nethackflag)
    Msg(0, "You cannot escape from window %d!", ForeNum);
  else
#endif
  Msg(0, "No other window.");
  return 0;
}

static void FreeWindow(wp)
struct win *wp;
{
#ifdef UTMPOK
  RemoveUtmp(wp);
#endif
#ifdef SUIDROOT
  (void) chmod(wp->tty, 0666);
  (void) chown(wp->tty, 0, 0);
#endif
  close(wp->ptyfd);
  if (wp->logfp != NULL)
    fclose(wp->logfp);
  ChangeWindowSize(wp, 0, 0);
  Free(wp);
}

int
MakeWindow(prog, args, aflag, flowflag, StartAt, dir, lflag, histheight, term)
char *prog, **args, *dir;
int aflag, flowflag, StartAt, lflag, histheight;
char *term; /* if term is nonzero we assume it "vt100" or the like.. */
{
  register struct win **pp, *p;
  register int n, f;
  int tf, tlflag;
  char ebuf[10];
#ifndef TIOCSWINSZ
  char libuf[20], cobuf[20];
#endif
  char tebuf[25];

  pp = wtab + StartAt;
  do
    {
      if (*pp == 0)
	break;
      if (++pp == wtab + MAXWIN)
	pp = wtab;
    } while (pp != wtab + StartAt);
  if (*pp)
    {
      Msg(0, "No more windows.");
      return -1;
    }

   if (((tlflag = lflag) == -1) && ((tlflag = loginflag) == -1))
	tlflag = LOGINDEFAULT;

#ifdef USRLIMIT
  /*
   * Count current number of users, if logging windows in.
   */
  if (tlflag == 1 && CountUsers() >= USRLIMIT)
    {
      Msg(0, "User limit reached.  Window will not be logged in.");
      tlflag = 0;
    }
#endif
  n = pp - wtab;
  debug1("Makewin creating %d\n", n);
  if ((f = OpenPTY()) == -1)
    {
      Msg(0, "No more PTYs.");
      return -1;
    }
#ifdef SYSV
  (void) fcntl(f, F_SETFL, O_NDELAY);
#else
  (void) fcntl(f, F_SETFL, FNDELAY);
#endif
#ifdef TIOCPKT
    {
# ifdef sgi
      /*
       * on IRIX 3.3, regardless of stream head's read mode (RNORM/RMSGN/RMSGD)
       * we loose data in TIOCPKT mode if our buffer is too small (IOSIZE)
       * to hold the whole packet at first read().
       * (Marc Boucher)
       */
      int flag = 0;
# else /* sgi */
      int flag = 1;
# endif /* sgi */

      if (ioctl(f, TIOCPKT, &flag))
	{
	  Msg(errno, "TIOCPKT ioctl");
	  close(f);
	  return -1;
	}
    }
#endif
  if ((p = (struct win *) malloc(sizeof(struct win))) == 0)
    {
      close(f);
      Msg_nomem;
      return -1;
    }
  bzero((char *) p, (int) sizeof(struct win));
  p->ptyfd = f;
  p->aflag = aflag;
  if (flowflag < 0)
    flowflag = default_flow;
  p->flow = flowflag | ((flowflag & FLOW_AUTOFLAG) ? (FLOW_AUTO|FLOW_NOW) : FLOW_AUTO);
  if (!prog)
    prog = Filename(args[0]);
  strncpy(p->cmd, prog, MAXSTR - 1);
  if ((prog = rindex(p->cmd, '|')) != NULL)
    {
      *prog++ = '\0';
      prog += strlen(prog);
      p->akapos = prog - p->cmd;
      p->autoaka = 0;
    }
  else
    p->akapos = 0;
  p->monitor = default_monitor;
  p->norefresh = 0;
  strncpy(p->tty, TtyName, MAXSTR - 1);
#ifdef SUIDROOT
  (void) chown(TtyName, real_uid, real_gid);
# ifdef UTMPOK
  (void) chmod(TtyName, tlflag ? TtyMode : (TtyMode & ~022));
# else
  (void) chmod(TtyName, TtyMode);
# endif
#endif

  if (histheight < 0)
    histheight = default_histheight;
  if (ChangeWindowSize(p, default_width, default_height))
    {
      FreeWindow(p);
      return -1;
    }
  ChangeScrollback(p, histheight, default_width);
  ResetScreen(p);
  debug("forking...\n");
  switch (p->wpid = fork())
    {
    case -1:
      Msg(errno, "fork");
      FreeWindow(p);
      return -1;
    case 0:
      signal(SIGHUP, SIG_DFL);
      signal(SIGINT, SIG_DFL);
      signal(SIGQUIT, SIG_DFL);
      signal(SIGTERM, SIG_DFL);
#ifdef BSDJOBS
      signal(SIGTTIN, SIG_DFL);
      signal(SIGTTOU, SIG_DFL);
#endif
      setuid(real_uid);
      setgid(real_gid);
      if (dir && chdir(dir) == -1)
	{
	  SendErrorMsg("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
	  eexit(1);
	}

      freetty();
      if ((tf = open(TtyName, O_RDWR)) == -1)
	{
	  SendErrorMsg("Cannot open %s: %s", TtyName, sys_errlist[errno]);
	  eexit(1);
	}
#ifdef SVR4
      if (ioctl(tf, I_PUSH, "ptem"))
	{
	  SendErrorMsg("Cannot I_PUSH ptem %s %s", TtyName, sys_errlist[errno]);
	  eexit(1);
	}
      if (ioctl(tf, I_PUSH, "ldterm"))
	{
	  SendErrorMsg("Cannot I_PUSH ldterm %s %s", TtyName, sys_errlist[errno]);
	  eexit(1);
	}
      if (ioctl(tf, I_PUSH, "ttcompat"))
	{
	  SendErrorMsg("Cannot I_PUSH ttcompat %s %s", TtyName, sys_errlist[errno]);
	  eexit(1);
	}
#endif
      (void) dup2(tf, 0);
      (void) dup2(tf, 1);
      (void) dup2(tf, 2);
#ifdef DEBUG
      dfp = stderr;
#endif
      closeallfiles();
      fgtty();
#ifdef TIOCSWINSZ
      glwz.ws_col = p->width;
      glwz.ws_row = p->height;
      (void) ioctl(0, TIOCSWINSZ, &glwz);
#else
      sprintf(libuf, "LINES=%d", p->height);
      sprintf(cobuf, "COLUMNS=%d", p->width);
      NewEnv[4] = libuf;
      NewEnv[5] = cobuf;
#endif
      SetTTY(0, &OldMode);
      if (aflag)
        NewEnv[2] = MakeTermcap(1);
      else
        NewEnv[2] = Termcap;
      if (term && *term && strcmp(screenterm, term) &&
	  (strlen(term) < 20))
	{
          char *s1, *s2, tl;

	  sprintf(tebuf, "TERM=%s", term);
	  debug2("Makewindow %d with %s\n", n, tebuf);
          tl = strlen(term);
	  NewEnv[1] = tebuf;
          if (s1 = index(Termcap, '|'))
	    {
	      if (s2 = index(++s1, '|'))
		{
		  if (strlen(Termcap) - (s2 - s1) + tl < 1024)
		    {
		      bcopy(s2, s1 + tl, strlen(s2) + 1);
		      bcopy(term, s1, tl);
		    }
		}
            }
	}
      sprintf(ebuf, "WINDOW=%d", n);
      NewEnv[3] = ebuf;

      execvpe(*args, args, NewEnv);
      SendErrorMsg("Cannot exec %s: %s", *args, sys_errlist[errno]);
      exit(1);
    } /* end fork switch */
  /*
   * Place the newly created window at the head of the most-recently-used list.
   */
  *pp = p;
  p->WinLink = WinList;
  WinList = n;
  HasWindow = 1;
#ifdef UTMPOK
  debug1("MakeWindow will %slog in.\n", tlflag?"":"not ");
  if (tlflag == 1)
    SetUtmp(p, n);
  else
    p->slot = (slot_t) -1;
#endif
  SetForeWindow(n);
  Activate(0);
  return n;
}

static void execvpe(prog, args, env)
char *prog, **args, **env;
{
  register char *path, *p;
  char buf[1024];
  char *shargs[MAXARGS + 1];
  register int i, eaccess = 0;

  if (prog[0] == '/')
    path = "";
  else if ((path = getenv("PATH")) == 0)
    path = DefaultPath;
  do
    {
      p = buf;
      while (*path && *path != ':')
	*p++ = *path++;
      if (p > buf)
	*p++ = '/';
      strcpy(p, prog);
      if (*path)
	++path;
      execve(buf, args, env);
      switch (errno)
	{
	case ENOEXEC:
	  shargs[0] = DefaultShell;
	  shargs[1] = buf;
	  for (i = 1; (shargs[i + 1] = args[i]) != NULL; ++i)
	    ;
	  execve(DefaultShell, shargs, env);
	  return;
	case EACCES:
	  eaccess = 1;
	  break;
	case ENOMEM:
	case E2BIG:
	case ETXTBSY:
	  return;
	}
    } while (*path);
  if (eaccess)
    errno = EACCES;
}


static void LogToggle()
{
  char buf[1024];

  sprintf(buf, "screenlog.%d", ForeNum);
  if (fore->logfp != NULL)
    {
      Msg(0, "Logfile \"%s\" closed.", buf);
      fclose(fore->logfp);
      fore->logfp = NULL;
      return;
    }
  if ((fore->logfp = secfopen(buf, "a")) == NULL)
    {
      Msg(errno, "Error opening logfile \"%s\"", buf);
      return;
    }
  Msg(0, "%s logfile \"%s\"", ftell(fore->logfp) ? "Appending to" : "Creating", buf);
}

#ifdef NOREUID
static int UserPID;
static sig_t (*Usersigcld)__P(SIGPROTOARG);
#endif
static int UserSTAT;

int UserContext()
{
#ifdef NOREUID
  if (eff_uid == real_uid)
    return(1);
# ifdef SYSV
  Usersigcld = signal(SIGCLD, SIG_DFL);
# else
  Usersigcld = signal(SIGCHLD, SIG_DFL);
# endif
  debug("UserContext: forking.\n");
  switch (UserPID = fork())
    {
    case -1:
      Msg(errno, "fork");
      return -1;
    case 0:
      signal(SIGHUP, SIG_DFL);
      signal(SIGINT, SIG_IGN);
      signal(SIGQUIT, SIG_DFL);
      signal(SIGTERM, SIG_DFL);
# ifdef BSDJOBS
      signal(SIGTTIN, SIG_DFL);
      signal(SIGTTOU, SIG_DFL);
# endif
      setuid(real_uid);
      setgid(real_gid);
      return 1;
    default:
      return 0;
    }
#else
  setreuid(eff_uid, real_uid);
  setregid(eff_gid, real_gid);
  return 1;
#endif
}

void
UserReturn(val)
int val;
{
#if defined(NOREUID)
  if (eff_uid == real_uid)
    UserSTAT = val;
  else
    exit(val);
#else
  setreuid(real_uid, eff_uid);
  setregid(real_gid, eff_gid);
  UserSTAT = val;
#endif
}

int UserStatus()
{
#ifdef NOREUID
  int i;
# ifdef BSDWAIT
  union wait wstat;
# else
  int wstat;
# endif

  if (eff_uid == real_uid)
    return UserSTAT;
  if (UserPID < 0)
    return -1;
  while ((errno = 0, i = wait(&wstat)) != UserPID)
    if (i < 0 && errno != EINTR)
      break;
# ifdef SYSV
  (void) signal(SIGCLD, Usersigcld);
# else
  (void) signal(SIGCHLD, Usersigcld);
# endif
  if (i == -1)
    return -1;
  return (WEXITSTATUS(wstat));
#else
  return UserSTAT;
#endif
}

static void ShowWindows()
{
  char buf[1024];
  register char *s;
  register struct win **pp, *p;
  register int i, OtherNum = fore->WinLink;
  register char *cmd;

  for (i = 0, s = buf, pp = wtab; pp < wtab + MAXWIN; ++i, ++pp)
    {
      if ((p = *pp) == 0)
	continue;

      if (p->akapos)
	{
	  if (*(p->cmd + p->akapos) && *(p->cmd + p->akapos - 1) != ':')
	    cmd = p->cmd + p->akapos;
	  else
	    cmd = p->cmd + strlen(p->cmd) + 1;
	}
      else
	cmd = p->cmd;
      if (s - buf + 5 + strlen(cmd) > fore->width - 1)
	break;
      if (s > buf)
	{
	  *s++ = ' ';
	  *s++ = ' ';
	}
      *s++ = i + '0';
      if (i == ForeNum)
	*s++ = '*';
      else if (i == OtherNum)
	*s++ = '-';
      if (p->monitor == MON_DONE)
	*s++ = '@';
      if (p->bell == BELL_DONE)
	*s++ = '!';
#ifdef UTMPOK
      if (p->slot != (slot_t) 0 && p->slot != (slot_t) -1)
	*s++ = '$';
#endif
      if (p->logfp != NULL)
	{
	  strcpy(s, "(L)");
	  s += 3;
	}
      *s++ = ' ';
      strcpy(s, cmd);
      s += strlen(s);
      if (i == ForeNum)
	{
	  /* 
	   * this is usually done by Activate(), but when looking
	   * on your current window, you may get annoyed, as there is still
	   * that temporal '!' and '@' displayed.
	   * So we remove that after displaying it once.
	   */
	  p->bell = BELL_OFF;
	  if (p->monitor != MON_OFF)
	    p->monitor = MON_ON;
	}
    }
  *s++ = ' ';
  *s = '\0';
  Msg(0, "%s", buf);
}

static void ShowTime()
{
  char buf[512];
  struct tm *tp;
  time_t now;

  (void) time(&now);
  tp = localtime(&now);
  sprintf(buf, "%2d:%02.2d:%02.2d %s", tp->tm_hour, tp->tm_min, tp->tm_sec,
	  HostName);
  {
    static int loads = 0;
    double loadav[3];
    int n;

    if (loads >= 0)		/* If it failed, don't try again.  */
      {
	loads = getloadavg(loadav, 3);
	for (n = 0; n < loads; n++)
	  {
	    char *p = buf + strlen(buf);
	    sprintf(p, " %2.2f", loadav[n]);
	  }
      }
  }
  Msg(0, "%s", buf);
}

static void ShowInfo()
{
  char buf[512], *p;
  register struct win *wp = fore;
  register int i;

  sprintf(buf, "(%d,%d)/(%d,%d)+%d %c%sflow %cins %corg %cwrap %capp %clog %cmon %cr",
	  wp->x + 1, wp->y + 1, wp->width, wp->height,
	  wp->histheight,
	  (wp->flow & FLOW_NOW) ? '+' : '-',
	  (wp->flow & FLOW_AUTOFLAG) ? "" : ((wp->flow & FLOW_AUTO) ? "(+)" : "(-)"),
	  wp->insert ? '+' : '-', wp->origin ? '+' : '-',
	  wp->wrap ? '+' : '-', wp->keypad ? '+' : '-',
	  (wp->logfp != NULL) ? '+' : '-',
	  (wp->monitor != MON_OFF) ? '+' : '-',
	  wp->norefresh ? '-' : '+');
  if (ISO2022)
    {
      p = buf + strlen(buf);
      sprintf(p, " G%1d [", wp->LocalCharset);
      for (i = 0; i < 4; i++)
	p[i + 5] = wp->charsets[i] ? wp->charsets[i] : 'B';
      p[9] = ']';
      p[10] = '\0';
    }
  Msg(0, "%s", buf);
}

#if defined(sequent) || defined(_SEQUENT_) || defined(SVR4)

static int OpenPTY()
{
  char *m, *s;
  register int f;
# ifdef SVR4
  char *ptsname();
  sig_t (*sigcld)();

  if ((f = open("/dev/ptmx", O_RDWR)) == -1)
    return(-1);

  /*
   * SIGCLD set to SIG_DFL for grantpt() because it fork()s and
   * exec()s pt_chmod
   */
  sigcld = signal(SIGCLD, SIG_DFL);
       
  if ((m = ptsname(f)) == NULL || unlockpt(f) || grantpt(f))
    {
      signal(SIGCLD, sigcld);
      close(f);
      return(-1);
    } 
  signal(SIGCLD, sigcld);
  strncpy(TtyName, m, sizeof TtyName);
# else /* SVR4 */
  if ((f = getpseudotty(&s, &m)) < 0)
    return(-1);
  strncpy(PtyName, m, sizeof PtyName);
  strncpy(TtyName, s, sizeof TtyName);
# endif /* SVR4 */
# ifdef POSIX
  tcflush(f, TCIOFLUSH);
# else
  (void) ioctl(f, TIOCFLUSH, (char *) 0);
# endif
# ifdef LOCKPTY
  (void) ioctl(f, TIOCEXCL, (char *) 0);
# endif
  return (f);
}

#else /* defined(sequent) || defined(_SEQUENT_) || defined(SVR4) */
# ifdef MIPS

static int OpenPTY()
{
  register char *p, *l, *d;
  register f, tf;
  register my_minor;
  struct stat buf;
   
  strcpy(PtyName, PtyProto);
  for (p = PtyName; *p != 'X'; ++p)
    ;
  for (l = PTY_FIRST_CHARS; *p = *l; ++l)
    {
      for (d = PTY_SECOND_CHARS; p[1] = *d; ++d)
	{
	  if ((f = open(PtyName, O_RDWR)) != -1)
	    {
	      fstat(f, &buf);
	      my_minor = minor(buf.st_rdev);
	      sprintf(TtyName, "/dev/ttyq%d", my_minor);
	      if ((tf = open(TtyName, O_RDWR)) != -1)
		{
		  close(tf);
#ifdef LOCKPTY
		  (void) ioctl(f, TIOCEXCL, (char *)0);
#endif
		  return f;
		}
	      close(f);
	    }
	}
    }
  return -1;
}

# else  /* MIPS */
#  ifdef sgi

static int OpenPTY()
{
  register f;
  register my_minor;
  struct stat buf;
   
  strcpy(PtyName, "/dev/ptc");
  f = open(PtyName, O_RDWR|O_NDELAY);
  if (f >= 0)
    {
      if (fstat(f, &buf) < 0)
	{
	  close(f);
	  return -1;
	}
      my_minor = minor(buf.st_rdev);
      sprintf(TtyName, "/dev/ttyq%d", my_minor);
    }
  return f;
}

#  else /* sgi */
#   ifdef _AIX /* RS6000 */

static int OpenPTY()
{
  register int i, f, tf;

  strcpy (PtyName, "/dev/ptc");
  if ((f = open (PtyName, O_RDWR)) != -1)
    {
      strcpy (TtyName, ttyname (f));
      strcpy (PtyName, TtyName);
      PtyName [7] = 'c';
#ifdef LOCKPTY
      if (ioctl (f, TIOCEXCL, (char *) 0) == -1)
	return -1;
#endif
      if ((tf = open (TtyName, O_RDWR)) != -1)
	{
	  close (tf);
	  return f;
	}
      close (f);
    }
  return -1;
}

#   else /* _AIX, RS6000 */
#     ifdef linux

static int OpenPTY()
{
  register char *p, *q, *l, *d;
  register int f, tf;

  strcpy(PtyName, PtyProto);
  strcpy(TtyName, TtyProto);
  for (p = PtyName; *p != 'X'; ++p)
    ;
  for (q = TtyName; *q != 'X'; ++q)
    ;
  for (l = PTY_FIRST_CHARS; (*p = *l) != '\0'; ++l)
    {
      for (d = PTY_SECOND_CHARS; (p[1] = *d) != '\0'; ++d)
	{
	  if ((f = open(PtyName, O_RDWR)) != -1)
	    {
	      q[0] = *l;
	      q[1] = *d;
#ifdef LOCKPTY
	      (void) ioctl(f, TIOCEXCL, (char *) 0);
#endif
	      return f;
	    }
	}
    }
  return -1;
}

#     else /* linux */

static int OpenPTY()
{
  register char *p, *q, *l, *d;
  register int f, tf;

#    if !defined(hpux)
  debug("Hello, You are none of: sequent, _SEQUENT_, SVR4, MIPS, sgi, AIX, linux\n");
  debug("       This OpenPTY() is for hpux, ... and for you?\n");
#    endif
  strcpy(PtyName, PtyProto);
  strcpy(TtyName, TtyProto);
  for (p = PtyName; *p != 'X'; ++p)
    ;
  for (q = TtyName; *q != 'X'; ++q)
    ;
  for (l = PTY_FIRST_CHARS; (*p = *l) != '\0'; ++l)
    {
      for (d = PTY_SECOND_CHARS; (p[1] = *d) != '\0'; ++d)
	{
	  if ((f = open(PtyName, O_RDWR)) != -1)
	    {
	      q[0] = *l;
	      q[1] = *d;
	      if ((tf = open(TtyName, O_RDWR)) != -1)
		{
		  /* close tf, thus we also get rid of an unwanted
		   * controlling terminal! 
		   */
		  close(tf);
#ifdef LOCKPTY
		  (void) ioctl(f, TIOCEXCL, (char *) 0);
#endif
		  return f;
		}
	      close(f);
	    }
	}
    }
  return -1;
}

#    endif /* linux */
#   endif /* _AIX, RS6000 */
#  endif /* sgi */
# endif /* MIPS */
#endif

void 
SetTTY(fd, mp)
int fd;
struct mode *mp;
{
  errno = 0;
#ifdef POSIX
  tcsetattr(fd, TCSADRAIN, &mp->tio);
# ifdef hpux
  ioctl(fd, TIOCSLTC, &mp->m_ltchars);
# endif
#else
# ifdef TERMIO
  ioctl(fd, TCSETA, &mp->tio);
# else
  /* ioctl(fd, TIOCSETP, &mp->m_ttyb); */
  ioctl(fd, TIOCSETC, &mp->m_tchars);
  ioctl(fd, TIOCSLTC, &mp->m_ltchars);
  ioctl(fd, TIOCLSET, &mp->m_lmode);
  ioctl(fd, TIOCSETD, &mp->m_ldisc);
  ioctl(fd, TIOCSETP, &mp->m_ttyb);
# endif
#endif
  if (errno)
    Msg(0, "SetTTY: ioctl failed");
}

void
GetTTY(fd, mp)
int fd;
struct mode *mp;
{
  errno = 0;
#ifdef POSIX
  tcgetattr(fd, &mp->tio);
# ifdef hpux
  ioctl(fd, TIOCGLTC, &mp->m_ltchars);
# endif
#else
# ifdef TERMIO
  ioctl(fd, TCGETA, &mp->tio);
# else
  ioctl(fd, TIOCGETP, &mp->m_ttyb);
  ioctl(fd, TIOCGETC, &mp->m_tchars);
  ioctl(fd, TIOCGLTC, &mp->m_ltchars);
  ioctl(fd, TIOCLGET, &mp->m_lmode);
  ioctl(fd, TIOCGETD, &mp->m_ldisc);
# endif
#endif
  if (errno)
    Msg(0, "GetTTY: ioctl failed");
}

void
SetMode(op, np)
struct mode *op, *np;
{
  *np = *op;

#if defined(TERMIO) || defined(POSIX)
  np->tio.c_iflag &= ~ICRNL;
# ifdef ONLCR
  np->tio.c_oflag &= ~ONLCR;
# endif
  np->tio.c_lflag &= ~(ICANON | ECHO);

  /*
   * Unfortunately, the master process never will get SIGINT if the real
   * terminal is different from the one on which it was originaly started
   * (process group membership has not been restored or the new tty could not
   * be made controlling again). In my solution, it is the attacher who
   * receives SIGINT (because it is always correctly associated with the real
   * tty) and forwards it to the master [kill(MasterPid, SIGINT)]. 
   * Marc Boucher (marc@CAM.ORG)
   */
  np->tio.c_lflag |= ISIG;
  /* 
   * careful, careful catche monkey..
   * never set VMIN and VTIME to zero, if you want blocking io.
   */
  np->tio.c_cc[VMIN] = 1;
  np->tio.c_cc[VTIME] = 0;
#ifdef VSTART
  startc = op->tio.c_cc[VSTART];
#endif
#ifdef VSTOP
  stopc = op->tio.c_cc[VSTOP];
#endif
  if (iflag)
    intrc = op->tio.c_cc[VINTR];
  else
    intrc = np->tio.c_cc[VINTR] = 0377;
  np->tio.c_cc[VQUIT] = 0377;
  if (flow == 0)
    {
      np->tio.c_cc[VINTR] = 0377;
#ifdef VSTART
      np->tio.c_cc[VSTART] = 0377;
#endif
#ifdef VSTOP
      np->tio.c_cc[VSTOP] = 0377;
#endif
      np->tio.c_iflag &= ~IXON;
    }
#ifdef VDISCARD
  np->tio.c_cc[VDISCARD] = 0377;
#endif
#ifdef VSUSP
  np->tio.c_cc[VSUSP] = 0377;
#endif
# ifdef hpux
  np->m_ltchars.t_suspc = 0377;
  np->m_ltchars.t_dsuspc = 0377;
  np->m_ltchars.t_flushc = 0377;
  np->m_ltchars.t_lnextc = 0377;
# else
#  ifdef VDSUSP
  np->tio.c_cc[VDSUSP] = 0377;
#  endif
# endif
#else
  startc = op->m_tchars.t_startc;
  stopc = op->m_tchars.t_stopc;
  if (iflag)
    intrc = op->m_tchars.t_intrc;
  else
    intrc = np->m_tchars.t_intrc = -1;
  np->m_ttyb.sg_flags &= ~(CRMOD | ECHO);
  np->m_ttyb.sg_flags |= CBREAK;
  np->m_tchars.t_quitc = -1;
  if (flow == 0)
    {
      np->m_tchars.t_intrc = -1;
      np->m_tchars.t_startc = -1;
      np->m_tchars.t_stopc = -1;
    }
  np->m_ltchars.t_suspc = -1;
  np->m_ltchars.t_dsuspc = -1;
  np->m_ltchars.t_flushc = -1;
  np->m_ltchars.t_lnextc = -1;
#endif				/* defined(TERMIO) || defined(POSIX) */
}

void
SetFlow(on)
int on;
{
  if (flow == on)
    return;
#if defined(TERMIO) || defined(POSIX)
  if (on)
    {
      NewMode.tio.c_cc[VINTR] = intrc;
#ifdef VSTART
      NewMode.tio.c_cc[VSTART] = startc;
#endif
#ifdef VSTOP
      NewMode.tio.c_cc[VSTOP] = stopc;
#endif
      NewMode.tio.c_iflag |= IXON;
    }
  else
    {
      NewMode.tio.c_cc[VINTR] = 0377;
#ifdef VSTART
      NewMode.tio.c_cc[VSTART] = 0377;
#endif
#ifdef VSTOP
      NewMode.tio.c_cc[VSTOP] = 0377;
#endif
      NewMode.tio.c_iflag &= ~IXON;
    }
# ifdef POSIX
  if (tcsetattr(0, TCSADRAIN, &NewMode.tio))
# else
  if (ioctl(0, TCSETA, &NewMode.tio) != 0)
# endif
    debug1("SetFlow: ioctl errno %d\n", errno);
#else
  if (on)
    {
      NewMode.m_tchars.t_intrc = intrc;
      NewMode.m_tchars.t_startc = startc;
      NewMode.m_tchars.t_stopc = stopc;
    }
  else
    {
      NewMode.m_tchars.t_intrc = -1;
      NewMode.m_tchars.t_startc = -1;
      NewMode.m_tchars.t_stopc = -1;
    }
  if (ioctl(0, TIOCSETC, &NewMode.m_tchars) != 0)
    debug1("SetFlow: ioctl errno %d\n", errno);
#endif				/* defined(TERMIO) || defined(POSIX) */
  flow = on;
}

/* we return 1 if we could attach one, or 0 if none */
static int Attach(how)
int how;
{
  int lasts;
  struct msg m;
  struct stat st;
  char *s;

  if (how == MSG_WINCH)
    {
      bzero((char *) &m, sizeof(m));
      m.type = how;
      if ((lasts = MakeClientSocket(0, SockName)) >= 0)
	{
          write(lasts, &m, sizeof(m));
          close(lasts);
	}
      return 0;
    }

  if (how == MSG_CONT)
    {
      if ((lasts = MakeClientSocket(0, SockName)) < 0)
        {
          printf("Sorry, cannot contact session \"%s\" again\r\n",
                 SockName);
          sleep(2);
          how = MSG_ATTACH;
        }
    }
    
  if (how != MSG_CONT)
    {
      switch (FindSocket(how, &lasts))
        {
        case 0:
          if (rflag == 2)
	    return 0;
          if (quietflag)
	    eexit(10);
          if (SockName && *SockName)
            Msg(0, "There is no screen to be %sed matching %s.", 
	        dflag ? "detach" : "resum", SockName);
          else
            Msg(0, "There is no screen to be %sed.",
                dflag ? "detach" : "resum");
          /* NOTREACHED */
        case 1:
          break;
        default:
          Msg(0, "Type \"screen [-d] -r [pid.]tty.host\" to resume one of them.");
          /* NOTREACHED */
        }
      /*
       * Go in UserContext. Advantage is, you can kill your attacher
       * when things go wrong. Any disadvantages? jw.
       */
      setuid(real_uid);
      setgid(real_gid);

      SockName = SockNamePtr;
      MasterPid = 0;
      while (*SockName)
        {
          if (*SockName > '9' || *SockName < '0')
	    break;
          MasterPid = 10 * MasterPid + *SockName - '0';
          SockName++;
        }
      SockName = SockNamePtr;
      debug1("Attach decided, it is '%s'\n", SockPath);
      debug1("Attach found MasterPid == %d\n", MasterPid);
      if (stat(SockPath, &st) == -1)
        Msg(errno, "stat %s", SockPath);
      if ((st.st_mode & 0700) != (dflag ? 0700 : 0600))
        Msg(0, "That screen is %sdetached.", dflag ? "already " : "not ");
#ifdef REMOTE_DETACH
      if (dflag &&
          (how == MSG_ATTACH || how == MSG_DETACH || how == MSG_POW_DETACH))
        {
          strcpy(m.m.detach.tty, attach_tty);
          debug1("attach_tty is %s\n", attach_tty);
          m.m.detach.dpid = getpid();
# ifdef POW_DETACH
          if (dflag == 2)
	    m.type = MSG_POW_DETACH;
          else
# endif
	    m.type = MSG_DETACH;
          if (write(lasts, (char *) &m, sizeof(m)) != sizeof(m))
    	    Msg(errno, "write");
          close(lasts);
          if (how != MSG_ATTACH)
    	    return 0;	/* we detached it. jw. */
          sleep(1);	/* we dont want to overrun our poor backend. jw. */
          if ((lasts = MakeClientSocket(0, SockName)) == -1)
            Msg(0, "Cannot contact screen again. Shit.");
        }
#endif
    }
  m.type = how;
  strcpy(m.m.attach.tty, attach_tty);
  debug1("attach_tty is %s\n", attach_tty);
  s = getenv("TERM");
  if (s)
    {
      if (strlen(s) >= MAXPATH - 5)
	Msg(0, "$TERM too long - sorry.");
      sprintf(m.m.attach.envterm, "TERM=%s", s);
    }
  else
    *m.m.attach.envterm = '\0';
  debug1("attach: sending %d bytes... ", sizeof m);

  m.m.attach.apid = getpid();
  m.m.attach.adaptflag = adaptflag;
  m.m.attach.lines = m.m.attach.columns = 0;
  if (s = getenv("LINES"))
    m.m.attach.lines = atoi(s);
  if (s = getenv("COLUMNS"))
    m.m.attach.columns = atoi(s);

#ifdef PASSWORD
  if (how == MSG_ATTACH || how == MSG_CONT)
    trysend(lasts, &m, m.m.attach.password);
  else
#endif
    {
      if (write(lasts, (char *) &m, sizeof(m)) != sizeof(m))
	Msg(errno, "write");
      close(lasts);
    }
  debug1("Attach(%d): sent\n", m.type);
  Suspended = 0;
  rflag = 0;
  return 1;
}


#ifdef PASSWORD

static trysendstat;

static sig_t trysendok(SIGDEFARG)
{
  trysendstat = 1;
}

static sig_t trysendfail(SIGDEFARG)
{
  trysendstat = -1;
# ifdef SYSV
  signal(SIG_PW_FAIL, trysendfail);
# endif /* SYSV */
}

static char screenpw[9];

static void trysend(fd, m, pwto)
int fd;
struct msg *m;
char *pwto;
{
  char *npw = NULL;
  sig_t (*sighup)();
  sig_t (*sigusr1)();
  int tries;

  sigusr1 = signal(SIG_PW_OK, trysendok);
  sighup = signal(SIG_PW_FAIL, trysendfail);
  for (tries = 0; ; )
    {
      strcpy(pwto, screenpw);
      trysendstat = 0;
      if (write(fd, (char *) m, sizeof(*m)) != sizeof(*m))
	Msg(errno, "write");
      close(fd);
      while (trysendstat == 0)
	pause();
      if (trysendstat > 0)
	{
	  signal(SIG_PW_OK, sigusr1);
	  signal(SIG_PW_FAIL, sighup);
	  return;
	}
      if (++tries > 1 || (npw = getpass("Screen Password:")) == 0 || *npw == 0)
	Msg(0, "Password incorrect");
      strncpy(screenpw, npw, 8);
      if ((fd = MakeClientSocket(0, SockName)) == -1)
	Msg(0, "Cannot contact screen again. Shit.");
    }
}
#endif /* PASSWORD */


/*
 * Unfortunatelly this is also the SIGHUP handler, so we have to
 * check, if the backend is already detached.
 */

static sig_t AttacherFinit(SIGDEFARG)
{
  struct stat statb;
  struct msg m;
  int s;

  debug("AttacherFinit();\n");
  signal(SIGHUP, SIG_IGN);
  /* Check if signal comes from backend */
  if (SockName)
    {
      strcpy(SockNamePtr, SockName);
      if (stat(SockPath, &statb) == 0 && (statb.st_mode & 0777) != 0600)
	{
	  debug("Detaching backend!\n");
	  bzero((char *) &m, sizeof(m));
	  strcpy(m.m.detach.tty, attach_tty);
          debug1("attach_tty is %s\n", attach_tty);
	  m.m.detach.dpid = getpid();
	  m.type = MSG_HANGUP;
	  if ((s = MakeClientSocket(0, SockName)) >= 0)
	    {
	      write(s, &m, sizeof(m));
	      close(s);
	    }
	}
    }
  exit(0);
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}

#ifdef POW_DETACH
static sig_t AttacherFinitBye(SIGDEFARG)
{
  int ppid;
  debug("AttacherFintBye()\n");
  freetty();
  setuid(real_uid);
  setgid(real_gid);
  /* we don't want to disturb init (even if we were root), eh? jw */
  if ((ppid = getppid()) > 1)
    Kill(ppid, SIGHUP);		/* carefully say good bye. jw. */
  exit(0);
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}
#endif

static SuspendPlease;

static sig_t SigStop(SIGDEFARG)
{
  debug("SigStop()\n");
  SuspendPlease = 1;
#ifndef SIGVOID
  return((sig_t) 0);
#endif
}

#ifdef LOCK
static LockPlease;

static sig_t DoLock(SIGDEFARG)
{
  debug("DoLock()\n");
  LockPlease = 1;
# ifdef SYSV
  signal(SIG_LOCK, DoLock);
# endif
# ifndef SIGVOID
  return((sig_t) 0);
# endif
}
#endif

#if defined(SIGWINCH) && defined(TIOCGWINSZ)
static SigWinchPlease;

static sig_t SigAttWinch(SIGDEFARG)
{
  debug("SigAttWinch()\n");
  SigWinchPlease = 1;
# ifndef SIGVOID
  return((sig_t) 0);
# endif
}
#endif

static void Attacher()
{
  /*
   * permanent in UserContext. Advantage is, you can kill your attacher
   * when things go wrong. Any disadvantages? jw.
   */
  setuid(real_uid);	/* XXX: already done in Attach() */
  setgid(real_gid);	/* XXX: already done in Attach() */

  signal(SIGHUP, AttacherFinit);
  signal(SIG_BYE, AttacherFinit);
#ifdef POW_DETACH
  signal(SIG_POWER_BYE, AttacherFinitBye);
#endif
#ifdef LOCK
  signal(SIG_LOCK, DoLock);
#endif
  signal(SIGINT, AttacherSigInt);
#ifdef BSDJOBS
  signal(SIG_STOP, SigStop);
#endif
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
  signal(SIGWINCH, SigAttWinch);
#endif
#ifdef DEBUG
# ifdef SYSV
  signal(SIGCLD, FEChld);
# else
  signal(SIGCHLD, FEChld);
# endif
#endif
  debug("attacher: going for a nap.\n");
  dflag = 0;
  while (1)
    {
      pause();
      debug("attacher: huh! a signal!\n");
#ifdef DEBUG
      if (FEpanic)
        {
	  printf("\n\rSuddenly the Dungeon collapses!! - You die...\n\r");
	  SetTTY(0, &OldMode);
	  eexit(1);
        }
#endif
#ifdef BSDJOBS
      if (SuspendPlease)
	{
	  SuspendPlease = 0;
	  signal(SIGTSTP, SIG_DFL);
	  debug("attacher: killing myself SIGTSTP\n");
	  kill(getpid(), SIGTSTP);

	  debug1("attacher: continuing from stop(%d)\n", Suspended);
	  signal(SIG_STOP, SigStop);
	  (void) Attach(MSG_CONT);
	}
#endif
#ifdef LOCK
      if (LockPlease)
	{
	  LockPlease = 0;
	  LockTerminal();
# ifdef SYSV
	  signal(SIG_LOCK, DoLock);
# endif
	  (void) Attach(MSG_CONT);
	}
#endif	/* LOCK */
#if defined(SIGWINCH) && defined(TIOCGWINSZ)
      if (SigWinchPlease)
	{
	  SigWinchPlease = 0;
# ifdef SYSV
	  signal(SIGWINCH, SigAttWinch);
# endif
	  (void) Attach(MSG_WINCH);
	}
#endif	/* SIGWINCH */
    }
}

#ifdef LOCK

/* ADDED by Rainer Pruy 10/15/87 */
/* POLISHED by mls. 03/10/91 */

static char LockEnd[] = "Welcome back to screen !!\n";

static void LockTerminal()
{
  char *prg;
  int sig, pid;
  sig_t (*sigs[NSIG])__P(SIGPROTOARG);

  for (sig = 1; sig < NSIG; sig++)
    {
      sigs[sig] = signal(sig, SIG_IGN);
    }
  SetTTY(0, &OldMode);
  printf("\n");

  prg = getenv("LOCKPRG");
  if (prg && strcmp(prg, "builtin") && !access(prg, X_OK))
    {
# ifdef SYSV
      signal(SIGCLD, SIG_DFL);
# else /* SYSV */
      signal(SIGCHLD, SIG_DFL);
# endif /* SYSV */
      debug1("lockterminal: '%s' seems executable, execl it!\n", prg);
      if ((pid = fork()) == 0)
        {
          /* Child */
          setuid(real_uid);	/* this should be done already */
          setgid(real_gid);
          closeallfiles();	/* important: /etc/shadow may be open */
          execl(prg, "SCREEN-LOCK", NULL);
          exit(errno);
        }
      if (pid == -1)
        {
#ifdef NETHACK
          if (nethackflag)
            Msg(errno, "Cannot fork terminal - lock failed");
          else
#endif
          Msg(errno, "Cannot lock terminal - fork failed");
        }
      else
        {
#ifdef BSDWAIT
          union wait wstat;
#else
          int wstat;
#endif
          int wret;

#ifdef hpux
          signal(SIGCLD, SIG_DFL);
#endif
          errno = 0;
          while (((wret = wait((int *) &wstat)) != pid) ||
	         ((wret == -1) && (errno == EINTR))
	         )
	    errno = 0;
    
          if (errno)
	    {
	      perror("Lock");
	      sleep(2);
	    }
	  else if (WTERMSIG(wstat) != 0)
	    {
	      fprintf(stderr, "Lock: %s: Killed by signal: %d%s\n", prg,
		      WTERMSIG(wstat), WIFCORESIG(wstat) ? " (Core dumped)" : "");
	      sleep(2);
	    }
	  else if (WEXITSTATUS(wstat))
	    {
	      debug2("Lock: %s: return code %d\n", prg, WEXITSTATUS(wstat));
	    }
          else
	    printf(LockEnd);
        }
    }
  else
    {
      if (prg)
	{
          debug1("lockterminal: '%s' seems NOT executable, we use our builtin\n", prg);
	}
      else
	{
	  debug("lockterminal: using buitin.\n");
	}
      screen_builtin_lck();
    }
  /* reset signals */
  for (sig = 1; sig < NSIG; sig++)
    {
      if (sigs[sig] != (sig_t(*) ()) - 1)
	signal(sig, sigs[sig]);
    }
}				/* LockTerminal */

/* -- original copyright by Luigi Cannelloni 1985 (luigi@faui70.UUCP) -- */
void
screen_builtin_lck()
{
  char fullname[100], *cp1, message[BUFSIZ];
  char c, *pass, mypass[9];
#ifdef SHADOWPW
  struct spwd *sss = NULL;
#endif
  int t;

#ifdef undef
  /* get password entry */
  if ((ppp = getpwuid(real_uid)) == NULL)
    {
      fprintf(stderr, "screen_builtin_lck: No passwd entry.\007\n");
      sleep(2);
      return;
    }
  if (!isatty(0))
    {
      fprintf(stderr, "screen_builtin_lck: Not a tty.\007\n");
      sleep(2);
      return;
    }
#endif
  pass = ppp->pw_passwd;
realpw:
  for (t = 0; t < 13; t++)
    {
      c = pass[t];
      if (!(c == '.' || c == '/' ||
            (c >= '0' && c <= '9') || 
            (c >= 'a' && c <= 'z') || 
            (c >= 'A' && c <= 'Z'))) 
        break;
    }
  if (t < 13)
    {
      debug("builtin_lock: ppp->pw_passwd bad, has it a shadow?\n");
#ifdef SHADOWPW
      setspent(); /* rewind shadow file */
      if ((sss == NULL) && (sss = getspnam(ppp->pw_name)))
        {
          pass = sss->sp_pwdp;
          goto realpw;
        }
#endif /* SHADOWPW */
      if (pass = getpass("Key:   "))
        {
          strncpy(mypass, pass, 8);
          mypass[8] = 0;
          if (*mypass == 0)
            return;
          if (pass = getpass("Again: "))
            {
              if (strcmp(mypass, pass))
                {
                  fprintf(stderr, "Passwords don't match.\007\n");
                  sleep(2);
                  return;
                }
            }
        }
      if (pass == 0)
        {
          fprintf(stderr, "Getpass error.\007\n");
          sleep(2);
          return;
        }
      pass = 0;
    }

  debug("screen_builtin_lck looking in gcos field\n");
  strcpy(fullname, ppp->pw_gecos);
  if ((cp1 = index(fullname, ',')) != NULL)
    *cp1 = '\0';
  if ((cp1 = index(fullname, '&')) != NULL)
    {
      sprintf(cp1, "%s", ppp->pw_name);
      *cp1 = islower(*cp1) ? toupper(*cp1) : *cp1;
    }

  sprintf(message, "Screen used by %s <%s>.\nPassword:\007",
          fullname, ppp->pw_name);

  /* loop here to wait for correct password */
  for (;;)
    {
      debug("screen_builtin_lck awaiting password\n");
      if ((cp1 = getpass(message)) == NULL)
        {
          AttacherFinit(SIGARG);
          /* NOTREACHED */
        }
      if (pass)
        {
          if (!strcmp(crypt(cp1, pass), pass))
            break;
        }
      else
        {
          if (!strcmp(cp1, mypass))
            break;
        }
      debug("screen_builtin_lck: NO!!!!!\n");
    }
  debug("password ok.\n");
}

#endif	/* LOCK */

/*
 * Detach now has the following modes:
 *	D_DETACH	SIG_BYE		detach backend and exit attacher
 *	D_STOP		SIG_STOP	stop attacher (and detach backend)
 *	D_REMOTE	SIG_BYE		remote detach -- reattach to new attacher
 *	D_POWER 	SIG_POWER_BYE 	power detach -- attacher kills his parent
 *	D_REMOTE_POWER	SIG_POWER_BYE	remote power detach -- both
 *	D_LOCK		SIG_LOCK	lock the attacher
 * (jw)
 * we always remove our utmp slots. (even when "lock" or "stop")
 * Note: Take extra care here, we may be called by unterrupt!
 */
void
Detach(mode)
int mode;
{
  int sign = 0;
#ifdef UTMPOK
  register int n;
#endif

  if (Detached)
    return;
  debug1("Detach(%d)\n", mode);
  if (fore && status)
    RemoveStatus();
  signal(SIGHUP, SIG_IGN);
  SetTTY(0, &OldMode);
  FinitTerm();
  switch (mode)
    {
    case D_DETACH:
      printf("\n[detached]\n");
      sign = SIG_BYE;
      break;
#ifdef BSDJOBS
    case D_STOP:
      (void) fflush(stdout);
      sign = SIG_STOP;
      break;
#endif
#ifdef REMOTE_DETACH
    case D_REMOTE:
      printf("\n[remote detached]\n");
      sign = SIG_BYE;
      break;
#endif
#ifdef POW_DETACH
    case D_POWER:
      printf("\n[power detached]\n");
      if (PowDetachString) 
        printf("%s\n", PowDetachString);
      sign = SIG_POWER_BYE;
      break;
#ifdef REMOTE_DETACH
    case D_REMOTE_POWER:
      printf("\n[remote power detached]\n");
      if (PowDetachString) 
        printf("%s\n", PowDetachString);
      sign = SIG_POWER_BYE;
      break;
#endif
#endif
    case D_LOCK:
      ClearDisplay();
      sign = SIG_LOCK;
      /* tell attacher to lock terminal with a lockprg. */
      break;
    }
#ifdef UTMPOK
  for (n = WinList; n != -1; n = wtab[n]->WinLink)
    if (wtab[n]->slot != (slot_t) -1)
      {
	RemoveUtmp(wtab[n]);
        /*
	 * Set the slot to 0 to get the window
         * logged in again.
	 */
	wtab[n]->slot = (slot_t) 0;
      }
  RestoreLoginSlot();
#endif
  freetty();
  (void) chmod(SockPath, /* S_IFSOCK | */ 0600); /* Flag detached-ness */
    /*
     * tell father to father what to do. We do that after we
     * freed the tty, thus getty feels more comfortable on hpux
     * if it was a power detach.
     */
  Kill(AttacherPid, sign);
  debug2("Detach: Signal %d to Attacher(%d)!\n", sign, AttacherPid);
  if (mode != D_LOCK && mode != D_STOP)
    AttacherPid = 0;

  Detached = 1;
  Suspended = (mode == D_STOP) ? 1 : 0;
  if (fore)
    fore->active = 0;
  debug("Detach returns, we are successfully detached.\n");
}

void
Kill(pid, sig)
int pid, sig;
{
  if (pid < 2)
    return;
  (void) kill(pid, sig);
}

static int IsSymbol(e, s)
register char *e, *s;
{
  register char *p;
  register int n;

  for (p = e; *p && *p != '='; ++p)
    ;
  if (*p)
    {
      *p = '\0';
      n = strcmp(e, s);
      *p = '=';
      return n == 0;
    }
  return 0;
}

static void MakeNewEnv()
{
  register char **op, **np;
  static char buf[MAXSTR];

  for (op = environ; *op; ++op)
    ;
  NewEnv = np = (char **) malloc((unsigned) (op - environ + 6 + 1) * sizeof(char **));
  if (!NewEnv)
    Msg_nomem;
  if (strlen(SockName) > MAXSTR - 5)
    SockName = "?";
  sprintf(buf, "STY=%s", SockName);
  *np++ = buf;	                /* NewEnv[0] */
  *np++ = Term;	                /* NewEnv[1] */
#ifdef TIOCSWINSZ
  np += 2;	/* room for TERMCAP and WINDOW */
#else
  np += 4;	/* room for TERMCAP WINDOW LINES COLUMNS */
#endif

  for (op = environ; *op; ++op)
    {
      if (!IsSymbol(*op, "TERM") && !IsSymbol(*op, "TERMCAP")
	  && !IsSymbol(*op, "STY") && !IsSymbol(*op, "WINDOW")
	  && !IsSymbol(*op, "SCREENCAP")
#ifndef TIOCGWINSZ
	  && !IsSymbol(*op, "LINES") && !IsSymbol(*op, "COLUMNS")
#endif
	  )
	*np++ = *op;
    }
  *np = 0;
}

void
#ifdef USEVARARGS
/*VARARGS2*/
# if defined(__STDC__)
Msg(int err, char *fmt, ...)
# else
Msg(err, fmt, va_alist)
int err;
char *fmt;
va_dcl
# endif
{
  static va_list ap = 0;
#else
/*VARARGS2*/
Msg(err, fmt, p1, p2, p3, p4, p5, p6)
int err;
char *fmt;
unsigned long p1, p2, p3, p4, p5, p6;
{
#endif
  char buf[MAXPATH*2];
  char *p = buf;

  if (Detached)
    return;
#ifdef USEVARARGS
# if defined(__STDC__)
  va_start(ap, fmt);
# else
  va_start(ap);
# endif
  (void) vsprintf(p, fmt, ap);
  va_end(ap);
#else
  sprintf(p, fmt, p1, p2, p3, p4, p5, p6);
#endif
  if (err)
    {
      p += strlen(p);
      if (err > 0 && err < sys_nerr)
	sprintf(p, ": %s", sys_errlist[err]);
      else
	sprintf(p, ": Error %d", err);
    }
  if (HasWindow)
    {
      debug1("Msg('%s');\n", p);
      MakeStatus(buf);
    }
  else
    {
      printf("%s\r\n", buf);
      if (DeadlyMsg)
	{
          debug1("Msg('%s') screen is not up, exiting..\n", buf);
          Kill(AttacherPid, SIG_BYE);
          eexit(1);
	}
      else
	debug1("Harmless; Msg('%s');\n", buf);
    }
  DeadlyMsg = 1;
}

char *Filename(s)
char *s;
{
  register char *p;

  if (s == NULL) 
    return s;
  p = s + strlen(s) - 1;
  while (p >= s && *p != '/')
    --p;
  return ++p;
}

/*
 * '^' is allowed as an escape mechanism for control characters. jw.
 */
static char *MakeWinMsg(s, n)
register char *s;
int n;
{
  static char buf[MAXSTR];
  register char *p = buf;
  register int ctrl;

  ctrl = 0;
  for (; *s && p < buf + MAXSTR - 1; s++, p++)
    if (ctrl)
      {
        ctrl = 0;
        if (*s == '^' || *s < 64)
          *p = *s;
        else 
          *p = *s - 64;
      }
    else
      {
        switch (*s)
          {
          case '%':
    	    *p = n + '0';
	    break;
          case '~':
	    *p = BELL;
	    break;
	  case '^':
	    ctrl = 1;
	    *p-- = '^';
	    break;
          default:
	    *p = *s;
	    break;
          }
      }
  *p = '\0';
  return buf;
}

