[net.sources] Emacs upgrade part 1

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

This is the first of many scripts containing mods to Gosling Emacs
to give it new features, fix a few bugs here and there, and to make
it compatible with 4.1a and 4.1c BSD.  (I'm not sure yet just how
many scripts I'll post, since I haven't made all of them yet.)
Instructions will be in one of the later postings.

Chris ("I could swear I left in it /tmp someplace") Torek
-------------------------------------------
: 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 config.h'
sed 's/^X//' <<'//go.sysin dd *' >config.h
X/* Emacs configuration file -- all site-dependant definitions should be made
   here.  Each site should only have to edit this file. */

#ifndef SystemName
#define SystemName "umcp-cs"
#endif
				/* Define this symbol to be a string that
				   represents the name of your site */
#define BackupExtension ".bak"	/* This string gets appended to filenames to
				   generate the file name used for making
				   backups.  The folks at BBN like to use the
				   string "~" because it takes up fewer
				   characters. */
#define CheckpointExtension ".CKP"	/* This string gets appended to
					   filenames to generate the file
					   name used for making
					   checkpoints. */
X/* #define PrependExtension	/* If this is defined then the backup and
				   checkpoint extensions will be prepended to
				   the file name, rather than appended.  Some
				   folks like to use (for example) # as the
				   first character of a filename to indicate
				   that it can be deleted if it's more than
				   a few days old. */
#define subprocesses		/* Define this symbol if you want the
				   subprocess control stuff.  This works
				   on modified 4.1BSD Unix systems, and on
				   4.1a/4.1c systems.  On 4.1, it uses the
				   mpxio facility.
				   Even those sites that can run it might not
				   want to, since it induces people to use
				   more cycles. */
#ifndef PATH_LOADSEARCH
#define PATH_LOADSEARCH ":/usr/emacs/maclib:/usr/emacs/loclib"
#endif
				/* the default search path for loading macro
				   packages */
#define DefaultProfile "/usr/emacs/maclib/profile.ml"
				/* If a user doesn't have a ".emacs_pro" in
				   their home directory, then the
				   DefaultProfile file is used as their
				   profile instead */
#define OneEmacsPerTty		/* Define this symbol if only one Emacs is
				   allowed to run per tty.  This is usually
				   only necessary to get around an obnoxious
				   bug in the mpxio facility which is used
				   when the subprocess control feature is
				   used.  If you define subprocesses, then
				   you should define this symbol -- unless
				   you have fixed the kernel bug */
#define OneEmacsWarning		/* Define this symbol to make it harder for
				   people to run more than one Emacs. */
#define MailOriginator	((char *) FullNameFromGecos(pw))
X/* #define MailOriginator pw->pw_gecos */
				/* MailOriginator should be an expression
				   that will evaluate to the name of the
				   originator of a message.  "pw" is set to
				   point to a passwd struct for the current
				   user.  At CMU we put a users full name in
				   the pw_gecos field, so we use that as the
				   name for the originator -- our mail system
				   understands such full names as message
				   destinations.  Other folks might want to
				   use pw->pw_name.  This is also used to
				   evaluate users-full-name. */
#define AddSiteName		/* Define this if you want the name of the
				   origninating site to be added to the
				   "from" field of outgoing mail. */
#define CatchSig		/* Define this to catch SIGINT and SIGTERM
				   (although they may have to be sent from
				   another terminal) */
#define MPXcode			/* Define this to get the 4.1BSD mpx-file-
				   based multiprocessing; otherwise the
				   4.1[ac] ptys will be used. */
X/* #define TTYconnect		/* Define this to get the 4.1[ac] ipc
				   features for "unexpected" opens.  You
				   always get this when MPXcode is defined. */
#define	ECHOKEYS		/* Define this to enable echo-keystrokes */
X/* #define BSD41c		/* Define this if you are running 4.1c. */
X/* #define LIBNDIR		/* Define this to use the directory I/O
				   library (4.1[ac]). */
#define	MaxPathNameLen 500	/* Maximum path name length expected.
				   Many routines will die if this is
				   exceeded, so make it big enough!! */
#define	EmacsVersionNum 85	/* Version number (for schan.c) -- be sure
				   to change to 264 if running Emacs #264! */
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 config.h
	/bin/echo -n '	'; /bin/ls -ld config.h
fi
/bin/echo 'Extracting makefile'
sed 's/^X//' <<'//go.sysin dd *' >makefile
DESTDIR=/usr/local/lib/emacs
EMDESTDIR=/usr/local
LIBES=
# DumpableEmacs is Spencer Thomas's dump-emacs facility
# newmalloc is the CalTech power-of-2 malloc, suitably modified
CFLAGS=-O -R -DNewC -DDumpableEmacs -Dnewmalloc
obj1=buffer.o display.o emacs.o dsp.o window.o keyboard.o simplecoms.o \
	minibuf.o fileio.o windowman.o search.o subproc.o metacoms.o \
	errlog.o TrmC100.o TrmAmb.o columns.o options.o TrmTERM.o \
	macros.o casefiddle.o TrmI400.o mlisp.o ndbm.o \
	arithmetic.o abbrev.o sindex.o TrmVT100.o undo.o \
	syntax.o TrmMiniB.o TrmTEK4025.o mchan.o pchan.o schan.o \
	dbmanager.o abspath.o 2malloc.o unexec.o \
	UMfuncs.o filecomp.o TrmBitG.o TrmGT40.o bcopy.o TrmGigi.o sleep.o \
	TrmHP.o
obj=lispfuncs.o ${obj1}
cvlobj=cvllispfuncs.o ${obj1}
eeobj=eelispfuncs.o ${obj1}
first: temacs
all: temacs makemail collectmail grep dbadd dbprint dblist \
	loadst filesort newloadst
collectmail: collectmail.c sindex.o config.h UMfuncs.o
	cc -o collectmail -g collectmail.c sindex.o UMfuncs.o ${LIBES}
makemail: makemail.c config.h UMfuncs.o
	cc -o makemail -g makemail.c UMfuncs.o ${LIBES}
