Date: Mon, 19 Mar 1990 6:34:35 CST
From: Werner Uhrig <werner@rascal.ics.utexas.edu>
Subject: here comes MacLayers_1.0_UNIX-files_shar

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by rascal!werner on Mon Mar 19 04:58:04 CST 1990
# Contents:  README layers.1 layers.c layers.h layersize.c layertitle.c
#	macbput.1 macbput.c makefile protocol.c
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'
                             Layers 1.0 Release

                           ***    CONTENTS     ***

The following files are provided with the MacLayers package:

  README            -  this file
  layers.1          -  the layers command manual page
  makefile          -  the layers makefile
  layers.h          -  Unix source for the layers command
  layers.c          -                ditto
  protocol.c        -                ditto
  layersize.c       -  Unix source for the layersize command
  layertitle.c      -  Unix source for the layertitle command
  MacLayers.sit.Hqx -  the MacLayers Application download file
  MacLayers.doc     -  the MacLayers User's Manual
  macbput.c         -  a downloading utility
  macbput.1         -  the macbput command manual page

The MacLayers.sit.Hqx file is in hexbin format. After downloading it must be
processed with a Stuffit compatible utility to produce the MacLayers 
application.

Macbput is a public domain download utility for downloading macbinary files.
Macbinary files allow downloading to correctly set the Macintosh file creation 
date and file last used date. The version with this package has an important 
bug fixed.


                         ***  SUPPORTED SYSTEMS  ***

MacLayers has been successfully installed on the following systems:

   SunOS 4.0.3
   SunOS 3.2
   Sequent DYNIX 3.0.12 (#DEFINE SEQUENT)
   Sequent DYNIX 3.0.4  (#DEFINE SEQUENT)

MacLayers has failed to install on these systems:

   AIX 2.2.1 on IBM RT        Unsupported ioctls


                           ***   INSTALLATION   ***

There are two ways to install the layers system:

    make install

 or
    make installnopriv

The default is to install the 'layers' command set-uid which allows it to 
update /etc/utmp and perform chown/chmod on its allocated pseudo-ttys. 
This enables commands like 'who' to know about the presence of layers users 
and commands like 'write' and 'talk' to communicate with them. This is not 
a mandatory requirement however and the layers system will otherwise function.

For set-uid installers (especially Sequent users) there are a few customizing 
parameters which may be of interest at the front of layers.c.

                              *end of document*
@//E*O*F README//
chmod u=rw,g=rw,o=rw README
 
echo x - layers.1
sed 's/^@//' > "layers.1" <<'@//E*O*F layers.1//'
@.TH LAYERS l  "17 March 1990"
@.SH NAME
@.PP
layers, layertitle \- Protocol for MacLayers Multiwindow Terminal Emulator for Unix
@.SH SYNOPSIS
layers                - start window protocol
@.LP
layers [-l] [command] - create new window
@.LP
layertitle string     - retitle current layers window
@.IX  "layers" "start layers protocol, create new window"
@.IX  "layertitle" "rename current layers window"
@.SH DESCRIPTION
@.PP
MacLayers provides multi-window capability for a Macintosh (greater than 128K)
connected to a host UNIX(TM) system with sockets support. Each window may be 
associated with a shell, login to a different host, or an individual 
command. Complete facilities are available for controlling the window 
and the associated host processes attached thereto.
@.PP
To use MacLayers, you must have the MacLayers vt-100 terminal emulation
program on a Macintosh and the layers protocol program installed on your
Unix host.
@.SH MACLAYERS OPERATION
@.PP
The Maclayers Application on the Mac starts up as a garden variety 
host-to-terminal vt-100 emulator. (As such you can run it with any
host, not just a Unix machine.) Baud rate and other configurations are set 
by selections in the Control Menu.
@.PP
Enter the 
@.I layers
command to the Unix host using no options or parameters
to start the layers protocol.  The initial terminal window will be closed
and replaced with a layers protocol window.  A shell is run in this
window, either /bin/sh or the shell indicated by your $SHELL variable.
@.PP
You can start a new shell layer by picking "New" on the Layers menu. You can
also start a new layer window by issuing the
@.I layers 
command to a
shell layer window. If you use no operands then the new layer window
will be a shell. However, you can specify any command you wish by
simply adding it as a parameter.  Examples: "layers vi testfile.c",
"layers telnet earth."
@.PP
If you are specifying a shell then you can also elect to have it be a 
login shell by adding a -l option. This allows broadcast/write/talk 
capabilities for that window. The initial layer window shell defaults
to a login shell.
@.PP
When a layer process group terminates its window is automatically closed.
MacLayers exits layers mode when the last (or only) layer window is closed.
You may also use the Layers Menu "Shutdown" to terminate layers mode.
You cannot quit the MacLayers application while in layers mode but must 
Shutdown the multi-window mode first.
@.PP
You can abort host 
@.I layers
by using the Control Menu "Abort Host Layers" 
item which is always available. This may be necessary if your Mac 
loses contact with the host and you restart the MacLayers application at
which time the host would still be in multi-window layers mode while
the application would not. If the MacLayers application terminates due
to a non-recoverable problem it will always issue an order to terminate
layers mode on the host before returning to the Finder.
@.SH XMODEM DOWNLOADING
@.PP
MacLayers has a download facility for downloading XMODEM MacTerminal
('macput' command) and MacBinary ('macbput' command) files. Straight 
vanilla XMODEM is not supported.
Only one window can be doing a XMODEM download at any one time.
Downloading does not effect any other MacLayers operations so you can
freely use any other windows or applications (with MultiFinder) while 
a download is in progress. Remember though that the topmost window 
receives the highest priority data transfer from the host. So for the 
fastest downloading keep the XMODEM layer window the active window.
@.SH FILES
@.PP
 /usr/tmp/layers/<login>       Directory created by 
@.I layers
 /usr/tmp/layers/<login>/host.ttySocket Created by 
@.I layers
@.SH AUTHOR
Dave Trissel
@.SH BUGS AND CAVEATS
@.PP
 *) The shell TERM variable must have the same value in your 
layer shells as it does when you initially start 
@.I layers
up.
@.PP
 *) If you set the BSD shell TERMCAP variable then that variable must
be set in your .login file. It may not be changed to something
different in .cshrc.
@.PP
 *) The 
@.I layers
command will not properly work when being issued from a
remote login into the same machine which is already running the
initial 
@.I layers
startup command.
@.PP
 *) There is no file upload facility.
@.PP
 *) MacLayers will access the disk less often if you have RAM Cache
enabled on the Macintosh.
@.PP
 *)Layers must be installed as set-uid with owner root in order
to  be  able to correctly change the owner of the tty device
file for  each  window. Special  permission  may  also  be
required to write the file "/etc/utmp".
@.SH SEE ALSO
@.PP
The MacLayers program is
completely described in the manual that accompanies it.
@.PP
Manual page courtesy of Peter Newton.
@.PP
UNIX(TM) is a registered trademark of American Telephone and Telegraph.
Macintosh is a trademark of McIntosh Laboratories and is licensed to Apple 
Computer.
@//E*O*F layers.1//
chmod u=rw,g=rw,o=rw layers.1
 
echo x - layers.c
sed 's/^@//' > "layers.c" <<'@//E*O*F layers.c//'
/********					Layers.c
*********
*********	Layers - MacLayers Multiwindow BSD Socket Driver
*********
*********          Dave Trissel oakhill!davet
*********
********* The sockets handling portion of this control module is based 
********* upon 'screen' by Oliver Laumann whose copyright remains below. 
********* The rest is
 *
 *             Copyright (C) 1989 by David W. Trissel
 *
 *              Not derived from licensed software.
 *
 * Permission is granted to freely use, copy, modify, and redistribute
 * this software, provided that no attempt is made to gain profit from it,
 * the author is not construed to be liable for any results of using the
 * software, alterations are clearly marked as such, and this notice is
 * not modified.
 *
 */

static char LayersVersion[] = "layers 1.00 17-Mar-1990";

/* Layers Changes:

	Version .92 22-Mar-1989

		Original Distributed version

	Version .93 31-Mar-1989

		Deleted dl and al termcap entries since they didn't help any
		(al was redundant with sc (scroll) so should never have been created)

		SIGINT no longer causes us to quit (left debugging code in by mistake)

		Layer #1 is always logged in and takes over as user's login console
		(Real tty disconnected from /etc/utmp file while layers is running)

	Version .93b 05-May-1989

		Try getenv("PWD") before getwd() so Sun networking won't hang us up

    Version .93n 07-Jan-1990

        Reset TTY back to normal if initial link handshake fails.

    Version 1.00b 22-Jan-1990

        Corrected problem of layers forcing all umasks to 000.

    Version 1.00  17-Mar-1990

        First public release.
*/


/* Copyright (c) 1987,1988 Oliver Laumann, Technical University of Berlin.
 * Not derived from licensed software.
 *
 * Permission is granted to freely use, copy, modify, and redistribute
 * this software, provided that no attempt is made to gain profit from it,
 * the author is not construed to be liable for any results of using the
 * software, alterations are clearly marked as such, and this notice is
 * not modified.
 *
 *	Modified by Patrick Wolfe (pat@kai.com, kailand!pat)
 *	Do whatever you want with (my modifications of) this program, but
 *	don't claim you wrote them, don't try to make money from them, and
 *	don't remove this notice.
 */

/*
 *	Beginning of User Configuration Section
 */

/*
 * SEQUENT   -- your host system is Sequent. This changes a setvbuf()
 *              call to a setlinebuf(). [Suggested by Peter Newton 
 *              <newton@cs.utexas.edu>]
 *
 */
#undef SEQUENT


/*
 * GETTTYENT -- your system has the new format /etc/ttys (like 4.3 BSD)
 *              and the getttyent(3) library functions.
 *
 */
#undef GETTTYENT


/*
 * LOGINDEFAULT -- if set to 1 (one), windows will login (add entries to
 *                 /etc/utmp) by default.  Set to 0 if you don't want this.
 *                 (Also see USERLIMIT below). [NOTE: current code always
 *                 logs layer #1 only unless -l option used on 'layers'
 *                 command.]
 */
#define LOGINDEFAULT 0

/*
 * USERLIMIT  --   count all non-null entries in /etc/utmp before adding a 
 *				   new entry. Some machine manufacturers (incorrectly) count 
 *				   logins by counting non-null entries in /etc/utmp (instead 
 *				   of counting non-null entries with no hostname and not on 
 *				   a pseudo tty). Sequent does this, so you might reach your 
 *				   limited user license early.
 */
#define USRLIMIT 32

/*
 * SOCKDIR      -- If defined, this directory is where layers sockets will be 
 *				   placed, (actually in a subdirectory by the user's loginid).
 *				   This is neccessary because NFS doesn't support socket 
 *				   operations, and many people's homes are on NFS mounted 
 *				   partitions.  Layers will create this directory if it needs 
 *				   to.
 */
#define SOCKDIR "/tmp/layers"	/* NFS doesn't support named sockets */

/*
 *	End of User Configuration Section
 */

#include <stdio.h>
#include <sgtty.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <utmp.h>
#include <pwd.h>
#include <nlist.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/ioctl.h>

#include "layers.h"

#ifdef GETTTYENT
#include <ttyent.h>
#else
static struct ttyent
  { char *ty_name;
  } *getttyent();
static char *tt, *ttnext;
static char ttys[] = "/etc/ttys";
#endif

#ifndef FSCALE
#define FSCALE 1000.0		/* Sequent doesn't define FSCALE...grrrr */
#endif

#ifdef  USRLIMIT
struct utmp utmpbuf;
int UserCount;
#endif

#define Ctrl(c) ((c)&037)

/* C library items */
extern char **environ;
extern errno;
extern sys_nerr;
extern char *sys_errlist[];
extern char *index(), *rindex(), *malloc(), *getenv();
extern char *getlogin(), *ttyname();

/* Local items */
static void FAbort(), SigHup(), SigChld(), AddCap(), FinitTerm();
static char  *MakeTermcap(), *Filename(), **SaveArgs(), *GetTtyName();
static void	InitWorld(), ClearShape(), BuildTitle(), KillPG();
static void SetWindowSize(), WriteUtmp();
static int	ReadUtmp(), FindUtmp(), SetUtmp();

static int	loginflag = -1;
static char PtyName[32], TtyName[32];
static char *ShellProg;
static char *ShellArgs[2];
static inlen;
static ESCseen;
static GotSignal;
static char DefaultShell[] = "/bin/sh";
static char DefaultPath[] = ":/usr/ucb:/bin:/usr/bin";
static char PtyProto[] = "/dev/ptyXY";
static char TtyProto[] = "/dev/ttyXY";
static int TtyMode = 0622;
static struct stat RealTtyStat;					/* Real tty stat */
static char RealTtyName[32] = "";				/* Real tty name */
static int	RealSlot = 0;						/* Real tty logon slot */
static struct utmp	RealUtmp;					/* Real tty logon utmp entry */
static int	RealTtyMode = 0;					/* Real tty mode */
static int	Oumask;								/* Original user's umask */
static char SockPath[512];
#ifdef SOCKDIR
static char SockDir[] = SOCKDIR;
#else
static char SockDir[] = ".layers";
#endif
static char *SockNamePtr, *SockName;
static ServerSocket;
static char *NewEnv[MAXARGS];
static char Esc = Ctrl('a');
static char MetaEsc = 'a';
static char *home;
static Abortonmsg;
static utmp, utmpf;
static char UtmpName[] = "/etc/utmp";
static char *LoginName;
static mflag, nflag, fflag, rflag;
static char HostName[MAXSTR];
static char *myname;
static DevTty;

static struct mode {
    struct sgttyb m_ttyb;
    struct tchars m_tchars;
    struct ltchars m_ltchars;
    int m_ldisc;
    int m_lmode;
} OldMode, NewMode;

#define MSG_CREATE    0
#define MSG_ERROR     1

struct msg
  {
    int type;
    union
	  { struct
		  {	int	lflag;				/* login flag */
			struct Shape shape;		/* window shape */
	    	int nargs;
	    	char line[MAXLINE];
	    	char dir[1024];
		  } create;
		char message[MAXLINE];
      } m;
  };


			/* dynamic keyboard input buffer definition */
struct Kbuff
  {	struct Kbuff * next;			/* next buffer in chain (or NULL) */
	int			size;				/* size of data in this buffer */
	int			offset;				/* start of first data in buffer */
	unsigned char text[IOSIZE];		/* text buffer itself */
  };

			/* World layer definition */
