[net.sources] Emacs mods: #3

chris@umcp-cs.UUCP (08/28/83)

This is #3 so far.  I wonder how many there will be... looks like about
5, now.

Chris

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting mchan.c'
sed 's/^X//' <<'//go.sysin dd *' >mchan.c
X/* mchan.c - MPXio handler for multiple processes */

X/* Original code (c) 1981 Carl Ebeling */

X/* A tremendous number of changes by Chris Torek of Umcp-Cs */

X/* Still more changes by Spencer Thomas of Utah-Cs */


#include "config.h"
#ifdef	MPXcode			/* the entire file!!! */

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <wait.h>
#include <sgtty.h>
#include <sys/mx.h>
#include "window.h"
#include "keyboard.h"
#include "buffer.h"
#include "mlisp.h"
#include "macros.h"
#include "mchan.h"


#ifdef subprocesses

extern	int	child_changed;	/* all these from schan.c */
extern	int	PopUpUnexpected;
extern	int	EmacsShare;
extern	struct	BoundName	*UnexpectedProc;
extern	struct	BoundName	*UnexpectedSent;
extern	struct	process_blk	*GetBufProc ();
extern	struct	process_blk	*find_process ();

static	kbd_fd;			/* keyboard file descriptor, from extract() */

X/* ioans_rec is a structure used to return the IOANS message to a sender 
   process. The structure is initialized in InitMpx() by gtty on the 
   terminal. */

static	struct w_msg {
    short	    code;
    struct sgttyb   sgtty_ans;
} ioans_rec;

static	struct wh	ioans;		/* The record actually used to send
					   IOANS_REC */
static struct sgttyb	standard_ans;	/* A vanilla terminal */
static int		other_ans;	/* For other ioctl answer value(s) */
static short		short_ans;	/* For answers that need a short */
static struct tchars	special_ans;	/* More vanilla */
static int		lget_ans;	/* For TIOCLGET */
static struct ltchars	ltc_ans;	/* ltchars answer val */

static	char mpx_filename[50];		/* Name of Multiplexed file */

static	int	mpx_fd;			/* Multiplexed file */
static	char stdin_buf[BUFSIZ];		/* used to buffer stdin chars */

X/* Set ioans_rec.sgtty_ans */

X/* VARARGS */
static	setans (from, count)
register char *from;
register count;
{
    register char *to = (char *) &ioans_rec.sgtty_ans;
    while (count--)
	*to++ = *from++;
}


X/* Start up a subprocess with its standard input and output connected to 
   a channel on the mpx file.  Also set its process group so we can kill it
   and set up its process block.  The process block is assumed to be pointed
   to by current_process. */

create_process (command)
register char  *command;
{
    register index_t channel;
    register newfd;
    register	pid;
    extern char *shell ();
    extern UseCshOptionF;
    extern UseUsersShell;

    if ((channel = chan (mpx_fd)) == -1) {
	extern int errno, sys_nerr;
	extern char *sys_errlist[];
	error ("Can't connect subchannel: %s",
	    (errno > 0 && errno <= sys_nerr) ? sys_errlist[errno] : "?");
	return (-1);
    }

    newfd = extract (channel, mpx_fd);

    sighold (SIGCHLD);
    if ((pid = vfork ()) < 0) {
	error ("Fork failed");
	detach (channel, mpx_fd);
	close (newfd);
	return (-1);
    }

    if (pid == 0) {
#ifdef ce
	fprintf (err_file, "Creating pid %d on index %d\n", getpid(), channel);
#endif
	sigrelse (SIGCHLD);
	setpgrp (0, getpid ());
	sigsys (SIGINT, SIG_DFL);
	sigsys (SIGQUIT, SIG_DFL);
	close (0);
	dup (newfd);
	close (1);
	dup (newfd);
	close (2);
	dup (newfd);
	execlp (shell (), shell (),
		UseUsersShell && UseCshOptionF ? "-cf" : "-c", command, 0);
	write (1, "Couldn't exec the shell\n", 24);
	_exit (1);
    }

    current_process -> p_name = command;
    current_process -> p_pid = pid;
    current_process -> p_gid = pid;
    current_process -> p_flag = RUNNING | CHANGED;
    child_changed++;
    current_process -> p_chan.ch_index = channel;
    current_process -> p_chan.ch_ptr = NULL;
    current_process -> p_chan.ch_count = 0;
    current_process -> p_chan.ch_outrec.index = channel;
    current_process -> p_chan.ch_outrec.count = 0;
    current_process -> p_chan.ch_outrec.ccount = 0;
    close (newfd);  
    return 0;
}
#endif	subprocesses


X/* Process a signal from a child process and make the appropriate change in 
   Look through channel blocks to find the matching the channel index.
   There should probably be a short vector crossreferencing channel to process
   so these look-ups are not quite so stupid (ce) */

static	struct channel_blk *find_channel (index)
register index_t   index;
{
    register struct process_blk *p;

    if (index == mpxin -> ch_index)
	return (mpxin);
    for (p = process_list; p != NULL; p = p -> next_process)
	if (index == p -> p_chan.ch_index)
	    return (&p -> p_chan);
    return (NULL);
}

X/* Find the corresponding process for a pointer to a channel block. */

static	struct process_blk *index_to_process (index)
register index_t   index;
{
    register struct process_blk *p;

    if (index == mpxin -> ch_index)
	return (NULL);
    for (p = process_list; p != NULL; p = p -> next_process) {
	if (!active_process (p))
	    continue;
	if (p -> p_chan.ch_index == index)
	    break;
    }
    return (p);
}


X/* This corresponds to the filbuf routine used by getchar.  This handles all 
   the input from the mpx file.  Input coming from the terminal is sent back
   to getchar() in the same manner as filbuf.  Control messages are sent to
   Take_msg for interpretation.  Normal input from other channels is routed
   to the correct buffer. */

static char cbuffer[BUFSIZ];	/* used for reading mpx file */
static int  mpx_count;		/* number of unprocessed characters in
				   buffer */
static struct rh   *MXP;	/* pointer into buffer of records */

