purtilo@uiuccsb.UUCP (08/07/83)
#N:uiuccsb:7400001:000:41198 uiuccsb!purtilo Aug 6 18:00:00 1983 Date: 2 Aug 83 10:24:00 (DT) (Tue) From: mcvax!guido (Guido van Rossum) Subject: Re: emacs fix in 4.1c Message-Id: <8308020824.AA11799@MCVAX.UUCP> Received: by MCVAX.UUCP (3.327/3.14) id AA11799; 2 Aug 83 10:24:00 (DT) (Tue) To: philabs!uiucdcs!uiuccsb!purtilo Cc: decvax!pegasus!hansen Jim, Could you please post the fixes to "mchan.h" to net.emacs? In Europe we don't receive that group, and consequently can't post to it either. Thanks in advance, Guido van Rossum, Math. Centre, Amsterdam ...!{decvax,philabs}!mcvax!guido Done ... what follows is "mchan.c" then "mchan.h". When I added it locally I ifdef'd it so that by not defining "uiuc" you got the original mchan, more or less. You should either define uiuc in your config.h or just edit out all the conditionals appropriately below. Thanks to Guido. We found it convenient to hack "dsp.c" slightly as well. Our version is appended as well. Change is basically for folks who invoke an emacs over an rlogin, sends a null back to propagate ioctl's to the local tty driver. Jim Purtilo ...!uiucdcs!purtilo /********************** mchan.c *************************/ /* These procedures hope to give EMACS a facility for dealing with multiple processes in a reasonable way */ /* Copyright (c) 1981 Carl Ebeling */ /* Modified 8-Sept-81 Jeffrey Mogul (JCM) at Stanford * - removed RstDsp() after failure to open mpx file; this * was leading to nasty infinite loop. Probably this should * be done better and in other situations as well. * * Modified 20-Jul-83 Guido van Rossum at Mathematical Centre, Amsterdam * - replaced multiplexed file stuff to use 4.1c pseudo-terminals, * see pty(4). * These modifications (c) 1983 Mathematical Centre, Amsterdam. * * Guido's mods added (with thanks) with only slight modification, * 28-Jul-83 James Purtilo, University of Illinois at Urbana-Champaign. */ #include "config.h" #include <stdio.h> #include <signal.h> #include <errno.h> #include <wait.h> #include <sgtty.h> #ifndef uiuc #include <sys/mpx.h> #endif #include <sys/types.h> #include <sys/stat.h> #include "window.h" #include "keyboard.h" #include "buffer.h" #include "mlisp.h" #include "macros.h" #ifdef subprocesses #include "mchan.h" #else extern int errno; #endif #ifdef subprocesses #define ChunkSize 500 /* amount to truncate when buffer overflows */ static int ProcessBufferSize; /* Maximum size for process buffer */ /* 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. */ struct w_msg { short code; struct sgttyb ioctl_ans; #ifdef uiuc } iopg_rec, ioany_rec; #else } ioans_rec, iopg_rec, ioany_rec; #endif #ifndef uiuc struct wh ioans; /* The record actually used to send IOANS_REC not any more ... purtilo */ #endif struct wh iopg; /* The record actually used to send IOPG_REC */ struct wh ioany; char mpx_filename[50]; /* Name of Multiplexed file */ int mpx_fd; /* Multiplexed file */ #ifdef ce FILE *err_file; /* file to which we will dump all unexpected stuff */ #endif int err_id; /* User's ID for error messages */ struct channel_blk stdin_chan; int child_changed; /* Flag when a child process has ceased to be */ struct VariableName *MPX_process; struct channel_blk *MPX_chan; char *SIG_names[] = /* this list needs updating ... purtilo */ { "", "Hanged", "Interrupted", "Quit*", "Illegal instruction*", "Trapped*", "Iot*", "EMT*", "FPE*", "Killed", "Bus error*", "Segment violation*", "Sys call error*", "Pipe write error", "Alarm clock", "Termination", "", "Stopped", "Stopped", "", "", "", "", "", "", "" }; /* 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 channel, newfd, pid, i; extern char *shell (); #ifndef uiuc if ((channel = chan (mpx_fd)) == -1) { error ("Can't connect subchannel\n"); return - 1; } newfd = extract (channel, mpx_fd); #else #define STRLEN(x) (sizeof(x)-1) { char line[50]; /* Name for pseudo tty device */ char c; int i; /* Find an available pseudo terminal. (Code adapted from rlogind.) We use the variable names from the mpx code to keep changes as few as possible: `channel' is the master side, `newfd' is the slave side of the pseudo terminal. */ for (c = 'p'; c <= 's'; c++) { struct stat stb; strcpy(line, "/dev/ptyXX"); line[STRLEN("/dev/pty")] = c; line[STRLEN("/dev/ptyp")] = '0'; if (stat(line, &stb) < 0) break; for (i = 0; i < 16; i++) { line[STRLEN("/dev/ptyp")] = "0123456789abcdef"[i]; channel = open(line, 2); if (channel >= 0) { line[STRLEN("/dev/")] = 't'; newfd = open(line, 2); if (newfd < 0) { close(channel); line[STRLEN("/dev/")] = 'p'; } else goto gotpty; /* Multi-level break */ } } } error("No pseudo tty available"); return - 1; gotpty: { int yes = 1; ioctl(channel, TIOCREMOTE, &yes); ioctl(channel, FIONBIO, &yes); /* do not block on writes */ } } #endif /* done with uiuc insertions */ if ((pid = vfork ()) < 0) { error ("Fork failed"); #ifdef uiuc close(channel); #else detach (channel, mpx_fd); #endif close (newfd); return - 1; } if (pid == 0) { 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); #ifdef uiuc close (newfd); for (newfd = 3; newfd < 20; ++newfd) close(newfd); #endif execlp (shell (), shell (), "-c", command, 0); write (1, "Couldn't exec the shell\n", 24); _exit (1); } current_process -> p_name = command; current_process -> p_id = pid; current_process -> p_flag = RUNNING; 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 /* 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; register struct process_blk *p; extern struct process_blk *get_next_process (); loop: pid = wait3 (&w.w_status, WUNTRACED | WNOHANG, 0); 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; } #ifndef subprocesses goto loop; } #else for (p = process_list; p != NULL; p = p -> next_process) if (pid == p -> p_id) break; if (p == NULL) goto loop; /* We don't know who this is */ if (WIFSTOPPED (w)) { p -> p_flag = STOPPED; p -> p_reason = w.w_stopsig; } 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; child_changed++; p -> p_reason = w.w_termsig; } if (!active_process (current_process)) current_process = get_next_process (); goto loop; } /* 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) */ struct channel_blk *find_channel (index) register int 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); } /* Find the corresponding process for a pointer to a channel block. */ struct process_blk *index_to_process (index) register int 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); } /* 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); } /* 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 != NULL && !active_process (p); p = p -> next_process); return (p); } /* 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 int mpx_share; /* channel index for someone sharing stdin */ 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 */ #ifdef uiuc fill_chan (chan) register struct channel_blk *chan; { extern struct channel_blk *find_channel(); register struct channel_blk *this_channel; register struct process_blk *p; register int nfds; register int fd; int mpx_count; int readfds; int writefds; readloop: /* weed out dead processes before going to sleep */ if (child_changed) { child_changed = 0; change_msgs (); } readfds = 1; /* Set bit for stdin */ writefds = 0; for (p = process_list; p != NULL; p = p->next_process) { if (active_process (p)) { readfds |= 1 << (p -> p_chan.ch_index); if (p -> p_chan.ch_outrec.count || p -> p_chan.ch_outrec.ccount) writefds |= 1 << (p -> p_chan.ch_outrec.index); } } errno = 0; nfds = select (20, &readfds, &writefds, (int*)0, (int*)0); /* This blocks until input is available somewhere */ if (nfds < 0) { if (errno == EINTR) goto readloop; error ("Select system call failed"); DoDsp(1); return EOF; } for (fd = 0; fd < 20; ++fd) { /* Note - this order serves stdin first, which is desirable (think of a sub- process flooding us with data while the user wants to continue editing). */ if (writefds & (1<<fd)) { /* Retry pending output on fd */ for (p = process_list; p != NULL; p = p -> next_process) { if (active_process (p) && p -> p_chan.ch_outrec.index == fd) { send_chan (p); break; } } if (p == NULL) { /* process must have died in the meantime */ goto readloop; } } if (readfds & (1<<fd)) { /* Input available on fd */ loop: errno = 0; mpx_count = read (fd, cbuffer, sizeof cbuffer); if (mpx_count <= 0) { if (errno == EINTR) goto loop; error ("Failed to read ready descriptor"); DoDsp(1); return EOF; } if (fd == 0) /* This is stdin */ this_channel = mpxin; else { this_channel = find_channel (fd); if (this_channel == NULL) { /* probably a process that just died */ goto readloop; } } this_channel -> ch_ptr = cbuffer; this_channel -> ch_count = mpx_count; if (this_channel == mpxin) { mpxin -> ch_count--; return (*mpxin -> ch_ptr++ & 0377); } else stuff_buffer (this_channel); } } goto readloop; } #else fill_chan (chan) register struct channel_blk *chan; { extern int mpx_fd; /* mpx file number */ extern struct channel_blk *find_channel (); register struct mpx_msg *msg; register int record_size, msg_length; register struct channel_blk *this_channel; readloop: /* Temporary hack until unblocking is fixed up: retry pending output whenever anything is read. */ { register struct process_blk *p; for (p = process_list; p != NULL; p = p -> next_process) if (active_process (p) && (p -> p_chan.ch_outrec.count || p -> p_chan.ch_outrec.ccount)) send_chan (p); } if (mpx_count == 0) { mpx_count = read (mpx_fd, cbuffer, BUFSIZ); if (mpx_count <= 0) return (EOF); MXP = (struct rh *) cbuffer; } if (child_changed) { child_changed = 0; change_msgs (); } 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 (MXP -> index == mpx_share) this_channel = mpxin; else if ((this_channel = find_channel (MXP -> index)) == NULL) { error ("Illegal channel index"); return (EOF); } 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) { mpxin -> ch_count--; return (*mpxin -> ch_ptr++ & 0377); } else stuff_buffer (this_channel); } } goto readloop; } #endif /* fill_chan change.... purtilo */ /* 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 */ #ifndef uiuc Take_msg (msg, index) register struct mpx_msg *msg; { extern mpx_fd; register struct process_blk *p; switch (msg -> mpx_code & 0377) { case M_IOCTL: if (msg -> mpx_arg == TIOCGETP) { ioans.index = index; if (write (mpx_fd, &ioans, sizeof (ioans)) != sizeof (ioans)) error ("Unable to reply to process IOCTL"); } else if (msg -> mpx_arg == TIOCGPGRP) { /* In response to TIOCGPGRP return an "error" * record. This is so csh will get the "right" * answer to its TIOCGPGRP. */ iopg.index = index; if (write (mpx_fd, &iopg, sizeof (iopg)) != sizeof (iopg)) error ("Unable to reply to process IOCTL"); } else { /* In response to anything else, reflect the same data * back. */ ioany.index = index; ioany_rec.ioctl_ans = msg -> mpx_ioctl; if (write (mpx_fd, &ioany, sizeof (ioany)) != sizeof (ioany)) 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. Keep track of the shared input by saving the index in share. (For now only one shared open) */ case M_WATCH: #ifdef ce fprintf(err_file, "Watch: %d\n", index); #endif if ((mpx_share == -1) && sflag) { mpx_share = index;/* Save the index of the shared channel */ attach(index, mpx_fd);/* And attach... */ message ("Connecting a shared channel"); DoDsp(1); } else { detach(index, mpx_fd);/* Do not allow the open */ message ("Blocking shared open"); DoDsp(1); } 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); if (index == mpx_share) {detach(index, mpx_fd); message ("Disconnecting shared channel"); DoDsp(1); } */ return (4); /* ignore ! */ case M_CLOSE: #ifdef ce fprintf(err_file, "Close: %d\n", index); #endif detach (index, mpx_fd); /* reuse channel */ if (index == mpx_share) { message ("Disconnecting shared channel"); mpx_share = -1; } 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); } } #endif /* 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; char line[50]; for (p = process_list; p != NULL; p = p -> next_process) if (p -> p_flag & CHANGED) { 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\n", SIG_names[p -> p_reason]); InsStr (line); #ifdef uiuc close (p -> p_chan.ch_index); p -> p_chan.ch_index = -1; #endif break; case EXITED: SetBfp (p -> p_chan.ch_buffer); SetDot (bf_s1 + bf_s2 + 1); sprintfl (line, sizeof line, "Exited%s\n", (p -> p_reason == 0 ? "" : " Abnormally")); InsStr (line); #ifdef uiuc close (p -> p_chan.ch_index); p -> p_chan.ch_index = -1; #endif break; } } DoDsp (1); SetBfp (old); } /* 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 */ } #ifdef uiuc if (write (output -> index, output -> data, output -> count) != output -> count){ if(errno == EWOULDBLOCK){ /* message ("Blocking")*/ ; } } #else if (write (mpx_fd, output, sizeof (*output)) != sizeof (*output)) /* message ("Blocking")*/ ; #endif else { output -> count = 0; output -> ccount = 0; } } /* Output has been recieved from a process on "chan" and should be stuffed in the correct buffer */ stuff_buffer (chan) register struct channel_blk *chan; { struct buffer *old_buffer = bf_cur; static int lockout; /* lockout other stuffs if one is in progress */ if (lockout) { error ("On-output procedure asked for input: you lose big!!!"); return - 1; } else lockout = 1; 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) WindowOn (bf_cur); } else { MPX_process -> v_binding -> b_exp -> exp_int = strlen (chan -> ch_buffer -> b_name); MPX_process -> v_binding -> b_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 = NULL; /* a very short time only */ SetBfp (chan -> ch_buffer); if ((bf_s1 + bf_s2) > ProcessBufferSize) { DelFrwd (1, ChunkSize); DotLeft (ChunkSize); } SetBfp (old_buffer); } chan -> ch_count = 0; lockout = 0; } /* 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); } /* 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; } /* Kill off all active processes: usually 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)) killpg (p -> p_id, SIGKILL); #ifndef uiuc detach (mpxin -> ch_index, mpx_fd); #endif } /* Start up a new process by creating the process block and initializing things correctly */ start_process (com, buf, proc) register char *com, *buf; { 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; } #ifdef uiuc current_process -> p_chan.ch_index = -1; /* So close won't do harm */ current_process -> p_flag = 0; /* Let no-one think we're active */ /* This last line took us more than a day to come up with! */ #endif 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; current_process -> p_chan.ch_proc = (proc < 0 ? NULL : MacBodies[proc]); sigrelse (SIGCHLD); return 0; } /* 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; start_process (com, buf, -1); } /* 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; int proc; 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); proc = getword (MacNames, "On-output procedure: "); start_process (com, bufname[0] ? bufname : NULL, proc); return 0; } /* 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 char *proc_name = getstr ("Process: "); register struct process_blk *process; register int proc; if ((process = find_process (proc_name)) == NULL) { error ("Process does not exist"); return 0; } proc = getword (MacNames, "On-output procedure: "); process -> p_chan.ch_proc = (proc < 0 ? NULL : MacBodies[proc]); return (0); } /* Reset filter rebinds the process filter to NULL */ ResetFilter () { register char *proc_name = getstr ("Process: "); register struct process_blk *process; if ((process = find_process (proc_name)) == NULL) { error ("Process does not exist"); return 0; } process -> p_chan.ch_proc = NULL; return 0; } /* ProcessFilterName returns the name of the process filter */ ProcessFilterName () { register char *proc_name = getstr ("Process: "); register struct process_blk *process; char *name; if ((process = find_process (proc_name)) == NULL) { error ("Process does not exist"); 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; } /* 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]; 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 (line, sizeof line, "Exited %-10s", (p -> p_reason == 0 ? "" : "Abnormally")); flush_process (p); break; case SIGNALED: sprintfl (line, sizeof line, "%-17s", SIG_names[p -> p_reason]); flush_process (p); break; default: 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; } /* Take input from mark to dot and feed to the subprocess */ RegionToProcess () { register left, right; register char *proc_name = getstr ("To process: "); register struct process_blk *process; register struct wh *output; if ((process = find_process (proc_name)) == NULL) { error ("Process does not exist"); 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 data 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; } /* Send a string to the process as input */ StringToProcess () { register char *proc_name = getstr ("To process: "); register char *input_string; register struct process_blk *process; register struct wh *output; if ((process = find_process (proc_name)) == NULL) { error ("Process does not exist"); return 0; } input_string = getstr ("String:"); if (input_string == 0) return 0; output = &process -> p_chan.ch_outrec; if (output -> count || output -> ccount) error ("Overwriting data 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; } /* 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); strcpyn (MLvalue -> exp_v.v_string, MPX_chan -> ch_ptr, MLvalue -> exp_int); MLvalue -> exp_v.v_string[MLvalue -> exp_int] = '\0'; return 0; } /* Send an signal to the specified process group */ sig_process (signal) { register struct process_blk *process; register char *buf = getstr ("Process in buffer: "); if ((process = find_process (buf)) == NULL) { error ("Not a process"); return 0; } /* 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; sigrelse (SIGCHLD); } killpg (process -> p_id, signal); return 0; } IntProcess () { return (sig_process (SIGINT)); } QuitProcess () { return (sig_process (SIGQUIT)); } KillProcess () { return (sig_process (SIGKILL)); } StopProcess () { return (sig_process (SIGSTOP)); } ContProcess () { return (sig_process (SIGCONT)); } /* * guido said to replace EOTProcess with a dummy, but we think it works ok * with only a slight fix. purtilo * */ EOTProcess () { register struct process_blk *process; register char *buf = getstr ("Process in buffer: "); struct { short code, arg; } EOT_msg; register struct wh *output; if ((process = find_process (buf)) == 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 = EOF; /* crude, no? .... purtilo */ /* EOT_msg.code = M_EOT; <-- old line from the mpx world */ send_chan (process); } /* #endif */ /* Some useful functions on the process */ StrFunc (CurrentProcess, (current_process ? current_process -> p_chan.ch_buffer -> b_name : "")); /* 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; } /* Change the current-process to the one indicated */ ChangeCurrentProcess () { register char *p_name = getstr ("Process name: "); register struct process_blk *process; process = find_process (p_name); if (process == NULL) { error ("Not a process"); return 0; } current_process = process; return 0; } /* Return the process' status -1 - not an active process 0 - a stopped process 1 - a running process */ ProcessStatus () { 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 = -1; else if (process -> p_flag & RUNNING) MLvalue -> exp_int = 1; else MLvalue -> exp_int = 0; return 0; } /* Get the process id */ ProcessID () { 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 MLvalue -> exp_int = process -> p_id; return 0; } #endif /* Initialize things on the multiplexed file. This involves connecting the standard input to a channel on the mpx file. Someday we will be brave and close 0. */ InitMpx () { #ifdef subprocesses register struct channel_blk *in; int newfd; extern child_sig (); struct sgttyb new; #ifdef uiuc mpxin -> ch_index = 0; #else #ifdef ce err_file = fopen("/usr/ce/.mpx.err", "a"); #endif err_id = getuid (); /* We will make children think they have a vanilla terminal */ ioans_rec.code = M_IOANS; ioctl (0, TIOCGETP, &(ioans_rec.ioctl_ans)); ioans.ccount = sizeof (ioans_rec); ioans.data = (char *) & ioans_rec; /* Answer TIOCGPGRP with a record containing a -1. This is so csh will work. */ iopg_rec.code = M_IOANS; *(int *)(&iopg_rec.ioctl_ans) = -1; /* gross! */ iopg.ccount = sizeof iopg_rec; iopg.data = (char *) & iopg_rec; /* Finally, any other ioctls get reflected. */ ioany_rec.code = M_IOANS; ioany.ccount = sizeof ioany_rec; ioany.data = (char *) & ioany_rec; /* 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, "%s/.emacs_share", getenv("HOME")); unlink(mpx_filename); /* Remove the file first */ } if ((mpx_fd = mpx (sflag ? mpx_filename : 0, 0666)) < 0) { fprintf (stderr, "Can't open the multiplexed file\n"); /* RstDsp (); */ /* JCM - seems to cause infinite loop */ exit (1); } if (sflag) chmod(mpx_filename, 0666); mpx_share = -1; /* Illegal channel index until shared */ mpxin -> ch_index = chan (mpx_fd); ioctl (mpx_fd, MXNBLK, 0); /* set up non-blocking mode */ if (mpxin -> ch_index == -1) { fprintf (stderr, "Couldn't get a channel to mpx file"); RstDsp (); exit (1); } connect (0, extract (mpxin -> ch_index, mpx_fd), 0); /*close(0);*/ #endif mpxin -> ch_ptr = NULL; mpxin -> ch_count = 0; #endif sigset (SIGCHLD, child_sig); #ifdef subprocesses DefStrVar ("MPX-process", ""); MPX_process = NextInitVarDesc[-1]; /* see if we can make that a little more ..... */ ProcessBufferSize = 50000; /* # of chars in buffer before truncating */ /* Old size was ProcessBufferSize = 10000; # of chars in buffer before truncating * but we like it bigger ... doesn't seem to hurt. purtilo */ DefIntVar ("process-buffer-size", &ProcessBufferSize); 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 (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 (ProcessOutput, "process-output"); defproc (ListProcesses, "list-processes"); #endif } /************************** mchan.h *******************************/ #ifdef uiuc struct wh { short index; short count; short ccount; char *data; }; /* Replacing definition from #include <sys/mpx.h> */ #endif #define debug(x) fprintf(stderr,"x:%d\n",x);fflush(stderr) #define debugh(x) fprintf(stderr,"x");fflush(stderr) #define mpx_getc(p) (--(p)->ch_count>=0? *(p)->ch_ptr++&0377:fill_chan(p)) #define mpxin (&stdin_chan) #define STOPPED (1<<0) #define RUNNING (1<<1) #define EXITED (1<<2) #define SIGNALED (1<<3) #define CHANGED (1<<6) #define active_process(x) ((x!=NULL)&& (x->p_flag&(RUNNING|STOPPED))) extern int errno; /* 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; }; /* Structure records pertinent information about channels open on the mpx file There is one channel associated with each process. */ struct channel_blk {int 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 */ }; /* Standard input is connected to the multiplexed file using a channel described by stdin_chan. */ extern struct channel_blk stdin_chan; /* 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_id; /* process id (group id as well */ 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) */ /************************ dsp.c ******************************/ /* Display routines */ /* Copyright (c) 1981,1980 James Gosling */ #include "config.h" #include "keyboard.h" #include "buffer.h" #include "window.h" #include "display.h" #include <sys/ioctl.h> #include <sgtty.h> #include <stdio.h> struct sgttyb old; /* The initial tty mode bits */ /* to save to old flow control characters while flow is managed by the socket world. purtilo */ #ifdef uiuc struct tchars oldtt; struct tchars *oldttp; char oldxoff, oldxon; #endif InitDsp () { struct sgttyb sg; extern char _sobuf[]; #ifdef uiuc int newmode; oldttp = &oldtt; #endif gtty (0, &old); sg = old; sg.sg_flags = (sg.sg_flags & ~(ECHO | CRMOD | XTABS)) | RAW; stty (0, &sg); #ifdef uiuc ioctl( 0, TIOCGETC, oldttp ); oldxon = oldttp->t_startc; oldttp->t_startc = 0377; oldxoff = oldttp->t_stopc; oldttp->t_stopc = 0377; ioctl( 0, TIOCSETC, oldttp ); printf("%c",NULL); /* remove this and get surprise ... purtilo */ #endif ScreenGarbaged = 1; setbuf (stdout, _sobuf); term_init (); if (tt.t_window) (*tt.t_window) (0); } RstDsp () { #ifdef uiuc oldttp->t_startc = oldxon; oldttp->t_stopc = oldxoff; ioctl( 0, TIOCSETC, oldttp ); #endif if (tt.t_window) (*tt.t_window) (0); (*tt.t_topos) (ScreenLength, 1); (*tt.t_wipeline) (0); (*tt.t_cleanup) (); fflush (stdout); stty (0, &old); }
joe@cvl.UUCP (Joseph I. Pallas) (08/19/83)
Purtilo@uiuccsb.UUCP claims that the select call should be changed from the old line ... nfds = select (20, &readfds, &writefds, (int*)0, (int*)0); the new line ... nfds = select (20, &readfds, &writefds, (int*)100000); to keep emacs from doing a poll instead of blocking (I assume the missing argument in the new line is an oversight). The key point is that this reveals a failure to understand the following critical line in the manual section for select(2): "If timeout is a zero pointer, the select blocks indefinitely. To affect [sic] a poll, the timeout argument should be non-zero, pointing to a zero valued timeval structure." A zero value passed to select does NOT indicate a poll, and a value of 100000 is meaningless--*((int *)100000) is definitely NOT 100000. Joe Pallas joe@cvl.uucp (seismo!rlgvax!cvl!joe) joe@cvl.arpa
chris@umcp-cs.UUCP (08/20/83)
Actually, the problem is that some people are running 4.1a and some people are running 4.1c. The specification for select changed! The 4.1a select has 3 arguments; the 4.1c has 4. Moreover, the way timeouts are specified is different. Anyway, to get around all this, I'm going to post a whole slew of changes (as soon as I finish reading all this news!) which I promised long ago. -- 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
purtilo@uiuccsb.UUCP (08/23/83)
#R:uiuccsb:7400001:uiuccsb:7400003:000:1364 uiuccsb!purtilo Aug 22 23:40:00 1983 Concerning that mchan.c fix: Got word of a fix to the fix ... Date: 21 Aug 1983 13:46-EST From: James Gosling <ucbvax!cmuitca!jag@jag> Subject: Re: guido mchan fix (LONG MSG) - (nf) To: pur-ee!uiucdcs!uiuccsb!purtilo@BERKELEY In-Reply-To: pur-ee!uiucdcs!uiuccsb!purtilo's message of 18 Aug 83 Unfortunatly, your "fix" is wrong and the original was right. I quote from the manual: If timeout is a non-zero pointer, it specifies a maximum interval to wait for the selection to complete. If timeout is a zero pointer, the select blocks indefinitely. To affect a poll, the timeout argument should be non-zero, pointing to a zero valued timeval structure. So the correct value for the "timeout" parameter was the original zero pointer. Also, you forgot the "exceptfds" parameter and to put the "timeout" in a pass-by-reference "timeval" structure. Please post a correction to unix-emacs. James. ----------------- Well, my man page didn't read that way; a little poking around here uncovered for me a 4.1c man page which does read that way. We're running 4.1a here, so if that's your flavor too then use the fix I had posted, else use the fix to the fix (i.e. use the original text). Looking forward to when the powers that be here get us 4.1c ... Jim Purtilo