struct Layer {
	int		chan;					/* channel represented by this layer */
    int		allocated;				/* layer allocated */
    int		ptyfd;					/* psuedo tty */
	int		ptymask;				/* mask for pty descriptor */
	int		lpid;					/* layer head process ID */
	int		slot;					/* utmp slot number */
	struct Kbuff *kbuff;			/* keyboard input buffers */
	struct Shape shape;				/* Shape structure to/from MacLayers */
    char	cmd[MAXSTR];			/* command to execute */
    char	tty[MAXSTR];			/* psuedo tty ID */
	};

static struct Layer World[MAXPCHAN]; /* all layer structures */

static int rows = 24;				/* default window height in lines */
static int cols = 80;				/* default window width in chars */
static char Termcap[1024];
static char Term[MAXSTR] = "TERM=";	/* window's terminal type */
static char	*UserTerm;				/* terminal ID we are mimmicing */
static int flowctl;
static tcLineLen = 100;

/* co#80 and li$24 added dynamically for proper window size */
static char TermcapConst1[] = "TERMCAP=SC|";
static char TermcapConst3[] = "|MacLayers virtual terminal|\\\n\
	:cr=^M:do=^J:nl=^J:bl=^G:cl=\\E[;H\\E[2J:\\\n\
	:le=^H:bs:am:cm=\\E[%i%d;%dH:nd=\\E[C:up=\\E[A:\\\n\
	:ce=\\E[K:cd=\\E[J:so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\\n\
	:md=\\E[1m:mr=\\E[7m:mb=\\E[5m:me=\\E[m:is=\\E[1;24r\\E[24;1H:\\\n\
	:rf=/usr/lib/tabset/vt100:\\\n\
	:rs=\\E>\\E[?3l\\E[?4l\\E[?5l\\E[?7h\\E[?8h:ks=\\E[?1h\\E=:ke=\\E[?1l\\E>:\\\n\
	:ku=\\EOA:kd=\\EOB:kr=\\EOC:kl=\\EOD:kb=^H:\\\n\
	:ho=\\E[H:k1=\\EOP:k2=\\EOQ:k3=\\EOR:k4=\\EOS:ta=^I:pt:sr=\\EM:vt#3:xn:\\\n\
	:sc=\\E7:rc=\\E8:cs=\\E[%i%d;%dr:\\\n\
	:dc=\\ED:ic=\\EI:";
/* NOTE: the above two cababilities are beyond vt100 - unique to MacLayers */

int		Dflag;							/* debug dump flag */

							/* main() */
main(ac, av)
char **av;
{
    register n;
    register len;
    register struct Layer *layer;
    char	*ap;
    struct passwd *ppp;
    int		s;
	int		r;							/* read fd test bits */
	int		w;							/* write fd test bits */
	int		stall;						/* stall flag for priority channel */
	int		fderr;						/* error fd test bits */
    struct timeval tv;
	struct Shape shape;					/* window shape */
    time_t	now;
    char	buf[IOSIZE];
    char	rc[256];
    struct stat st;
	struct Kbuff *kbptr;				/* input keyboard buffer pointer */

	Abortonmsg = 1;						/* quit if message generated */
	ClearShape(&shape);					/* initialize shape structure */
	myname = (ac == 0) ? "layers" : av[0];
	InitWorld();						/* clear World array structures */

    while (ac > 0)
	  {	ap = *++av;
		if (--ac > 0 && *ap == '-')
		  {	switch (ap[1])
			{ case 'l':		/* login this command line */
				loginflag = 1;
				break;

			  case 'd':		/* dump debugging flag */
				Dflag = 1;
				(void) freopen("layers.dump", "a", stderr); /* append mode */
#ifdef SEQUENT
				setlinebuf(stderr);
#else
				setvbuf(stderr, NULL, _IOLBF, 0);
#endif
				break;

	    	  case 'u':		/* do not login this command line */
				loginflag = 0;
				break;

		      case 'm':		/* force this to be master and not a client */
				mflag = 1;
				break;

	    	  case 'n':		/* no flow control */
				nflag = 1;
				break;

	    	  case 'f':		/* flow control on */
				fflag = 1;
				break;

			  case 'v':		/* do nothing but issue layers version */
				printf("%s\n", LayersVersion);
				exit(0);

	    	  default:
	    	help:
				Msg (0,"Use: %s [-f] [-l | -u] [-m] [-n] [cmd args]\n", myname);

	    	} /* end switch on '-' option */

	    } /* end if '-' */

		else
			break;

      } /* end while parameters */

    if (nflag && fflag)
		Msg (0, "-f and -n are conflicting options.");

    if ((ShellProg = getenv ("SHELL")) == 0)
		ShellProg = DefaultShell;
	DO DEBUG("ShellProg %s\n", ShellProg);

	/* we mimmic the user's $TERM ID */
	if ((UserTerm = getenv("TERM")) == 0)
		UserTerm = "layers";				/* use "layers" if none */
	(void) strcat(Term, UserTerm);
	DO DEBUG("%s\n", Term);

    ShellArgs[0] = ShellProg;
    if (ac == 0)
	  { ac = 1;
		av = ShellArgs;
		shape.wattr |= Wa_shell;			/* indicate a shell window */
      }

    if ((home = getenv ("HOME")) == 0)
		Msg (0, "$HOME is undefined.");
	DO DEBUG("home %s\n", home);

    if ((LoginName = getlogin ()) == 0 || LoginName[0] == '\0')
	  { if ((ppp = getpwuid (getuid ())) == 0)
	   		return;
		LoginName = ppp->pw_name;
      }
	DO DEBUG("LoginName %s\n", LoginName);

    if ((Oumask=umask(0)) == -1)
		Msg (errno, "Cannot change umask to zero");
	DO DEBUG("Original umask o%o\n", Oumask);

#ifdef SOCKDIR
    if (stat (SOCKDIR, &st) == -1)
	  {	if (errno == ENOENT)
		  { if (mkdir (SOCKDIR, 0777) == -1)
				Msg (errno, "Cannot make directory %s", SOCKDIR);
	    	(void) chown (SOCKDIR, 0, 0);
		  }
		else
			Msg (errno, "Cannot get status of %s", SOCKDIR);
      }
	else
	  { if ((st.st_mode & S_IFMT) != S_IFDIR)
	    	Msg (0, "%s is not a directory.", SOCKDIR);
		if ((st.st_mode & 0777) != 0777)
	    	Msg (0, "Directory %s must have mode 777.", SOCKDIR);
      }
    sprintf (SockPath, "%s/%s", SockDir, LoginName);
#else
    sprintf (SockPath, "%s/%s", home, SockDir);
#endif
	DO DEBUG("SockPath %s\n", SockPath);

    if (stat (SockPath, &st) == -1)
	  { if (errno == ENOENT)
		  { if (mkdir (SockPath, 0700) == -1)
				Msg (errno, "Cannot make directory %s", SockPath);
	    	(void) chown (SockPath, getuid (), getgid ());
			DO DEBUG("SockPath directory made\n");
		  }
		else
			Msg (errno, "Cannot get status of %s", SockPath);
      }
	else
	  { if ((st.st_mode & S_IFMT) != S_IFDIR)
	   		Msg (0, "%s is not a directory.", SockPath);
		if ((st.st_mode & 0777) != 0700)
	    	Msg (0, "Directory %s must have mode 700.", SockPath);
		if (st.st_uid != getuid ())
	    	Msg (0, "You are not the owner of %s.", SockPath);
      }

	(void) strcpy(RealTtyName, GetTtyName());		/* real tty name */
	if (stat(RealTtyName, &RealTtyStat) == -1)		/* get current mode */
		Msg(errno, "Cannot get status of %s", RealTtyName);
	DO DEBUG("Mode of %s is %#o\n", RealTtyName, RealTtyStat.st_mode);
	RealTtyMode = RealTtyStat.st_mode;		/* save mode for later restore */

    (void) gethostname (HostName, MAXSTR);
    HostName[MAXSTR-1] = '\0';
	DO DEBUG("HostName %s\n", HostName);

    if (ap = index (HostName, '.'))
		*ap = '\0';
	if (ap)
		DO DEBUG("*ap %s\n", *ap);

    strcat (SockPath, "/");
    SockNamePtr = SockPath + strlen (SockPath);

	/* if we are a client send create message to startup window and exit */
    if (GetSockName ())
	  {	DO DEBUG("GetSockName says that we are client\n");
		DO DEBUG("SockName '%s'\n", SockName);
		s = MakeClientSocket (1);
		DO DEBUG("Client socket is %d\n", s);
		DO DEBUG("SendCreateMsg()\n");
		SendCreateMsg (s, ac, av, loginflag, &shape);
		close (s);
		DO DEBUG("after SendCreateMsg(), now exit(0)\n");
		exit (0);
      }

	/* we are the server */
	DO DEBUG("SockName '%s'\n", SockName);
	DO DEBUG("We are server\n");
    if ((DevTty = open ("/dev/tty", O_RDWR|O_NDELAY)) == -1)
		Msg (errno, "/dev/tty");
	DO DEBUG("opened /dev/tty fd %d\n", DevTty);

    ServerSocket = MakeServerSocket ();
	DO DEBUG("ServerSocket %d\n", ServerSocket);
    s = ServerSocket;

    if (fflag)
		flowctl = 1;
    else
	if (nflag)
		flowctl = 0;

    if (loginflag == -1)
        loginflag = LOGINDEFAULT;

    MakeNewEnv ();
    InitUtmp ();
	RealSlot = FindUtmp(RealTtyName);	/* find current logon slot */
	if (RealSlot)
	  {	if (ReadUtmp(RealSlot, &RealUtmp) > 0)	/* read real login utmp */
			RemoveUtmp(RealSlot);		/* remove original logon slot */
		else
			RealSlot = 0;				/* something's wrong */
	  }

    signal (SIGHUP, SigHup);
    signal (SIGINT, SIG_IGN);			/* we should never see this */
    signal (SIGQUIT, FAbort);			/* quit layers on these 2 signals */
    signal (SIGTERM, FAbort);
    signal (SIGTTIN, SIG_IGN);
    signal (SIGTTOU, SIG_IGN);
	signal (SIGALRM, SIG_IGN);			/* alarm clock used by protocol.c */

    GetTTY (0, &OldMode);
    SetMode (&OldMode, &NewMode);
    SetTTY (0, &NewMode);

	if (Initlink() == 0)
	  {	SetTTY(0, &OldMode);			/* revert tty back */
	    Msg (0, "\n\n  You are not running under MacLayers.");
	  }
		
    sprintf (rc, "%.*s/.layersrc", 245, home);
#if 0 /* NOT YET SUPPORTED */
	/* if no window list start up a default shell window */
    if (ReadRc(rc) == 0)			
#endif
      {	n = MakeWindow (0, *av, av, (char *)0, loginflag, &shape);
    	if (n == -1)
	  	  { SetTTY (0, &OldMode);
			FQuit(1);
			/* NOT REACHED */
      	  }
	  }

	(void) chmod(RealTtyName, 0600);		/* lock out broadcasts */
    Abortonmsg = 0;							/* don't abort on msg from now on */
    signal (SIGCHLD, SigChld);
    tv.tv_usec = 0;
	tv.tv_sec = 0;							/* default no timer wanted */

	/* client/Maclayers processing loop */

	/* poll .20 secs for new startups */
#define WUSSTARTUP	200000				
	/* stall nonpriority windows .5 seconds when top window I/O active */
#define WUSSTALL	500000

	stall = 0;								/* startout no stalled channels */
	fderr = 0;								/* startout no error fd's */

    while (1)
	  {	int			priochan;				/* the top window channel */

		priochan = TopChannel();			/* find highest priority channel */

		/* check for I/O on all available I/O descriptors */
		r = 1<<0;							/* always read MacLayers stream */
		r |= 1<<s;							/* always read server socket */
		w = 0;								/* initalize to no write tests */
		tv.tv_usec = 0;						/* default no startup poll */

		/* for all active layers set read and write bits as appropriate */
		if (stall)
			stall = 1;						/* start counting output layers */
		for (n=0; n<MAXPCHAN; n++)
		 if ((layer = &World[n])->allocated) /* if layer exists ... */
		  {	/* if not yet started or has just terminated ... */
			if (layer->ptymask & fderr)
				tv.tv_usec = WUSSTARTUP;	/* don't spinloop but wait-a-bit */
			else
		  	  {	if (layer->kbuff && layer->kbuff->size)
					/* keyboard input for layer */
					w |= layer->ptymask;	/* try write to it */
				/* read layer output unless we're being nice to top window */
				if (!stall)
					r |= layer->ptymask;	/* read output from layer */
				else
				if (layer->chan == priochan)
					r |= layer->ptymask;	/* read top priority output */
				else
					stall++;				/* indicate something to stall */
			   }
		    }

		if (stall > 1)
			if (!tv.tv_usec)
				tv.tv_usec = WUSSTALL;		/* set stall timout */

		/* process signals before trying select */
		if (GotSignal)
		  { SigHandler ();
	    	continue;
		  }

		DO DEBUG("Select(r %x, w %x, fderr %x, stall %d, prio %d, us %d)\n",
					r, w, fderr, stall, priochan, tv.tv_usec);

		switch ( select(32, &r, &w, NULL, tv.tv_usec ? &tv : NULL) )
		{ case -1:
			/* errno has report */
			if (errno == EINTR)				/* signal delivered or timout */
			  { errno = 0;
				tv.tv_usec = 0;				/* clear timer wait value */
				fderr = 0;					/* turn off error stall */
				stall = 0;					/* turn off output priority stall */
				DO DEBUG("select errno EINTR\n");
				continue;					/* re-loop */
	    	  }
	    	Abortonmsg = 1;
			DO DEBUG("select errno %d\n", errno);
	    	Msg (errno, "select");
	    	/*NOTREACHED*/

		  case 0:
			/* timeout reached */
			tv.tv_usec = 0;					/* clear timer wait value */
			stall = 0;						/* turn off stall */
			fderr = 0;						/* turn off error stall */
			continue;						/* re-loop */

		  default:
			/* a channel has read/write status pending */
			break;
		}

		DO DEBUG("after select r %x w %x\n", r, w);

		/* handle any signal arriving up during select wait */
		if (GotSignal)
		  { SigHandler ();
	    	continue;
		  }

		/* if server socket has command process that now */
		if (r & 1 << s)
	      {	ReceiveMsg(s);					/* process client control packet */
			continue;						/* status may have changed */
		  }

		/* next process input stream from MacLayers */
		if (r & 1 << 0)
		  { ProcessStreamin();				/* key input and control packets */
			continue;						/* status may have changed */
		  }

		/* process keyboard input first so output doesn't hold up
		** keyboard echo and break/interrupt processing
		*/
		priochan = TopChannel();			/* find top priority channel */
		stall = 0;							/* assume no stall needed */
		for (n=0; n<MAXPCHAN; n++)
		  if ((layer = &World[n])->allocated)
			if (w & layer->ptymask)
				while ((kbptr=layer->kbuff)->size)
				  {	/* pass as much keyboard as possible */
					if (layer->chan == priochan)
						stall = 1;			/* stall lower priority channels */
					len = write(layer->ptyfd, &kbptr->text[kbptr->offset],
						 		kbptr->size);
					DO DEBUG("keyin len %d to chan %d\n", len, layer->chan);
					if (len <= 0)			/* if no data accepted ... */
					  {	if (errno == EIO)	/* if I/O error indicated ... */
							fderr |= layer->ptymask; /* wait-a-bit on this */
						errno = 0;			/* clear errno */
						break;				/* try again later */
					  }
					/* some of buffer accepted */
					kbptr->size -= len;		/* length processed */
					kbptr->offset += len;	/* bump up offset */
					if (kbptr->size > 0)	/* not all buffer accepted ... */
						break;				/* try feed again later */
					/* see if another buffer chained */
					if (kbptr->next)
					  {	/* yes, free up current buffer and queue next */
						layer->kbuff = kbptr->next; /* to next buffer */
						free(kbptr);	/* free this buffer up */
					  }
					else
					  {	/* otherwise leave this for next input */
						kbptr->size = 0;	/* empty buffer */
						kbptr->offset = 0;	/* refill from the start */
					  }
				  }

		/* first process the highest priority channel (top window) */
		if (priochan > 0 && priochan <= MAXPCHAN) /* if valid ... */
		  if ((layer = &World[priochan-1])->allocated)
			if (r & layer->ptymask)
			  {	/* output to send to top MacLayers window */
				len = read(layer->ptyfd, buf, IOSIZE);
				if (len >= 0)				/* if no error ... */
				  {	DO DEBUG("read output len %d chan %d\n", len, layer->chan);
				  }
				else
				  {	/* We expect EIO error if socket not yet open on other end
					** or if process using socket has terminated. We expect
					** EWOULDBLOCK also after process terminates.
					**/
					DO DEBUG("read output err chan %d errno %d len %d\n",
								layer->chan, errno, len);
					if (errno == EIO || errno == EWOULDBLOCK)
						DO DEBUG(" ...anticipated\n");
					/* layer not ready or just terminated so wait-a-bit */
					fderr |= layer->ptymask;
					r &= ~layer->ptymask; /* don't read it again below */
					errno = 0;				/* clear errno */
				  }
				if (len > 0)
					SendData(layer->chan, buf, len);

				if (len >= 0)
					/* To keep lower priority channels from hogging the line
					** we delay any output from them until the primary window 
					** has no more data to be sent to it.
					*/
					stall = 1;				/* stall output from others */
			  }

		/* now pass all available output to MacLayers */
		if (!stall)
		 for (n=0; n<MAXPCHAN; n++)
		  if ((layer = &World[n])->allocated)
			if (r & layer->ptymask)
			  {	/* output to send to MacLayers window */
				len = read(layer->ptyfd, buf, IOSIZE);
				if (len >= 0)				/* if no error ... */
				  {	DO DEBUG("output chan %d len %d\n", layer->chan, len);
				  }
				else
				  {	/* We expect EIO error if socket not yet open on other end
					** or if process using socket has terminated. We expect
					** EWOULDBLOCK also after process terminates.
					**/
					DO DEBUG("read output err chan %d errno %d len %d\n",
								layer->chan, errno, len);
					if (errno == EIO || errno == EWOULDBLOCK)
					  {	DO DEBUG(" ...anticipated\n");
					  }
					/* layer not ready or just terminated so wait-a-bit */
					fderr |= layer->ptymask;
					errno = 0;				/* clear errno */
				  }
				if (len > 0)
					SendData(layer->chan, buf, len);
			  }

		/* handle signals again */
		if (GotSignal)
			SigHandler ();

      } /* end while (1) */

	/* NOT REACHED */

} /* main() */

					/* ReceiveQuit() - MacLayers sends Quit packet */