X/* ARGSUSED */
#ifdef ECHOKEYS
fill_chan (chan, alrmtime)
#else
fill_chan (chan)
#endif
register struct channel_blk *chan;
{
    register struct mpx_msg *msg;
    register int    record_size,
		    msg_length;
    register struct channel_blk *this_channel;

readloop: 

X/* Temporary hack until unblocking is fixed up: retry pending output whenever
   anything is read. */
#define Count	p_chan.ch_outrec.count
#define	CCount	p_chan.ch_outrec.ccount
    {
	register struct process_blk *p;

	for (p = process_list; p != NULL; p = p -> next_process)
	    if (active_process (p) && (p -> Count || p -> CCount))
		send_chan (p);
    }
    if (mpx_count == 0) {
#ifdef ECHOKEYS
	if (alrmtime > 0)
	    alarm ((unsigned) alrmtime);
#endif
	mpx_count = read (mpx_fd, cbuffer, BUFSIZ);
#ifdef ECHOKEYS
	alarm (0);
#endif
	if (mpx_count <= 0)
	    return (EOF);
	MXP = (struct rh *) cbuffer;
    }

    while (mpx_count > 0) {
	record_size = (MXP -> count + MXP -> ccount + 7) & 0xFFFE;
	if (MXP -> count == 0) {/* process a mpx channel record msg */
	    for (msg = (struct mpx_msg *) (MXP + 1); MXP -> ccount > 0;) {
		msg_length = Take_msg (msg, MXP -> index);
		msg = (struct mpx_msg  *) ((int) msg + msg_length);
		MXP -> ccount -= msg_length;
	    }
	    mpx_count -= record_size;
	    MXP = (struct rh *) ((int) MXP + record_size);
	}
	else {
	    if ((this_channel = find_channel (MXP -> index)) == NULL) {
		error ("Illegal channel index");
		return (EOF);	/* DANGER - THIS EXITS */
	    }
	if (this_channel == mpxin) {
	    if (this_channel -> ch_count <= 0) {
		this_channel -> ch_ptr = stdin_buf;
		cpyn (stdin_buf, (char *) (MXP +1), MXP -> count);
		this_channel -> ch_count = MXP -> count;
	    }
	    else {
		register count = MXP -> count;
		if (count + this_channel -> ch_count +
			(this_channel -> ch_ptr - stdin_buf) >= BUFSIZ)
		count = BUFSIZ - this_channel -> ch_count -
			(this_channel -> ch_ptr - stdin_buf);
		cpyn (this_channel -> ch_ptr + this_channel -> ch_count,
			(char *) (MXP + 1), count);
		this_channel -> ch_count += count;
		}
	    }
	else {
	    this_channel -> ch_ptr = (char *) (MXP + 1);
	    this_channel -> ch_count = MXP -> count;
	}
	    mpx_count -= record_size;
	    MXP = (struct rh *) ((int) MXP + record_size);

	/* input from TTY comes through the distinguished channel block */

	    if (this_channel == mpxin) {
		if (chan != NULL) {
		    if (child_changed) {/* need to do this here, too */
			change_msgs ();
			child_changed = 0;
		    }
		mpxin -> ch_count--;
		return (*mpxin -> ch_ptr++ & 0377);
		}
	    }
	    else
		stuff_buffer (this_channel);
	}
    }
    if (child_changed) {
	change_msgs ();
	child_changed = 0;
    }
    if (chan != NULL || mpx_count != 0)
        goto readloop;
    return 0;
}


X/* Take a channel message and do the right thing with it:
	IOCTL -> return appropriate IOANS
	EOT   -> ignore (spurious & bogus EOT's seem to thrive !!)
	CLOSE -> close the channel
	anything else -> print msg and ignore
*/

static	Take_msg (msg, index)
register struct mpx_msg *msg;
register index_t index;
{
    extern  mpx_fd;
    register struct process_blk *p;

    switch (msg -> mpx_code & 0377) {
	case M_IOCTL:
	    ioans.index = index;
	    ioans_rec.sgtty_ans =
		msg -> mpx_ioctl;	/* init answer, in case we send back
					   less than sizeof(struct sgttyb) */
	    switch (msg -> mpx_arg) {
	    default:		/* ignore most ioctls */
		break;
	    case TIOCGETD:	/* Get line discipline, give NTTYDISC */
		other_ans = NTTYDISC;
		setans (&other_ans, sizeof (int));
		break;
	    case TIOCGETP:	/* Give vanilla terminal description */
		setans (&standard_ans, sizeof (struct sgttyb));
		break;
	    case TIOCSTI:	/* Simulate Terminal Input */
				/* this one should eventually be
				   fixed, for ucbmail */
		break;
	    case TIOCGPGRP:	/* Get process group */
		p = index_to_process (index);
		if (p) {
#ifdef ce
		    fprintf (err_file, "GPGRP returning %d\n",p->p_gid);
#endif
		    short_ans = p -> p_gid;
		}
		else
		    short_ans = -1;
		setans (&short_ans, sizeof (short));
		break;
	    case TIOCSPGRP:	/* Set process group */
#ifdef ce
		if (index == mpxin->ch_index) {
		    fprintf (err_file, "somethin' funny here\n");
		    return 4+sizeof(struct sgttyb);
		}
#endif
		p = index_to_process (index);
		if (p) {
		    short t;
		    sighold (SIGCHLD);
		    t = *((short *)(&msg->mpx_ioctl));/* kludge! */
		    if (getpgrp (t) < 0) {/* klude some more */
X/* Believe it or not, the above test is necessary or the cshell gets very
   confused on interrupts. */
#ifdef ce
			fprintf (err_file, "SPGRP on chan %d, to %d FAILED\n",
			index, t);
#endif
			short_ans = -1;
		    }
		    else {
			p -> p_gid = t;
#ifdef ce
			fprintf (err_file, "SPGRP on chan %d, to %d\n", index,
			t);
#endif
			short_ans = t;
		    }
		    sigrelse (SIGCHLD);
		}
		else
		    short_ans = -1;
		setans (&short_ans, sizeof (short));
		break;
	    case TIOCGETC:	/* Get special chars */
		setans (&special_ans, sizeof (struct tchars));
		break;
	    case TIOCLGET:	/* Get local mode word */
		setans (&lget_ans, sizeof (int));
		break;
	    case TIOCGLTC:	/* Get ltchars */
		setans (&ltc_ans, sizeof (struct ltchars));
		break;
	    }
	    if (write (mpx_fd, &ioans, sizeof (ioans)) != sizeof (ioans))
		error ("Unable to reply to process IOCTL");
	    return (4 + sizeof (struct sgttyb));

	/* We get a WATCH message when someone opens our multiplexed file. If
	   we do an attach then they can connect.
	*/
	case M_WATCH:
#ifdef ce
	    fprintf(err_file, "Watch: %d\n", index); 
#endif
	    if (sflag && EmacsShare &&
		start_other_process (msg -> mpx_arg, index) == 0)
		    attach (index, mpx_fd);/* Let him open */
	    else
		detach (index, mpx_fd);/* Don't allow open */
	    return(4);

	case M_UBLK: 
	    if ((p = index_to_process (index)) == NULL) {
#ifdef ce
		fprintf(err_file, "%d: Msg code:%d, arg:%d, index:%d\n",
		err_id, msg->mpx_code, msg->mpx_arg, index); 
#endif
		return (4);
	    }
	    message ("Unblocking");
	    send_chan (p);
	    return (4);

	case M_EOT: 
	 /* if (index != mpxin->ch_index) 
		fprintf(err_file, "%d: Msg code:%d, arg:%d, index:%d\n", 
			err_id, msg->mpx_code, msg->mpx_arg, index); */
	   return (4);				/* ignore ! */
	case M_CLOSE: 
#ifdef ce
	    fprintf(err_file, "Close: %d\n", index); 
#endif
	    detach (index, mpx_fd);		/* reuse channel */
	    {
		register struct process_blk *p = index_to_process (index);
		if (p) {
		    p -> p_flag = EXITED | CHANGED;
		    p -> p_reason = 0;
		    child_changed++;
		}
	    }
	    return (4);
	case M_SIG:
#ifdef ce
	    fprintf(err_file, "SIG: %d\n", index);  
#endif
	    return (4);
	case M_BLK:
#ifdef ce
	    fprintf(err_file, "Blocking: %d\n", index);  
#endif
	    return (4);
	case M_OPEN:
#ifdef ce
	    fprintf(err_file, "Opening: %d\n", index);
#endif
	    return (4);
	default: 
#ifdef ce
	    fprintf(err_file, "%d: Msg code:%d, arg:%d, index:%d\n",
		err_id, msg->mpx_code, msg->mpx_arg, index);
#endif

	    return (4);
    }
}