temacs: ${obj}
	rm -f /tmp/Emacs-
	emacs -d -q -l./version.ml -eversion </dev/null >/dev/null
	rm -f temacs
	cc -z ${CFLAGS} version.c ${obj} -ltermlib -ljobs -o temacs ${LIBES}
cvltemacs: ${cvlobj}
	rm -f cvltemacs
	cc -z ${CFLAGS} version.c ${cvlobj} -ltermlib -ljobs -o cvltemacs ${LIBES}
eetemacs: ${eeobj}
	rm -f eetemacs
	cc -z -s ${CFLAGS} version.c ${eeobj} -ltermlib -ljobs -o eetemacs ${LIBES}
pemacs: ${obj}
	rm -f pemacs
	cc -z -p ${CFLAGS} version.c ${obj} -ltermlib -ljobs -o pemacs ${LIBES}
loadst: loadst.c
	cc -O -s -DSETUID loadst.c -o loadst ${LIBES}
	chmod u+s loadst
newloadst: loadst.c
	cc -O -s -DSETUID -DNEWONLY loadst.c -o newloadst ${LIBES}
	chmod u+s newloadst
TrmAmb.o: TrmAmb.c Trm.h keyboard.h
TrmC100.o: TrmC100.c Trm.h display.h
TrmI400.o: TrmI400.c Trm.h
TrmMiniB.o: TrmMiniB.c Trm.h display.h
TrmTEK4025.o: TrmTEK4025.c Trm.h display.h
TrmTERM.o: TrmTERM.c Trm.h keyboard.h config.h
TrmGT40.o: TrmGT40.c Trm.h
TrmBitG.o: TrmBitG.c Trm.h
TrmGigi.o: TrmGigi.c Trm.h
TrmHP.o: TrmHP.c Trm.h
TrmVT100.o: TrmVT100.c Trm.h
abbrev.o: abbrev.c abbrev.h buffer.h window.h keyboard.h syntax.h macros.h mlisp.h
abspath.o: abspath.c config.h keyboard.h mlisp.h
arithmetic.o: arithmetic.c mlisp.h keyboard.h buffer.h window.h
buffer.o: buffer.c config.h buffer.h syntax.h abbrev.h keyboard.h mchan.h mlisp.h
casefiddle.o: casefiddle.c buffer.h window.h keyboard.h syntax.h
columns.o: columns.c buffer.h window.h
dbmanager.o: dbmanager.c ndbm.h buffer.h window.h keyboard.h
display.o: display.c display.h Trm.h mlisp.h
dsp.o: dsp.c buffer.h display.h window.h buffer.h config.h keyboard.h Trm.h mlisp.h
emacs.o: emacs.c buffer.h keyboard.h macros.h keyboard.h config.h mlisp.h
errlog.o: errlog.c buffer.h window.h keyboard.h search.h
fileio.o: fileio.c keyboard.h window.h buffer.h config.h mlisp.h
keyboard.o: keyboard.c keyboard.h window.h buffer.h config.h mchan.h mlisp.h
lispfuncs.o: lispfuncs.c buffer.h window.h macros.h mlisp.h config.h keyboard.h
cvllispfuncs.o: lispfuncs.c buffer.h window.h macros.h mlisp.h config.h keyboard.h
	$(CC) '-DSystemName="cvl"' $(CFLAGS) -c -S lispfuncs.c
	as -o cvllispfuncs.o lispfuncs.s
	@rm -f lispfuncs.s
eelispfuncs.o: lispfuncs.c buffer.h window.h macros.h mlisp.h config.h keyboard.h
	$(CC) '-DSystemName="eneevax"' '-DPATH_LOADSEARCH=":/p/emacs/maclib:/p/emacs/loclib"' $(CFLAGS) -c -S lispfuncs.c
	as -o eelispfuncs.o lispfuncs.s
	@rm -f lispfuncs.s
macros.o: macros.c  keyboard.h macros.h buffer.h mlisp.h
mchan.o: mchan.c config.h window.h keyboard.h buffer.h mlisp.h macros.h mchan.h
pchan.o: pchan.c config.h window.h keyboard.h buffer.h mlisp.h macros.h mchan.h
schan.o: schan.c config.h window.h keyboard.h buffer.h mlisp.h macros.h mchan.h
filecomp.o: buffer.h window.h keyboard.h mlisp.h
metacoms.o: metacoms.c buffer.h window.h keyboard.h syntax.h syntax.h mlisp.h
minibuf.o: minibuf.c keyboard.h window.h buffer.h mlisp.h
mlisp.o: mlisp.c keyboard.h mlisp.h buffer.h window.h macros.h config.h search.h Trm.h
options.o: options.c buffer.h window.h macros.h config.h mlisp.h keyboard.h
search.o: search.c keyboard.h window.h buffer.h syntax.h mlisp.h search.h
simplecoms.o: simplecoms.c keyboard.h window.h buffer.h mlisp.h macros.h
subproc.o: subproc.c keyboard.h window.h buffer.h mlisp.h
syntax.o: syntax.c syntax.h buffer.h window.h keyboard.h
undo.o: undo.c undo.h buffer.h window.h keyboard.h
window.o: config.h window.c buffer.h display.h window.h Trm.h mlisp.h
windowman.o: windowman.c buffer.h window.h keyboard.h mlisp.h
2malloc.o: 2malloc.c buffer.h keyboard.h window.h
sindex.o: sindex.c
	cc -c -O sindex.c
grep: grep.c
	cc grep.c -o grep
dbadd: ndbm.h ndbm.o dbadd.o
	cc -o dbadd dbadd.o ndbm.o ${LIBES}
dbprint: ndbm.h ndbm.o dbprint.o
	cc -o dbprint dbprint.o ndbm.o ${LIBES}
dblist: ndbm.h ndbm.o dblist.o
	cc -o dblist dblist.o ndbm.o ${LIBES}
ndbm.o : ndbm.h ndbm.c
filesort: filesort.c sindex.o
	cc -g filesort.c sindex.o -o filesort ${LIBES}

