🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.sources.mac
Article #7

MacLayers 1.00 (part 3 of 6)

#7
From: Werner Uhrig
Date: Sat, 02 Jun 1990 08:00
2242 lines
62612 bytes
[MacLayers 1.00 - part 3 of 6]

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#
#	layers.c
#
# This archive created: Thu May 31 21:58:18 1990
# By:	Roger L. Long (macintosh@dhw68k.cts.com)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'layers.c'" '(56819 characters)'
if test -f 'layers.c'
then
	echo shar: will not over-write existing file "'layers.c'"
else
sed 's/^X//' << \SHAR_EOF > 'layers.c'
X/********					Layers.c
X*********
X*********	Layers - MacLayers Multiwindow BSD Socket Driver
X*********
X*********          Dave Trissel oakhill!davet
X*********
X********* The sockets handling portion of this control module is based 
X********* upon 'screen' by Oliver Laumann whose copyright remains below. 
X********* The rest is
X *
X *             Copyright (C) 1989 by David W. Trissel
X *
X *              Not derived from licensed software.
X *
X * Permission is granted to freely use, copy, modify, and redistribute
X * this software, provided that no attempt is made to gain profit from it,
X * the author is not construed to be liable for any results of using the
X * software, alterations are clearly marked as such, and this notice is
X * not modified.
X *
X */
X
Xstatic char LayersVersion[] = "layers 1.00 17-Mar-1990";
X
X/* Layers Changes:
X
X	Version .92 22-Mar-1989
X
X		Original Distributed version
X
X	Version .93 31-Mar-1989
X
X		Deleted dl and al termcap entries since they didn't help any
X		(al was redundant with sc (scroll) so should never have been created)
X
X		SIGINT no longer causes us to quit (left debugging code in by mistake)
X
X		Layer #1 is always logged in and takes over as user's login console
X		(Real tty disconnected from /etc/utmp file while layers is running)
X
X	Version .93b 05-May-1989
X
X		Try getenv("PWD") before getwd() so Sun networking won't hang us up
X
X    Version .93n 07-Jan-1990
X
X        Reset TTY back to normal if initial link handshake fails.
X
X    Version 1.00b 22-Jan-1990
X
X        Corrected problem of layers forcing all umasks to 000.
X
X    Version 1.00  17-Mar-1990
X
X        First public release.
X*/
X
X
X/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
X * Not derived from licensed software.
X *
X * Permission is granted to freely use, copy, modify, and redistribute
X * this software, provided that no attempt is made to gain profit from it,
X * the author is not construed to be liable for any results of using the
X * software, alterations are clearly marked as such, and this notice is
X * not modified.
X *
X *	Modified by Patrick Wolfe (pat@kai.com, kailand!pat)
X *	Do whatever you want with (my modifications of) this program, but
X *	don't claim you wrote them, don't try to make money from them, and
X *	don't remove this notice.
X */
X
X/*
X *	Beginning of User Configuration Section
X */
X
X/*
X * SEQUENT   -- your host system is Sequent. This changes a setvbuf()
X *              call to a setlinebuf(). [Suggested by Peter Newton 
X *              <newton@cs.utexas.edu>]
X *
X */
X#undef SEQUENT
X
X
X/*
X * GETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
X *              and the getttyent(3) library functions.
X *
X */
X#undef GETTTYENT
X
X
X/*
X * LOGINDEFAULT -- if set to 1 (one), windows will login (add entries to
X *                 /etc/utmp) by default.  Set to 0 if you don't want this.
X *                 (Also see USERLIMIT below). [NOTE: current code always
X *                 logs layer #1 only unless -l option used on 'layers'
X *                 command.]
X */
X#define LOGINDEFAULT 0
X
X/*
X * USERLIMIT  --   count all non-null entries in /etc/utmp before adding a 
X *				   new entry. Some machine manufacturers (incorrectly) count 
X *				   logins by counting non-null entries in /etc/utmp (instead 
X *				   of counting non-null entries with no hostname and not on 
X *				   a pseudo tty). Sequent does this, so you might reach your 
X *				   limited user license early.
X */
X#define USRLIMIT 32
X
X/*
X * SOCKDIR      -- If defined, this directory is where layers sockets will be 
X *				   placed, (actually in a subdirectory by the user's loginid).
X *				   This is neccessary because NFS doesn't support socket 
X *				   operations, and many people's homes are on NFS mounted 
X *				   partitions.  Layers will create this directory if it needs 
X *				   to.
X */
X#define SOCKDIR "/tmp/layers"	/* NFS doesn't support named sockets */
X
X/*
X *	End of User Configuration Section
X */
X
X#include <stdio.h>
X#include <sgtty.h>
X#include <signal.h>
X#include <errno.h>
X#include <ctype.h>
X#include <utmp.h>
X#include <pwd.h>
X#include <nlist.h>
X#include <fcntl.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <sys/wait.h>
X#include <sys/socket.h>
X#include <sys/un.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <sys/ioctl.h>
X
X#include "layers.h"
X
X#ifdef GETTTYENT
X#include <ttyent.h>
X#else
Xstatic struct ttyent
X  { char *ty_name;
X  } *getttyent();
Xstatic char *tt, *ttnext;
Xstatic char ttys[] = "/etc/ttys";
X#endif
X
X#ifndef FSCALE
X#define FSCALE 1000.0		/* Sequent doesn't define FSCALE...grrrr */
X#endif
X
X#ifdef  USRLIMIT
Xstruct utmp utmpbuf;
Xint UserCount;
X#endif
X
X#define Ctrl(c) ((c)&037)
X
X/* C library items */
Xextern char **environ;
Xextern errno;
Xextern sys_nerr;
Xextern char *sys_errlist[];
Xextern char *index(), *rindex(), *malloc(), *getenv();
Xextern char *getlogin(), *ttyname();
X
X/* Local items */
Xstatic void FAbort(), SigHup(), SigChld(), AddCap(), FinitTerm();
Xstatic char  *MakeTermcap(), *Filename(), **SaveArgs(), *GetTtyName();
Xstatic void	InitWorld(), ClearShape(), BuildTitle(), KillPG();
Xstatic void SetWindowSize(), WriteUtmp();
Xstatic int	ReadUtmp(), FindUtmp(), SetUtmp();
X
Xstatic int	loginflag = -1;
Xstatic char PtyName[32], TtyName[32];
Xstatic char *ShellProg;
Xstatic char *ShellArgs[2];
Xstatic inlen;
Xstatic ESCseen;
Xstatic GotSignal;
Xstatic char DefaultShell[] = "/bin/sh";
Xstatic char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
Xstatic char PtyProto[] = "/dev/ptyXY";
Xstatic char TtyProto[] = "/dev/ttyXY";
Xstatic int TtyMode = 0622;
Xstatic struct stat RealTtyStat;					/* Real tty stat */
Xstatic char RealTtyName[32] = "";				/* Real tty name */
Xstatic int	RealSlot = 0;						/* Real tty logon slot */
Xstatic struct utmp	RealUtmp;					/* Real tty logon utmp entry */
Xstatic int	RealTtyMode = 0;					/* Real tty mode */
Xstatic int	Oumask;								/* Original user's umask */
Xstatic char SockPath[512];
X#ifdef SOCKDIR
Xstatic char SockDir[] = SOCKDIR;
X#else
Xstatic char SockDir[] = ".layers";
X#endif
Xstatic char *SockNamePtr, *SockName;
Xstatic ServerSocket;
Xstatic char *NewEnv[MAXARGS];
Xstatic char Esc = Ctrl('a');
Xstatic char MetaEsc = 'a';
Xstatic char *home;
Xstatic Abortonmsg;
Xstatic utmp, utmpf;
Xstatic char UtmpName[] = "/etc/utmp";
Xstatic char *LoginName;
Xstatic mflag, nflag, fflag, rflag;
Xstatic char HostName[MAXSTR];
Xstatic char *myname;
Xstatic DevTty;
X
Xstatic struct mode {
X    struct sgttyb m_ttyb;
X    struct tchars m_tchars;
X    struct ltchars m_ltchars;
X    int m_ldisc;
X    int m_lmode;
X} OldMode, NewMode;
X
X#define MSG_CREATE    0
X#define MSG_ERROR     1
X
Xstruct msg
X  {
X    int type;
X    union
X	  { struct
X		  {	int	lflag;				/* login flag */
X			struct Shape shape;		/* window shape */
X	    	int nargs;
X	    	char line[MAXLINE];
X	    	char dir[1024];
X		  } create;
X		char message[MAXLINE];
X      } m;
X  };
X
X
X			/* dynamic keyboard input buffer definition */
Xstruct Kbuff
X  {	struct Kbuff * next;			/* next buffer in chain (or NULL) */
X	int			size;				/* size of data in this buffer */
X	int			offset;				/* start of first data in buffer */
X	unsigned char text[IOSIZE];		/* text buffer itself */
X  };
X
X			/* World layer definition */
Xstruct Layer {
X	int		chan;					/* channel represented by this layer */
X    int		allocated;				/* layer allocated */
X    int		ptyfd;					/* psuedo tty */
X	int		ptymask;				/* mask for pty descriptor */
X	int		lpid;					/* layer head process ID */
X	int		slot;					/* utmp slot number */
X	struct Kbuff *kbuff;			/* keyboard input buffers */
X	struct Shape shape;				/* Shape structure to/from MacLayers */
X    char	cmd[MAXSTR];			/* command to execute */
X    char	tty[MAXSTR];			/* psuedo tty ID */
X	};
X
Xstatic struct Layer World[MAXPCHAN]; /* all layer structures */
X
Xstatic int rows = 24;				/* default window height in lines */
Xstatic int cols = 80;				/* default window width in chars */
Xstatic char Termcap[1024];
Xstatic char Term[MAXSTR] = "TERM=";	/* window's terminal type */
Xstatic char	*UserTerm;				/* terminal ID we are mimmicing */
Xstatic int flowctl;
Xstatic tcLineLen = 100;
X
X/* co#80 and li$24 added dynamically for proper window size */
Xstatic char TermcapConst1[] = "TERMCAP=SC|";
Xstatic char TermcapConst3[] = "|MacLayers virtual terminal|\\\n\
X	:cr=^M:do=^J:nl=^J:bl=^G:cl=\\E[;H\\E[2J:\\\n\
X	:le=^H:bs:am:cm=\\E[%i%d;%dH:nd=\\E[C:up=\\E[A:\\\n\
X	:ce=\\E[K:cd=\\E[J:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\\n\
X	:md=\\E[1m:mr=\\E[7m:mb=\\E[5m:me=\\E[m:is=\\E[1;24r\\E[24;1H:\\\n\
X	:rf=/usr/lib/tabset/vt100:\\\n\
X	:rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:\\\n\
X	:ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n\
X	:ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:ta=^I:pt:sr=\\EM:vt#3:xn:\\\n\
X	:sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:\\\n\
X	:dc=\\ED:ic=\\EI:";
X/* NOTE: the above two cababilities are beyond vt100 - unique to MacLayers */
X
Xint		Dflag;							/* debug dump flag */
X
X							/* main() */
Xmain(ac, av)
Xchar **av;
X{
X    register n;
X    register len;
X    register struct Layer *layer;
X    char	*ap;
X    struct passwd *ppp;
X    int		s;
X	int		r;							/* read fd test bits */
X	int		w;							/* write fd test bits */
X	int		stall;						/* stall flag for priority channel */
X	int		fderr;						/* error fd test bits */
X    struct timeval tv;
X	struct Shape shape;					/* window shape */
X    time_t	now;
X    char	buf[IOSIZE];
X    char	rc[256];
X    struct stat st;
X	struct Kbuff *kbptr;				/* input keyboard buffer pointer */
X
X	Abortonmsg = 1;						/* quit if message generated */
X	ClearShape(&shape);					/* initialize shape structure */
X	myname = (ac == 0) ? "layers" : av[0];
X	InitWorld();						/* clear World array structures */
X
X    while (ac > 0)
X	  {	ap = *++av;
X		if (--ac > 0 && *ap == '-')
X		  {	switch (ap[1])
X			{ case 'l':		/* login this command line */
X				loginflag = 1;
X				break;
X
X			  case 'd':		/* dump debugging flag */
X				Dflag = 1;
X				(void) freopen("layers.dump", "a", stderr); /* append mode */
X#ifdef SEQUENT
X				setlinebuf(stderr);
X#else
X				setvbuf(stderr, NULL, _IOLBF, 0);
X#endif
X				break;
X
X	    	  case 'u':		/* do not login this command line */
X				loginflag = 0;
X				break;
X
X		      case 'm':		/* force this to be master and not a client */
X				mflag = 1;
X				break;
X
X	    	  case 'n':		/* no flow control */
X				nflag = 1;
X				break;
X
X	    	  case 'f':		/* flow control on */
X				fflag = 1;
X				break;
X
X			  case 'v':		/* do nothing but issue layers version */
X				printf("%s\n", LayersVersion);
X				exit(0);
X
X	    	  default:
X	    	help:
X				Msg (0,"Use: %s [-f] [-l | -u] [-m] [-n] [cmd args]\n", myname);
X
X	    	} /* end switch on '-' option */
X
X	    } /* end if '-' */
X
X		else
X			break;
X
X      } /* end while parameters */
X
X    if (nflag && fflag)
X		Msg (0, "-f and -n are conflicting options.");
X
X    if ((ShellProg = getenv ("SHELL")) == 0)
X		ShellProg = DefaultShell;
X	DO DEBUG("ShellProg %s\n", ShellProg);
X
X	/* we mimmic the user's $TERM ID */
X	if ((UserTerm = getenv("TERM")) == 0)
X		UserTerm = "layers";				/* use "layers" if none */
X	(void) strcat(Term, UserTerm);
X	DO DEBUG("%s\n", Term);
X
X    ShellArgs[0] = ShellProg;
X    if (ac == 0)
X	  { ac = 1;
X		av = ShellArgs;
X		shape.wattr |= Wa_shell;			/* indicate a shell window */
X      }
X
X    if ((home = getenv ("HOME")) == 0)
X		Msg (0, "$HOME is undefined.");
X	DO DEBUG("home %s\n", home);
X
X    if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0')
X	  { if ((ppp = getpwuid (getuid ())) == 0)
X	   		return;
X		LoginName = ppp->pw_name;
X      }
X	DO DEBUG("LoginName %s\n", LoginName);
X
X    if ((Oumask=umask(0)) == -1)
X		Msg (errno, "Cannot change umask to zero");
X	DO DEBUG("Original umask o%o\n", Oumask);
X
X#ifdef SOCKDIR
X    if (stat (SOCKDIR, &st) == -1)
X	  {	if (errno == ENOENT)
X		  { if (mkdir (SOCKDIR, 0777) == -1)
X				Msg (errno, "Cannot make directory %s", SOCKDIR);
X	    	(void) chown (SOCKDIR, 0, 0);
X		  }
X		else
X			Msg (errno, "Cannot get status of %s", SOCKDIR);
X      }
X	else
X	  { if ((st.st_mode & S_IFMT) != S_IFDIR)
X	    	Msg (0, "%s is not a directory.", SOCKDIR);
X		if ((st.st_mode & 0777) != 0777)
X	    	Msg (0, "Directory %s must have mode 777.", SOCKDIR);
X      }
X    sprintf (SockPath, "%s/%s", SockDir, LoginName);
X#else
X    sprintf (SockPath, "%s/%s", home, SockDir);
X#endif
X	DO DEBUG("SockPath %s\n", SockPath);
X
X    if (stat (SockPath, &st) == -1)
X	  { if (errno == ENOENT)
X		  { if (mkdir (SockPath, 0700) == -1)
X				Msg (errno, "Cannot make directory %s", SockPath);
X	    	(void) chown (SockPath, getuid (), getgid ());
X			DO DEBUG("SockPath directory made\n");
X		  }
X		else
X			Msg (errno, "Cannot get status of %s", SockPath);
X      }
X	else
X	  { if ((st.st_mode & S_IFMT) != S_IFDIR)
X	   		Msg (0, "%s is not a directory.", SockPath);
X		if ((st.st_mode & 0777) != 0700)
X	    	Msg (0, "Directory %s must have mode 700.", SockPath);
X		if (st.st_uid != getuid ())
X	    	Msg (0, "You are not the owner of %s.", SockPath);
X      }
X
X	(void) strcpy(RealTtyName, GetTtyName());		/* real tty name */
X	if (stat(RealTtyName, &RealTtyStat) == -1)		/* get current mode */
X		Msg(errno, "Cannot get status of %s", RealTtyName);
X	DO DEBUG("Mode of %s is %#o\n", RealTtyName, RealTtyStat.st_mode);
X	RealTtyMode = RealTtyStat.st_mode;		/* save mode for later restore */
X
X    (void) gethostname (HostName, MAXSTR);
X    HostName[MAXSTR-1] = '\0';
X	DO DEBUG("HostName %s\n", HostName);
X
X    if (ap = index (HostName, '.'))
X		*ap = '\0';
X	if (ap)
X		DO DEBUG("*ap %s\n", *ap);
X
X    strcat (SockPath, "/");
X    SockNamePtr = SockPath + strlen (SockPath);
X
X	/* if we are a client send create message to startup window and exit */
X    if (GetSockName ())
X	  {	DO DEBUG("GetSockName says that we are client\n");
X		DO DEBUG("SockName '%s'\n", SockName);
X		s = MakeClientSocket (1);
X		DO DEBUG("Client socket is %d\n", s);
X		DO DEBUG("SendCreateMsg()\n");
X		SendCreateMsg (s, ac, av, loginflag, &shape);
X		close (s);
X		DO DEBUG("after SendCreateMsg(), now exit(0)\n");
X		exit (0);
X      }
X
X	/* we are the server */
X	DO DEBUG("SockName '%s'\n", SockName);
X	DO DEBUG("We are server\n");
X    if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
X		Msg (errno, "/dev/tty");
X	DO DEBUG("opened /dev/tty fd %d\n", DevTty);
X
X    ServerSocket = MakeServerSocket ();
X	DO DEBUG("ServerSocket %d\n", ServerSocket);
X    s = ServerSocket;
X
X    if (fflag)
X		flowctl = 1;
X    else
X	if (nflag)
X		flowctl = 0;
X
X    if (loginflag == -1)
X        loginflag = LOGINDEFAULT;
X
X    MakeNewEnv ();
X    InitUtmp ();
X	RealSlot = FindUtmp(RealTtyName);	/* find current logon slot */
X	if (RealSlot)
X	  {	if (ReadUtmp(RealSlot, &RealUtmp) > 0)	/* read real login utmp */
X			RemoveUtmp(RealSlot);		/* remove original logon slot */
X		else
X			RealSlot = 0;				/* something's wrong */
X	  }
X
X    signal (SIGHUP, SigHup);
X    signal (SIGINT, SIG_IGN);			/* we should never see this */
X    signal (SIGQUIT, FAbort);			/* quit layers on these 2 signals */
X    signal (SIGTERM, FAbort);
X    signal (SIGTTIN, SIG_IGN);
X    signal (SIGTTOU, SIG_IGN);
X	signal (SIGALRM, SIG_IGN);			/* alarm clock used by protocol.c */
X
X    GetTTY (0, &OldMode);
X    SetMode (&OldMode, &NewMode);
X    SetTTY (0, &NewMode);
X
X	if (Initlink() == 0)
X	  {	SetTTY(0, &OldMode);			/* revert tty back */
X	    Msg (0, "\n\n  You are not running under MacLayers.");
X	  }
X		
X    sprintf (rc, "%.*s/.layersrc", 245, home);
X#if 0 /* NOT YET SUPPORTED */
X	/* if no window list start up a default shell window */
X    if (ReadRc(rc) == 0)			
X#endif
X      {	n = MakeWindow (0, *av, av, (char *)0, loginflag, &shape);
X    	if (n == -1)
X	  	  { SetTTY (0, &OldMode);
X			FQuit(1);
X			/* NOT REACHED */
X      	  }
X	  }
X
X	(void) chmod(RealTtyName, 0600);		/* lock out broadcasts */
X    Abortonmsg = 0;							/* don't abort on msg from now on */
X    signal (SIGCHLD, SigChld);
X    tv.tv_usec = 0;
X	tv.tv_sec = 0;							/* default no timer wanted */
X
X	/* client/Maclayers processing loop */
X
X	/* poll .20 secs for new startups */
X#define WUSSTARTUP	200000				
X	/* stall nonpriority windows .5 seconds when top window I/O active */
X#define WUSSTALL	500000
X
X	stall = 0;								/* startout no stalled channels */
X	fderr = 0;								/* startout no error fd's */
X
X    while (1)
X	  {	int			priochan;				/* the top window channel */
X
X		priochan = TopChannel();			/* find highest priority channel */
X
X		/* check for I/O on all available I/O descriptors */
X		r = 1<<0;							/* always read MacLayers stream */
X		r |= 1<<s;							/* always read server socket */
X		w = 0;								/* initalize to no write tests */
X		tv.tv_usec = 0;						/* default no startup poll */
X
X		/* for all active layers set read and write bits as appropriate */
X		if (stall)
X			stall = 1;						/* start counting output layers */
X		for (n=0; n<MAXPCHAN; n++)
X		 if ((layer = &World[n])->allocated) /* if layer exists ... */
X		  {	/* if not yet started or has just terminated ... */
X			if (layer->ptymask & fderr)
X				tv.tv_usec = WUSSTARTUP;	/* don't spinloop but wait-a-bit */
X			else
X		  	  {	if (layer->kbuff && layer->kbuff->size)
X					/* keyboard input for layer */
X					w |= layer->ptymask;	/* try write to it */
X				/* read layer output unless we're being nice to top window */
X				if (!stall)
X					r |= layer->ptymask;	/* read output from layer */
X				else
X				if (layer->chan == priochan)
X					r |= layer->ptymask;	/* read top priority output */
X				else
X					stall++;				/* indicate something to stall */
X			   }
X		    }
X
X		if (stall > 1)
X			if (!tv.tv_usec)
X				tv.tv_usec = WUSSTALL;		/* set stall timout */
X
X		/* process signals before trying select */
X		if (GotSignal)
X		  { SigHandler ();
X	    	continue;
X		  }
X
X		DO DEBUG("Select(r %x, w %x, fderr %x, stall %d, prio %d, us %d)\n",
X					r, w, fderr, stall, priochan, tv.tv_usec);
X
X		switch ( select(32, &r, &w, NULL, tv.tv_usec ? &tv : NULL) )
X		{ case -1:
X			/* errno has report */
X			if (errno == EINTR)				/* signal delivered or timout */
X			  { errno = 0;
X				tv.tv_usec = 0;				/* clear timer wait value */
X				fderr = 0;					/* turn off error stall */
X				stall = 0;					/* turn off output priority stall */
X				DO DEBUG("select errno EINTR\n");
X				continue;					/* re-loop */
X	    	  }
X	    	Abortonmsg = 1;
X			DO DEBUG("select errno %d\n", errno);
X	    	Msg (errno, "select");
X	    	/*NOTREACHED*/
X
X		  case 0:
X			/* timeout reached */
X			tv.tv_usec = 0;					/* clear timer wait value */
X			stall = 0;						/* turn off stall */
X			fderr = 0;						/* turn off error stall */
X			continue;						/* re-loop */
X
X		  default:
X			/* a channel has read/write status pending */
X			break;
X		}
X
X		DO DEBUG("after select r %x w %x\n", r, w);
X
X		/* handle any signal arriving up during select wait */
X		if (GotSignal)
X		  { SigHandler ();
X	    	continue;
X		  }
X
X		/* if server socket has command process that now */
X		if (r & 1 << s)
X	      {	ReceiveMsg(s);					/* process client control packet */
X			continue;						/* status may have changed */
X		  }
X
X		/* next process input stream from MacLayers */
X		if (r & 1 << 0)
X		  { ProcessStreamin();				/* key input and control packets */
X			continue;						/* status may have changed */
X		  }
X
X		/* process keyboard input first so output doesn't hold up
X		** keyboard echo and break/interrupt processing
X		*/
X		priochan = TopChannel();			/* find top priority channel */
X		stall = 0;							/* assume no stall needed */
X		for (n=0; n<MAXPCHAN; n++)
X		  if ((layer = &World[n])->allocated)
X			if (w & layer->ptymask)
X				while ((kbptr=layer->kbuff)->size)
X				  {	/* pass as much keyboard as possible */
X					if (layer->chan == priochan)
X						stall = 1;			/* stall lower priority channels */
X					len = write(layer->ptyfd, &kbptr->text[kbptr->offset],
X						 		kbptr->size);
X					DO DEBUG("keyin len %d to chan %d\n", len, layer->chan);
X					if (len <= 0)			/* if no data accepted ... */
X					  {	if (errno == EIO)	/* if I/O error indicated ... */
X							fderr |= layer->ptymask; /* wait-a-bit on this */
X						errno = 0;			/* clear errno */
X						break;				/* try again later */
X					  }
X					/* some of buffer accepted */
X					kbptr->size -= len;		/* length processed */
X					kbptr->offset += len;	/* bump up offset */
X					if (kbptr->size > 0)	/* not all buffer accepted ... */
X						break;				/* try feed again later */
X					/* see if another buffer chained */
X					if (kbptr->next)
X					  {	/* yes, free up current buffer and queue next */
X						layer->kbuff = kbptr->next; /* to next buffer */
X						free(kbptr);	/* free this buffer up */
X					  }
X					else
X					  {	/* otherwise leave this for next input */
X						kbptr->size = 0;	/* empty buffer */
X						kbptr->offset = 0;	/* refill from the start */
X					  }
X				  }
X
X		/* first process the highest priority channel (top window) */
X		if (priochan > 0 && priochan <= MAXPCHAN) /* if valid ... */
X		  if ((layer = &World[priochan-1])->allocated)
X			if (r & layer->ptymask)
X			  {	/* output to send to top MacLayers window */
X				len = read(layer->ptyfd, buf, IOSIZE);
X				if (len >= 0)				/* if no error ... */
X				  {	DO DEBUG("read output len %d chan %d\n", len, layer->chan);
X				  }
X				else
X				  {	/* We expect EIO error if socket not yet open on other end
X					** or if process using socket has terminated. We expect
X					** EWOULDBLOCK also after process terminates.
X					**/
X					DO DEBUG("read output err chan %d errno %d len %d\n",
X								layer->chan, errno, len);
X					if (errno == EIO || errno == EWOULDBLOCK)
X						DO DEBUG(" ...anticipated\n");
X					/* layer not ready or just terminated so wait-a-bit */
X					fderr |= layer->ptymask;
X					r &= ~layer->ptymask; /* don't read it again below */
X					errno = 0;				/* clear errno */
X				  }
X				if (len > 0)
X					SendData(layer->chan, buf, len);
X
X				if (len >= 0)
X					/* To keep lower priority channels from hogging the line
X					** we delay any output from them until the primary window 
X					** has no more data to be sent to it.
X					*/
X					stall = 1;				/* stall output from others */
X			  }
X
X		/* now pass all available output to MacLayers */
X		if (!stall)
X		 for (n=0; n<MAXPCHAN; n++)
X		  if ((layer = &World[n])->allocated)
X			if (r & layer->ptymask)
X			  {	/* output to send to MacLayers window */
X				len = read(layer->ptyfd, buf, IOSIZE);
X				if (len >= 0)				/* if no error ... */
X				  {	DO DEBUG("output chan %d len %d\n", layer->chan, len);
X				  }
X				else
X				  {	/* We expect EIO error if socket not yet open on other end
X					** or if process using socket has terminated. We expect
X					** EWOULDBLOCK also after process terminates.
X					**/
X					DO DEBUG("read output err chan %d errno %d len %d\n",
X								layer->chan, errno, len);
X					if (errno == EIO || errno == EWOULDBLOCK)
X					  {	DO DEBUG(" ...anticipated\n");
X					  }
X					/* layer not ready or just terminated so wait-a-bit */
X					fderr |= layer->ptymask;
X					errno = 0;				/* clear errno */
X				  }
X				if (len > 0)
X					SendData(layer->chan, buf, len);
X			  }
X
X		/* handle signals again */
X		if (GotSignal)
X			SigHandler ();
X
X      } /* end while (1) */
X
X	/* NOT REACHED */
X
X} /* main() */
X
X					/* ReceiveQuit() - MacLayers sends Quit packet */
X
Xvoid
XReceiveQuit()
X{
X	/* We completely quit layers cancelling all active processes */
X	DO DEBUG("ReceiveQuit()\n");
X	FQuit(0);								/* normal termination */
X	/* NOT REACHED */
X
X} /* ReceiveQuit() */
X
X
X				/* ReceiveNew() - MacLayers requests a new shell layer */
X
Xvoid
XReceiveNew(chanid, shape)
Xint		chanid;								/* channel for new shell layer */
Xstruct Shape *shape;						/* shape for new channel */
X{
X	DO DEBUG("ReceiveNew(%d)\n", chanid);
X	(void) MakeWindow (chanid, *ShellArgs, ShellArgs,
X					(char *) 0, loginflag, shape);
X
X} /* ReceiveNew() */
X
X
X				/* ReceiveDelete() - MacLayers has removed a layer */
Xvoid
XReceiveDelete(chanid)
Xint		chanid;								/* channel which was deleted */
X{
X	struct Layer *layer;					/* layer pointer */
X
X	/* validate channel */
X	DO DEBUG("ReceiveDelete(%d)\n", chanid);
X	if (chanid <= 0 || chanid > MAXPCHAN)
X		return;								/* ignore invalid channel */
X
X	/* if this layer active then kill it off, else ignore request */
X	layer = &World[chanid-1];				/* locate target layer */
X	if (layer->allocated)
X		KillWindow(layer);
X
X} /* ReceiveDelete() */
X	
X	
X		/* ReceiveSignal() - send signal to layer's process group */
X
Xvoid
XReceiveSignal(chanid, signal)
Xint			chanid;							/* layer's channel */
Xint			signal;							/* signal.h signal ID */
X{
X	struct Layer *layer;					/* layer pointer */
X
X	DO DEBUG("ReceiveSignal(%d,%d)\n", chanid, signal);
X	/* verify channel */
X	if (chanid <= 0 || chanid > MAXPCHAN)
X		return;								/* ignore invalid channel */
X
X	/* if this layer is active send the signal to the process group */
X	layer = &World[chanid-1];				/* locate target layer */
X	if (layer->allocated && layer->lpid)
X		KillPG(layer, signal);
X
X} /* ReceiveSignal() */
X
X
X				/* ReceiveReshape() - windowsize and location updated */
Xvoid
XReceiveReshape(chanid, shape)
Xint				chanid;					/* channel having shape */
Xstruct Shape	*shape;					/* shape structure */
X{
X	struct Layer *layer;				/* layer pointer */
X
X	DO DEBUG("ReceiveReshape(%d)\n", chanid);
X
X	/* verify channel */
X	if (chanid <= 0 || chanid > MAXPCHAN)
X		return;								/* ignore invalid channel */
X
X	/* if this layer is active then reshape it's window */
X	layer = &World[chanid-1];				/* locate target layer */
X	if (layer->allocated && layer->lpid)
X	  {	layer->shape = *shape;				/* install as our new shape */
X		SetWindowSize(layer);				/* udpate the O/S window info */
X	  }
X
X} /* ReceiveReshape() */
X
X
X			/* SetWindowSize() - tell O/S about new window size */
X
Xstatic void
XSetWindowSize(layer)
Xstruct Layer	*layer;					/* layer to resize */
X{
X#ifdef TIOCSWINSZ
X	struct	winsize	wsize;				/* window size structure */
X	int			retcode;				/* ioctl return code */
X
X	wsize.ws_col = layer->shape.wchars; /* character width */
X	wsize.ws_row = layer->shape.wlines; /* window height */
X	wsize.ws_xpixel = 0;				/* necessary? */
X	wsize.ws_ypixel = 0;
X	/* update O/S window state */
X	retcode = ioctl(layer->ptyfd, TIOCSWINSZ, &wsize);
X	DO DEBUG("SetWindowSize(chan %d) col %d, row %d iotcl() = %d\n",
X			layer->chan, layer->shape.wchars, layer->shape.wlines,
X			retcode);
X
X	retcode = ioctl(layer->ptyfd, TIOCGWINSZ, &wsize);
X	DO DEBUG("TIOCGWINSZ: col %d, row %d iotcl() = %d\n",
X			wsize.ws_col, wsize.ws_row, retcode);
X
X#endif
X}  /* SetWindowSize() */
X
X
X				/* ReceiveData() - received keyboard input for layer */
Xvoid
XReceiveData(chanid, buff, cnt)
Xint		chanid;							/* channel receiving input */
Xchar	*buff;							/* buffer containing data */
Xint		cnt;							/* count of data bytes */
X{
X	struct Layer *layer;				/* layer pointer */
X	struct Kbuff *kb;					/* keybuff pointer */
X
X	DO DEBUG("ReceiveData(%d, '%.*s')\n", chanid, cnt, buff);
X	/* verify channel */
X	if (chanid <= 0 || chanid > MAXPCHAN)
X		return;								/* ignore invalid channel */
X	layer = &World[chanid-1];				/* locate target layer */
X
X	/* add character stream to layer's input buffers for main loop processing */
X	for (kb=layer->kbuff; kb->next; kb=kb->next); /* find oldest buffer */
X	while (cnt--)
X	  {	
X		/* if current buffer full then chain in a new one */
X		if (kb->offset+kb->size >= IOSIZE)
X		  { kb->next = (struct Kbuff *) malloc(sizeof(struct Kbuff));
X			kb = kb->next;					/* base new keybuff */
X			kb->next = NULL;				/* no next yet */
X			kb->size = 0;					/* this character is first */
X			kb->offset = 0;					/* at zero offset */
X		  }
X
X		/* add new character to the end of this buffer */
X		kb->text[kb->offset+kb->size++] = *buff++; /* insert at end of data */
X	  }
X
X} /* ReceiveData() */
X
X
X
X					/* InitWorld() - initialize layer structures */
X
Xstatic void
XInitWorld()
X{
X	struct Layer *layer;				/* layer pointer */
X	struct Kbuff *kb;					/* keybuff pointer */
X	int		i;							/* work variable */
X
X	for (i=0; i<MAXPCHAN; i++)
X	  {	layer = &World[i];				/* point to layer */
X		layer->chan = i+1;				/* channel ID */
X		layer->allocated = 0;			/* does not exist yet */
X		layer->lpid = 0;				/* head process */
X		layer->ptyfd = 0;				/* no pseduo pty yet */
X		layer->ptymask = 0;				/* no pty mask yet */
X		layer->slot = 0;				/* no Utmp slot */
X		ClearShape(&layer->shape);		/* clear shape structure */
X
X		/* allocate the primary input keybuffer for this layer */
X		layer->kbuff = (struct Kbuff *) malloc(sizeof(struct Kbuff));
X		layer->kbuff->next = NULL;		/* no next buffer */
X		layer->kbuff->size = 0;			/* no data in buffer */
X		layer->kbuff->offset = 0;		/* start filling at front */
X
X	  } /* end for layer scan */
X
X} /* InitWorld() */
X
X
X					/* clearshape() - initialize shape structure */
X
Xstatic void
XClearShape(shape)
Xstruct Shape	*shape;					/* shape structure pointer */
X{
X	shape->worigv = 0;					/* default window position */
X	shape->worigh = 0;
X	shape->wlines = 0;					/* default size */
X	shape->wchars = 0;
X	shape->wfont = 0;					/* default font size */
X	shape->wattr = 0;					/* no attributes */
X
X} /* clearshape() */
X
X
X					/* SigHandler() - process signals */
X
XSigHandler ()
X{
X	DO DEBUG("GotSignal()\n");
X    while (GotSignal)
X	  { GotSignal = 0;
X		DoWait ();		/* handle dead or stopped children processes */
X      }
X}
X
Xstatic void
XSigChld ()
X{
X	DO DEBUG("SigChld()\n");
X	/* flag child process is stopped or dead */
X    GotSignal = 1;
X}
X
Xstatic void
XSigHup ()
X{
X	DO DEBUG("SigHup()\n");
X    /* Detach (0); */
X	FQuit(1);			/* stop all processes */
X	/* NOT REACHED */
X
X}
X
X	/* DoWait() -  send SIGCONT to stopped windows, Free dead process windows */
Xstatic
XDoWait()
X{
X    register pid;
X    register struct Layer *layer;
X    union wait wstat;
X	int		i;
X
X	DO DEBUG("DoWait()\n");
X    while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0)
X		/* dead or stopped child process found */
X		for (i=0; i<MAXPCHAN; i++)
X			if ((layer = &World[i])->lpid == pid)
X			  { if (WIFSTOPPED (wstat))
X				  {	/* stopped process so restart it */
X					/*** DO WE REALLY NEED TO DO THIS? ***/
X					DO DEBUG("killpg(, SIGCONT)\n");
X					KillPG(layer, SIGCONT);
X				  }
X				else
X				  {	/* remove dead process's layer */
X					DO DEBUG("kill dead process window %d\n", layer->chan);
X			 		KillWindow (layer);
X					/* tell MacLayers layer is dead */
X					SendDelete(layer->chan);
X				  }
X	 	     }
X
X} /* DoWait() */
X
X
X				/* KillPG() - send signal to layer's process group */
X
Xstatic void
XKillPG(layer, signal)
Xstruct Layer	*layer;					/* layer to signal */
Xint				signal;					/* signal to send */
X{
X	int			retcode;				/* work variable */
X	int			pgrp;					/* process group for layer */
X	int			tgrp;					/* terminal control process group */
X
X	DO DEBUG("KillPG(%d, sig %d)\n", layer->chan, signal);
X
X	if (layer->lpid)
X	  {	pgrp = getpgrp(layer->lpid);	/* get process group */
X		DO DEBUG("getpgrp() = %d\n", pgrp);
X		if (pgrp != -1)
X		  {	retcode = killpg(pgrp, signal);	/* signal it */
X			DO DEBUG("killpg() = %d\n", retcode);
X		  }
X
X		/* After a lot of experimenting it was determined that csh
X		** creates a new terminal control group when it runs a command.
X		** Thus the above code failed to forward SIGINT to such commands.
X		** (Csh would get it but just ignore it.) Thus the following code.
X		*/ 
X
X		/* forward SIGINT to the terminal control group also */
X		if (signal == SIGINT)
X		  {	retcode = ioctl(layer->ptyfd, TIOCGPGRP, &tgrp);
X			DO DEBUG("ioctl(ptyfd,TIOCGPGRP) termcntlgrp %d, retcode = %d\n",
X							tgrp, retcode);
X			/* but only if not the same process group */
X			if (retcode != -1 && tgrp != pgrp)
X			  {	retcode = killpg(tgrp, signal);	/* signal it */
X				DO DEBUG("killpg(%d) = %d\n", tgrp, retcode);
X		  	  }
X		  }
X	  }
X
X} /* KillPG() */
X
X
X				/* KillWindow() - remove a layer from the system */
X
X/* Note: This function does NOT tell MacLayers about the dead layer */
X
Xstatic
XKillWindow (layer)
Xstruct Layer *layer;
X{
X	struct Kbuff	*kb;				/* work buffer free pointer */
X
X	if (layer->allocated)
X	  { 								/* SHOULD THIS BE SIGKILL ??? */
X		if (layer->lpid)				/* if layer process started ... */
X		  {	KillPG(layer, SIGHUP);		/* kill processes */
X			layer->lpid = 0;			/* clear pid field */
X		  }
X    	RemoveUtmp(layer->slot);
X    	(void) chmod(layer->tty, 0666);
X    	(void) chown(layer->tty, 0, 0);
X    	close(layer->ptyfd);
X		DO DEBUG("chmod/chown %s, SendDelete(%d)\n",layer->tty, layer->chan);
X
X		ClearShape(&layer->shape);		/* reset the shape structure */
X		/* free all keybuffers but one and reprime it */
X		for (kb=layer->kbuff; kb->next; kb=kb->next)
X			free(kb);					/* free input buffers */
X		kb->size = 0;					/* empty buffer */
X		kb->offset = 0;					/* start refill from front */
X    	layer->allocated = 0;			/* window no longer allocated */
X	  }
X
X} /* KillWindow() */
X
X
X					/* FAbort() - signal catcher for quitting */
Xstatic void
XFAbort()
X{
X	DO DEBUG("FAbort()\n");
X	FQuit (1);							/* quit with error exit */
X
X} /* FAbort() */
X
X
X					/* FQuit() - terminate layers */
Xvoid
XFQuit(exitcode)
Xint		exitcode;
X{
X	int			i;
X
X	DO DEBUG("FQuit(%d)\n",exitcode);
X	for (i=0; i<MAXPCHAN; i++)
X		KillWindow(&World[i]);			/* kill all windows */
X
X	DO DEBUG("SendQuit()\n");
X	SendQuit();							/* tell MacLayers to exit layers mode */
X    SetTTY (0, &OldMode);
X	if (RealTtyMode)
X		(void) chmod(RealTtyName, RealTtyMode);	/* restore mode */
X	if (RealSlot)
X		WriteUtmp(RealSlot, &RealUtmp);	/* restore original login */
X    FinitTerm ();
X	sleep(2);							/* wait for port to reset */
X    printf ("[layers terminated]\n");
X
X    exit (exitcode);
X
X} /* FQuit() */
X
X
X					/* MakeWindow() - create new layer */
Xstatic
XMakeWindow (chan, prog, args, dir, lflag, shape)
Xint		chan;							/* zero or channel to use for window */
Xchar	*prog;
Xchar 	**args;
Xchar	*dir;
Xint		lflag;							/* one if this to be logged in */
Xstruct Shape *shape;					/* shape to use for window */
X{
X    register struct Layer *layer;
X    register char **cp;
X    register f, j;
X    int tf;
X    int mypid;
X    char ebuf[10];
X
X	DO DEBUG("MakeWindow(%d, %s, %s, dir %s, ",
X				chan, prog, args[0], dir ? dir : "(none)");
X	DO DEBUG("login %d\n", lflag);
X	DO DEBUG("    origv %d, origh %d, lines %d, chars %d, ",
X				shape->worigv, shape->worigh, shape->wlines, shape->wchars);
X	DO DEBUG("font %d, attr 0x%x\n", shape->wfont, shape->wattr);
X					
X    if ((f = OpenPTY ()) == -1)
X	  { Msg (0, "No more PTYs.");
X		return ( -1 );
X      }
X
X	/* if channel not given obtain one from MacLayers */
X	if (chan == 0)
X	  {	chan = SendNew(shape);				/* try to get free window */
X    	if (chan == 0)
X	  	  {	Msg (0, "No more windows.");
X			return ( -1 );
X      	  }
X		DO DEBUG("SendNew() == %d\n", chan);
X	  }
X
X	/* verify channel */
X	if (chan <= 0 || chan > MAXPCHAN)
X	  {	Msg(0, "Invalid channel %d.", chan);
X		return ( -1 );
X	  }
X
X	/* login this window if it's layer #1 */
X	if (chan == 1)
X		lflag = 1;
X					
X    if (lflag == -1)
X		lflag = loginflag;
X	
X#ifdef USRLIMIT
X    /*
X     *	Count current number of users, and if logging windows in,
X     */
X    if (lflag == 1)
X	  { (void) lseek (utmpf, 0, 0);
X		UserCount = 0;
X		while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0)
X		  { if (utmpbuf.ut_name[0] != '\0')
X				UserCount++;
X		  }
X		if (UserCount >= USRLIMIT)
X		  { Msg (0, "User limit reached.  Window will not be logged in.");
X	    	lflag = 0;
X		  }
X      }
X#endif USRLIMIT
X
X	layer = &World[chan-1];					/* find layer structure */
X	layer->shape = *shape;					/* install new window shape */
X
X	/* ??? What do we do if layer is still active as far as we're concerned? */
X	if (layer->allocated)
X	  {	DO DEBUG("??? newlayer not free !!!\n");
X		KillWindow(layer);					/* kill off old layer */
X		SendDelete(layer->chan);			/* kill window back off */
X		Msg (0, "Makewindow error: Duplicate active layer %d.", chan);
X		return ( -1 );						/* return failed */
X	  }
X
X	layer->allocated = 1;					/* show layer now in use */
X	BuildTitle(chan, prog, args);			/* install window title */
X
X    (void) fcntl (f, F_SETFL, FNDELAY);
X    layer->ptyfd = f;						/* pseudo pty for task's I/O */
X	layer->ptymask = 1<<f;					/* set pty device mask */
X    strncpy (layer->cmd, Filename (args[0]), MAXSTR-1);
X    layer->cmd[MAXSTR-1] = '\0';
X    strncpy (layer->tty, TtyName, MAXSTR-1);
X	DO DEBUG("forking %s, tty %s, ptyfd %d, mask %x\n",
X				layer->cmd, layer->tty, layer->ptyfd, layer->ptymask);
X    (void) chown (TtyName, getuid (), getgid ());
X    if (lflag == 1)
X	  { layer->slot = SetUtmp(TtyName, chan == 1);
X		if (chan == 1 && RealTtyMode)
X			/* set to original tty umask */
X			(void) chmod(TtyName, RealTtyMode);
X		else
X			/* force to this mode */
X			(void) chmod(TtyName, TtyMode);
X      }
X    else
X	  { layer->slot = -1;
X		/* do not allow any other user access to this device */
X		(void) chmod (TtyName, 0600);
X      }
X    switch (layer->lpid = fork ())
X	{ case -1:
X		Msg (errno, "fork");
X		layer->lpid = 0;					/* clear pid field */
X		return ( -1 );						/* return failed */
X
X      case 0:
X		signal (SIGHUP, SIG_DFL);
X		signal (SIGINT, SIG_DFL);
X		signal (SIGQUIT, SIG_DFL);
X		signal (SIGTERM, SIG_DFL);
X		signal (SIGTTIN, SIG_DFL);
X		signal (SIGTTOU, SIG_DFL);
X		signal (SIGALRM, SIG_DFL);
X		setuid (getuid ());
X		setgid (getgid ());
X		if (dir && chdir (dir) == -1)
X		  { SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
X	    	exit (1);
X		  }
X		mypid = getpid ();
X		ioctl (DevTty, TIOCNOTTY, (char *)0);
X		if ((tf = open (TtyName, O_RDWR)) == -1)
X		  { SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
X	    	exit (1);
X		  }
X		DO DEBUG("Now in new process\n");
X		(void) dup2 (tf, 0);
X		(void) dup2 (tf, 1);
X		(void) dup2 (tf, 2);
X		for (f = getdtablesize () - 1; f > 2; f--)
X	    	close (f);
X		ioctl (0, TIOCSPGRP, &mypid);
X		(void) setpgrp (0, mypid);
X		SetTTY (0, &OldMode);
X
X		  {	struct	winsize	wsize;		/* window size structure */
X			int			retcode;		/* ioctl return code */
X
X			wsize.ws_col = layer->shape.wchars; /* character width */
X			wsize.ws_row = layer->shape.wlines; /* window height */
X			wsize.ws_xpixel = 0;				/* necessary? */
X			wsize.ws_ypixel = 0;
X			/* update O/S window state */
X			retcode = ioctl(0, TIOCSWINSZ, &wsize);
X		  }
X		(void) umask(Oumask);			/* restore user's original umask */
X		NewEnv[2] = MakeTermcap(layer->shape.wlines, layer->shape.wchars);
X		sprintf (ebuf, "LAYER=%d", chan);
X		NewEnv[3] = ebuf;
X		execvpe (prog, args, NewEnv);
X		printf("%s: cannot exec %s: %s", myname, prog, sys_errlist[errno]);
X		exit (1);
X    }
X
X    return ( chan );
X
X} /* MakeWindow() */
X
Xstatic
Xexecvpe (prog, args, env)
Xchar *prog, **args, **env;
X{
X    register char *path, *p;
X    char buf[1024];
X    char *shargs[MAXARGS+1];
X    register i;
X	register eaccess = 0;
X
X    if (prog[0] == '/')
X		path = "";
X    else
X	if ((path = getenv ("PATH")) == 0)
X		path = DefaultPath;
X    do
X	  { p = buf;
X		while (*path && *path != ':')
X	    	*p++ = *path++;
X		if (p > buf)
X	    	*p++ = '/';
X		strcpy (p, prog);
X		if (*path)
X	    	++path;
X		execve (buf, args, env);
X		switch (errno)
X		{ case ENOEXEC:
X	    	shargs[0] = DefaultShell;
X	    	shargs[1] = buf;
X	    	for (i = 1; shargs[i+1] = args[i]; ++i);
X	    	execve (DefaultShell, shargs, env);
X	    	return;
X
X		  case EACCES:
X	    	eaccess = 1;
X	    	break;
X
X		  case ENOMEM: case E2BIG: case ETXTBSY:
X	    	return;
X
X		} /* end switch */
X
X    } while (*path);
X
X    if (eaccess)
X		errno = EACCES;
X
X} /* execvpe() */
X
X
X				/* BuildTitle() - create and install window title */
X
Xstatic void
XBuildTitle(chan, prog, args)
Xint			chan;						/* channel for title */
Xchar		*prog;						/* program being executed */
Xchar		**args;						/* arg list */
X{
X	int		i;							/* arg scan index */
X	char	buff[1024];					/* super huge title buffer */
X
X	/* skip any leading "/bin/" */
X	if (strncmp(prog, "/bin/", 5) == 0)
X		strcpy(buff, prog+5);			/* leave /bin off */
X	else
X		strcpy(buff, prog);				/* start with program name */
X
X	/* add all aguments but stop if option ("-") seen */
X	for (i=1; args[i] && args[i][0] != '-'; i++)
X	  {	strcat(buff, " ");				/* delimiter */
X		strcat(buff, args[i]);			/* add next parameter */
X	  }
X
X	SendTitle(chan, buff, strlen(buff)); /* set new window title */
X
X} /* BuildTitle() */
X
X
X#ifdef sequent
Xstatic
XOpenPTY ()
X{
X	char *m, *s;
X	register f;
X
X	f = getpseudotty (&s, &m);
X	strncpy (PtyName, m, sizeof (PtyName));
X	strncpy (TtyName, s, sizeof (TtyName));
X	ioctl (f, TIOCFLUSH, (char *)0);
X	return (f);
X}
X
X#else
X
Xstatic
XOpenPTY ()
X{
X    register char *p, *l, *d;
X    register i, f, tf;
X
X    strcpy (PtyName, PtyProto);
X    strcpy (TtyName, TtyProto);
X    for (p = PtyName, i = 0; *p != 'X'; ++p, ++i);
X    for (l = "qpr"; *p = *l; ++l)
X	  { for (d = "0123456789abcdef"; p[1] = *d; ++d)
X		  { if ((f = open (PtyName, O_RDWR)) != -1)
X			  { TtyName[i] = p[0];
X				TtyName[i+1] = p[1];
X				if ((tf = open (TtyName, O_RDWR)) != -1)
X				  { close (tf);
X		    		return f;
X				  }
X				close (f);
X	    	  }
X		  }
X      }
X
X    return -1;
X
X} /* OpenPTY() */
X#endif
X
Xstatic
XSetTTY (fd, mp)
Xstruct mode *mp;
X{
X    ioctl (fd, TIOCSETP, &mp->m_ttyb);
X    ioctl (fd, TIOCSETC, &mp->m_tchars);
X    ioctl (fd, TIOCSLTC, &mp->m_ltchars);
X    ioctl (fd, TIOCLSET, &mp->m_lmode);
X    ioctl (fd, TIOCSETD, &mp->m_ldisc);
X
X} /* SetTTY() */
X
Xstatic
XGetTTY (fd, mp)
Xstruct mode *mp;
X{
X    ioctl (fd, TIOCGETP, &mp->m_ttyb);
X    ioctl (fd, TIOCGETC, &mp->m_tchars);
X    ioctl (fd, TIOCGLTC, &mp->m_ltchars);
X    ioctl (fd, TIOCLGET, &mp->m_lmode);
X    ioctl (fd, TIOCGETD, &mp->m_ldisc);
X
X} /* GetTTY() */
X
Xstatic
XSetMode (op, np)
Xstruct mode *op, *np;
X{
X    *np = *op;
X#if 1
X	if (flowctl)
X	  {	np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
X		np->m_ttyb.sg_flags |= CBREAK | ANYP;
X	  }
X	else
X		np->m_ttyb.sg_flags = RAW | ANYP;
X#else
X    np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
X    np->m_ttyb.sg_flags |= CBREAK | ANYP;
X#endif
X    np->m_tchars.t_intrc = -1;
X    np->m_tchars.t_quitc = -1;
X    if (!flowctl)
X	  { np->m_tchars.t_startc = -1;
X		np->m_tchars.t_stopc = -1;
X      }
X    np->m_ltchars.t_suspc = -1;
X    np->m_ltchars.t_dsuspc = -1;
X    np->m_ltchars.t_flushc = -1;
X    np->m_ltchars.t_lnextc = -1;
X
X} /* SetMode() */
X
Xstatic char *
XGetTtyName ()
X{
X    int n;
X	char *p;
X
X    for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++);
X
X    if (!p || *p == '\0')
X		Msg (0, "layers must run on a tty.");
X
X    return ( p );
X
X} /* GetTtyName() */
X
X
Xstatic
XKill (pid, sig)
X{
X    if (pid != 0)
X		(void) kill (pid, sig);
X}
X
X			/* GetSockName() - set SockName; if LTY env return 1 else 0 */
Xstatic
XGetSockName ()
X{
X    register client;
X    static char buf[2*MAXSTR];
X
X    if (!mflag && (SockName = getenv ("LTY")) != 0 && *SockName != '\0')
X	  { client = 1;
X		setuid (getuid ());
X		setgid (getgid ());
X      }
X	else
X	  { sprintf (buf, "%s.%s", HostName, Filename (RealTtyName));
X		SockName = buf;
X		client = 0;
X      }
X    return client;
X
X} /* GetSockName() */
X
Xstatic
XMakeServerSocket ()
X{
X    register s;
X    struct sockaddr_un a;
X    char *p;
X
X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
X		Msg (errno, "socket");
X    a.sun_family = AF_UNIX;
X    strcpy (SockNamePtr, SockName);
X    strcpy (a.sun_path, SockPath);
X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1)
X	  { p = Filename (SockPath);
X		Msg (0, "You already have a layers running on %s.", p);
X		/*NOTREACHED*/
X      }
X	DO DEBUG("MakeServerSocket: unlink(SockPath)/bind()/chown/listen\n");
X    (void) unlink (SockPath);
X    if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
X		Msg (errno, "bind");
X    (void) chown (SockPath, getuid (), getgid ());
X    if (listen (s, 5) == -1)
X		Msg (errno, "listen");
X    return s;
X
X} /* MakeServerSocket() */
X
Xstatic
XMakeClientSocket (err)
X{
X    register s;
X    struct sockaddr_un a;
X
X    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
X		Msg (errno, "socket");
X    a.sun_family = AF_UNIX;
X    strcpy (SockNamePtr, SockName);
X    strcpy (a.sun_path, SockPath);
X    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
X	  { if (err)
X		  { Msg (errno, "connect: %s", SockPath); }
X		else
X		  { close (s);
X	    	return -1;
X		  }
X      }
X    return s;
X
X} /* MakeClientSocket() */
X
Xstatic
XSendCreateMsg (s, ac, av, lflag, shape)
Xchar **av;
Xstruct Shape *shape;
X{
X    struct msg m;
X    register char *p;
X    register len, n;
X	char	*pwd;					/* PWD environment string */
X
X	DO DEBUG("SendCreateMsg(%d, ac %d, lflag %d\n", s, ac, lflag);
X    m.type = MSG_CREATE;
X    p = m.m.create.line;
X    for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n)
X	  { len = strlen (*av) + 1;
X		if (p + len >= m.m.create.line+MAXLINE)
X	    	break;
X		strcpy (p, *av);
X		p += len;
X      }
X	DO DEBUG("  nargs %d, create line = '%s'\n", n, m.m.create.line);
X    m.m.create.nargs = n;
X    m.m.create.lflag = lflag;
X	m.m.create.shape = *shape;			/* pass window shape */
X
X	/* Since Suns can hang up on getwd() [damn their stupid networking]
X	** we try to get the current working directory first from the PWD
X	** environment variable.
X	*/
X	if ((pwd=getenv("PWD")) && strlen(pwd) < 1024)
X		(void) strcpy(m.m.create.dir, pwd);
X	else
X    if (getwd (m.m.create.dir) == 0)
X	  {	DO DEBUG("getwd() failed!!\n");
X		Msg (0, "%s", m.m.create.dir);
X	  }
X	DO DEBUG("  create.dir = '%s'\n", m.m.create.dir);
X
X    if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
X	  {	DO DEBUG("  write failed!!\n");
X		Msg (errno, "write");
X	  }
X	DO DEBUG("SendCreateMsg() done\n");
X
X} /* SendCreateMsg() */
X
X/*VARARGS1*/
Xstatic
XSendErrorMsg (fmt, p1, p2, p3, p4, p5, p6)
Xchar *fmt;
X{
X    register s;
X    struct msg m;
X
X    s = MakeClientSocket (1);
X    m.type = MSG_ERROR;
X    sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
X    (void) write (s, (char *)&m, sizeof (m));
X    close (s);
X    sleep (2);
X}
X
Xstatic
XReceiveMsg (s)
X{
X    register ns;
X    struct sockaddr_un a;
X    int left, len = sizeof (a);
X    struct msg m;
X    char *p;
X
X	DO DEBUG ("ReceiveMsg()\n");
X    if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1)
X	  { Msg (errno, "accept");
X		return;
X      }
X    p = (char *)&m;
X    left = sizeof (m);
X    while (left > 0 && (len = read (ns, p, left)) > 0)
X	  { p += len;
X		left -= len;
X      }
X    close (ns);
X    if (len == -1)
X		Msg (errno, "read");
X    if (left > 0)
X		return;
X    switch (m.type)
X	{ case MSG_CREATE:
X		DO DEBUG("MSG_CREATE:\n");
X	    ExecCreate (&m);
X		break;
X
X      case MSG_ERROR:
X		DO DEBUG("MSG_ERROR:\n");
X		Msg (0, "%s", m.m.message);
X		break;
X
X      default:
X		Msg (0, "Invalid message (type %d).", m.type);
X
X    } /* end switch */
X
X} /* ReceiveMsg() */
X
Xstatic
XExecCreate (mp)
Xstruct msg *mp;
X{
X    char *args[MAXARGS];
X    register n;
X    register char **pp = args, *p = mp->m.create.line;
X
X    for (n = mp->m.create.nargs; n > 0; --n)
X	  { *pp++ = p;
X		p += strlen (p) + 1;
X      }
X    *pp = 0;
X    n = MakeWindow (0, mp->m.create.line, args, mp->m.create.dir,
X						mp->m.create.lflag, &mp->m.create.shape);
X
X} /* ExecCreate() */
X
X#if 0
Xstatic
XReadRc (fn)
Xchar *fn;
X{
X    FILE *f;
X    register char *p, **pp, **ap;
X    register argc, num, c;
X    char buf[256];
X    char *args[MAXARGS];
X    int key;
X	struct Shape shape;						/* shape for new window */
X
X	ClearShape(&shape);						/* initialize shape */
X    ap = args;
X    if (access (fn, R_OK) == -1)
X		return;
X    if ((f = fopen (fn, "r")) == NULL)
X		return;
X    while (fgets (buf, 256, f) != NULL)
X	  { if (p = rindex (buf, '\n'))
X	    	*p = '\0';
X		if ((argc = Parse (fn, buf, ap)) == 0)
X	    	continue;
X		if (strcmp (ap[0], "escape") == 0)
X		  { p = ap[1];
X	    	if (argc < 2 || strlen (p) != 2)
X				Msg (0, "%s: two characters required after escape.", fn);
X	    	Esc = *p++;
X	    	MetaEsc = *p;
X            ktab[Esc].type = KEY_OTHER;
X		  }
X		else
X		if (strcmp (ap[0], "login") == 0)
X		  { loginflag = 1;
X		  }
X		else
X		if (strcmp (ap[0], "unlogin") == 0)
X		  { loginflag = 0; }
X		else
X		if (strcmp (ap[0], "nologin") == 0)
X		  { loginflag = 0; }
X		else
X		if (strcmp (ap[0], "chdir") == 0)
X	 	  { p = argc < 2 ? home : ap[1];
X	    	if (chdir (p) == -1)
X			Msg (errno, "%s", p);
X		  }
X		else
X		if (strcmp (ap[0], "mode") == 0)
X		  { if (argc != 2)
X			  { Msg (0, "%s: mode: one argument required.", fn); }
X			else
X			if (!IsNum (ap[1], 7))
X			  { Msg (0, "%s: mode: octal number expected.", fn); }
X			else
X				(void) sscanf (ap[1], "%o", &TtyMode);
X		  }
X		else
X		if (strcmp (ap[0], "bell") == 0)
X		  { if (argc != 2)
X			  { Msg (0, "%s: bell: one argument required.", fn); }
X			else
X			  { if ((BellString = malloc (strlen (ap[1]) + 1)) == 0)
X		    		Msg (0, "Out of memory.");
X				istrcpy (BellString, ap[1]);
X	    	  }
X		  }
X		else
X		if (strcmp (ap[0], "screen") == 0)
X		  { num = 0;
X	    	if (argc > 1 && IsNum (ap[1], 10))
X			  { num = atoi (ap[1]);
X				if (num < 0 || num > MAXWIN-1)
X		    		Msg (0, "%s: illegal screen number %d.", fn, num);
X				--argc; ++ap;
X	    	  }
X	    	if (argc < 2)
X			  { ap[1] = ShellProg; argc = 2; }
X	    	ap[argc] = 0;
X	    	(void) MakeWindow (0, ap[1], ap+1, (char *)0, loginflag, &shape);
X		  }
X		else
X		if (strcmp (ap[0], "bind") == 0)
X		  { p = ap[1];
X	    	if (argc < 2 || *p == '\0')
X				Msg (0, "%s: key expected after bind.", fn);
X	    	if (p[1] == '\0')
X			  { key = *p; }
X			else
X			if (p[0] == '^' && p[1] != '\0' && p[2] == '\0')
X			  { c = p[1];
X				if (isupper (c))
X		    		p[1] = tolower (c);    
X				key = Ctrl(c);
X	    	  }
X			else
X			if (IsNum (p, 7))
X			  { (void) sscanf (p, "%o", &key);
X	    	  }
X			else
X			  { Msg (0, "%s: bind: character, ^x, or octal number expected.", fn); }
X	    	ktab[key].lflag = loginflag;
X	    	if (argc < 3)
X			  { ktab[key].type = 0;
X	    	  }
X			else
X			  { for (pp = KeyNames; *pp; ++pp)
X		    	if (strcmp (ap[2], *pp) == 0) break;
X				if (*pp)
X				  { ktab[key].type = pp-KeyNames+1; }
X				else
X				  { ktab[key].type = KEY_CREATE;
X		    		ktab[key].args = SaveArgs (argc-2, ap+2);
X				  }
X	    	  }
X		  }
X		else
X			Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
X      }
X    (void) fclose (f);
X
X} /* ReadRc() */
X
Xstatic
XParse (fn, buf, args)
Xchar *fn, *buf, **args;
X{
X    register char *p, **ap;
X    register delim, argc;
X
X	p = buf;
X	ap = args;
X    argc = 0;
X    for (;;)
X	  { while (*p && (*p == ' ' || *p == '\t'))
X			++p;
X		if (*p == '\0' || *p == '#')
X	    	return argc;
X		if (argc > MAXARGS-1)
X	    	Msg (0, "%s: too many tokens.", fn);
X		delim = 0;
X		if (*p == '"' || *p == '\'')
X		  { delim = *p; *p = '\0'; ++p; }
X		++argc;
X		*ap = p; ++ap;
X		while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
X	    	++p;
X		if (*p == '\0')
X		  { if (delim)
X				Msg (0, "%s: Missing quote.", fn);
X	    	else
X				return argc;
X		  }
X		*p++ = '\0';
X      }
X
X} /* Parse() */
X
Xstatic char **
XSaveArgs (argc, argv)
Xregister argc;
Xregister char **argv;
X{
X    register char **ap, **pp;
X
X    if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
X		Msg (0, "Out of memory.");
X    while (argc--)
X	  { if ((*pp = malloc (strlen (*argv)+1)) == 0)
X	    	Msg (0, "Out of memory.");
X		strcpy (*pp, *argv);
X		++pp; ++argv;
X      }
X    *pp = 0;
X    return ap;
X
X} /* SaveArgs() */
X#endif
X
Xstatic
XMakeNewEnv ()
X{
X    register char **op, **np = NewEnv;
X    static char buf[MAXSTR];
X
X    if (strlen (SockName) > MAXSTR-5)
X		SockName = "?";
X    sprintf (buf, "LTY=%s", SockName);
X    *np++ = buf;
X    *np++ = Term;
X    np += 2;
X    for (op = environ; *op; ++op)
X	  { if (np == NewEnv + MAXARGS - 1)
X	    	break;
X		if (   !IsSymbol (*op, "TERM")
X			&& !IsSymbol (*op, "TERMCAP")
X			&& !IsSymbol (*op, "LTY")
X		   )
X	    	*np++ = *op;
X      }
X    *np = 0;
X
X} /* MakeNewEnv() */
X
Xstatic
XIsSymbol (e, s)
Xregister char *e, *s;
X{
X    register char *p;
X    register n;
X
X    for (p = e; *p && *p != '='; ++p);
X    if (*p)
X	  { *p = '\0';
X		n = strcmp (e, s);
X		*p = '=';
X		return n == 0;
X      }
X
X    return 0;
X
X} /* IsSymbol() */
X
X/*VARARGS2*/
XMsg (err, fmt, p1, p2, p3, p4, p5, p6)
Xchar *fmt;
X{
X    char buf[1024];
X    register char *p = buf;
X
X    sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
X    if (err)
X	  { p += strlen (p);
X		if (err > 0 && err < sys_nerr)
X	    	sprintf (p, ": %s", sys_errlist[err]);
X		else
X	    	sprintf (p, ": Error %d", err);
X      }
X    if (!Abortonmsg)
X	  { /* MakeStatus (buf, curr);*/
X		printf("%s\r\n", buf);
X	  } 
X	else
X	  { printf ("%s\r\n", buf);
X		exit (1);
X      }
X
X} /* Msg() */
X
Xstatic char *
XFilename (s)
Xchar *s;
X{
X    register char *p;
X
X    p = s + strlen (s) - 1;
X    while (p >= s && *p != '/')
X		--p;
X    return ++p;
X
X} /* Filename() */
X
Xstatic
XIsNum (s, base)
Xregister char *s;
Xregister base;
X{
X    for (base += '0'; *s; ++s)
X		if (*s < '0' || *s > base)
X	    	return 0;
X    return 1;
X
X} /* IsNum() */
X
X
Xstatic
XInitUtmp ()
X{
X    if ((utmpf = open (UtmpName, O_RDWR)) == -1)
X	  { if (errno != EACCES)
X	    	Msg (errno, UtmpName);
X		return;
X      }
X    utmp = 1;
X
X} /* InitUtmp() */
X
X
Xstatic int
XFindUtmp(name)
Xchar *name;
X{
X    register char *p;
X    register struct ttyent *tp;
X    register slot;
X
X	DO DEBUG("FindUtmp(%s)\n", name);
X	slot = 1;
X    if (!utmp)
X		return 0;
X    if (p = rindex (name, '/'))
X		++p;
X    else
X		p = name;
X    setttyent ();
X    while (   (tp = getttyent ()) != NULL
X		   && strcmp (p, tp->ty_name) != 0
X		  )
X		++slot;
X    if (tp == NULL)
X		return 0;
X
X	DO DEBUG(" slot %d\n", slot);
X    return slot;
X
X} /* FindUtmp() */
X
X
Xstatic int
XSetUtmp (name, mainlogin)
Xchar	*name;							/* tty name */
Xint		mainlogin;						/* this is primary login */
X{
X    register char *p;
X    register slot;
X    struct utmp u;
X
X	if ((slot=FindUtmp(name)) == 0)
X		return ( 0 );
X
X    if (p = rindex (name, '/'))
X		++p;
X    else
X		p = name;
X
X    strncpy (u.ut_line, p, 8);
X    strncpy (u.ut_name, LoginName, 8);
X#if 1
X	strncpy(u.ut_host,  Filename (RealTtyName), 16); /* host is real tty */
X#else
X    u.ut_host[0] = '\0';
X#endif
X	if (RealSlot && mainlogin)
X		u.ut_time = RealUtmp.ut_time;		/* use original login time */
X	else
X    	time (&u.ut_time);
X    (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
X    (void) write (utmpf, (char *)&u, sizeof (u));
X
X    return ( slot );
X
X} /* SetUtmp() */
X
Xstatic int
XReadUtmp(slot, entry)
Xint		slot;								/* slot to read */
Xstruct utmp	*entry;							/* entry to read into */
X{
X	int		cnt;							/* return count */
X
X	if (!utmp)
X		return;								/* no utmp access */
X
X	(void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
X	cnt =  read(utmpf, (char *)entry, sizeof(struct utmp));
X	DO DEBUG("ReadUtmp cnt %d, errno %d, line %.8s, name %.8s, host %.16s\n",
X		cnt, errno, entry->ut_line, entry->ut_name, entry->ut_host);
X
X	return ( cnt );
X
X} /* ReadUtmp() */
X
Xstatic void
XWriteUtmp(slot, entry)
Xint		slot;								/* slot to write */
Xstruct utmp	*entry;							/* entry to write from */
X{
X	int		cnt;							/* write return code */
X
X	if (!utmp)
X		return;								/* no utmp access */
X
X	(void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
X	cnt = write(utmpf, (char *)entry, sizeof(struct utmp));
X	DO DEBUG("WriteUtmp() slot %d cnt %d line %.8s name %.8s host %.16s\n",
X				slot, cnt, entry->ut_line, entry->ut_name, entry->ut_host);
X
X} /* WriteUtmp() */
X
Xstatic
XRemoveUtmp (slot)
X{
X    struct utmp u;
X
X    if (slot)
X	  { bzero ((char *)&u, sizeof (u));
X		(void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
X		(void) write (utmpf, (char *)&u, sizeof (u));
X      }
X
X} /* RemoveUtmp() */
X
X#ifndef GETTTYENT
X
Xstatic
Xsetttyent ()
X{
X    struct stat s;
X    register f;
X    register char *p, *ep;
X
X    if (ttnext)
X	  { ttnext = tt;
X		return;
X      }
X    if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
X		Msg (errno, ttys);
X    if ((tt = malloc (s.st_size + 1)) == 0)
X		Msg (0, "Out of memory.");
X    if (read (f, tt, s.st_size) != s.st_size)
X		Msg (errno, ttys);
X    close (f);
X    for (p = tt, ep = p + s.st_size; p < ep; ++p)
X		if (*p == '\n')
X			*p = '\0';
X    *p = '\0';
X    ttnext = tt;
X
X} /* setttyent() */
X
Xstatic struct ttyent *
Xgetttyent ()
X{
X    static struct ttyent t;
X
X    if (*ttnext == '\0')
X		return NULL;
X    t.ty_name = ttnext + 2;
X    ttnext += strlen (ttnext) + 1;
X    return &t;
X
X} /* getttyend() */
X
X#endif
X
X
X
X					/* FinitTerm() - reset vt100 terminal */
Xstatic void
XFinitTerm ()
X{
X	/* print out termcap 'is' string to reset terminal */
X#if 0
X	/* This string sets scroll region 1-24 and puts cursor at bottom line */
X	printf("\033[1;24r\033[24;1H");
X#endif
X	fflush(stdout);
X}
X
Xstatic void
XAddCap (s)
Xchar *s;
X{
X    register n;
X
X    if (tcLineLen + (n = strlen (s)) > 55)
X	  { strcat (Termcap, "\\\n\t:");
X		tcLineLen = 0;
X      }
X    strcat (Termcap, s);
X    tcLineLen += n;
X}
X
Xstatic char *
XMakeTermcap(lines, chars)
Xint		lines;							/* default window lines */
Xint		chars;							/* default window chars */
X{
X    char buf[1024];
X    register char **pp, *p;
X
X    strcpy(Termcap, TermcapConst1);		/* start TERMCAP build */
X	strcat(Termcap, UserTerm);			/* fill in User's terminal type */
X	strcat(Termcap, TermcapConst3);		/* finish our own definition */
X
X	if (lines <= 0 || lines > 200)
X		lines = rows;					/* force default if none or invalid */
X	if (chars <= 0 || chars > 300)
X		chars = cols;					/* force default if none or invalid */
X
X    sprintf(buf, "li#%d:co#%d:", lines, chars);
X    AddCap(buf);
X
X    return ( Termcap );
X
X} /* MakeTermcap() */
X
X
X				/* DEBUG() - dump output routine */
X
Xvoid
XDEBUG(format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
Xchar		*format;
Xint			arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8;
X{
X	fprintf(stderr, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
X}
SHAR_EOF
if test 56819 -ne "`wc -c < 'layers.c'`"
then
	echo shar: error transmitting "'layers.c'" '(should have been 56819 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
--- end of part 3 ---
Technical Details
Message-ID: <1990Jun2.080116.19047@dhw68k.cts.com>
Path: arc1-ca.pugleaf.net!wuarchive!swbatl!texbell!cs.utexas.edu!sdd.hp.com!elroy.jpl.nasa.gov!zardoz.cpd.com!dhw68k!macintosh
References: <1990Jun1.070105.1489@dhw68k.cts.com> <1990Jun1.190100.8893@dhw68k.cts.com>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!wuarchive!swbatl!texbell!cs.utexas.edu!sdd.hp.com!elroy.jpl.nasa.gov!zardoz.cpd.com!dhw68k!macintosh
From: werner@rascal.ics.utexas.edu (Werner Uhrig)
Newsgroups: comp.sources.mac
Subject: MacLayers 1.00 (part 3 of 6)
Message-ID: <1990Jun2.080116.19047@dhw68k.cts.com>
Date: 2 Jun 90 08:00:09 GMT
References: <1990Jun1.070105.1489@dhw68k.cts.com> <1990Jun1.190100.8893@dhw68k.cts.com>
Reply-To: layers@rascal.ics.utexas.edu
Organization: U. Texas CS Dept., Austin, Texas
Lines: 2242
Approved: bytebug@dhw68k.cts.com (Roger L. Long)