#ifdef	subprocesses

X/* Send any pending output as indicated in the process block to the 
   appropriate channel. */

send_chan (process)
register struct process_blk *process;
{
    register struct wh *output;

    output = &process -> p_chan.ch_outrec;
    if (output -> count == 0 && output -> ccount == 0) {
	/* error ("Null output"); */
	return 0;		/* No output to be done */
    }
    if (write (mpx_fd, output, sizeof (*output)) != sizeof (*output))
	/* message ("Blocking")*/ ;
    else {
	output -> count = 0;
	output -> ccount = 0;
    }
    return 0;			/* ACT 8-Sep-1982 */
}


X/* Kill off all active processes: done only to exit when user really
   insists */

kill_processes () {
    register struct process_blk *p;

    for (p = process_list; p != NULL; p = p -> next_process) {
	if (active_process (p)) {
	    if (p -> p_gid != -1)
		killpg (p -> p_gid, SIGKILL);
	    if (p -> p_pid != -1)
		killpg (p -> p_pid, SIGKILL);
	}
    }
    detach (mpxin -> ch_index, mpx_fd);
}


X/* Start up a new process caused by someone opening the share file */

static	start_other_process (uid, index)
register index_t index;
{
    static char buf[40];

    register struct buffer *old = bf_cur;
    register struct process_blk *newproc;

    sighold (SIGCHLD);

    sprintf (buf, "proc_%d", uid);
    if (find_process (buf)) {
	register i = 1;
	do sprintf (buf, "proc_%d<%d>", uid, i++);
	while (find_process (buf));
    }
    newproc = (struct process_blk *) malloc (sizeof (struct process_blk));
    if (newproc == NULL) {
	sigrelse (SIGCHLD);
	error ("Out of memory");
	return -1;
    }
    newproc -> next_process = process_list;
    process_list = newproc;
    newproc -> p_name = savestr (buf);
    newproc -> p_pid = -1;
    newproc -> p_gid = -1;
    newproc -> p_flag = RUNNING;
    newproc -> p_chan.ch_index = index;
    newproc -> p_chan.ch_ptr = NULL;
    newproc -> p_chan.ch_count = 0;
    newproc -> p_chan.ch_outrec.index = index;
    newproc -> p_chan.ch_outrec.count = 0;
    newproc -> p_chan.ch_outrec.ccount = 0;

    SetBfn (newproc -> p_name);
    if (PopUpUnexpected)
	WindowOn (bf_cur);
    newproc -> p_chan.ch_buffer = bf_cur;
    newproc -> p_chan.ch_proc = UnexpectedProc;
    newproc -> p_chan.ch_sent = UnexpectedSent;
    sigrelse (SIGCHLD);
    bf_modified = 0;
    bf_cur -> b_mode.md_NeedsCheckpointing = 0;
    SetBfp (old);
    if (PopUpUnexpected)
	WindowOn (bf_cur);
    return 0;
}


X/* Send an signal to the specified process group.  Goes to leader
   (process which started whole mess) iff "leader". */

sig_process (signal, leader) register leader; {
    register struct process_blk *process;

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a process");
	return 0;
    }

X/* We must update the process flag explicitly in the case of continuing a 
   process since no signal will come back */

    if (signal == SIGCONT) {
	sighold (SIGCHLD);
	process -> p_flag = (process -> p_flag & ~STOPPED) | RUNNING | CHANGED;
	child_changed++;
	sigrelse (SIGCHLD);
    }


#ifdef ce
    fprintf (err_file, "Sending signal %d to proc (%d, %d), leader=%d\n",
	signal, process -> p_pid, process -> p_gid, leader);
#endif
    leader = leader ? process -> p_pid : process -> p_gid;
    if (leader != -1)
	killpg (leader, signal);
    return 0;
}


EOTProcess () {
    register struct process_blk *process;
    struct {
	short	code,
		arg;
    }	EOT_msg;
    register struct wh *output;

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a process");
	return (0);
    }
    output = &process -> p_chan.ch_outrec;
    if (output -> count || output -> ccount)
	error ("Overwriting on blocked channel");

    output -> index = process -> p_chan.ch_index;
    output -> count = 0;
    output -> ccount = sizeof (EOT_msg);
    output -> data = (char *) & EOT_msg;
    EOT_msg.code = M_EOT;
    send_chan (process);
    return 0;			/* ACT 8-Sep-1982 */
}


PID (leader) {
    register char  *p_name = getstr ("Process name: ");
    register struct process_blk *process;

    process = find_process (p_name);
    MLvalue -> exp_type = IsInteger;
    if (process == NULL)
	MLvalue -> exp_int = 0;
    else
	MLvalue -> exp_int = leader ? process -> p_pid : process -> p_gid;
    return 0;
}


static char *tail (s)
register char *s;
{
    register char *t = s;

    while (*s) if (*s++ == '/' && *s) t = s;
    return t;
}

static char tempname[50];

#endif	subprocesses


X/* Initialize things on the multiplexed file.  This involves connecting the
   standard input to a channel on the mpx file. */

InitProcesses () {
#ifdef subprocesses
    extern char *MyTtyName;

 /* We will make children think they have a vanilla terminal */
    ioans_rec.code = M_IOANS;
    ioans.ccount = sizeof (ioans_rec);
    ioans.data = (char *) &ioans_rec;
    ioctl (0, TIOCGETP, &standard_ans);	/* Set up GETP answer */
    ioctl (0, TIOCGETC, &special_ans);	/* Set up GETC answer */
    ioctl (0, TIOCLGET, &lget_ans);	/* Set up LGET answer */
#ifdef TIOCGLTC
    ioctl (0, TIOCGLTC, &ltc_ans);	/* Set up GLTC answer */
#endif

X/* Open the multiplexed file with a file name so that it can be shared from
   the outside */

    if (sflag) {
	sprintfl(mpx_filename, sizeof mpx_filename,
		 "/tmp/dev_%s", tail (MyTtyName));
	unlink(mpx_filename);	/* Remove the file first */
#ifdef DumpableEmacs
	*tempname = 0;
#endif
    }
    if ((mpx_fd = mpx (sflag ? mpx_filename : 0, 0666)) < 0) {
	quit (1, "Can't open mpx file.\n");
    }
  
    if (sflag) chmod(mpx_filename, 0666);

    mpxin -> ch_index = chan (mpx_fd);
    ioctl (mpx_fd, MXNBLK, 0);	/* set up non-blocking mode */
    if (mpxin -> ch_index == (index_t) -1) {
	quit (1, "Couldn't get a channel to mpx file.\n");
    }
    kbd_fd = extract (mpxin -> ch_index, mpx_fd);
    connect (0, kbd_fd, 0);
#else
    mpxin -> ch_index = 0;
#endif
    mpxin -> ch_ptr = NULL;
    mpxin -> ch_count = 0;
}

QuitMpx () {
#ifdef subprocesses
    if (sflag) {
	unlink (mpx_filename);
	if (*tempname)
	    unlink (tempname);
    }
#endif
}