install:
	@echo "Don't use install for normal installation; use 'make sys'"
	@echo "(Use 'really-install' to install all)"
really-install: all # mv -f temacs $(EMDESTDIR)/emacs
	cp makemail $(DESTDIR)
	cp collectmail $(DESTDIR)
	cp loadst $(DESTDIR)
	chmod u+s $(DESTDIR)/loadst
	cp newloadst $(DESTDIR)
	chmod u+s $(DESTDIR)/newloadst
#	cp grep $(DESTDIR)
#	cp dbadd $(DESTDIR)
#	cp dbprint $(DESTDIR)
#	cp dblist $(DESTDIR)
#	cp loadst $(DESTDIR)
#	cp dbcreate $(DESTDIR)
clean:
	rm -f ${obj} temacs pemacs cvltemacs cvllispfuncs.o

sys:	temacs cvlsys eetemacs		# Sets up with sticky bit & all
	@-size temacs /usr/local/emacs
	-chmod 755 /usr/local/emacs
	-/usr/local/emacs -q -eexit-emacs </dev/null >/dev/null
	rm -f /usr/local/emacs
	mv temacs /usr/local/emacs
#	strip /usr/local/emacs
	chmod 1755 /usr/local/emacs

cvlsys:	cvltemacs
#	strip cvltemacs
	uucp -c cvltemacs cvl!~uucp/emacs
	mail -s 'A new emacs is in ~uucp/emacs' cvl!staff </dev/null
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 makefile
	/bin/echo -n '	'; /bin/ls -ld makefile
fi
/bin/echo 'Extracting mchan.h'
sed 's/^X//' <<'//go.sysin dd *' >mchan.h
#define debug(x) fprintf(stderr,"x:%d\n",x);fflush(stderr)
#define debugh(x) fprintf(stderr,"x");fflush(stderr)

#ifdef ECHOKEYS
#define mpx_getc(p,t) (--(p)->ch_count>=0? *(p)->ch_ptr++&0377:fill_chan(p,t))
#else
#define mpx_getc(p) (--(p)->ch_count>=0? *(p)->ch_ptr++&0377:fill_chan(p))
#endif
#define mpxin (&stdin_chan)
#ifdef MPXcode
#define index_t unsigned short	/* Was `int' but is `short' in <sys/mx.h> */
#else
#define	index_t int		/* Is `int' again for 4.1a */
#endif

#define STOPPED		(1<<0)
#define RUNNING		(1<<1)
#define EXITED		(1<<2)
#define	SIGNALED	(1<<3)
#define	COREDUMPED	(1<<4)
#define CHANGED		(1<<6)

#define active_process(x) ((x!=NULL)&& (x->p_flag&(RUNNING|STOPPED)))

extern int errno;

#ifdef MPXcode
X/* mpx_msg is the structure of a mpx control message read from the mpx file
*/
struct mpx_msg {
    short mpx_code, mpx_arg;
    struct sgttyb mpx_ioctl;
};
#else
struct wh {
    short    index, count, ccount;
    char    *data;
};
#endif
X/* Structure records pertinent information about channels open on the mpx file
   There is one channel associated with each process.
*/
struct channel_blk
    {index_t ch_index;
     struct wh ch_outrec;	/* Output record */
     char *ch_ptr;		/* Pointer to next input character */
     short ch_count;		/* Count of characters remaining in buffer */
     struct buffer *ch_buffer;	/* Process is bound to this buffer */
     struct BoundName *ch_proc;	/* Procedure which gets called on output */
     struct BoundName *ch_sent;	/* Procedure which gets called on exit */
    };       

X/* Standard input is connected to the multiplexed file using a channel 
   described by stdin_chan.
*/
extern struct channel_blk stdin_chan;

X/* Structure for information needed for each sub process started */

struct process_blk {
    struct process_blk *next_process;	/* link to next process */
    char *p_name;			/* command that started process */
    short p_pid;			/* process id */
    short p_gid;			/* process pgrp */
    char p_flag;			/* RUNNING, STOPPED, etc */
    char p_reason;			/* signal causing p_flag */
    struct channel_blk p_chan;		/* process i/o connected to channel */
};

struct process_blk *process_list,	/* all existing processes */
                   *current_process;	/* the one that we're currently
					   dealing with */
int sflag;			/* the -s command line switch (enables the
				   share-emacs facility) */
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 mchan.h
	/bin/echo -n '	'; /bin/ls -ld mchan.h
fi
/bin/echo 'Extracting schan.c'
sed 's/^X//' <<'//go.sysin dd *' >schan.c
X/* These procedures hope to give EMACS a facility for dealing with multiple
   processes in a reasonable way */

X/* This file used to be known as mchan.c - it's now called schan.c.
   It contains the general multiple process handling routines.
   Specific routines, which deal with low-level MPXio or PTYio (depending
   on the setting of MPXcode) can be found in mchan.c or pchan.c
   (respectively).

 * Among the things found in this version
	- support for csh
	- unexpected processes
	- process sentinels

 * The original MPXio code is (c) 1981 Carl Ebeling

 * The changes for MPXio to handle this were made by Chris Torek
   <Chris@Umcp-CS>

 * The original changes to convert to PTYio are (c) 1982 William N. Joy and
   Regents of UC

 * Their code didn't quite work right, so Spencer Thomas
   <UTAH-GR.thomas@Utah-Cs> fixed things up

 * Marshall Rose <MRose@UCI> made some more changes for 4.1a, and finally
   split mchan.c into three files.

 * Changes for 4.1c by Chris Kent at DecWRL, incorporated by Spencer Thomas.

 */

X/* Both mchan and pchan contain these routines used by schan (and others):
	create_process - starts a subprocess (on a PTY or MPX channel)
	fill_chan - read from subprocesses
	kill_processes - terminate all subprocesses without predjudice
	sig_process - send a signal to a subprocess (or process group)
	EOTProcess - send an EOT to a process group
	PID - return the process-id of a subprocess (or process group)
	InitProcesses - initialize for subprocess handling
	QuitMPX - clean-up after subprocess handling
	SuspendMPX - temporarily stop subprocess handling
	ResumeMPX - resume subprocess handling

   In addition, mchan and pchan contain some static routines for internal use.
 */