void
ReceiveQuit()
{
	/* We completely quit layers cancelling all active processes */
	DO DEBUG("ReceiveQuit()\n");
	FQuit(0);								/* normal termination */
	/* NOT REACHED */

} /* ReceiveQuit() */


				/* ReceiveNew() - MacLayers requests a new shell layer */

void
ReceiveNew(chanid, shape)
int		chanid;								/* channel for new shell layer */
struct Shape *shape;						/* shape for new channel */
{
	DO DEBUG("ReceiveNew(%d)\n", chanid);
	(void) MakeWindow (chanid, *ShellArgs, ShellArgs,
					(char *) 0, loginflag, shape);

} /* ReceiveNew() */


				/* ReceiveDelete() - MacLayers has removed a layer */
void
ReceiveDelete(chanid)
int		chanid;								/* channel which was deleted */
{
	struct Layer *layer;					/* layer pointer */

	/* validate channel */
	DO DEBUG("ReceiveDelete(%d)\n", chanid);
	if (chanid <= 0 || chanid > MAXPCHAN)
		return;								/* ignore invalid channel */

	/* if this layer active then kill it off, else ignore request */
	layer = &World[chanid-1];				/* locate target layer */
	if (layer->allocated)
		KillWindow(layer);

} /* ReceiveDelete() */
	
	
		/* ReceiveSignal() - send signal to layer's process group */

void
ReceiveSignal(chanid, signal)
int			chanid;							/* layer's channel */
int			signal;							/* signal.h signal ID */
{
	struct Layer *layer;					/* layer pointer */

	DO DEBUG("ReceiveSignal(%d,%d)\n", chanid, signal);
	/* verify channel */
	if (chanid <= 0 || chanid > MAXPCHAN)
		return;								/* ignore invalid channel */

	/* if this layer is active send the signal to the process group */
	layer = &World[chanid-1];				/* locate target layer */
	if (layer->allocated && layer->lpid)
		KillPG(layer, signal);

} /* ReceiveSignal() */


				/* ReceiveReshape() - windowsize and location updated */
void
ReceiveReshape(chanid, shape)
int				chanid;					/* channel having shape */
struct Shape	*shape;					/* shape structure */
{
	struct Layer *layer;				/* layer pointer */

	DO DEBUG("ReceiveReshape(%d)\n", chanid);

	/* verify channel */
	if (chanid <= 0 || chanid > MAXPCHAN)
		return;								/* ignore invalid channel */

	/* if this layer is active then reshape it's window */
	layer = &World[chanid-1];				/* locate target layer */
	if (layer->allocated && layer->lpid)
	  {	layer->shape = *shape;				/* install as our new shape */
		SetWindowSize(layer);				/* udpate the O/S window info */
	  }

} /* ReceiveReshape() */


			/* SetWindowSize() - tell O/S about new window size */

static void
SetWindowSize(layer)
struct Layer	*layer;					/* layer to resize */
{
#ifdef TIOCSWINSZ
	struct	winsize	wsize;				/* window size structure */
	int			retcode;				/* ioctl return code */

	wsize.ws_col = layer->shape.wchars; /* character width */
	wsize.ws_row = layer->shape.wlines; /* window height */
	wsize.ws_xpixel = 0;				/* necessary? */
	wsize.ws_ypixel = 0;
	/* update O/S window state */
	retcode = ioctl(layer->ptyfd, TIOCSWINSZ, &wsize);
	DO DEBUG("SetWindowSize(chan %d) col %d, row %d iotcl() = %d\n",
			layer->chan, layer->shape.wchars, layer->shape.wlines,
			retcode);

	retcode = ioctl(layer->ptyfd, TIOCGWINSZ, &wsize);
	DO DEBUG("TIOCGWINSZ: col %d, row %d iotcl() = %d\n",
			wsize.ws_col, wsize.ws_row, retcode);

#endif
}  /* SetWindowSize() */


				/* ReceiveData() - received keyboard input for layer */
void
ReceiveData(chanid, buff, cnt)
int		chanid;							/* channel receiving input */
char	*buff;							/* buffer containing data */
int		cnt;							/* count of data bytes */
{
	struct Layer *layer;				/* layer pointer */
	struct Kbuff *kb;					/* keybuff pointer */

	DO DEBUG("ReceiveData(%d, '%.*s')\n", chanid, cnt, buff);
	/* verify channel */
	if (chanid <= 0 || chanid > MAXPCHAN)
		return;								/* ignore invalid channel */
	layer = &World[chanid-1];				/* locate target layer */

	/* add character stream to layer's input buffers for main loop processing */
	for (kb=layer->kbuff; kb->next; kb=kb->next); /* find oldest buffer */
	while (cnt--)
	  {	
		/* if current buffer full then chain in a new one */
		if (kb->offset+kb->size >= IOSIZE)
		  { kb->next = (struct Kbuff *) malloc(sizeof(struct Kbuff));
			kb = kb->next;					/* base new keybuff */
			kb->next = NULL;				/* no next yet */
			kb->size = 0;					/* this character is first */
			kb->offset = 0;					/* at zero offset */
		  }

		/* add new character to the end of this buffer */
		kb->text[kb->offset+kb->size++] = *buff++; /* insert at end of data */
	  }

} /* ReceiveData() */



					/* InitWorld() - initialize layer structures */

static void
InitWorld()
{
	struct Layer *layer;				/* layer pointer */
	struct Kbuff *kb;					/* keybuff pointer */
	int		i;							/* work variable */

	for (i=0; i<MAXPCHAN; i++)
	  {	layer = &World[i];				/* point to layer */
		layer->chan = i+1;				/* channel ID */
		layer->allocated = 0;			/* does not exist yet */
		layer->lpid = 0;				/* head process */
		layer->ptyfd = 0;				/* no pseduo pty yet */
		layer->ptymask = 0;				/* no pty mask yet */
		layer->slot = 0;				/* no Utmp slot */
		ClearShape(&layer->shape);		/* clear shape structure */

		/* allocate the primary input keybuffer for this layer */
		layer->kbuff = (struct Kbuff *) malloc(sizeof(struct Kbuff));
		layer->kbuff->next = NULL;		/* no next buffer */
		layer->kbuff->size = 0;			/* no data in buffer */
		layer->kbuff->offset = 0;		/* start filling at front */

	  } /* end for layer scan */

} /* InitWorld() */


					/* clearshape() - initialize shape structure */

static void
ClearShape(shape)
struct Shape	*shape;					/* shape structure pointer */
{
	shape->worigv = 0;					/* default window position */
	shape->worigh = 0;
	shape->wlines = 0;					/* default size */
	shape->wchars = 0;
	shape->wfont = 0;					/* default font size */
	shape->wattr = 0;					/* no attributes */

} /* clearshape() */


					/* SigHandler() - process signals */

SigHandler ()
{
	DO DEBUG("GotSignal()\n");
    while (GotSignal)
	  { GotSignal = 0;
		DoWait ();		/* handle dead or stopped children processes */
      }
}

static void
SigChld ()
{
	DO DEBUG("SigChld()\n");
	/* flag child process is stopped or dead */
    GotSignal = 1;
}

static void
SigHup ()
{
	DO DEBUG("SigHup()\n");
    /* Detach (0); */
	FQuit(1);			/* stop all processes */
	/* NOT REACHED */

}

	/* DoWait() -  send SIGCONT to stopped windows, Free dead process windows */
static
DoWait()
{
    register pid;
    register struct Layer *layer;
    union wait wstat;
	int		i;

	DO DEBUG("DoWait()\n");
    while ((pid = wait3 (&wstat, WNOHANG|WUNTRACED, NULL)) > 0)
		/* dead or stopped child process found */
		for (i=0; i<MAXPCHAN; i++)
			if ((layer = &World[i])->lpid == pid)
			  { if (WIFSTOPPED (wstat))
				  {	/* stopped process so restart it */
					/*** DO WE REALLY NEED TO DO THIS? ***/
					DO DEBUG("killpg(, SIGCONT)\n");
					KillPG(layer, SIGCONT);
				  }
				else
				  {	/* remove dead process's layer */
					DO DEBUG("kill dead process window %d\n", layer->chan);
			 		KillWindow (layer);
					/* tell MacLayers layer is dead */
					SendDelete(layer->chan);
				  }
	 	     }

} /* DoWait() */


				/* KillPG() - send signal to layer's process group */

static void
KillPG(layer, signal)
struct Layer	*layer;					/* layer to signal */
int				signal;					/* signal to send */
{
	int			retcode;				/* work variable */
	int			pgrp;					/* process group for layer */
	int			tgrp;					/* terminal control process group */

	DO DEBUG("KillPG(%d, sig %d)\n", layer->chan, signal);

	if (layer->lpid)
	  {	pgrp = getpgrp(layer->lpid);	/* get process group */
		DO DEBUG("getpgrp() = %d\n", pgrp);
		if (pgrp != -1)
		  {	retcode = killpg(pgrp, signal);	/* signal it */
			DO DEBUG("killpg() = %d\n", retcode);
		  }

		/* After a lot of experimenting it was determined that csh
		** creates a new terminal control group when it runs a command.
		** Thus the above code failed to forward SIGINT to such commands.
		** (Csh would get it but just ignore it.) Thus the following code.
		*/ 

		/* forward SIGINT to the terminal control group also */
		if (signal == SIGINT)
		  {	retcode = ioctl(layer->ptyfd, TIOCGPGRP, &tgrp);
			DO DEBUG("ioctl(ptyfd,TIOCGPGRP) termcntlgrp %d, retcode = %d\n",
							tgrp, retcode);
			/* but only if not the same process group */
			if (retcode != -1 && tgrp != pgrp)
			  {	retcode = killpg(tgrp, signal);	/* signal it */
				DO DEBUG("killpg(%d) = %d\n", tgrp, retcode);
		  	  }
		  }
	  }

} /* KillPG() */


				/* KillWindow() - remove a layer from the system */