SuspendMpx () {
#ifdef subprocesses
    if (sflag) {		/* must save mpx file... */
	sprintfl (tempname, sizeof tempname, "/tmp/Emacs-Mpx%d", getpid ());
	unlink (tempname);
	link (mpx_filename, tempname);
	unlink (mpx_filename);
    }
    close (kbd_fd);
    detach (mpxin -> ch_index, mpx_fd);/* dis-connect() tty */
    mpxin -> ch_index = chan (mpx_fd);
    if (mpxin -> ch_index == (index_t) -1) {
	quit (1, "Couldn't recreate a channel for mpxin.\n");
    }
    mpxin -> ch_ptr = NULL;
    mpxin -> ch_count = 0;
#endif
}

ResumeMpx () {
#ifdef subprocesses
    if (sflag) {
	unlink (mpx_filename);
	link (tempname, mpx_filename);
	unlink (tempname);
    }
    kbd_fd = extract (mpxin -> ch_index, mpx_fd);
    connect (0, kbd_fd, 0);
#endif
}

#endif	MPXcode
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 mchan.c
	/bin/echo -n '	'; /bin/ls -ld mchan.c
fi
/bin/echo 'Extracting pchan.c'
sed 's/^X//' <<'//go.sysin dd *' >pchan.c
X/* pchan.c - PTYio handler for multiple processes */

X/* Original changes for 4.2bsd (c) 1982 William N. Joy and Regents of UC */

X/* More changes for 4.1aBSD by Spencer Thomas of Utah-Cs */

X/* Still more changes for 4.1aBSD by Marshall Rose of UCI */

X/* Changes for 4.1cBSD by Chris Kent of Dec-Wrl */

#include "config.h"
#ifndef	MPXcode			/* the entire file!!! */

#ifndef	subprocesses
#undef	TTYconnect
#endif	not subprocesses

#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <wait.h>
#include <sgtty.h>
#ifdef	BSD41c
#include <time.h>
#endif	BSD41c
#include <sys/types.h>
#include <sys/stat.h>
#ifdef	TTYconnect
#include <sys/socket.h>
#ifndef	BSD41c
#include <net/in.h>
#else	not BSD41c
#include <netinet.h>
#endif	not BSD41c
#endif	TTYconnect
#include "window.h"
#include "keyboard.h"
#include "buffer.h"
#include "mlisp.h"
#include "macros.h"
#include "mchan.h"


#ifdef	subprocesses

extern	int	child_changed;	/* all these from schan.c */
extern	int	PopUpUnexpected;
extern	int	EmacsShare;
extern	struct	BoundName	*UnexpectedProc;
extern	struct	BoundName	*UnexpectedSent;
extern	struct	process_blk	*GetBufProc ();
extern	struct	process_blk	*find_process ();

extern int  errno;
extern int  sys_nerr;
extern char *sys_errlist[];

static	int     sel_ichans;		/* input channels */
static	int     sel_ochans;		/* blocked output channels */

static	struct sgttyb mysgttyb;
static	struct tchars mytchars;
static	struct ltchars myltchars;
static	int mylmode;

#ifdef	TTYconnect
#ifndef	BSD41c
#define	SO_OPTIONS	(SO_ACCEPTCONN | SO_DONTLINGER | SO_KEEPALIVE)
#else	not BSD41c
#undef	TTYD
#endif	not BSD41c
#ifndef	TTYD
#define	IPPORT_EMACS	010000
#endif	not TTYD

#ifndef	BSD41c
#define	htons(x)	(((x << 8) & 0xff00) | ((x >> 8) & 0xff))
#define	ntohs(x)	(((x << 8) & 0xff00) | ((x >> 8) & 0xff))
#endif	not BSD41c

#define	NOTOK		(-1)
#define	OK		0

static int  tty_port;
#ifdef	TTYD
static char myhost[BUFSIZ];
static char myport[BUFSIZ];
#endif	TTYD

static int  sd;
static struct sockaddr_in   tty_socket;
static struct sockaddr_in   unx_socket;

#ifdef	mtr
static  FILE * log_file;
#endif

char   *RAddr ();
#endif	TTYconnect


X/* Find a free pty and open it. */