#include "config.h"

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


#ifdef subprocesses

#define ChunkSize 500		/* amount to truncate when buffer overflows */

int	PopUpUnexpected;	/* True iff unexpected opens pop up windows */
int	EmacsShare;		/* Share flag, true iff opens allowed */
struct BoundName
	*UnexpectedProc;	/* What to do with unexpected procs */
struct BoundName
	*UnexpectedSent;	/* What to do when unexpected procs exit */
static	ProcessBufferSize;	/* Maximum size for process buffer */

#ifdef ce
FILE *err_file;			/* Debugging file */
int	err_id;			/* User's ID for error messages */
#endif

struct channel_blk
	stdin_chan;

int	child_changed;		/* Flag when a child process changes status */
struct VariableName
	*MPX_process;
struct channel_blk
	*MPX_chan;

char   *SIG_names[] = {		/* descriptive (?) names of signals */
    "",
    "Hangup",
    "Interrupt",
    "Quit",
    "Illegal instruction",
    "Trace/BPT trap",
    "IOT trap",
    "EMT trap",
    "Floating exception",
    "Killed",
    "Bus error",
    "Segmentation fault",
    "Bad system call",
    "Broken pipe",
    "Alarm clock",
    "Terminated",
#ifdef	SIGURG
    "Urgent I/O condition",
#else
    "Signal 16",
#endif
    "Stopped (signal)",
    "Stopped",
    "Continued",
    "Child exited",
    "Stopped (tty input)",
    "Stopped (tty output)",
    "Tty input interrupt",
    "Cputime limit exceeded",
    "Filesize limit exceeded",
    "Signal 26",
    "Signal 27",
    "Signal 28",
    "Signal 29",
    "Signal 30",
    "Signal 31",
    "Signal 32"
};

static char *KillNames[] = {	/* names used for signal-to-process */
    "",     "HUP",  "INT",  "QUIT", "ILL",  "TRAP", "IOT",  "EMT",
    "FPE",  "KILL", "BUS",  "SEGV", "SYS",  "PIPE", "ALRM", "TERM",
#ifdef	SIGURG
    "URG",
#else
    "",     
#endif
	    "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU",
#ifdef	SIGTINT
"TINT",
#else
#ifdef	SIGIO
    "IO",
#else
    "",
#endif
#endif
    "XCPU", "XFSZ"
};

#endif


X/* Process a signal from a child process and make the appropriate change in 
   the process block. Since signals are NOT queued, if two signals are
   received before this routine gets called, then only the first process in
   the process list will be handled.  We will try to get the MPX file stuff
   to help us out since it passes along signals from subprocesses.
*/
int	subproc_id;		/* The process id of a subprocess
				   started by the old subproc stuff.
				   We will zero it so they will know it
				   has finished */
child_sig () {
    register int    pid;
    union wait w;
#ifdef	subprocesses
    register struct process_blk *p;
    extern struct process_blk  *get_next_process ();
#endif

loop: 
    pid = wait3 (&w.w_status, WUNTRACED | WNOHANG, 0);
#ifdef	ce
    fprintf (err_file, "Wait3 pid %d w_status 0x%x\n", pid, w.w_status);
#endif
    if (pid <= 0) {
	if (errno == EINTR) {
	    errno = 0;
	    goto loop;
	}
#ifdef subprocesses
	if (pid == -1) {
	    if (!active_process (current_process))
		current_process = get_next_process ();
	}
#endif
	return;
    }
    if (pid == subproc_id) {	/* It may not be our progeny */
	subproc_id = 0;		/* Take care of those subprocesses first 
				*/
	goto loop;
    }

#ifdef subprocesses
    for (p = process_list; p != NULL; p = p -> next_process)
	if (pid == p -> p_pid)
	    break;
    if (p == NULL)
	goto loop;		/* We don't know who this is */

    if (WIFSTOPPED (w)) {
	p -> p_flag = STOPPED | CHANGED;
	p -> p_reason = w.w_stopsig;
    child_changed++;
    }
    else
	if (WIFEXITED (w)) {
	    p -> p_flag = EXITED | CHANGED;
	    child_changed++;
	    p -> p_reason = w.w_retcode;
	}
	else
	    if (WIFSIGNALED (w)) {
		p -> p_flag = SIGNALED | CHANGED;
		if (w.w_coredump)
		    p -> p_flag |= COREDUMPED;
		child_changed++;
		p -> p_reason = w.w_termsig;
	    }
    if (!active_process (current_process))
	current_process = get_next_process ();
#endif
    goto loop;
}


#ifdef	subprocesses

X/* Find the process which is connected to buf_name */

struct process_blk *find_process (buf_name)
register char   *buf_name;
{
    register struct process_blk *p;

    if (buf_name == NULL)
	return (NULL);
    for (p = process_list; p != NULL; p = p -> next_process) {
	if (!active_process (p))
	    continue;
	if (strcmp (p -> p_chan.ch_buffer -> b_name, buf_name) == 0)
	    break;
    }
    return (p);
}

X/* Get the first active process in the process list and assign to the current
   process */

struct process_blk *get_next_process () {
    register struct process_blk *p;

    for (p = process_list; p && !active_process (p); p = p -> next_process);
    return p;
}

X/* Give a message that a process has changed and indicate why.  Dead processes
   are not removed until after a Display Processes command has been issued so
   that the user doesn't wonder where his process went in times of intense
   hacking. */