/* Note: This function does NOT tell MacLayers about the dead layer */

static
KillWindow (layer)
struct Layer *layer;
{
	struct Kbuff	*kb;				/* work buffer free pointer */

	if (layer->allocated)
	  { 								/* SHOULD THIS BE SIGKILL ??? */
		if (layer->lpid)				/* if layer process started ... */
		  {	KillPG(layer, SIGHUP);		/* kill processes */
			layer->lpid = 0;			/* clear pid field */
		  }
    	RemoveUtmp(layer->slot);
    	(void) chmod(layer->tty, 0666);
    	(void) chown(layer->tty, 0, 0);
    	close(layer->ptyfd);
		DO DEBUG("chmod/chown %s, SendDelete(%d)\n",layer->tty, layer->chan);

		ClearShape(&layer->shape);		/* reset the shape structure */
		/* free all keybuffers but one and reprime it */
		for (kb=layer->kbuff; kb->next; kb=kb->next)
			free(kb);					/* free input buffers */
		kb->size = 0;					/* empty buffer */
		kb->offset = 0;					/* start refill from front */
    	layer->allocated = 0;			/* window no longer allocated */
	  }

} /* KillWindow() */


					/* FAbort() - signal catcher for quitting */
static void
FAbort()
{
	DO DEBUG("FAbort()\n");
	FQuit (1);							/* quit with error exit */

} /* FAbort() */


					/* FQuit() - terminate layers */
void
FQuit(exitcode)
int		exitcode;
{
	int			i;

	DO DEBUG("FQuit(%d)\n",exitcode);
	for (i=0; i<MAXPCHAN; i++)
		KillWindow(&World[i]);			/* kill all windows */

	DO DEBUG("SendQuit()\n");
	SendQuit();							/* tell MacLayers to exit layers mode */
    SetTTY (0, &OldMode);
	if (RealTtyMode)
		(void) chmod(RealTtyName, RealTtyMode);	/* restore mode */
	if (RealSlot)
		WriteUtmp(RealSlot, &RealUtmp);	/* restore original login */
    FinitTerm ();
	sleep(2);							/* wait for port to reset */
    printf ("[layers terminated]\n");

    exit (exitcode);

} /* FQuit() */


					/* MakeWindow() - create new layer */
static
MakeWindow (chan, prog, args, dir, lflag, shape)
int		chan;							/* zero or channel to use for window */
char	*prog;
char 	**args;
char	*dir;
int		lflag;							/* one if this to be logged in */
struct Shape *shape;					/* shape to use for window */
{
    register struct Layer *layer;
    register char **cp;
    register f, j;
    int tf;
    int mypid;
    char ebuf[10];

	DO DEBUG("MakeWindow(%d, %s, %s, dir %s, ",
				chan, prog, args[0], dir ? dir : "(none)");
	DO DEBUG("login %d\n", lflag);
	DO DEBUG("    origv %d, origh %d, lines %d, chars %d, ",
				shape->worigv, shape->worigh, shape->wlines, shape->wchars);
	DO DEBUG("font %d, attr 0x%x\n", shape->wfont, shape->wattr);
					
    if ((f = OpenPTY ()) == -1)
	  { Msg (0, "No more PTYs.");
		return ( -1 );
      }

	/* if channel not given obtain one from MacLayers */
	if (chan == 0)
	  {	chan = SendNew(shape);				/* try to get free window */
    	if (chan == 0)
	  	  {	Msg (0, "No more windows.");
			return ( -1 );
      	  }
		DO DEBUG("SendNew() == %d\n", chan);
	  }

	/* verify channel */
	if (chan <= 0 || chan > MAXPCHAN)
	  {	Msg(0, "Invalid channel %d.", chan);
		return ( -1 );
	  }

	/* login this window if it's layer #1 */
	if (chan == 1)
		lflag = 1;
					
    if (lflag == -1)
		lflag = loginflag;
	
#ifdef USRLIMIT
    /*
     *	Count current number of users, and if logging windows in,
     */
    if (lflag == 1)
	  { (void) lseek (utmpf, 0, 0);
		UserCount = 0;
		while (read(utmpf, &utmpbuf, sizeof(struct utmp)) > 0)
		  { if (utmpbuf.ut_name[0] != '\0')
				UserCount++;
		  }
		if (UserCount >= USRLIMIT)
		  { Msg (0, "User limit reached.  Window will not be logged in.");
	    	lflag = 0;
		  }
      }
#endif USRLIMIT

	layer = &World[chan-1];					/* find layer structure */
	layer->shape = *shape;					/* install new window shape */

	/* ??? What do we do if layer is still active as far as we're concerned? */
	if (layer->allocated)
	  {	DO DEBUG("??? newlayer not free !!!\n");
		KillWindow(layer);					/* kill off old layer */
		SendDelete(layer->chan);			/* kill window back off */
		Msg (0, "Makewindow error: Duplicate active layer %d.", chan);
		return ( -1 );						/* return failed */
	  }

	layer->allocated = 1;					/* show layer now in use */
	BuildTitle(chan, prog, args);			/* install window title */

    (void) fcntl (f, F_SETFL, FNDELAY);
    layer->ptyfd = f;						/* pseudo pty for task's I/O */
	layer->ptymask = 1<<f;					/* set pty device mask */
    strncpy (layer->cmd, Filename (args[0]), MAXSTR-1);
    layer->cmd[MAXSTR-1] = '\0';
    strncpy (layer->tty, TtyName, MAXSTR-1);
	DO DEBUG("forking %s, tty %s, ptyfd %d, mask %x\n",
				layer->cmd, layer->tty, layer->ptyfd, layer->ptymask);
    (void) chown (TtyName, getuid (), getgid ());
    if (lflag == 1)
	  { layer->slot = SetUtmp(TtyName, chan == 1);
		if (chan == 1 && RealTtyMode)
			/* set to original tty umask */
			(void) chmod(TtyName, RealTtyMode);
		else
			/* force to this mode */
			(void) chmod(TtyName, TtyMode);
      }
    else
	  { layer->slot = -1;
		/* do not allow any other user access to this device */
		(void) chmod (TtyName, 0600);
      }
    switch (layer->lpid = fork ())
	{ case -1:
		Msg (errno, "fork");
		layer->lpid = 0;					/* clear pid field */
		return ( -1 );						/* return failed */

      case 0:
		signal (SIGHUP, SIG_DFL);
		signal (SIGINT, SIG_DFL);
		signal (SIGQUIT, SIG_DFL);
		signal (SIGTERM, SIG_DFL);
		signal (SIGTTIN, SIG_DFL);
		signal (SIGTTOU, SIG_DFL);
		signal (SIGALRM, SIG_DFL);
		setuid (getuid ());
		setgid (getgid ());
		if (dir && chdir (dir) == -1)
		  { SendErrorMsg ("Cannot chdir to %s: %s", dir, sys_errlist[errno]);
	    	exit (1);
		  }
		mypid = getpid ();
		ioctl (DevTty, TIOCNOTTY, (char *)0);
		if ((tf = open (TtyName, O_RDWR)) == -1)
		  { SendErrorMsg ("Cannot open %s: %s", TtyName, sys_errlist[errno]);
	    	exit (1);
		  }
		DO DEBUG("Now in new process\n");
		(void) dup2 (tf, 0);
		(void) dup2 (tf, 1);
		(void) dup2 (tf, 2);
		for (f = getdtablesize () - 1; f > 2; f--)
	    	close (f);
		ioctl (0, TIOCSPGRP, &mypid);
		(void) setpgrp (0, mypid);
		SetTTY (0, &OldMode);

		  {	struct	winsize	wsize;		/* window size structure */
			int			retcode;		/* ioctl return code */

			wsize.ws_col = layer->shape.wchars; /* character width */
			wsize.ws_row = layer->shape.wlines; /* window height */
			wsize.ws_xpixel = 0;				/* necessary? */
			wsize.ws_ypixel = 0;
			/* update O/S window state */
			retcode = ioctl(0, TIOCSWINSZ, &wsize);
		  }
		(void) umask(Oumask);			/* restore user's original umask */
		NewEnv[2] = MakeTermcap(layer->shape.wlines, layer->shape.wchars);
		sprintf (ebuf, "LAYER=%d", chan);
		NewEnv[3] = ebuf;
		execvpe (prog, args, NewEnv);
		printf("%s: cannot exec %s: %s", myname, prog, sys_errlist[errno]);
		exit (1);
    }

    return ( chan );

} /* MakeWindow() */

static
execvpe (prog, args, env)
char *prog, **args, **env;
{
    register char *path, *p;
    char buf[1024];
    char *shargs[MAXARGS+1];
    register i;
	register 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]; ++i);
	    	execve (DefaultShell, shargs, env);
	    	return;

		  case EACCES:
	    	eaccess = 1;
	    	break;

		  case ENOMEM: case E2BIG: case ETXTBSY:
	    	return;

		} /* end switch */

    } while (*path);

    if (eaccess)
		errno = EACCES;

} /* execvpe() */


				/* BuildTitle() - create and install window title */

static void
BuildTitle(chan, prog, args)
int			chan;						/* channel for title */
char		*prog;						/* program being executed */
char		**args;						/* arg list */
{
	int		i;							/* arg scan index */
	char	buff[1024];					/* super huge title buffer */

	/* skip any leading "/bin/" */
	if (strncmp(prog, "/bin/", 5) == 0)
		strcpy(buff, prog+5);			/* leave /bin off */
	else
		strcpy(buff, prog);				/* start with program name */

	/* add all aguments but stop if option ("-") seen */
	for (i=1; args[i] && args[i][0] != '-'; i++)
	  {	strcat(buff, " ");				/* delimiter */
		strcat(buff, args[i]);			/* add next parameter */
	  }

	SendTitle(chan, buff, strlen(buff)); /* set new window title */

} /* BuildTitle() */


#ifdef sequent
static
OpenPTY ()
{
	char *m, *s;
	register f;

	f = getpseudotty (&s, &m);
	strncpy (PtyName, m, sizeof (PtyName));
	strncpy (TtyName, s, sizeof (TtyName));
	ioctl (f, TIOCFLUSH, (char *)0);
	return (f);
}

#else

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

    strcpy (PtyName, PtyProto);
    strcpy (TtyName, TtyProto);
    for (p = PtyName, i = 0; *p != 'X'; ++p, ++i);
    for (l = "qpr"; *p = *l; ++l)
	  { for (d = "0123456789abcdef"; p[1] = *d; ++d)
		  { if ((f = open (PtyName, O_RDWR)) != -1)
			  { TtyName[i] = p[0];
				TtyName[i+1] = p[1];
				if ((tf = open (TtyName, O_RDWR)) != -1)
				  { close (tf);
		    		return f;
				  }
				close (f);
	    	  }
		  }
      }

    return -1;

} /* OpenPTY() */
#endif

static
SetTTY (fd, mp)
struct mode *mp;
{
    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);

} /* SetTTY() */

static
GetTTY (fd, mp)
struct mode *mp;
{
    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);

} /* GetTTY() */

static
SetMode (op, np)
struct mode *op, *np;
{
    *np = *op;
#if 1
	if (flowctl)
	  {	np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
		np->m_ttyb.sg_flags |= CBREAK | ANYP;
	  }
	else
		np->m_ttyb.sg_flags = RAW | ANYP;
#else
    np->m_ttyb.sg_flags &= ~(CRMOD|ECHO);
    np->m_ttyb.sg_flags |= CBREAK | ANYP;
#endif
    np->m_tchars.t_intrc = -1;
    np->m_tchars.t_quitc = -1;
    if (!flowctl)
	  { 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;

} /* SetMode() */

static char *
GetTtyName ()
{
    int n;
	char *p;

    for (p = 0, n = 0; n <= 2 && !(p = ttyname (n)); n++);

    if (!p || *p == '\0')
		Msg (0, "layers must run on a tty.");

    return ( p );

} /* GetTtyName() */


static
Kill (pid, sig)
{
    if (pid != 0)
		(void) kill (pid, sig);
}