static	char *pty(ptyv)
int *ptyv;
{
	struct stat stb;
	static char name[24];
	int on = 1, i;

	strcpy(name, "/dev/ptypX");
	for (;;) {
		name[strlen("/dev/ptyp")] = '0';
		if (stat(name, &stb) < 0)
			return (0);
		for (i = 0; i < 16; i++) {
			name[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
			*ptyv = open(name, 2);
			if (*ptyv >= 0) {
				ioctl(*ptyv, FIONBIO, &on);
				name[strlen("/dev/")] = 't';
				return (name);
			}
		}
		name[strlen("/dev/pty")]++;
	}
}

X/* Start up a subprocess with its standard input and output connected to 
   a channel on a pty.  Also set its process group so we can kill it
   and set up its process block.  The process block is assumed to be pointed
   to by current_process. */

create_process (command)
register char  *command;
{
    index_t channel;
    int     pgrp,
	    len,
	    ld;
    char   *ptyname;
    register	pid;
    extern char *shell ();
    extern UseCshOptionF;
    extern UseUsersShell;

    ptyname = pty (&channel);
    if (ptyname == 0) {
	error ("Can't get a pty");
	return (-1);
    }
    sel_ichans |= 1<<channel;

    sighold (SIGCHLD);
    if ((pid = vfork ()) < 0) {
	error ("Fork failed");
	close (channel);
	sel_ichans &= ~(1<<channel);
	return (-1);
    }

    if (pid == 0) {
#ifdef	ce
	fprintf (err_file, "Creating pid %d on %s\n", getpid (), ptyname);
#endif
	close (channel);
	sigrelse (SIGCHLD);
	setpgrp (0, getpid ());
	sigsys (SIGINT, SIG_DFL);
	sigsys (SIGQUIT, SIG_DFL);
	if ((ld = open ("/dev/tty", 2)) >= 0) {
	    ioctl (ld, TIOCNOTTY, 0);
	    close (ld);
	}
	close (2);
	if (open (ptyname, 2) < 0) {
	     write (1, "Can't open tty\n", 15);
	     _exit (1);
	}
	pgrp = getpid();
	setpgrp (0, pgrp);
	ioctl (2, TIOCSPGRP, &pgrp);
	close (0);
	close (1);
	dup (2);
	dup (2);
	ioctl (0, TIOCSETP, &mysgttyb);
	ioctl (0, TIOCSETC, &mytchars);
	ioctl (0, TIOCSLTC, &myltchars);
	ioctl (0, TIOCLSET, &mylmode);
	len = 0;			/* set page features to 0 */
#ifdef	TIOCSWID
	ioctl (0, TIOCSWID, &len);	/* page width */
#endif
#ifdef	TIOCSLEN
	ioctl (0, TIOCSLEN, &len);	/* page len (CCA uses TIOCSSCR) */
#endif
	len = UseUsersShell;
	UseUsersShell = 1;
	ld = strcmp(shell(), "/bin/csh") ? OTTYDISC : NTTYDISC;
	ioctl (0, TIOCSETD, &ld);
	UseUsersShell = len;
	execlp (shell (), shell (),
		UseUsersShell && UseCshOptionF ? "-cf" : "-c", command, 0);
	write (1, "Couldn't exec the shell\n", 24);
	_exit (1);
    }

    current_process -> p_name = command;
    current_process -> p_pid = pid;
    current_process -> p_gid = pid;
    current_process -> p_flag = RUNNING | CHANGED;
    child_changed++;
    current_process -> p_chan.ch_index = channel;
    current_process -> p_chan.ch_ptr = NULL;
    current_process -> p_chan.ch_count = 0;
    current_process -> p_chan.ch_outrec.index = channel;
    current_process -> p_chan.ch_outrec.count = 0;
    current_process -> p_chan.ch_outrec.ccount = 0;
    return 0;
}
#endif	subprocesses


X/* This corresponds to the filbuf routine used by getchar.  This handles all 
   the input from a pty.  Input coming from the terminal is sent back
   to getchar() in the same manner as filbuf.

   With pty:s, when the parent process of a pty exits we are notified,
   just as we would be with any of our other children.  After the process
   exits, select() will indicate that we can read the channel.  When we
   do this, read() returns 0.  Upon receiving this, we close the channel.

   For unexpected processes, when the peer closes the connection, select()
   will indicate that we can read the channel.  When we do this, read()
   returns -1 with errno = ECONNRESET.  Since we never get notified of
   this via wait3(), we must explictly mark the process as having exited.
   (This corresponds to the action performed when a M_CLOSE is received
   with the MPXio version of Emacs -- see mchan.c)
 */

static char cbuffer[BUFSIZ];	/* used for reading mpx file */
static int  mpx_count;		/* number of unprocessed characters in
				   buffer */

X/* ARGSUSED */
#ifdef	ECHOKEYS
fill_chan (chan, alrmtime)
#else	ECHOKEYS
fill_chan (chan)
#endif	ECHOKEYS
register struct channel_blk *chan;
{
    int ichans, ochans, cc;
    register struct channel_blk *this_chan;
    register struct process_blk *p;
#ifdef	ECHOKEYS
#ifdef	BSD41c
    struct timeval	timeout;
#endif	BSD41c

    if (alrmtime <= 0 || alrmtime > 100000000)
	alrmtime = 100000;
#ifndef	BSD41c
    alrmtime *= 1000;			/* convert to millisec */
#endif	not BSD41c
#endif	ECHOKEYS

readloop:
    if (err != 0)			/* check for ^G interrupts */
	return 0;

    ichans = sel_ichans; ochans = sel_ochans;
    if (chan == NULL)
	ichans &= ~1;			/* don't look at tty in this case */

    /* 
     * If we do this here, iff there is no input, then it will always
     * happen asap.
     */
    if (child_changed) {
	int c_ichans = ichans;
#ifdef	BSD41c
	timeout.tv_sec = 0; timeout.tv_usec = 0;
	if (select(32, &c_ichans, 0, 0, &timeout) <= 0)	/* if none waiting */
#else	BSD41c
	if (select(32, &c_ichans, 0, 0) <= 0)	/* if none waiting */
#endif	BSD41c
	{
	    change_msgs ();
	    child_changed = 0;
	}
    }

#ifdef	ECHOKEYS
#ifdef	BSD41c
    timeout.tv_sec = alrmtime; timeout.tv_usec = 0;
    if ((cc = select(32, &ichans, &ochans, 0, &timeout)) < 0)
#else	BSD41c
    if ((cc = select(32, &ichans, &ochans, alrmtime)) < 0)
#endif	BSD41c
	goto readloop;			/* try again */
    else
	if (cc == 0) {
	    EchoThem (1);
#ifndef	BSD41c
	    alrmtime = 10000000;
#else	not BSD41c
	    alrmtime = 10000;
#endif	not BSD41c
	}
#else	ECHOKEYS
#ifdef	BSD41c
    timeout.tv_sec = 100000; timeout.tv_usec = 0;
    if (select(32, &ichans, &ochans, 0, &timeout) < 0)
#else	BSD41c
    if (select(32, &ichans, &ochans, 1000000) < 0)
#endif	BSD41c
	goto readloop;			/* try again */
#endif	ECHOKEYS

#ifdef	TTYconnect
    AttachSocket (ichans);		/* check for a new socket */
#endif	TTYconnect

    if (ichans&1) {
        ichans &= ~1;
	cc = read(0, cbuffer, sizeof (cbuffer));
	if (cc > 0) {
	    if (child_changed) {
		change_msgs ();
		child_changed = 0;
	    }
	    mpxin->ch_ptr = cbuffer;
	    mpxin->ch_count = cc - 1;
	    stdin->_flag &= ~_IOEOF;
	    return (*mpxin->ch_ptr++ & 0377);
	}
	else if (cc == 0)
	{
	    fprintf(stderr,"null read from stdin\r\n");
	    stdin->_flag |= _IOEOF;	/* mark EOF encountered */
	    return(EOF);
	}
    }
    for (p = process_list; p != NULL; p = p->next_process) {
	this_chan = &p->p_chan;
	if (ichans & (1<<this_chan->ch_index)) {
	    ichans &= ~(1<<this_chan->ch_index);
	    cc = read(this_chan->ch_index, cbuffer, sizeof (cbuffer));
	    if (cc > 0) {
		this_chan->ch_ptr = cbuffer;
		this_chan->ch_count = cc;
		stuff_buffer(this_chan);
	    }
            else if (cc <= 0)
	    {
#ifdef	ce
		fprintf (err_file, "%s read from %s on channel %d errno=%d\n",
			cc == 0 ? "null" : "error", p -> p_name,
			this_chan -> ch_index, cc < 0 ? errno : 0);
#endif
		sel_ichans &= ~(1 << this_chan -> ch_index); /* disconnect */
		sel_ochans &= ~(1 << this_chan -> ch_index); /* disconnect */
		close (this_chan->ch_index);
#ifdef	TTYconnect
		if (p -> p_pid == -1) {/* peer dropped it */
		    p -> p_flag = EXITED | CHANGED;
		    p -> p_reason = 0;
		    child_changed++;
		}
#endif	TTYconnect
	    }
	}
	if (ochans & (1<<this_chan->ch_index)) {
	    ochans &= ~(1<<this_chan->ch_index);
	    if (this_chan->ch_outrec.ccount) {
	       cc = write(this_chan->ch_index, "", 0);
	       if (cc < 0)
		   continue;
	       this_chan->ch_outrec.ccount = 0;
	    }
	    if (this_chan->ch_outrec.count) {
	       cc = write(this_chan->ch_index,
		   this_chan->ch_outrec.data, this_chan->ch_outrec.count);
	       if (cc > 0) {
		   this_chan->ch_outrec.data += cc;
		   this_chan->ch_outrec.count -= cc;
	       }
	    }
	    if (this_chan->ch_outrec.count == 0)
		sel_ochans &= ~(1<<this_chan->ch_index);
	}
    }
    if (child_changed) {
	change_msgs ();
	child_changed = 0;
    }

    if (chan != NULL)
	goto readloop;

    return 0;
}


#ifdef	subprocesses

X/* Send any pending output as indicated in the process block to the 
   appropriate channel. */

send_chan (process)
register struct process_blk *process;
{
    register struct wh *output;

    output = &process -> p_chan.ch_outrec;
    if (output -> count == 0 && output -> ccount == 0) {
	/* error ("Null output"); */
	return 0;		/* No output to be done */
    }
    if (output->ccount) {
	if (write(output->index, "", 0) >= 0) {
	    output->ccount = 0;
	    return 0;
	}
    } else {
	if (output->count) {
	    int cc = write(output->index, output->data, output->count);
	    if (cc > 0) {
		output->data += cc;
		output->count -= cc;
	    }
	}
        if (output->count == 0)
	    return 0;
    }
    sel_ochans |= 1<<(output->index);
    return 0;			/* ACT 8-Sep-1982 */
}


X/* Kill off all active processes: done only to exit when user really
   insists. */

kill_processes () {
    register struct process_blk *p;

    for (p = process_list; p != NULL; p = p -> next_process) {
	if (active_process (p)) {
	    ioctl (p -> p_chan.ch_index, TIOCGPGRP, &(p -> p_gid));
	    if (p -> p_gid != -1)
		killpg (p -> p_gid, SIGKILL);
	    if (p -> p_pid != -1)
		killpg (p -> p_pid, SIGKILL);
	}
    }
}


X/* Send an signal to the specified process group.  Goes to leader
   (process which started whole mess) iff "leader". */

sig_process (signal, leader) register   leader; {
    register struct process_blk *process;
    struct tchars   mytchars;
    struct ltchars  myltchars;

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a process");
	return 0;
    }

#ifdef	ce
    fprintf (err_file, "Sending signal %d to proc (%d, %d), leader=%d\n",
	signal, process -> p_pid, process -> p_gid, leader);
#endif

X/* We must update the process flag explicitly in the case of continuing a 
   process since no signal will come back */
    if (signal == SIGCONT) {
	sighold (SIGCHLD);
	process -> p_flag =
	    (process -> p_flag & ~STOPPED) | RUNNING | CHANGED;
	child_changed++;
	sigrelse (SIGCHLD);
    }

    if (!leader)
	switch (signal) {
	    case SIGINT: 
		mytchars.t_intrc = -1;
		ioctl (process -> p_chan.ch_index, TIOCGETC, &mytchars);
		if (mytchars.t_intrc == -1)
		    break;
		return send_char (process, mytchars.t_intrc);

	    case SIGQUIT: 
		mytchars.t_quitc = -1;
		ioctl (process -> p_chan.ch_index, TIOCGETC, &mytchars);
		if (mytchars.t_quitc == -1)
		    break;
		return send_char (process, mytchars.t_quitc);

	    case SIGTSTP: 
		myltchars.t_suspc = -1;
		ioctl (process -> p_chan.ch_index, TIOCGLTC, &myltchars);
		if (myltchars.t_suspc == -1)
		    break;
		return send_char (process, myltchars.t_suspc);
	}
    ioctl (process -> p_chan.ch_index, TIOCGPGRP, &(process -> p_gid));

    leader = leader ? process -> p_pid : process -> p_gid;

#ifndef	TTYconnect
    if (leader != -1)
	killpg (leader, signal);
#else	not TTYconnect
    if (leader != -1)
	killpg (leader, signal);
    else
	if (process -> p_pid == -1 && signal == SIGKILL) {
	    sel_ichans &= ~(1 << process -> p_chan.ch_index);
	    sel_ochans &= ~(1 << process -> p_chan.ch_index);
	    close (process -> p_chan.ch_index);
	    sighold (SIGCHLD);
	    process -> p_flag = SIGNALED | CHANGED;
	    process -> p_reason = SIGKILL;
	    child_changed++;
	    sigrelse (SIGCHLD);
	}
#endif	not TTYconnect
    return 0;
}