change_msgs () {
    register struct process_blk *p;
    register struct buffer *old = bf_cur;
    int sent = 0;			/* if non-zero, call sentinel */
    char    line[50];

#ifdef HalfBaked
    sighold (SIGINT);
#endif
    for (p = process_list; p != NULL; p = p -> next_process)
	if (p -> p_flag & CHANGED) {
	    sent = 0;
	    p -> p_flag &= ~CHANGED;
	    switch (p -> p_flag & (SIGNALED | EXITED)) {
		case SIGNALED: 
		    SetBfp (p -> p_chan.ch_buffer);
		    SetDot (bf_s1 + bf_s2 + 1);
		    sprintfl (line, sizeof line, "%s%s\n",
			SIG_names[p -> p_reason],
			p -> p_flag & COREDUMPED ? " (core dumped)" : "");
		    if (p->p_chan.ch_sent == NULL)
			InsStr (line);
		    else
			sent++;		/* call sentinel */
		    break;
		case EXITED: 
			SetBfp (p -> p_chan.ch_buffer);
			SetDot (bf_s1 + bf_s2 + 1);
			sprintfl (line, sizeof line,
			    p -> p_reason ? "Exit %d\n" : "Exited\n",
			    p -> p_reason);
		    if (p -> p_chan.ch_sent == NULL)
			InsStr (line);
		    else
			sent++;

		    break;
		}

		if (p->p_flag & RUNNING)
		{
		    strcpy(line, "Running\n");
		    sent++;
		}
		if (p->p_flag & STOPPED)
		{
		    strcpy(line, "Stopped\n");
		    sent++;
		}

		if (p->p_chan.ch_sent != NULL && sent)
		{
		    register    Expression * MPX_Exp =
				MPX_process -> v_binding -> b_exp;
		    int     larg = arg;
		    enum ArgStates lstate = ArgState;
		    int     old_int = MPX_Exp -> exp_int;
		    char   *old_str = MPX_Exp -> exp_v.v_string;
		    register struct channel_blk *chan =
					       &(p -> p_chan);
		    register struct channel_blk *oldchan = MPX_chan;
#if EmacsVersionNum > 263
		    struct buffer *oldg = Generate;

		    Generate = 0;
#endif EmacsVersionNum > 263
		    arg = (p -> p_reason & 0xffff) | (p->p_flag << 16);
		    ArgState = HaveArg;
		    MPX_Exp -> exp_int =
			strlen (chan -> ch_buffer -> b_name);
		    MPX_Exp -> exp_v.v_string =
			chan -> ch_buffer -> b_name;
		    /* Set up so user can get string reason as well */
		    chan->ch_ptr = line;
		    chan->ch_count = strlen(line);
		    MPX_chan = chan;
		    ExecuteBound (chan -> ch_sent);
		    MPX_chan = oldchan;
		    chan->ch_ptr = NULL;
		    chan->ch_count = 0;
		    MPX_Exp -> exp_int = old_int;
		    MPX_Exp -> exp_v.v_string = old_str;
		    arg = larg;
		    ArgState = lstate;
#if EmacsVersionNum > 263
		    Generate = oldg;
#endif EmacsVersionNum > 263
		}
	}
#ifdef HalfBaked
    sigrelse (SIGINT);
#endif
    DoDsp (1);
    SetBfp (old);
}

X/* Output has been recieved from a process on "chan" and should be stuffed in
   the correct buffer */
X/* ACT 9-Sep-1982 Modified to remove "lockout" restriction and allow
   recursive stuffs. */

stuff_buffer (chan)
register struct channel_blk *chan;
{
    struct buffer  *old_buffer = bf_cur;
    int old_buffer_is_visible = wn_cur->w_buf==bf_cur;

#ifdef HalfBaked
    sighold (SIGINT);
#endif

    if (chan -> ch_proc == NULL) {
	SetBfp (chan -> ch_buffer);
	SetDot (bf_s1 + bf_s2 + 1);
	InsCStr (chan -> ch_ptr, chan -> ch_count);
	if ((bf_s1 + bf_s2) > ProcessBufferSize) {
	    DelFrwd (1, ChunkSize);
	    DotLeft (ChunkSize);
	}
	if (bf_cur -> b_mark == NULL)
	    bf_cur -> b_mark = NewMark ();
	SetMark (bf_cur -> b_mark, bf_cur, dot);
	DoDsp (1);
	SetBfp (old_buffer);
	if (interactive && old_buffer_is_visible)
	    WindowOn (bf_cur);
    }
    else {			/* ACT 31-Aug-1982 Added hold on prefix arg */
	register char	*old_str;
	int		larg = arg, old_int;
	enum ArgStates	lstate = ArgState;
	register Expression
			*MPX_Exp = MPX_process -> v_binding -> b_exp;
	struct channel_blk
			*old_chan = MPX_chan;

	old_int = MPX_Exp -> exp_int;
	old_str = MPX_Exp -> exp_v.v_string;
	arg = 1;
	ArgState = NoArg;	/* save arg & arg state */
	MPX_Exp -> exp_int = strlen (chan -> ch_buffer -> b_name);
	MPX_Exp -> exp_v.v_string =  chan -> ch_buffer -> b_name;
	MPX_chan = chan;	/* User will be able to get the output
				   for */
	ExecuteBound (chan -> ch_proc);
	MPX_chan = old_chan;	/* a very short time only */
	MPX_Exp -> exp_int = old_int;
	MPX_Exp -> exp_v.v_string = old_str;
	arg = larg;
	ArgState = lstate;	/* restore arg */
	SetBfp (chan -> ch_buffer);
	if ((bf_s1 + bf_s2) > ProcessBufferSize) {
	    DelFrwd (1, ChunkSize);
	    DotLeft (ChunkSize);
	}
	SetBfp (old_buffer);
    }
    chan -> ch_count = 0;

#ifdef HalfBaked
    sigrelse (SIGINT);
#endif
    return 0;			/* ACT 8-Sep-1982 */
}

X/* Return a count of all active processes */

count_processes () {
    register struct process_blk *p;
    register    count = 0;

    for (p = process_list; p != NULL; p = p -> next_process)
	if (active_process (p))
	    count++;
    return (count);
}

#ifdef DumpableEmacs
X/* Flush all processes, called after kill_processes by dump-emacs */
X/* Here since emacs.c doesnt want to know about this gunk */
flush_all_processes () {
    register struct process_blk *p;
    for (p = process_list; p; p = process_list) {
	process_list = p -> next_process;
	free (p);
    }
}
#endif DumpableEmacs