			/* GetSockName() - set SockName; if LTY env return 1 else 0 */
static
GetSockName ()
{
    register client;
    static char buf[2*MAXSTR];

    if (!mflag && (SockName = getenv ("LTY")) != 0 && *SockName != '\0')
	  { client = 1;
		setuid (getuid ());
		setgid (getgid ());
      }
	else
	  { sprintf (buf, "%s.%s", HostName, Filename (RealTtyName));
		SockName = buf;
		client = 0;
      }
    return client;

} /* GetSockName() */

static
MakeServerSocket ()
{
    register s;
    struct sockaddr_un a;
    char *p;

    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
		Msg (errno, "socket");
    a.sun_family = AF_UNIX;
    strcpy (SockNamePtr, SockName);
    strcpy (a.sun_path, SockPath);
    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) != -1)
	  { p = Filename (SockPath);
		Msg (0, "You already have a layers running on %s.", p);
		/*NOTREACHED*/
      }
	DO DEBUG("MakeServerSocket: unlink(SockPath)/bind()/chown/listen\n");
    (void) unlink (SockPath);
    if (bind (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
		Msg (errno, "bind");
    (void) chown (SockPath, getuid (), getgid ());
    if (listen (s, 5) == -1)
		Msg (errno, "listen");
    return s;

} /* MakeServerSocket() */

static
MakeClientSocket (err)
{
    register s;
    struct sockaddr_un a;

    if ((s = socket (AF_UNIX, SOCK_STREAM, 0)) == -1)
		Msg (errno, "socket");
    a.sun_family = AF_UNIX;
    strcpy (SockNamePtr, SockName);
    strcpy (a.sun_path, SockPath);
    if (connect (s, (struct sockaddr *)&a, strlen (SockPath)+2) == -1)
	  { if (err)
		  { Msg (errno, "connect: %s", SockPath); }
		else
		  { close (s);
	    	return -1;
		  }
      }
    return s;

} /* MakeClientSocket() */

static
SendCreateMsg (s, ac, av, lflag, shape)
char **av;
struct Shape *shape;
{
    struct msg m;
    register char *p;
    register len, n;
	char	*pwd;					/* PWD environment string */

	DO DEBUG("SendCreateMsg(%d, ac %d, lflag %d\n", s, ac, lflag);
    m.type = MSG_CREATE;
    p = m.m.create.line;
    for (n = 0; ac > 0 && n < MAXARGS-1; ++av, --ac, ++n)
	  { len = strlen (*av) + 1;
		if (p + len >= m.m.create.line+MAXLINE)
	    	break;
		strcpy (p, *av);
		p += len;
      }
	DO DEBUG("  nargs %d, create line = '%s'\n", n, m.m.create.line);
    m.m.create.nargs = n;
    m.m.create.lflag = lflag;
	m.m.create.shape = *shape;			/* pass window shape */

	/* Since Suns can hang up on getwd() [damn their stupid networking]
	** we try to get the current working directory first from the PWD
	** environment variable.
	*/
	if ((pwd=getenv("PWD")) && strlen(pwd) < 1024)
		(void) strcpy(m.m.create.dir, pwd);
	else
    if (getwd (m.m.create.dir) == 0)
	  {	DO DEBUG("getwd() failed!!\n");
		Msg (0, "%s", m.m.create.dir);
	  }
	DO DEBUG("  create.dir = '%s'\n", m.m.create.dir);

    if (write (s, (char *)&m, sizeof (m)) != sizeof (m))
	  {	DO DEBUG("  write failed!!\n");
		Msg (errno, "write");
	  }
	DO DEBUG("SendCreateMsg() done\n");

} /* SendCreateMsg() */

/*VARARGS1*/
static
SendErrorMsg (fmt, p1, p2, p3, p4, p5, p6)
char *fmt;
{
    register s;
    struct msg m;

    s = MakeClientSocket (1);
    m.type = MSG_ERROR;
    sprintf (m.m.message, fmt, p1, p2, p3, p4, p5, p6);
    (void) write (s, (char *)&m, sizeof (m));
    close (s);
    sleep (2);
}

static
ReceiveMsg (s)
{
    register ns;
    struct sockaddr_un a;
    int left, len = sizeof (a);
    struct msg m;
    char *p;

	DO DEBUG ("ReceiveMsg()\n");
    if ((ns = accept (s, (struct sockaddr *)&a, &len)) == -1)
	  { Msg (errno, "accept");
		return;
      }
    p = (char *)&m;
    left = sizeof (m);
    while (left > 0 && (len = read (ns, p, left)) > 0)
	  { p += len;
		left -= len;
      }
    close (ns);
    if (len == -1)
		Msg (errno, "read");
    if (left > 0)
		return;
    switch (m.type)
	{ case MSG_CREATE:
		DO DEBUG("MSG_CREATE:\n");
	    ExecCreate (&m);
		break;

      case MSG_ERROR:
		DO DEBUG("MSG_ERROR:\n");
		Msg (0, "%s", m.m.message);
		break;

      default:
		Msg (0, "Invalid message (type %d).", m.type);

    } /* end switch */

} /* ReceiveMsg() */

static
ExecCreate (mp)
struct msg *mp;
{
    char *args[MAXARGS];
    register n;
    register char **pp = args, *p = mp->m.create.line;

    for (n = mp->m.create.nargs; n > 0; --n)
	  { *pp++ = p;
		p += strlen (p) + 1;
      }
    *pp = 0;
    n = MakeWindow (0, mp->m.create.line, args, mp->m.create.dir,
						mp->m.create.lflag, &mp->m.create.shape);

} /* ExecCreate() */

#if 0
static
ReadRc (fn)
char *fn;
{
    FILE *f;
    register char *p, **pp, **ap;
    register argc, num, c;
    char buf[256];
    char *args[MAXARGS];
    int key;
	struct Shape shape;						/* shape for new window */

	ClearShape(&shape);						/* initialize shape */
    ap = args;
    if (access (fn, R_OK) == -1)
		return;
    if ((f = fopen (fn, "r")) == NULL)
		return;
    while (fgets (buf, 256, f) != NULL)
	  { if (p = rindex (buf, '\n'))
	    	*p = '\0';
		if ((argc = Parse (fn, buf, ap)) == 0)
	    	continue;
		if (strcmp (ap[0], "escape") == 0)
		  { p = ap[1];
	    	if (argc < 2 || strlen (p) != 2)
				Msg (0, "%s: two characters required after escape.", fn);
	    	Esc = *p++;
	    	MetaEsc = *p;
            ktab[Esc].type = KEY_OTHER;
		  }
		else
		if (strcmp (ap[0], "login") == 0)
		  { loginflag = 1;
		  }
		else
		if (strcmp (ap[0], "unlogin") == 0)
		  { loginflag = 0; }
		else
		if (strcmp (ap[0], "nologin") == 0)
		  { loginflag = 0; }
		else
		if (strcmp (ap[0], "chdir") == 0)
	 	  { p = argc < 2 ? home : ap[1];
	    	if (chdir (p) == -1)
			Msg (errno, "%s", p);
		  }
		else
		if (strcmp (ap[0], "mode") == 0)
		  { if (argc != 2)
			  { Msg (0, "%s: mode: one argument required.", fn); }
			else
			if (!IsNum (ap[1], 7))
			  { Msg (0, "%s: mode: octal number expected.", fn); }
			else
				(void) sscanf (ap[1], "%o", &TtyMode);
		  }
		else
		if (strcmp (ap[0], "bell") == 0)
		  { if (argc != 2)
			  { Msg (0, "%s: bell: one argument required.", fn); }
			else
			  { if ((BellString = malloc (strlen (ap[1]) + 1)) == 0)
		    		Msg (0, "Out of memory.");
				istrcpy (BellString, ap[1]);
	    	  }
		  }
		else
		if (strcmp (ap[0], "screen") == 0)
		  { num = 0;
	    	if (argc > 1 && IsNum (ap[1], 10))
			  { num = atoi (ap[1]);
				if (num < 0 || num > MAXWIN-1)
		    		Msg (0, "%s: illegal screen number %d.", fn, num);
				--argc; ++ap;
	    	  }
	    	if (argc < 2)
			  { ap[1] = ShellProg; argc = 2; }
	    	ap[argc] = 0;
	    	(void) MakeWindow (0, ap[1], ap+1, (char *)0, loginflag, &shape);
		  }
		else
		if (strcmp (ap[0], "bind") == 0)
		  { p = ap[1];
	    	if (argc < 2 || *p == '\0')
				Msg (0, "%s: key expected after bind.", fn);
	    	if (p[1] == '\0')
			  { key = *p; }
			else
			if (p[0] == '^' && p[1] != '\0' && p[2] == '\0')
			  { c = p[1];
				if (isupper (c))
		    		p[1] = tolower (c);    
				key = Ctrl(c);
	    	  }
			else
			if (IsNum (p, 7))
			  { (void) sscanf (p, "%o", &key);
	    	  }
			else
			  { Msg (0, "%s: bind: character, ^x, or octal number expected.", fn); }
	    	ktab[key].lflag = loginflag;
	    	if (argc < 3)
			  { ktab[key].type = 0;
	    	  }
			else
			  { for (pp = KeyNames; *pp; ++pp)
		    	if (strcmp (ap[2], *pp) == 0) break;
				if (*pp)
				  { ktab[key].type = pp-KeyNames+1; }
				else
				  { ktab[key].type = KEY_CREATE;
		    		ktab[key].args = SaveArgs (argc-2, ap+2);
				  }
	    	  }
		  }
		else
			Msg (0, "%s: unknown keyword \"%s\".", fn, ap[0]);
      }
    (void) fclose (f);

} /* ReadRc() */

static
Parse (fn, buf, args)
char *fn, *buf, **args;
{
    register char *p, **ap;
    register delim, argc;

	p = buf;
	ap = args;
    argc = 0;
    for (;;)
	  { while (*p && (*p == ' ' || *p == '\t'))
			++p;
		if (*p == '\0' || *p == '#')
	    	return argc;
		if (argc > MAXARGS-1)
	    	Msg (0, "%s: too many tokens.", fn);
		delim = 0;
		if (*p == '"' || *p == '\'')
		  { delim = *p; *p = '\0'; ++p; }
		++argc;
		*ap = p; ++ap;
		while (*p && !(delim ? *p == delim : (*p == ' ' || *p == '\t')))
	    	++p;
		if (*p == '\0')
		  { if (delim)
				Msg (0, "%s: Missing quote.", fn);
	    	else
				return argc;
		  }
		*p++ = '\0';
      }

} /* Parse() */

static char **
SaveArgs (argc, argv)
register argc;
register char **argv;
{
    register char **ap, **pp;

    if ((pp = ap = (char **)malloc ((argc+1) * sizeof (char **))) == 0)
		Msg (0, "Out of memory.");
    while (argc--)
	  { if ((*pp = malloc (strlen (*argv)+1)) == 0)
	    	Msg (0, "Out of memory.");
		strcpy (*pp, *argv);
		++pp; ++argv;
      }
    *pp = 0;
    return ap;

} /* SaveArgs() */
#endif

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

    if (strlen (SockName) > MAXSTR-5)
		SockName = "?";
    sprintf (buf, "LTY=%s", SockName);
    *np++ = buf;
    *np++ = Term;
    np += 2;
    for (op = environ; *op; ++op)
	  { if (np == NewEnv + MAXARGS - 1)
	    	break;
		if (   !IsSymbol (*op, "TERM")
			&& !IsSymbol (*op, "TERMCAP")
			&& !IsSymbol (*op, "LTY")
		   )
	    	*np++ = *op;
      }
    *np = 0;

} /* MakeNewEnv() */

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

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

    return 0;

} /* IsSymbol() */

/*VARARGS2*/
Msg (err, fmt, p1, p2, p3, p4, p5, p6)
char *fmt;
{
    char buf[1024];
    register char *p = buf;

    sprintf (p, fmt, p1, p2, p3, p4, p5, p6);
    if (err)
	  { p += strlen (p);
		if (err > 0 && err < sys_nerr)
	    	sprintf (p, ": %s", sys_errlist[err]);
		else
	    	sprintf (p, ": Error %d", err);
      }
    if (!Abortonmsg)
	  { /* MakeStatus (buf, curr);*/
		printf("%s\r\n", buf);
	  } 
	else
	  { printf ("%s\r\n", buf);
		exit (1);
      }

} /* Msg() */

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

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

} /* Filename() */

static
IsNum (s, base)
register char *s;
register base;
{
    for (base += '0'; *s; ++s)
		if (*s < '0' || *s > base)
	    	return 0;
    return 1;

} /* IsNum() */


static
InitUtmp ()
{
    if ((utmpf = open (UtmpName, O_RDWR)) == -1)
	  { if (errno != EACCES)
	    	Msg (errno, UtmpName);
		return;
      }
    utmp = 1;

} /* InitUtmp() */


static int
FindUtmp(name)
char *name;
{
    register char *p;
    register struct ttyent *tp;
    register slot;

	DO DEBUG("FindUtmp(%s)\n", name);
	slot = 1;
    if (!utmp)
		return 0;
    if (p = rindex (name, '/'))
		++p;
    else
		p = name;
    setttyent ();
    while (   (tp = getttyent ()) != NULL
		   && strcmp (p, tp->ty_name) != 0
		  )
		++slot;
    if (tp == NULL)
		return 0;

	DO DEBUG(" slot %d\n", slot);
    return slot;

} /* FindUtmp() */