X/* Send an EOT to a process. */

EOTProcess () {
    register struct process_blk *process;
    struct tchars   mytchars;

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a process");
	return (0);
    }

    mytchars.t_eofc = -1;
    ioctl (process -> p_chan.ch_index, TIOCGETC, &mytchars);
    if (mytchars.t_eofc == -1) {
	error ("Unable to determine EOT");
	return 0;
    }
    return send_char (process, mytchars.t_eofc);
}


X/* Send a special character to a process. */

static send_char (process, c)
register struct process_blk *process;
char c;
{
    register struct wh *output = &process -> p_chan.ch_outrec;

    if (output -> count || output -> ccount)
	error ("Overwriting on blocked channel");

    output -> index = process -> p_chan.ch_index;
    output -> ccount = 0;
    output -> count = 1;
    output -> data = &c;
    send_chan (process);
    return 0;
}


X/* Find the process-id of a process (or parent process). */

PID (leader) {
    register char  *p_name = getstr ("Process name: ");
    register struct process_blk *process;

    MLvalue -> exp_type = IsInteger;
    process = find_process (p_name);
    if (process == NULL)
	MLvalue -> exp_int = 0;
    else {
	ioctl (process -> p_chan.ch_index, TIOCGPGRP, &(process -> p_gid));
	MLvalue -> exp_int = leader ? process -> p_pid : process -> p_gid;
    }
    return 0;
}
#endif	subprocesses


X/* Initialize the PTYio system. */

X/* When a connection closes, any write()s to it will cause a SIGPIPE to
   be given to us.  By ignoring the signal, write() will return NOTOK
   after setting errno = EPIPE.  The relevant routines should test for
   this after a losing write().  In reality though, when the peer closes
   the connection, we'll find out via select() and an error read().
   Hence, fill_chan() will handle things for us. */

InitProcesses () {
#ifdef	TTYconnect
#ifndef	TTYD
    struct stat st;
#endif	not TTYD
#endif	TTYconnect

    mpxin -> ch_index = 0;
    mpxin -> ch_ptr = NULL;
    mpxin -> ch_count = 0;

#ifdef	subprocesses
    sel_ichans = 1 << 0;	/* stdin */

    ioctl (0, TIOCGETP, &mysgttyb);
    mysgttyb.sg_flags = EVENP | ODDP;
    ioctl (0, TIOCGETC, &mytchars);
    ioctl (0, TIOCGLTC, &myltchars);
    ioctl (0, TIOCLGET, &mylmode);

#ifdef	TTYconnect
    sigset (SIGPIPE, SIG_IGN);

#ifndef	TTYD
    if (fstat (0, &st) == NOTOK)
	quit (1, "fstat failed on stdin\n");
    tty_port = IPPORT_EMACS | minor (st.st_rdev);
#else	not TTYD
    gethostname (myhost, sizeof myhost);
    tact ("push", NULL);
#endif	not TTYD

#ifdef	mtr
    if (access ("/tmp/emacs.tcpdebug", 6) == NOTOK)
	unlink ("/tmp/emacs.tcpdebug");
    if ((log_file = fopen ("/tmp/emacs.tcpdebug", "a")) != NULL) {
	setbuf (log_file, NULL);
	fprintf (log_file, "tty_port=%d\n", tty_port);
#ifdef	TTYD
	fprintf (log_file, "myhost=%s\n", myhost);
#endif	TTYD
	chmod ("/tmp/emacs.tcpdebug", 0666);
    }
#endif

#ifndef	BSD41c
    sd = NOTOK;
#else	not BSD41c
    StartTtyAccept ();
#endif	not BSD41c
#endif	TTYconnect
#endif	subprocesses
}