X/* Flush a process but only if process is inactive */

flush_process (process)
register struct process_blk *process;
{
    register struct process_blk *p,
				*lp;

    if (active_process (process)) {
	error ("Can't flush an active process");
	return 0;
    }

    for (lp = NULL, p = process_list;
	    (p != NULL) && (p != process);
	    lp = p, p = p -> next_process);
    if (p != process) {
	error ("Can't find process");
	return 0;
    }
    if (lp == NULL)
	process_list = process -> next_process;
    else
	lp -> next_process = process -> next_process;
    free (process);
    return 0;
}

X/* Start up a new process by creating the process block and initializing 
   things correctly */

start_process (com, buf, proc)
register char	*com,
		*buf;
#if EmacsVersionNum > 263
struct BoundName *proc;
#endif EmacsVersionNum > 263
{
    extern struct process_blk  *get_next_process ();

    if (com == 0)
	return 0;
    current_process =
	(struct process_blk *) malloc (sizeof (struct process_blk));
    if (current_process == NULL) {
	error ("Out of memory");
	return 0;
    }
    sighold (SIGCHLD);
    current_process -> next_process = process_list;
    process_list = current_process;
    if (create_process (com) < 0) {/* job was not started, so undo */
	flush_process (current_process);
	current_process = get_next_process ();
	sigrelse (SIGCHLD);
	return 0;
    }
    SetBfn (buf == NULL ? "Command execution" : buf);
    if (interactive)
	WindowOn (bf_cur);
    current_process -> p_chan.ch_buffer = bf_cur;
#if EmacsVersionNum > 263
    current_process -> p_chan.ch_proc = proc;
#else
    current_process -> p_chan.ch_proc = (proc < 0 ? NULL : MacBodies[proc]);
#endif EmacsVersionNum > 263
    current_process -> p_chan.ch_sent = NULL;
    sigrelse (SIGCHLD);
    return 0;
}

X/* Emacs command to start up a default process: uses "Command Execution"
   buffer if one is not specified.  Also does default stuffing */

StartProcess () {
register char   *com = (char *) (savestr (getstr ("Command: ")));
register char   *buf;

    if ((com == 0) || (*com == 0)) {
	error ("No command");
	return 0;
    }
    buf = (char *) getstr ("Connect to buffer: ");
    if (*buf == 0)
	buf = NULL;
#if EmacsVersionNum > 263
    start_process (com, buf, NULL);
#else
    start_process (com, buf, -1);
#endif EmacsVersionNum > 263
    return 0;			/* ACT 8-Sep-1982 */
}

X/* Start up a process whose output will get filtered through a procedure
   specified by the user */

StartFilteredProcess () {
    register char   *com = (char *) (savestr (getstr ("Command: ")));
    register char   *buf;
#if EmacsVersionNum > 263
    struct BoundName *proc;
#else
    int	 proc;
#endif EmacsVersionNum > 263
    char bufname[200];

    if ((com == 0) || (*com == 0)) {
	error ("No command");
	return 0;
    }
    buf = getstr ("Connect to buffer: ");
    if (buf == 0) return 0;
    strcpy (bufname, buf);
#if EmacsVersionNum > 263
    proc = ProcArg ("On-output procedure: ");
    if (proc == NothingNode)
	proc = 0;
#else
    proc = getword (MacNames, "On-output procedure: ");
#endif EmacsVersionNum > 263
    start_process (com, bufname[0] ? bufname : NULL, proc);
    return 0;			/* ACT 8-Sep-1982 */
}

X/* Set the UnexpectedProc pointer */
static
SetUnexpectedProc () {
#if EmacsVersionNum > 263
    UnexpectedProc = ProcArg ("unexpected-process-filter: ");
    if (UnexpectedProc == NothingNode)
	UnexpectedProc = 0;
#else
    register proc = getword (MacNames, "unexpected-process-filter: ");
    UnexpectedProc = proc < 0 ? NULL : MacBodies[proc];
#endif EmacsVersionNum > 263
}

X/* Return a process buffer or NULL */
struct process_blk *
GetBufProc ()
{
    register    b = getword (BufNames, "Process: ");
    if (b < 0)
	return NULL;
    return find_process (BufNames[b]);
}

X/* Insert a filter-procedure between a process and emacs. This function
   should subsume the StartFilteredProcess function, but we should retain
   that one for compatibility I suppose. */
InsertFilter ()
{
    register struct process_blk *process;
#if EmacsVersionNum > 263
    register struct BoundName *proc;
#else
    register int proc;
#endif EmacsVersionNum > 263

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a Process");
	return 0;
    }
#if EmacsVersionNum > 263
    proc = ProcArg ("On-output procedure: ");
    process -> p_chan.ch_proc = proc == NothingNode ? 0 : proc;
#else
    proc = getword(MacNames, "On-output procedure: ");
    process -> p_chan.ch_proc = (proc < 0 ? NULL : MacBodies[proc]);
#endif EmacsVersionNum > 263
    return(0);
}

X/* Reset filter rebinds the process filter to NULL */
ResetFilter () {
    register struct process_blk *process;
  
    if ((process = GetBufProc ()) == NULL) {
	error ("Not a Process");
	return 0;
    }
    process -> p_chan.ch_proc = NULL;
    return 0;			/* ACT 8-Sep-1982 */
}

X/* ProcessFilterName returns the name of the process filter */
ProcessFilterName () {
    register struct process_blk *process;
    char   *name;

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a Process");
	return 0;
    }
    MLvalue -> exp_type = IsString;
    MLvalue -> exp_release = 0;
    name = process -> p_chan.ch_proc
	? process -> p_chan.ch_proc -> b_name : "";
    MLvalue -> exp_int = strlen (name);
    MLvalue -> exp_v.v_string = name;
    return 0;
}

static
InsertSentinel ()
{
    register struct process_blk *process;
#if EmacsVersionNum > 263
    register struct BoundName *proc;
#else
    register int proc;
#endif EmacsVersionNum > 263

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a Process");
	return 0;
    }
