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