X/* named this way for historical reasons... */

QuitMpx () {
#ifdef	TTYconnect
#ifdef	TTYD
    tact ("pop", NULL);
#endif	TTYD
#ifdef	mtr
    if (log_file)
	fclose (log_file);
#endif
#endif	TTYconnect
}

X/* This isn't quite correct, the close() should do it, but Unix doesn't
   fully cooperate with us -- it sometimes will tell other processes that
   the port is still open for business. */

SuspendMpx () {
#ifdef	TTYconnect
#ifdef	TTYD
    tact ("pop", NULL);
#endif	TTYD
#ifndef	BSD41c
#ifndef	TTYD
    if (sd != NOTOK) {
	sel_ichans &= ~(1 << sd);
	close (sd);
	sd = NOTOK;
    }
#endif	not TTYD
#else	not BSD41c
    sel_ichans &= ~(1 << sd);
    close (sd);
#endif	not BSD41c
#endif	TTYconnect
}

ResumeMpx () {
#ifdef	TTYconnect
#ifdef	TTYD
    tact ("push", NULL);
    if (sd != NOTOK) {
	int     i;
	struct sockaddr_in *tsock = &tty_socket;

	if ((i = socketaddr (sd, tsock)) != NOTOK) {
#ifdef	vax
	    tsock -> sin_port = htons (tsock -> sin_port);
#endif
	    i = tact ("port", tsock);
	}
	if (i == NOTOK) {
	    sel_ichans &= ~(1 << sd);
	    close (sd);
	    sd = NOTOK;
	}
    }
#endif	TTYD
#ifdef	BSD41c
    StartTtyAccept ();
#endif	BSD41c
    if (EmacsShare == NOTOK)	/* retry from error */
	EmacsShare = 1;
#endif	TTYconnect
}


#ifdef	TTYconnect

static	AttachSocket (mask)
int mask;
{
    int     enabled = (EmacsShare > 0) && sflag;
    struct sockaddr_in *usock = &unx_socket;
#ifdef	BSD41c
    int	    s, usocklen;
#endif	BSD41c

#ifndef	BSD41c
    if (sd == NOTOK) {
	if (enabled)
	    NewSocket ();
	return;
    }
#endif	not BSD41c

    if (!(mask & (1 << sd)))
	return;

#ifndef	BSD41c
    if (accept (sd, usock) == NOTOK) {
#else	not BSD41c
    usocklen = sizeof (*usock);
    if ((s = accept (sd, usock, &usocklen)) == NOTOK) {
#endif	not BSD41c
	switch (errno) {
	    default: 
		message ("unable to complete socket: %s",
			errno > 0 && errno <= sys_nerr ? sys_errlist[errno]
			: "unknown reason");
		EmacsShare = NOTOK;

	    case ECONNRESET: 	/* we were not quick enough... */
	    case EISCONN:	/* should not happen */
	    case EWOULDBLOCK:	/* select lied to us */
#ifdef	mtr
		if (log_file)
		    fprintf (log_file, "unable to complete socket: %d\n",
			    errno);
#endif
		break;
	}
#ifndef	BSD41c
	sel_ichans &= ~(1 << sd);
	close (sd);
	sd = NOTOK;
	if (enabled)
	    NewSocket ();
#endif	not BSD41c
	return;
    }

#if vax
    usock -> sin_port = ntohs (usock -> sin_port);
#endif

#ifdef	BSD41c
    sel_ichans |= (1 << s);
#endif	BSD41c
    if (!enabled) {		/* we do not want it now */
#ifndef	BSD41c
	sel_ichans &= ~(1 << sd);
	close (sd);
	sd = NOTOK;
#else	not BSD41c
	sel_ichans &= ~(1 << s);
	close (s);
#endif	not BSD41c
	return;
    }

#ifdef	mtr
    if (log_file) {
	struct sockaddr_in *tsock = &tty_socket;
	socketaddr (sd, tsock);
#ifdef	vax
	tsock -> sin_port = htons (tsock -> sin_port);
#endif
	fprintf (log_file,
		"socket completed from port_%s:%d to port_%s:%d\n",
		RAddr (&(tsock -> sin_addr)), tsock -> sin_port,
		RAddr (&(usock -> sin_addr)), usock -> sin_port);
    }
#endif

    if (usock -> sin_family != AF_INET) {/* wrong family */
#ifndef	BSD41c
	sel_ichans &= ~(1 << sd);
	close (sd);
	sd = NOTOK;
#else	not BSD41c
	sel_ichans &= ~(1 << s);
	close (s);
#endif	not BSD41c
    }
    else
#ifndef	BSD41c
	BuildIt ();
#else	not BSD41c
	BuildIt (s);
#endif	not BSD41c

#ifndef	BSD41c
    NewSocket ();
#endif	not BSD41c
}


#ifndef	BSD41c

static  NewSocket () {
    struct sockaddr_in *tsock = &tty_socket;

    if (!sflag)
	return;

    if ((sd = getport (tsock, SO_OPTIONS)) == NOTOK)
	switch (errno) {
	    default:		/* perhaps do something else here??? */
		message ("unable to start socket: %s",
			errno > 0 && errno <= sys_nerr ? sys_errlist[errno]
			: "unknown reason");

#ifndef	TTYD
	    case EADDRINUSE:	/* another Emacs on this tty */
	    case EADDRNOTAVAIL:	/* should not happen */
#endif	not TTYD
		EmacsShare = NOTOK;/* enough of this nonsense */
#ifdef	mtr
		if (log_file)
		    fprintf (log_file, "unable to start socket: %d\n",
			    errno);
#endif
		return;
	}

    sel_ichans |= 1 << sd;
}

static int  getport (tsock, options)
struct sockaddr_in  *tsock;
unsigned    options;
{
    int     block = 1;
    int     fd;
#ifdef	TTYD
    int     port;
#endif	TTYD

    tsock -> sin_family = AF_INET;
#ifndef	TTYD
    tsock -> sin_port = tty_port;
#ifdef	vax
    tsock -> sin_port = htons (tsock -> sin_port);
#endif
#endif	not TTYD
    tsock -> sin_addr.s_addr = (u_long) INADDR_ANY;

#ifndef	TTYD
    if ((fd = socket (SOCK_STREAM, NULL, tsock, options)) == NOTOK)
	return NOTOK;
#ifdef	vax
    tsock -> sin_port = ntohs (tsock -> sin_port);
#endif
#else	not TTYD
    for (port = IPPORT_RESERVED + 1;; port++) {
	tsock -> sin_port = port;
#ifdef	vax
	tsock -> sin_port = htons (tsock -> sin_port);
#endif

	if ((fd = socket (SOCK_STREAM, NULL, tsock, options)) == NOTOK)
	    switch (errno) {
		case EADDRINUSE:/* to be expected */
		case EADDRNOTAVAIL:/* should not happen */
		    continue;

		default: 
		    return NOTOK;
	    }
#ifdef	vax
	tsock -> sin_port = ntohs (tsock -> sin_port);
#endif
	break;
    }
    if (tact ("port", tsock) == NOTOK) {
	close (fd);
	return NOTOK;
    }
#endif	not TTYD

    ioctl (fd, FIONBIO, &block);
    return fd;
}
#else	not BSD41c

static	StartTtyAccept()
{
    struct sockaddr_in *tsock = &tty_socket;
    int block = 1;

    if (!sflag) {
	sd = NOTOK;
	return;
    }

    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd < 0)
	switch (errno) {
	    default:		/* perhaps do something else here??? */
		message ("unable to start socket: %s",
			errno > 0 && errno <= sys_nerr ? sys_errlist[errno]
			: "unknown reason");

		EmacsShare = NOTOK;/* enough of this nonsense */
#ifdef	mtr
		if (log_file)
		    fprintf (log_file, "unable to start socket: %d\n",
			    errno);
#endif
		return;
	}/*esac*/

    tsock -> sin_family = AF_INET;
    tsock -> sin_port = tty_port;
#ifdef	vax
    tsock -> sin_port = htons (tsock -> sin_port);
#endif
    tsock -> sin_addr.s_addr = (u_long) INADDR_ANY;

    setsockopt(sd, SOL_SOCKET, SO_DONTLINGER, (char *) 0, 0);
    setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, (char *) 0, 0);
    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) 0, 0);
    if(bind(sd, tsock, sizeof(*tsock)) < 0)
	switch (errno) {
	    default:		/* perhaps do something else here??? */
		message ("unable to bind socket: %s",
			errno > 0 && errno <= sys_nerr ? sys_errlist[errno]
			: "unknown reason");

	    case EADDRINUSE:	/* another Emacs on this tty */
	    case EADDRNOTAVAIL:	/* should not happen */
		EmacsShare = NOTOK; /* enough of this nonsense */
#ifdef	mtr
		if (log_file)
		    fprintf (log_file, "unable to bind socket: %d\n",
			    errno);
#endif
		return;
	}/*esac*/