#if EmacsVersionNum > 263
    proc = ProcArg ("On-exit procedure: ");
    process -> p_chan.ch_sent = proc == NothingNode ? 0 : proc;
#else
    proc = getword(MacNames, "On-exit procedure: ");
    process -> p_chan.ch_sent = (proc < 0 ? NULL : MacBodies[proc]);
#endif EmacsVersionNum > 263
    return(0);
}

static
ResetSentinel () {
    register struct process_blk *process;
  
    if ((process = GetBufProc ()) == NULL) {
	error ("Not a Process");
	return 0;
    }
    process -> p_chan.ch_sent = NULL;
    return 0;
}

static
ProcessSentinelName () {
    register struct process_blk *process;
    char   *name;

    if ((process = GetBufProc ()) == NULL) {
	error ("Not a Process");
	return 0;
    }
    MLvalue -> exp_type = IsString;
    MLvalue -> exp_release = 0;
    name = process -> p_chan.ch_sent
	? process -> p_chan.ch_sent -> b_name : "";
    MLvalue -> exp_int = strlen (name);
    MLvalue -> exp_v.v_string = name;
    return 0;
}

static
SetUnexpectedSent() {
#if EmacsVersionNum > 263
    UnexpectedSent = ProcArg ("unexpected-process-sentinel: ");
    if (UnexpectedSent == NothingNode)
	UnexpectedSent = 0;
#else
    register proc = getword (MacNames, "unexpected-process-sentinel: ");
    UnexpectedSent = proc < 0 ? NULL : MacBodies[proc];
#endif EmacsVersionNum > 263
}

X/* List the current processes.  After listing stopped or exited processes,
   flush them from the process list. */

ListProcesses () {
    register struct buffer *old = bf_cur;
    register struct process_blk *p;
    char    line[150], tline[20];

    SetBfn ("Process list");
    if (interactive)
	WindowOn (bf_cur);
    EraseBf (bf_cur);
    InsStr ("\
Buffer			Status		   Command\n\
------			------		   -------\n");
    sighold (SIGCHLD);
    for (p = process_list; p != NULL; p = p -> next_process) {
	sprintfl (line, sizeof line, "%-24s", p -> p_chan.ch_buffer -> b_name);
	InsStr (line);
	switch (p -> p_flag & (STOPPED | RUNNING | EXITED | SIGNALED)) {
	    case STOPPED: 
		sprintfl (line, sizeof line, "%-17s", "Stopped");
		break;
	    case RUNNING: 
		sprintfl (line, sizeof line, "%-17s", "Running");
		break;
	    case EXITED: 
		sprintfl (tline, sizeof tline,
			p -> p_reason ? "Exit %d" : "Exited", p -> p_reason);
		sprintf (line, "%-17s", tline);
		flush_process (p);
		break;
	    case SIGNALED: 
		sprintfl (tline, sizeof tline, "%s%s",
			SIG_names[p -> p_reason],
			p -> p_flag & COREDUMPED ? " (core dumped)" : "");
		sprintf (line, "%-17s", tline);
		flush_process (p);
		break;
	    default: 
		sprintf (line, "0x%x\n", p -> p_flag);
		InsStr (line);
		continue;
	}
	InsStr (line);
	sprintfl (line, sizeof line, "   %-32s\n", p -> p_name);
	InsStr (line);
    }
    sigrelse (SIGCHLD);
    bf_modified = 0;
    SetBfp (old);
    WindowOn (bf_cur);
    return 0;
}

X/* Take input from mark to dot and feed to the subprocess */
RegionToProcess () {
    register	left,
		right;
    register struct process_blk *process;
    register struct wh *output;

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

    if (bf_cur -> b_mark == 0) {
	error ("Mark not set");
	return 0;
    }
    left = ToMark (bf_cur -> b_mark);
    if (left <= dot)
	right = dot;
    else {
	right = left;
	left = dot;
    }
    if (right - left <= 0) {
	error ("Region is null");
	return 0;
    }
    if (left < bf_s1 && right >= bf_s1)
	GapTo (left);
    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 = right - left;
    output -> data = &CharAt (left);
    send_chan (process);
    return 0;
}

X/* Send a string to the process as input */
StringToProcess () {
    register char *input_string;
    register struct process_blk *process;
    register struct wh *output;
 
    if ((process = GetBufProc()) == NULL) {
	error ("Not a Process");
	return 0;
    }
    input_string = getstr("String: ");
    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 = strlen(input_string);
    if (output -> count <= 0)
	error("Null string");
    output -> data = input_string;
    send_chan (process);
    return 0;
}
   
X/* Get the current output which has been thrown at us and send it
   to the user as a string; this is only allowed if MPX_chan is non-null
   indicating that this has been indirectly called from stuff_buffer. */

ProcessOutput () {
    if (MPX_chan == NULL) {
	error ("process-output can only be called from filter");
	return 0;
    }

    MLvalue -> exp_type = IsString;
    MLvalue -> exp_release = 1;
    MLvalue -> exp_int = MPX_chan -> ch_count;
    MLvalue -> exp_v.v_string = (char *) malloc (MLvalue -> exp_int + 1);
    cpyn (MLvalue -> exp_v.v_string, MPX_chan -> ch_ptr,
	MLvalue -> exp_int);
    MLvalue -> exp_v.v_string[MLvalue -> exp_int] = '\0';
    return 0;
}

IntProcess () {
    return (sig_process (SIGINT, 0));
}

IntPLeader () {
    return (sig_process (SIGINT, 1));
}

QuitProcess () {
    return (sig_process (SIGQUIT, 0));
}

QuitPLeader () {
    return (sig_process (SIGQUIT, 1));
}

KillProcess () {
    return (sig_process (SIGKILL, 0));
}

KillPLeader () {
    return (sig_process (SIGKILL, 1));
}

StopProcess () {
    return (sig_process (SIGTSTP, 0));
}

StopPLeader () {
    return (sig_process (SIGTSTP, 1));
}