static int
SetUtmp (name, mainlogin)
char	*name;							/* tty name */
int		mainlogin;						/* this is primary login */
{
    register char *p;
    register slot;
    struct utmp u;

	if ((slot=FindUtmp(name)) == 0)
		return ( 0 );

    if (p = rindex (name, '/'))
		++p;
    else
		p = name;

    strncpy (u.ut_line, p, 8);
    strncpy (u.ut_name, LoginName, 8);
#if 1
	strncpy(u.ut_host,  Filename (RealTtyName), 16); /* host is real tty */
#else
    u.ut_host[0] = '\0';
#endif
	if (RealSlot && mainlogin)
		u.ut_time = RealUtmp.ut_time;		/* use original login time */
	else
    	time (&u.ut_time);
    (void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
    (void) write (utmpf, (char *)&u, sizeof (u));

    return ( slot );

} /* SetUtmp() */

static int
ReadUtmp(slot, entry)
int		slot;								/* slot to read */
struct utmp	*entry;							/* entry to read into */
{
	int		cnt;							/* return count */

	if (!utmp)
		return;								/* no utmp access */

	(void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
	cnt =  read(utmpf, (char *)entry, sizeof(struct utmp));
	DO DEBUG("ReadUtmp cnt %d, errno %d, line %.8s, name %.8s, host %.16s\n",
		cnt, errno, entry->ut_line, entry->ut_name, entry->ut_host);

	return ( cnt );

} /* ReadUtmp() */

static void
WriteUtmp(slot, entry)
int		slot;								/* slot to write */
struct utmp	*entry;							/* entry to write from */
{
	int		cnt;							/* write return code */

	if (!utmp)
		return;								/* no utmp access */

	(void) lseek(utmpf, (long)(slot * sizeof(struct utmp)), 0);
	cnt = write(utmpf, (char *)entry, sizeof(struct utmp));
	DO DEBUG("WriteUtmp() slot %d cnt %d line %.8s name %.8s host %.16s\n",
				slot, cnt, entry->ut_line, entry->ut_name, entry->ut_host);

} /* WriteUtmp() */

static
RemoveUtmp (slot)
{
    struct utmp u;

    if (slot)
	  { bzero ((char *)&u, sizeof (u));
		(void) lseek (utmpf, (long)(slot * sizeof (u)), 0);
		(void) write (utmpf, (char *)&u, sizeof (u));
      }

} /* RemoveUtmp() */

#ifndef GETTTYENT

static
setttyent ()
{
    struct stat s;
    register f;
    register char *p, *ep;

    if (ttnext)
	  { ttnext = tt;
		return;
      }
    if ((f = open (ttys, O_RDONLY)) == -1 || fstat (f, &s) == -1)
		Msg (errno, ttys);
    if ((tt = malloc (s.st_size + 1)) == 0)
		Msg (0, "Out of memory.");
    if (read (f, tt, s.st_size) != s.st_size)
		Msg (errno, ttys);
    close (f);
    for (p = tt, ep = p + s.st_size; p < ep; ++p)
		if (*p == '\n')
			*p = '\0';
    *p = '\0';
    ttnext = tt;

} /* setttyent() */

static struct ttyent *
getttyent ()
{
    static struct ttyent t;

    if (*ttnext == '\0')
		return NULL;
    t.ty_name = ttnext + 2;
    ttnext += strlen (ttnext) + 1;
    return &t;

} /* getttyend() */

#endif



					/* FinitTerm() - reset vt100 terminal */
static void
FinitTerm ()
{
	/* print out termcap 'is' string to reset terminal */
#if 0
	/* This string sets scroll region 1-24 and puts cursor at bottom line */
	printf("\033[1;24r\033[24;1H");
#endif
	fflush(stdout);
}

static void
AddCap (s)
char *s;
{
    register n;

    if (tcLineLen + (n = strlen (s)) > 55)
	  { strcat (Termcap, "\\\n\t:");
		tcLineLen = 0;
      }
    strcat (Termcap, s);
    tcLineLen += n;
}

static char *
MakeTermcap(lines, chars)
int		lines;							/* default window lines */
int		chars;							/* default window chars */
{
    char buf[1024];
    register char **pp, *p;

    strcpy(Termcap, TermcapConst1);		/* start TERMCAP build */
	strcat(Termcap, UserTerm);			/* fill in User's terminal type */
	strcat(Termcap, TermcapConst3);		/* finish our own definition */

	if (lines <= 0 || lines > 200)
		lines = rows;					/* force default if none or invalid */
	if (chars <= 0 || chars > 300)
		chars = cols;					/* force default if none or invalid */

    sprintf(buf, "li#%d:co#%d:", lines, chars);
    AddCap(buf);

    return ( Termcap );

} /* MakeTermcap() */


				/* DEBUG() - dump output routine */

void
DEBUG(format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
char		*format;
int			arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8;
{
	fprintf(stderr, format, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
}
@//E*O*F layers.c//
chmod u=rw,g=rw,o=rw layers.c
 
echo x - layers.h
sed 's/^@//' > "layers.h" <<'@//E*O*F layers.h//'
/*             Copyright (C) 1989 by David W. Trissel 
 *
 *  Not derived from licensed software.
 *
 * Permission is granted to freely use, copy, modify, and redistribute
 * this software, provided that no attempt is made to gain profit from it,
 * the author is not construed to be liable for any results of using the
 * software, alterations are clearly marked as such, and this notice is
 * not modified.
 *
 */

#define MAXPCHAN	7						/* maximum layers supported */

#define MAXSTR		200
#define	MAXARGS		64
#define MAXLINE		1024
#define IOSIZE		800						/* data gulp handling size */

/* WARNING - packet sizes must be insured to never match the ESCAPE char */
#define	ESCAPE		'}'						/* datalink escape character */

#define DO 		if (Dflag)					/* for debugging */

/* miscelaneous common data */
extern	int	Dflag;							/* debug dump indicator flag */

/* Shape structure passed between MacLayers and ourselves */
struct Shape
{	short	worigv;							/* verical window bit origin */
	short	worigh;							/* horizontal window bit origin */
	short	wlines;							/* window height */
	short	wchars;							/* window width */
	short	wfont;							/* window font size */
	short	wattr;							/* window attributes */
};

#define Wa_shell	0x01					/* window is a shell */


  /* The following modules define the complete protocol/server interface */

				/* layers.c */

extern void FQuit(/* exitcode */);
extern void ReceiveQuit();
extern void ReceiveNew(/* chanid, shape */);
extern void ReceiveDelete(/* chanid */);
extern void ReceiveSignal(/* chanid, signal */);
extern void	ReceiveData(/* chanid, buff, cnt */);
extern void ReceiveReshape(/*chanid, shape */);
extern void DEBUG(/* format, arg1, arg2, arg3, arg4 */);

				/* protocol.c */

extern int	InitLink();
extern int	TopChannel();
extern int	SendNew(/* shape */);
extern void	SendTitle(/* chan, buff, cnt */);
extern void SendDelete(/* chan */);
extern void SendQuit();
extern void	SendReshape(/* chan, shape */);
extern void	SendData(/* chan, buff, cnt */);
extern void	ProcessStreamin();
@//E*O*F layers.h//
chmod u=rw,g=rw,o=rw layers.h
 
echo x - layersize.c
sed 's/^@//' > "layersize.c" <<'@//E*O*F layersize.c//'
/*             Copyright (C) 1989 by David W. Trissel 
 *
 *  Not derived from licensed software.
 *
 * Permission is granted to freely use, copy, modify, and redistribute
 * this software, provided that no attempt is made to gain profit from it,
 * the author is not construed to be liable for any results of using the
 * software, alterations are clearly marked as such, and this notice is
 * not modified.
 *
 */
				/*         All rights reserved.        */

		/* layersize - update BSD Sun Unix with window size information */

#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>

extern int sys_nerr;
extern char	*sys_errlist[];

static void gotsyserr(/* char * */);
static void goterr(/* char * */);
static int	getnumber(/* char * */);


						/* main() - update BSD window size */

main(ac, av)
int			ac;							/* argument count */
char		**av;						/* argument vector */
{
	struct winsize wsize;				/* window size structure for ioctl() */
	char		*ap;					/* argument scan pointer */
	int			lines;					/* new lines value */
	int			cols;					/* new columns value */

	if (--ac != 2)
		goterr("Missing lines and column options");

	/* get window size (actually do this to set xpixel and ypixel values) */
	if (ioctl(0, TIOCGWINSZ, &wsize) == -1)
		gotsyserr("No window support in host"); /* terminate with message */

	/* scan looking for -l and -c line and column numeric sizes */
	lines = cols = 0;					/* reset values */
	while (ac > 0)
	  {	ap = *++av;						/* point to next argument string */
		if (ac-- > 0 && *ap == '-')		/* if option ... */
		switch (ap[1])
		{ case 'l':		/* lines */
			lines = getnumber(&ap[2]);
			break;

		  case 'c':		/* columns */
			cols = getnumber(&ap[2]);
			break;

		  default:
			goterr("Usupported option"); /* unsupported option */
			break;

		} /* end '-' argument */
		else
			goterr("Unsupported parameter"); /* unsupported parameter */

	  } /* end while argument vector scan */
			
	/* must have both lines and columns */
	if (lines == 0 || cols == 0)
		goterr("Must specify both lines and columns");

	wsize.ws_col = cols;				/* set columns */
	wsize.ws_row = lines;				/* set lines */
	/* update the kernel */
	if (ioctl(0, TIOCSWINSZ, &wsize) == -1)
		gotsyserr("Failed to update window size"); /* didn't go */

} 


				/* goterr() - issue error and terminate */

static void
goterr(msg)
char		*msg;						/* error message string */
{
	printf("%s\n", msg);				/* send error message to user */
	exit(1);							/* terminate with error */

} /* goterr() */


				/* gotsyserror() - system error return */

static void
gotsyserr(msg)
char		*msg;						/* error string */
{
	if (errno > 0 && errno < sys_nerr)
		printf("%s: %s\n", msg, sys_errlist[errno]);
	else
		printf("%s: Error %d\n", msg, errno);

	exit(1);							/* exit with failure */

} /* gotsyserr() */


					/* getnumber() - parse option number */

static int
getnumber(str)
char		*str;						/* start of option string */
{
	int			n;						/* number being built */

	if (str == NULL)
		goterr("Invalid numeric in option");

	/* skip any leading delimiters */
	while (*str && (*str == ' ' || *str == '\t'))
		str++;

	for(n=0; *str && *str >= '0' && *str <= '9'; str++)
		n = n*10 + *str - '0';			/* add next digit in */

	/* make sure number terminates legally */
	switch (*str)
	{ case '\0':
	  case ' ':
	  case '\t':
	  case '\n':
		if (n <= 0 || n > 200)
			goterr("Number out of range");
		break;							/* these are OK */

	  default:
		goterr("Invalid numeric in option");

	} /* end switch */

	return ( n );						/* return the number */

} /* getnumber() */
@//E*O*F layersize.c//
chmod u=rw,g=rw,o=rw layersize.c
 
echo x - layertitle.c
sed 's/^@//' > "layertitle.c" <<'@//E*O*F layertitle.c//'
/*             Copyright (C) 1989 by David W. Trissel
 *
 *  Not derived from licensed software.
 *
 * Permission is granted to freely use, copy, modify, and redistribute
 * this software, provided that no attempt is made to gain profit from it,
 * the author is not construed to be liable for any results of using the
 * software, alterations are clearly marked as such, and this notice is
 * not modified.
 *
 */
                /*         All rights reserved.        */

            /* layertitle - utility to specify window title */

#include <stdio.h>

#define ESC 0x1b

            /* main() - send string designating layers window title */

main(ac, av)
int         ac;                         /* argument count */
char        **av;                       /* argument vector */
{
    char        *ap;                    /* argument scan pointer */

    if (--ac != 1)
      { printf("usage: layertitle \"new window title\"\n");
        exit(1);
      }
       
    ap = *++av;                         /* point to argument string */

    /* Transmit the title string in the ANSI Private Message format
    ** which is
    **              ESC '^' message ESC '\'
    */
    printf("%c%c%s%c%c", ESC, '^', ap, ESC, '\\');

}  /* main() */
@//E*O*F layertitle.c//
chmod u=rw,g=rw,o=rw layertitle.c
 
echo x - macbput.1
sed 's/^@//' > "macbput.1" <<'@//E*O*F macbput.1//'
@.TH MACBPUT local "17 Mar 1990"
@.UC 4
@.SH NAME
macbput \- send file to macintosh via macbinary protocol
@.SH SYNOPSIS
@.B macbput
file
@.br
@.B macbput
[
@.B \-rdu
] file
[
@.B \-t
type
]
[
@.B \-a
owner
]
[
@.B \-n
name
]
@.SH DESCRIPTION
@.I Macbput
sends a file to a Macintosh running MacTerminal.
The File Transfer Protocol settings should specify the "MacBinary"
transfer method.
Macbput will also work with other communications programs for the Mac
(e.g. MacLayers);
consult the user's manual for your macbinary-compatable communications
program for more information.
This manual page will only address the use of macbput with MacTerminal.
@.PP
To use this program, log into the unix system using MacTerminal,
and run macbput specifying the desired options and one file to be sent.
If MacTerminal is properly configured, it will recognize that a file
is arriving on the serial line and put up an indicator showing how
much of the file has been sent.
Several Control-X's may be used to force macbput
to give up if the transfer fails.
@.PP
If none of the
@.B \-rdu
flags are specified,
@.I macbput
sends three unix files to the Mac:
@.IB file .info ,
@.IB file .data ,
and
@.IB file .rsrc .
These specify the three parts of one Mac file:  the .data file
becomes the data fork, the .rsrc file becomes the resource fork,
and the .info file specifies the sizes of the two forks, as well
as the file name, file type, creation date, and other information.
This is useful for returning files to the Mac which were stored
using macget or macbget.
@.PP
The
@.B \-r
flag specifies
@.I resource
mode.
Either
@.IB file .rsrc
or
@.I file
will be sent to the Mac, along with a forged
@.B .info
file and an empty
@.B .data
file.
The file sent becomes the resource fork of the Mac file.
@.PP
The
@.B \-d
flag specifies
@.I data
mode.
Either
@.IB file .data
,
@.IB file .text
or
@.I file
will be sent to the Mac, along with a forged
@.B .info
file and an empty
@.B .rsrc
file.
The file sent becomes the data fork of the Mac file.
@.PP
The
@.B \-u
flag requests
@.I unix
mode, which is the same as
@.I data
mode except unix newline characters are converted
into carriage returns.
Human-readable unix text files sent to the Mac using this option
will be compatible with applications which expect "text only" files.
@.PP
The remaining options serve to override the default
file type, owner, and file name to be used on the Mac.
The default type and owner for
@.I resource
and
@.I data
mode defaults are "????", "????", and
@.I unix
mode defaults are "TEXT" and "MACA".
@.SH SEE ALSO
macput(local), macget(local), xbin(local), macbin(local), mcvert(local)
@.SH BUGS
The macbinary protocol may not work over flow controlled communication lines,
some terminal concentrators, or when using rlogin.
@.SH FEATURES
Properly initializes the Creation Date.
@.SH AUTHOR
Original base code: Dave Johnson, Brown 7/31/84
@//E*O*F macbput.1//
chmod u=rw,g=rw,o=rw macbput.1
 
echo x - macbput.c
sed 's/^@//' > "macbput.c" <<'@//E*O*F macbput.c//'
/*
 * (originally macput) -- send file to Macintosh using MacBinary XMODEM protocol
 * Dave Johnson, Brown University Computer Science
 *
 * (c) 1984 Brown University 
 * may be used but not sold without permission
 *
 */

/* To compile:    
                  cc -O -o macbput macbput.c
    (Sun 4.2 BSD) cc -O -DSUNBSD42 -o macbput macbput.c
    (System V)    cc -O -DSYSV -o macbput macbput.c

   Latest modifications 10/20/88 by Trissel -

 1. General cleanup by removal of unused definitions and headers.
 2. Added #ifdefs to support System V and BSD 4.2 Sun compilation.
 3. Removed ancient Macterminal Beta 0.5X code.
 4. Fixed bad bug where XMODEM block count was not bumped up
    after the first fork transfer.

	Dave Trissel
	Motorola Inc.
	ut-sally!oakhill!davet

   This code is fundamentally from two earlier programmers:

	Jon Hueras
	Symantec/THINK Technologies
	singer@endor.harvard.edu

    who added 2-Byte CRC capability to code from:

	Dave Johnson
	ddj%brown@csnet-relay.arpa
	Brown University Computer Science

   who did the initial MacTerminal 1.1 transfer protocol.
*/

/* If you have System V define the following: */
	/* #define SYSV */

/* Sun BSD 4.2 systems should define the following: */
	/* #define SUNBSD42 */

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#ifdef SYSV
#include <termio.h>
#else
#include <sgtty.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>

#ifdef SUNBSD42
/* RAW is no longer being found on latest Sun system (??) (Trissel) */
#define RAW 0x20
#endif

#define RECORDBYTES 132
#define DATABYTES 128
#define NAMEBYTES 63

#define RETRIES 10
#define ACKTIMO 10

#define MAXRECNO 0xff
#define BYTEMASK 0xff

#define TMO -1
#define DUP '\000'
#define SOH '\001'
#define EOT '\004'
#define ACK '\006'
#define NAK '\025'
#define CAN '\030'
#define EEF '\032'
#define ESC '\033'

#define H_NLENOFF 1
#define H_NAMEOFF 2
/* 65 <-> 80 is the FInfo structure */
#define H_TYPEOFF 65
#define H_AUTHOFF 69

#define H_LOCKOFF 81
#define H_DLENOFF 83
#define H_RLENOFF 87
#define H_CTIMOFF 91
#define H_MTIMOFF 95

#define H_OLD_DLENOFF 81
#define H_OLD_RLENOFF 85

#define TEXT 0
#define DATA 1
#define RSRC 2
#define FULL 3

int mode, txtmode;

struct macheader {
	char m_name[NAMEBYTES+1];
	char m_type[4];
	char m_author[4];
	long m_datalen;
	long m_rsrclen;
	long m_createtime;
	long m_modifytime;
} mh;

struct filenames {
	char f_info[256];
	char f_data[256];
	char f_rsrc[256];
} files;

int recno, crc;
char buf[DATABYTES];

char usage[] =
    "usage: \"macbput [-rdu] [-t type] [-c creator] [-n name] filename\"\n";

main(ac, av)
char **av;
{
	int n;
	char *filename;

	if (ac == 1) {
		fprintf(stderr, usage);
		exit(1);
	}

	mode = FULL;
	ac--; av++;
	while (ac) {
		if (av[0][0] == '-') {
			switch (av[0][1]) {
			case 'r':
				mode = RSRC;
				strncpy(mh.m_type, "????", 4);
				strncpy(mh.m_author, "????", 4);
				break;
			case 'u':
				mode = TEXT;
				strncpy(mh.m_type, "TEXT", 4);
				strncpy(mh.m_author, "MACA", 4);
				break;
			case 'd':
				mode = DATA;
				strncpy(mh.m_type, "????", 4);
				strncpy(mh.m_author, "????", 4);
				break;
			case 'n':
				if (ac > 1) {
					ac--; av++;
					n = strlen(av[0]);
					if (n > NAMEBYTES) n = NAMEBYTES;
					strncpy(mh.m_name, av[0], n);
					mh.m_name[n] = '\0';
					break;
				}
				else goto bad_usage;
			case 't':
				if (ac > 1) {
					ac--; av++;
					strncpy(mh.m_type, av[0], 4);
					break;
				}
				else goto bad_usage;
			case 'c':
				if (ac > 1) {
					ac--; av++;
					strncpy(mh.m_author, av[0], 4);
					break;
				}
				else goto bad_usage;
			default:
bad_usage:
				fprintf(stderr, usage);
				exit(1);
			}
		}
		else {
			filename = av[0];
		}
		ac--; av++;
	}

	setup_tty();
	find_files(filename, mode);
	if (mode != FULL)
		forge_info();

	if (send_sync()) {
		recno = 1;
		txtmode = 0;
		send_file(files.f_info, 1);

		if (mode != FULL)
			unlink(files.f_info);

		if (mode == TEXT) txtmode++;
		send_file(files.f_data, 1);

		txtmode = 0;
		send_file(files.f_rsrc, 0);
	}
	reset_tty();
}

find_files(filename, mode)
char *filename;
{
	int n, tdiff;
	struct stat stbuf;

	sprintf(files.f_data, "%s.data", filename);
	sprintf(files.f_rsrc, "%s.rsrc", filename);

	if (mode == FULL) {
		sprintf(files.f_info, "%s.info", filename);
		if (stat(files.f_info, &stbuf) != 0) {
			perror(files.f_info);
			cleanup(-1);
		}
		return;
	}
	else {
		strcpy(files.f_info, "#machdrXXXXXX");
		mktemp(files.f_info);
	}

	if (mode == RSRC) {
		strcpy(files.f_data, "/dev/null");
		if (stat(files.f_rsrc, &stbuf) != 0) {
			strcpy(files.f_rsrc, filename);
			if (stat(files.f_rsrc, &stbuf) != 0) {
				perror(files.f_rsrc);
				cleanup(-1);
			}
		}
		mh.m_datalen = 0;
		mh.m_rsrclen = stbuf.st_size;
	}
	else {
		strcpy(files.f_rsrc, "/dev/null");
		if (stat(files.f_data, &stbuf) != 0) {
			sprintf(files.f_data, "%s.text", filename);
			if (stat(files.f_data, &stbuf) != 0) {
				strcpy(files.f_data, filename);
				if (stat(files.f_data, &stbuf) != 0) {
					perror(files.f_data);
					cleanup(-1);
				}
			}
		}
		mh.m_datalen = stbuf.st_size;
		mh.m_rsrclen = 0;
	}

	if (mh.m_name[0] == '\0') {
		n = strlen(filename);
		if (n > NAMEBYTES) n = NAMEBYTES;
		strncpy(mh.m_name, filename, n);
		mh.m_name[n] = '\0';
	}
}

forge_info()
{
	int n;
	char *np;
	FILE *fp;

	for (np = mh.m_name; *np; np++)
		if (*np == '_') *np = ' ';

	buf[H_NLENOFF] = n = np - mh.m_name;
	strncpy(buf + H_NAMEOFF, mh.m_name, n);
	strncpy(buf + H_TYPEOFF, mh.m_type, 4);
	strncpy(buf + H_AUTHOFF, mh.m_author, 4);
	put4(buf + H_DLENOFF, mh.m_datalen);
	put4(buf + H_RLENOFF, mh.m_rsrclen);
	put4(buf + H_CTIMOFF, mh.m_createtime);
	put4(buf + H_MTIMOFF, mh.m_modifytime);
	fp = fopen(files.f_info, "w");
	if (fp == NULL) {
		perror("temp file");
		cleanup(-1);
	}
	fwrite(buf, 1, DATABYTES, fp);
	fclose(fp);
}

send_sync()
	{
		int c;
		
		tputc(ESC);
		tputc('b');

		for (;;) {

			if ((c = tgetc(ACKTIMO)) == TMO)
			  {
				return(0);
				}

			if (c == NAK)
			  {
				return(1);
				}

			if (c == 'C') {
				crc++;
				return(1);
			}
		}
	}

send_file(fname, more)
char *fname;
int more;
{
	register int status, i, n;
	FILE *inf;

	inf = fopen(fname, "r");
	if (inf == NULL) {
		perror(fname);
		cleanup(-1);
	}
	for (;;) {
		n = fread(buf, 1, DATABYTES, inf);
		if (n > 0) {
			for (i = 0; i < RETRIES; i++) {
				send_rec(buf, DATABYTES);
				while ((status = tgetc(ACKTIMO)) != ACK && status != NAK && status != CAN);
				if (status != NAK)
					break;
			} 
			if (status != ACK) {
				if (status != CAN)
					while ((status = tgetc(ACKTIMO)) != CAN);
				fclose(inf);
				cleanup(-1);
				/* NOTREACHED */
			}
		}
		if (n < DATABYTES) {
			if (!more) {
				tputc(EOT);
				tgetc(ACKTIMO);
			}
			return;
		}
		recno++;
		recno &= MAXRECNO;
	}
}

send_rec(buf, recsize)
char buf[];
int recsize;
{
	int i, cksum;
	char *bp;
 
	if (txtmode || !crc) {
		cksum = 0;
		bp = buf;
		for (i = 0; i < recsize; i++, bp++) {
			if (txtmode && *bp == '\n')
				*bp = '\r';
			cksum += *bp;
		}
	}

	if (crc)
		cksum = calcrc(buf, recsize);

	tputc(SOH);
	tputc((char) recno);
	tputc((char) (MAXRECNO - recno));
	tputrec(buf, recsize);
	
	if (crc) {
		tputc((char) (cksum >> 8));
		tputc((char) cksum);
	} else
		tputc((char) cksum);
}

static int ttyfd;
static FILE *ttyf;
static jmp_buf timobuf;

tgetc(timeout)
int timeout;
{
	int c;

	if (setjmp(timobuf))
		return TMO;

	alarm(timeout);
	c = getc(ttyf);
	alarm(0);

	if (c == -1)	/* probably hung up or logged off */
		return EOT;
	else
		return c & BYTEMASK;
}

tputrec(buf, count)
char *buf;
int count;
{
	write(ttyfd, buf, count);
}

tputc(c)
char c;
{
	write(ttyfd, &c, 1);
}

timedout()
{
	signal(SIGALRM, timedout);	/* for pre-4.2 systems */
	longjmp(timobuf, 1);
}

#ifdef SYSV
static struct termio otty, ntty;
#else
static struct sgttyb otty, ntty;
#endif

/* should turn messages off */

setup_tty()
{
	int cleanup();
	int timedout();

	ttyf = stdin;
	ttyfd = fileno(stdout);
#ifdef SYSV
	ioctl(ttyfd, TCGETA, &otty);		/* get termio info */
#else
	ioctl(ttyfd, TIOCGETP, &otty);
#endif
	signal(SIGHUP, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGQUIT, cleanup);
	signal(SIGTERM, cleanup);
	signal(SIGALRM, timedout);
	ntty = otty;
#ifdef SYSV
	ntty.c_iflag = BRKINT;				/* only interrupt on break */
	ntty.c_oflag = 0;					/* no output processing */
	ntty.c_cflag |= CS8;				/* 8 bit characters */
	ntty.c_lflag = 0;					/* no echoing */
	ntty.c_cc[VEOF] = 1;				/* "MIN" minimum chars before input */
	ntty.c_cc[VEOL] = 1;				/* "TIME" maximum .1 secs before feed */
	ioctl(ttyfd, TCSETAF, &ntty);		/* set mode and flush input */
#else
	ntty.sg_flags = RAW;
	ioctl(ttyfd, TIOCSETP, &ntty);
#endif
}

reset_tty()
{
	if (ttyf != NULL) {
#ifdef SYSV
		ioctl(ttyfd, TCSETAF, &otty);	/* reset after output drains */
#else
		sleep (5);						/* wait for output to drain */
		ioctl(ttyfd, TIOCSETP, &otty);
#endif
	}
}

cleanup(sig)
int sig;
{
	reset_tty();
	exit(sig);
}

put4(bp, value)
char *bp;
long value;
{
	register int i, c;

	for (i = 0; i < 4; i++) {
		c = (value >> 24) & BYTEMASK;
		value <<= 8;
		*bp++ = c;
	}
}

int calcrc(ptr,	count)
char *ptr;
int count;
	{
		int	crc, i;

		crc	= 0;
		while (--count >= 0) {
		 crc ^= ((int) *ptr++) << 8;
		 for (i = 0; i < 8; ++i)
				 if (crc & 0x8000)
			 crc = crc <<	1 ^ 0x1021;
				 else
			 crc <<= 1;
		 }
		return (crc	& 0xFFFF);
	}
@//E*O*F macbput.c//
chmod u=rw,g=rw,o=rw macbput.c
 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'
#                   Makefile for Layers 1.0

BIN    = /usr/local
MANDIR = /usr/local/manl

PGM    = layers
PGM2   = layersize
PGM3   = layertitle
PGM4   = macbput
MS     = l
CFLAGS = -O
CFILES = layers.c protocol.c
OFILES = layers.o protocol.o

all: $(PGM) $(PGM2) $(PGM3) $(PGM4)

$(PGM): $(OFILES)
	$(CC) $(CFLAGS) -o $(PGM) $(OFILES)

#	$(CC) $(CFLAGS) -o $(PGM) $(OFILES) -ltermcap

layers.o: layers.c layers.h
	$(CC) $(CFLAGS) -c layers.c

protocol.o: protocol.c layers.h
	$(CC) $(CFLAGS) -c protocol.c

$(PGM2): layersize.o
	$(CC) $(CFLAGS) -o $(PGM2) layersize.o

$(PGM3): layertitle.o
	$(CC) $(CFLAGS) -o $(PGM3) layertitle.o

$(PGM4): macbput.o
	$(CC) $(CFLAGS) -o $(PGM4) macbput.o

layersize.o: layersize.c
	$(CC) $(CFLAGS) -c layersize.c

layertitle.o: layertitle.c
	$(CC) $(CFLAGS) -c layertitle.c

macbput.o: macbput.c
	$(CC) $(CFLAGS) -c macbput.c

install: $(PGM) $(PGM2) $(PGM3) $(PGM4)
	rm -f $(BIN)/$(PGM)
	install -c -s -o root -g daemon -m 4711 $(PGM) $(BIN)/$(PGM)
	install -c -s $(PGM2) $(BIN)/$(PGM2)
	install -c -s $(PGM3) $(BIN)/$(PGM3)
#	install -c -s $(PGM4) $(BIN)/$(PGM4)

installnopriv: $(PGM) $(PGM2) $(PGM3) $(PGM4)
	rm -f $(BIN)/$(PGM)
	install -c -s $(PGM) $(BIN)/$(PGM)
	install -c -s $(PGM2) $(BIN)/$(PGM2)
	install -c -s $(PGM3) $(BIN)/$(PGM3)
#	install -c -s $(PGM4) $(BIN)/$(PGM4)

manpage: layers.1
	rm -f $(MANDIR)/$(PGM).$(MS)
	cp layers.1 $(MANDIR)/$(PGM).$(MS)
	chmod 664 $(MANDIR)/$(PGM).$(MS)

clean:
	rm -f a.out core $(PGM) $(PGM2) $(PGM3) $(PGM4) *.o

shar: makefile 
	shar README layers.1 makefile layers.h layers.c \
	protocol.c layersize.c layertitle.c MacLayers.sit.Hqx MacLayers.doc \
	macbput.c macbput.1 >layers.shar
@//E*O*F makefile//
chmod u=rw,g=rw,o=rw makefile
 
echo x - protocol.c
sed 's/^@//' > "protocol.c" <<'@//E*O*F protocol.c//'
/*             Copyright (C) 1989 by David W. Trissel 
 *
 *              Not derived from licensed software.
 *
 * Permission is granted to freely use, copy, modify, and redistribute
 * this software, provided that no attempt is made to gain profit from it,
 * the author is not construed to be liable for any results of using the
 * software, alterations are clearly marked as such, and this notice is
 * not modified.
 *
 */

#include	<stdio.h>
#include	<signal.h>
#include	<errno.h>
#include	"layers.h"

				/* protocol.c - BSD MacLayers protocol driver */

/*	This module handles all interaction with the Macintosh MacLayers
**	program. Services provided are:
**
**		InitLink()	- initialize link to MacLayers
**
**		TopChannel() - return highest prority channel
**
**		SendNew()	- request new layer channel of MacLayers
**
**		SendTitle() - change window title to given string (NOT IMPLENTED YET)
**
**		SendDelete()- tell MacLayers indicated layer has died
**
**		SendQuit()	- order MacLayers to terminate layers mode
**
**		SendData()	- send output to indicated channel's window
**
**		SendReshape() - send Shape structure to MacLayers
**
**		ProcessStreamin() - data is ready to be processed from MacLayers
**
*/

#define DUMPALL
#undef	DUMPALL

/* C library calls */
unsigned alarm();				/* alarm system call */

static int		GetData();
static void		Packet();
static void		Parse();
static void		AwaitInput();
static void		asciishape();
static void		fill4();
static void		fill2();
static void		fill1();
static int		parseshape();
static unsigned	get4();
static unsigned	get2();
static unsigned	get1();

static char		inbuff[IOSIZE];		/* input buffer from MacLayers */
static char 	*inpos;				/* current input position in buffer */
static int		insize = 0;			/* characters left to process */
static int		Outstream = -1;		/* current output stream channel */
static int		Instream = -1;		/* current input stream channel */

static struct Shape	SNshape;		/* SendNew() shape response */
static int		SNresp = 0;			/* SendNew() reponse poll flag */
static int		SNchan = 0;			/* SendNew() channel return */
#define SN_WAITING	-1000			/* SendNew() waiting response value */

#define ATTRSIZE	15				/* size of window attribute string */

#define FLUSH	fflush(stdout)


				/* Initlink() - initialize link with MacLayers */

/* Returns:
**				0 - linkup failed
**				1 - linkup successful, Maclayers now in protocol mode
*/

int
Initlink()
{
	int Outstream = -1;				/* no default stream yet */
	int	Instream = -1;				/* no default stream yet */
	int		num1, num2, num3;		/* scanf item result */
	int		err;					/* error code */

#define WAITTIME	10				/* second wait response time */
#define INITTIME	2				/* wait time after succesful startup */

	/* we must non-buffer input since all input must be immediate */
	setbuf(stdin, NULL);			/* non-buffer all input */

	/* send intitial request for terminal type and version number */
	DO DEBUG("write: ESC [ c\n");
	if ((err=printf("\033[c")) < 0)
	  {
		DO DEBUG(" printf() error code %d\n", err);
		return ( 0 );				/* problem with stdout */
	  }
	FLUSH;							/* force output buffer */

	/* attempt to read "ESC [ ? 8 ; typedigits ; versiondigits c" */
	num1 = num2 = num3 = -1;		/* default to unsupplied values */
	(void) alarm(WAITTIME);			/* set timeout */
	DO DEBUG(" doing first scanf\n");
	(void) scanf("\033[?%d;%d;%dc", &num1, &num2, &num3);
	(void) alarm(0);				/* cancel alarm */
	DO DEBUG("read ESC [ ? %d ; %d; %d c\n", num1, num2, num3);
	if (num1 != 8 || num2 != 10)
		return ( 0 );				/* not correct response or layers term ID */

	/* ask terminal if ENC_ENABLE is to be forced */
	DO DEBUG("write: ESC [ F\n");
	(void) printf("\033[F");
	FLUSH;							/* force output buffer */
	(void) alarm(WAITTIME);			/* set timeout */

	/* attempt to read "ESC [ flag F" (flag indicates ENC_ENABLE status) */
	num1 = -1;						/* default to invalid response */
	(void) scanf("\033[%dF", &num1);
	(void) alarm(0);				/* cancel alarm */
	DO DEBUG("read ESC [ %d F\n", num1);
	if (num1 != 2 && num1 != 0)
		return ( 0 );				/* something's wrong */

	/* now startup packet mode in non ENC_ENABLE processing */
	DO DEBUG("write: ESC [ 2 ; 0 v\n");
	(void) printf("\033[2;0v");		/* "ESC [ 2 ; 0 v" */
	FLUSH;							/* force output buffer */

	/* we are now in packet mode */
	sleep( INITTIME );				/* let Macintosh keep up with us */
	return ( 1 );					/* return successful startup */

} /* Initlink() */


			/* TopChannel() - return highest prority channel */

int
TopChannel()
{
	return ( Instream );

} /* TopChannel() */


	/*
	** WARNING: Most of the following functions may be recursively called
	**			as control commands are processed from the input stream
	*/


			/* ProcessStreamin() - MacLayers has input to process */

void
ProcessStreamin()
{
	int		c;						/* input character being processed */



	DO DEBUG("ProcessStreamin()\n");



	GetData(0);						/* read some and don't timeout */



	while (insize > 0)				/* while more data to process ... */

		Parse();					/* process next chuck of data */



} /* ProcessStreamin() */





		/* SendNew() - request new layer channel from MacLayers */



/*	This command is unique in that it returns a response from MacLayers.

**	To do this we continue processing the input stream until we get

**	our return. (This leads to recursive conditions.) The variables

**	'SNresp', 'SNshape' and 'SNchan' are set when our reply is received.

*/

int

SendNew(shape)

struct Shape	*shape;				/* shape to use for new window */

{

	int			i;					/* attribute count variable */

	char		astring[ATTRSIZE];	/* copy of attribute string */



	DO DEBUG("SendNew() new layer requested: '~%cA'\n", '1'+ATTRSIZE);



	/* check for a recursive call */

	if (SNresp == SN_WAITING)

	  {	DO DEBUG("return 0 - recursive call\n");

		return ( 0 );				/* return failure */

	  }



	putchar(ESCAPE);				/* send start of control packet char */

	putchar('1' + ATTRSIZE);		/* send command size */

	putchar('A');					/* send command */

	asciishape(shape, astring);		/* convert shape to string */

	for (i=0; i < ATTRSIZE; i++)

		putchar(astring[i]);		/* send next attribute digit */

	FLUSH;



	/* now stay here and process the input stream until we see our response */

/**** THIS SHOULD BE ENHANCED TO TIMEOUT WITH GetData() AND REISSUE REQUEST */

	SNresp = SN_WAITING;			/* indicate we are waiting a response */

	while (SNresp == SN_WAITING)

	  { DO DEBUG(" while (SNresp %d == %d)\n", SNresp, SN_WAITING);

		AwaitInput();				/* wait till input from MacLayers arrives */

		ProcessStreamin();			/* process available input */

	  }



	if (SNresp == -1)				/* if Maclayers rejected request */

		SNchan = 0;					/* return failure channel of zero */

	else

		*shape = SNshape;			/* else update shape structure */



	DO DEBUG("SendNew() returning channel %d\n", SNchan);



	return ( SNchan );				/* return the indicated channel */



} /* SendNew() */





			/* SendReshape() - send to shape to MacLayers */



void

SendReshape(chan, shape)

int				chan;				/* channel shape belongs to */

struct Shape	*shape;				/* shape to use for new window */

{

	int			i;					/* attribute count variable */

	char		astring[ATTRSIZE];	/* copy of attribute string */



	DO DEBUG("SendReshape() reshape: '~%cA'\n", '2'+ATTRSIZE);



	if (chan <= 0 || chan > MAXPCHAN)

	  {	DO DEBUG("BAD CHANNEL!!!\n");

		return;						/* ignore request */

	  }



	putchar(ESCAPE);				/* send start of control packet char */

	putchar('2' + ATTRSIZE);		/* send command size */

	putchar('R');					/* send command */

	putchar(chan + '0');			/* send channel */

	asciishape(shape, astring);		/* convert shape to string */

	DO DEBUG("shape: %.*s\n", ATTRSIZE, astring);

	for (i=0; i < ATTRSIZE; i++)

		putchar(astring[i]);		/* send next attribute digit */

	FLUSH;



} /* SendReshape() */





			/* SendTitle() - set layer's window title */



void

SendTitle(chan, buff, cnt)

int			chan;					/* layer window ID */

char		*buff;					/* new title string */

int			cnt;					/* count of title length */

{

	int		i;						/* work variable */



	DO DEBUG("SendTitle(chan%d, len %d, '%.*s')\n", chan, cnt, cnt, buff);



	if (chan <= 0 || chan > MAXPCHAN)

	  {	DO DEBUG("BAD CHANNEL!!!\n");

		return;						/* ignore request */

	  }



	if (cnt < 0)

	  {	DO DEBUG("BAD COUNT!!!\n");

		return;						/* ignore request */

	  }



	/* for now chop title size to 29 chars since that's MacLayer's limit */

	if (cnt > 29)

		cnt = 29;					/* due to packet size limit */



	/* we must guarantee that the size will not appear to be another ESCAPE */

	if ('2' + cnt == ESCAPE)

		cnt--;						/* truncate to avoid ESCAPE ESCAPE */

	

	putchar(ESCAPE);				/* send start of control packet char */

	putchar('2' + cnt);				/* send size of packet */

	putchar('T');					/* send command */

	putchar(chan + '0');			/* send channel ID */

	for (i=0; i<cnt; i++)

		putchar(buff[i]);			/* send out title */

	FLUSH;



} /* SendTitle() */





			/* SendDelete() - tell Maclayers layer died */



void

SendDelete(chan)

int		chan;						/* dead channel ID */

{

	DO DEBUG("SendDelete(%d) '~2D%d'\n", chan, chan);



	if (chan <= 0 || chan > MAXPCHAN) /* check channel ID */

	  {	DO DEBUG("BAD CHANNEL!!!\n");

		return;						/* ignore request */

	  }



	putchar(ESCAPE);				/* send control packet start char */

	putchar('2');					/* send command size */

	putchar('D');					/* send command character */

	putchar(chan + '0');			/* channel ID in ascii */

	FLUSH;



} /* SendDelete() */





			/* SendQuit() - order MacLayers to end layers mode */



void

SendQuit(chan)

int		chan;						/* dead channel ID */

{

	DO DEBUG("SendQuit() '~1E'\n");



	putchar(ESCAPE);				/* send control packet start char */

	putchar('1');					/* send command size */

	putchar('E');					/* send command */

	FLUSH;



} /* SendQuit() */





			/* SendData() - send output to layer's window */



void

SendData(chan, buff, cnt)

int			chan;					/* layer window ID */

unsigned char *buff;				/* new title string */

int			cnt;					/* count of title length */

{

	unsigned c;						/* output character being sent */



	DO

	  {	int		dcnt;



		DEBUG("SendData(chan %d, len %d, '", chan, cnt, cnt, buff);

		for (dcnt=0; dcnt<cnt; dcnt++)

			DEBUG("%c", buff[dcnt]); /* dump each char so null doesn't stop */

		DEBUG("')\n");

	  }



	if (chan <= 0 || chan > MAXPCHAN)

	  {	DO DEBUG("BAD CHANNEL!!!\n");

		return;						/* ignore request */

	  }



	/* if new output channel stream then prefix redirect command */

	if (chan != Outstream)

	  {	DO DEBUG("Redirecting output to %d '~2O%d'\n", chan, chan);

		putchar(ESCAPE);			/* start of command sequence */

		putchar('2');				/* send command size */

		putchar('O');				/* send command */

		putchar(chan + '0');		/* put out channel in ASCII */

		Outstream = chan;			/* new output stream set */

	  }



	/* transmit the buffer converting the ESCAPE sequence to double ESCAPE */

	while (cnt--)

	  {	c = *buff++;				/* get next output character */

#ifdef DUMPALL

		DO DEBUG("outchar %c 0x%x\n", c, c);

#endif

		if (c == ESCAPE || c == (ESCAPE + 0x80))

		  {	putchar(c);				/* put it out twice */

#ifdef DUMPALL

			DO DEBUG(" Doubled Escape!\n");

#endif

		  }

		putchar(c);					/* write character out */

	  }



	FLUSH;							/* force out queued output characters */

		

} /* SendData() */





			/* Parse() - process next chunk of input stream */



static void

Parse()

{

#define	ST_NULL		0				/* not primed for next state yet */

#define	ST_STREAM	1				/* processing default stream input */

#define	ST_PKT		2				/* processing packet data */



	int		c;						/* input character being processed */



	static	int state = ST_NULL;	/* current input state */

	static	int psize = 0;			/* packet size */

	static	int	rempsize = 0;		/* remembered packet size */

	static	char pdata[MAXSTR];		/* area for packet data */

	static	char *ppos;				/* packet read insert position */

	static	int escapemode = 0;		/* processing escape character */

	static	int	escapechar;			/* escape character being processed */

	static	pchan = -1;				/* packet input stream channel */



	while (insize-- > 0)			/* while more data */

	  {	c = *inpos++;				/* get next character */

		switch (state)				/* process according to state */

		{ case ST_NULL:				/* prepare for new packet */

			DO DEBUG("ST_NULL\n");

			psize = 0;				/* clear packet size */

			ppos = pdata;			/* start fill at data position */

			pchan = Instream;		/* packet channel is current input stream */

			state = ST_STREAM;		/* default is stream processing */



		  case ST_STREAM:

			/* stream keyboard input for layer */

			/* check for escape char with possible high bit on */

#ifdef DUMPALL

			DO DEBUG("ST_STREAM %x/%x esc %d insz %d\n",

						c, c & 0x7f, escapemode, insize);

#endif

			if (c == ESCAPE || c == (ESCAPE | 0x80))

		 	 {	if (escapemode && c == escapechar) /* previous was ESCAPE */

				/* this is really a single ESCAPE character */

					escapemode = 0;		/* back out of ESCAPE mode */

				else

					/* what do we do with back to back esc esc+0x80 ? */

				  {	/* flag in escape mode */

					escapemode++;

					escapechar = c;		/* remember character used for escape */

					continue;			/* and continue scan */

				  }

			  }

			else

			if (escapemode)

			  { /* this is the start of a control packet */

				if (psize)				/* if we have previous data packet */

					Packet(pchan, psize, pdata); /* finish up previous pkt */

				/* process packet size */

				psize = (c & 0x7f) - '0'; /* save size byte */

				if (psize <= 0 || psize > MAXSTR)

				  {	/* bad size */

					DO DEBUG("Bad pkt size %d\n", psize);

					break;				/* trash this packet */

				  }

				rempsize = psize;		/* remember this size for later */

#if 0

				ptimo = rtimo;			/* start receive timeout */				

#endif

				escapemode = 0;			/* escape mode now off */

				ppos = pdata;			/* initialize data store pointer */

				state = ST_PKT;			/* expect packet data next */

				continue;				/* continue scan */

			  }

				  

			/* process standard data output character for current stream */

			

			*ppos++ = c;				/* save next data