#ifdef	vax
    tsock -> sin_port = ntohs (tsock -> sin_port);
#endif

    listen(sd, 5);
    ioctl(sd, FIOCLEX, 0);
    sel_ichans |= 1 << sd;
}
#endif	not BSD41c

#ifndef	BSD41c
static  BuildIt () {
#else	not BSD41c
static	BuildIt (s) {
#endif	not BSD41c
    int     i;
    char    name[50],
            port[40];
    struct in_addr *addr = &unx_socket.sin_addr;
    struct buffer  *old = bf_cur;
    struct process_blk *p;

    sighold (SIGCHLD);

    sprintf (port, "port_%s/%d", RAddr (addr), unx_socket.sin_port);
    for (i = 2, strcpy (name, port); find_process (name); i++)
	sprintfl (name, sizeof name, "%s<%d>", port, i);

    p = (struct process_blk *) malloc ((unsigned) sizeof *p);
    if (p == NULL) {
	sigrelse (SIGCHLD);
	message ("out of memory");
#ifndef	BSD41c
	sel_ichans &= ~(1 << sd);
	close (sd);
#else	not BSD41c
	sel_ichans &= ~(1 << s);
	close (s);
#endif	not BSD41c
	sd = NOTOK;
	return;
    }

    SetBfn (name);
    if (PopUpUnexpected)
	WindowOn (bf_cur);
    bf_modified = 0;
    bf_cur -> b_mode.md_NeedsCheckpointing = 0;

#ifndef	BSD41c
    sel_ichans |= 1 << sd;	/* not really needed */
#endif	not BSD41c
    p -> next_process = process_list;
    process_list = p;
    p -> p_name = savestr (name);
    p -> p_pid = p -> p_gid = -1;
    p -> p_flag = RUNNING | CHANGED;
#ifndef	BSD41c
    p -> p_chan.ch_index = sd;
    p -> p_chan.ch_outrec.index = sd;
#else	not BSD41c
    p -> p_chan.ch_index = s;
    p -> p_chan.ch_outrec.index = s;
#endif	not BSD41c
    p -> p_chan.ch_outrec.count = p -> p_chan.ch_outrec.ccount = 0;
    p -> p_chan.ch_outrec.data = NULL;
    p -> p_chan.ch_ptr = NULL;
    p -> p_chan.ch_count = 0;
    p -> p_chan.ch_buffer = bf_cur;
    p -> p_chan.ch_proc = UnexpectedProc;
    p -> p_chan.ch_sent = UnexpectedSent;

    sigrelse (SIGCHLD);

    SetBfp (old);
    if (PopUpUnexpected)
	WindowOn (bf_cur);
}

#ifdef	TTYD

static int  tact (cmd, sock)
char   *cmd;
struct sockaddr_in *sock;
{
    extern int  subproc_id,
                child_sig ();
#ifdef	mtr
    if (log_file)
	fprintf (log_file, sock ? "tact(%s,%d)\n" : "tact(%s,NULL)\n",
		cmd, sock ? sock -> sin_port : 0);
#endif
    if (!sflag)
	return NOTOK;

    if (sock)
	if (tty_port == sock -> sin_port)
	    return;
	else
	    tty_port = sock -> sin_port;
    else
	tty_port = NOTOK;

    sigset (SIGCHLD, child_sig);/* may not be set yet */

    sighold (SIGCHLD);
    switch (subproc_id = vfork ()) {
	case NOTOK: 
	    return NOTOK;

	case OK: 
	    sigrelse (SIGCHLD);
	    setpgrp (0, getpid ());
	    sigsys (SIGINT, SIG_IGN);
	    sigsys (SIGQUIT, SIG_IGN);
	    sigsys (SIGTERM, SIG_IGN);
	    sigsys (SIGTSTP, SIG_IGN);
	    sigsys (SIGTTOU, SIG_IGN);
	    close (0);
	    open ("/dev/null", 0);
	    dup2 (0, 1);
	    if (sock) {
		sprintfl (myport, sizeof myport, "%d", sock -> sin_port);
		execlp ("tact", "tact", "-quiet", cmd, myhost, myport, NULL);
	    }
	    else
		execlp ("tact", "tact", "-quiet", cmd, NULL);
	    _exit (1);

	default: 
#ifdef	mtr
	    if (log_file)
		fprintf (log_file, "begin wait for %d\n", subproc_id);
#endif		
	    sigrelse (SIGCHLD);
	    for (sighold (SIGCHLD); subproc_id; sighold (SIGCHLD)) {
#ifdef	mtr
		if (log_file)
		    fprintf (log_file, "waiting for %d\n", subproc_id);
#endif		
		sigpause (SIGCHLD);
	    }
	    sigrelse (SIGCHLD);
	    return OK;
    }
}
#endif

static char	*RAddr(ip)
struct	in_addr	*ip;
{
    static char host[200];
    char   *p,
           *raddr ();

    if (p = raddr ((int) (ip -> s_addr))) {
	strcpy (host, p);
	free (p);
	return host;
    }

    sprintfl (host, sizeof host, "%d.%d.%d.%d",
	    ip -> S_un.S_un_b.s_b1, ip -> S_un.S_un_b.s_b2,
	    ip -> S_un.S_un_b.s_b3, ip -> S_un.S_un_b.s_b4);
    return host;
}
#endif	TTYconnect

#endif	not MPXcode
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 pchan.c
	/bin/echo -n '	'; /bin/ls -ld pchan.c
fi
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris.umcp-cs@UDel-Relay