ContProcess () {
    return (sig_process (SIGCONT, 0));
}

ContPLeader () {
    return (sig_process (SIGCONT, 1));
}

SignalToProcess () {
    return SignalToProcOrLeader (0);
}

SignalToPLeader () {
    return SignalToProcOrLeader (1);
}

SignalToProcOrLeader (leader) {
    register char *s = getnbstr ("Signal: ");
    register i;

    if (!s || !*s) return 0;
    if (*s >= '0' && *s <= '9')
	return sig_process (atoi (s), leader);
    for (i = 0; i < sizeof KillNames/sizeof *KillNames; i++)
	if (strcmp (KillNames[i], s) == 0)
	    return sig_process (i, leader);
    error ("\"%s\" is not a signal name", s);
    return 0;
}

X/* Some useful functions on the process */
StrFunc (CurrentProcess,
    (current_process ? current_process -> p_chan.ch_buffer -> b_name : ""));

X/* Return the name of the currently active process: it is defined as the name
   of the current buffer if is attached to an active process. */

ActiveProcess () {
    register struct process_blk *p;

    for (p = process_list; p != NULL; p = p -> next_process)
	if (active_process (p) && (p -> p_chan.ch_buffer == bf_cur))
	    break;
    if (p == NULL)
	p = current_process;
    MLvalue -> exp_type = IsString;
    MLvalue -> exp_release = 0;
    if (p == NULL) {
	MLvalue -> exp_int = 0;
	MLvalue -> exp_v.v_string = NULL;
    }
    else {
	MLvalue -> exp_int = strlen (p -> p_chan.ch_buffer -> b_name);
	MLvalue -> exp_v.v_string = p -> p_chan.ch_buffer -> b_name;
    }
    return 0;
}

X/* Change the current-process to the one indicated */

ChangeCurrentProcess () {
    register struct process_blk *process = GetBufProc ();

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

X/* Return the process' status:
	-1 - not an active process
	 0 - a stopped process
	 1 - a running process
*/

X/* It's tempting to make this call GetBufProc() - but do not ... */
ProcessStatus () {
    register char *name = getstr ("Process: ");
    register struct process_blk *process = find_process (name);

    MLvalue -> exp_type = IsInteger;
    if (process == NULL)
	MLvalue -> exp_int = -1;
    else
	if (process -> p_flag & RUNNING)
	    MLvalue -> exp_int = 1;
	else
	    MLvalue -> exp_int = 0;
    return 0;
}

X/* Get the process id */
ProcessID () {
    return PID (0);
}

X/* Get the process leader id */
PLeaderID () {
    return PID (1);
}

X/* Get input from a subprocess (or the tty) and process it.
 * Tty input is just buffered until requested.
 */
static AwaitProcessInput ()		/* just poll for input */
{
#ifdef ECHOKEYS
    fill_chan(NULL, 0);
#else
    fill_chan(NULL);
#endif
}
#endif	subprocesses


X/* Initialize things for multiple process handling. */

InitMpx () {
    extern  child_sig ();
#ifdef	subprocesses
    extern  EOTProcess ();
#endif

#ifdef ce
    err_file = fopen ("/tmp/emacs.mxdebug", "a");
    if (err_file == NULL) {
	unlink ("/tmp/emacs.mxdebug");
	err_file = fopen ("/tmp/emacs.mxdebug", "a");
    }
    chmod ("/tmp/emacs.mxdebug", 0666);
    setbuf (err_file, NULL);
    err_id = getuid ();
#endif
    InitProcesses ();
    sigset (SIGCHLD, child_sig);
#ifdef subprocesses

#ifdef DumpableEmacs
    if (!Once)
#endif
    {
	DefStrVar ("MPX-process", "");
	MPX_process = NextInitVarDesc[-1];

	ProcessBufferSize = 10000;/* # of chars in buffer before truncating */
	DefIntVar ("process-buffer-size", &ProcessBufferSize);

	PopUpUnexpected = 1;
	DefIntVar ("pop-up-process-windows", &PopUpUnexpected);

	EmacsShare = 1;
	DefIntVar ("emacs-share", &EmacsShare);

	defproc (StartProcess, "start-process");
	defproc (StartFilteredProcess, "start-filtered-process");
	defproc (InsertFilter, "insert-filter");
	defproc (ResetFilter, "reset-filter");
	defproc (ProcessFilterName, "process-filter-name");
	defproc (RegionToProcess, "region-to-process");
	defproc (StringToProcess, "string-to-process");
	defproc (IntProcess, "int-process");
	defproc (QuitProcess, "quit-process");
	defproc (KillProcess, "kill-process");
	defproc (StopProcess, "stop-process");
	defproc (ContProcess, "continue-process");
	defproc (IntPLeader, "int-process-leader");
	defproc (QuitPLeader, "quit-process-leader");
	defproc (KillPLeader, "kill-process-leader");
	defproc (StopPLeader, "stop-process-leader");
	defproc (ContPLeader, "continue-process-leader");
	defproc (SignalToProcess, "signal-to-process");
	defproc (SignalToPLeader, "signal-to-process-leader");
	defproc (EOTProcess, "eot-process");
	defproc (CurrentProcess, "current-process");
	defproc (ProcessStatus, "process-status");
	defproc (ChangeCurrentProcess, "change-current-process");
	defproc (ActiveProcess, "active-process");
	defproc (ProcessID, "process-id");
	defproc (PLeaderID, "process-leader-id");
	defproc (ProcessOutput, "process-output");
	defproc (ListProcesses, "list-processes");
	defproc (SetUnexpectedProc, "unexpected-process-filter");
	defproc (InsertSentinel, "insert-sentinel");
	defproc (ResetSentinel, "reset-sentinel");
	defproc (ProcessSentinelName, "process-sentinel-name");
	defproc (SetUnexpectedSent, "unexpected-process-sentinel");
	defproc (AwaitProcessInput, "await-process-input");
    }
#endif
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 schan.c
	/bin/echo -n '	'; /bin/ls -ld schan.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