text: /* Copyright (c) 1979 Regents of the University of California */
  /*
  static char *sccsid = "@(#)ex_unix.c	6.1 10/18/80";
  */
  #include "ex.h"
  #include "ex_temp.h"
  #include "ex_tty.h"
  #include "ex_vis.h"
  
  /*
   * Unix escapes, filtering
   */
  
  /*
   * First part of a shell escape,
   * parse the line, expanding # and % and ! and printing if implied.
   */
  void
  unix0(bool warn)
  {
  	register char *up, *fp;
  	register short c;
  	char printub, puxb[UXBSIZE + sizeof (int)];
  
  	printub = 0;
  	CP(puxb, uxb);
  	c = ex_getchar();
  	if (c == '\n' || c == EOF)
  		error("Incomplete shell escape command@- use 'shell' to get a shell");
  	up = uxb;
  	do {
  		switch (c) {
  
  		case '\\':
  			if (any(peekchar(), "%#!"))
  				c = ex_getchar();
  		default:
  			if (up >= &uxb[UXBSIZE]) {
  tunix:
  				uxb[0] = 0;
  				error("Command too long");
  			}
  			*up++ = c;
  			break;
  
  		case '!':
  			fp = puxb;
  			if (*fp == 0) {
  				uxb[0] = 0;
  				error("No previous command@to substitute for !");
  			}
  			printub++;
  			while (*fp) {
  				if (up >= &uxb[UXBSIZE])
  					goto tunix;
  				*up++ = *fp++;
  			}
  			break;
  
  		case '#':
  			fp = altfile;
  			if (*fp == 0) {
  				uxb[0] = 0;
  				error("No alternate filename@to substitute for #");
  			}
  			goto uexp;
  
  		case '%':
  			fp = savedfile;
  			if (*fp == 0) {
  				uxb[0] = 0;
  				error("No filename@to substitute for %%");
  			}
  uexp:
  			printub++;
  			while (*fp) {
  				if (up >= &uxb[UXBSIZE])
  					goto tunix;
  				*up++ = *fp++ | QUOTE;
  			}
  			break;
  		}
  		c = ex_getchar();
  	} while (c == '"' || c == '|' || !endcmd(c));
  	if (c == EOF)
  		ungetchar(c);
  	*up = 0;
  	if (!inopen)
  		resetflav();
  	if (warn)
  		ckaw();
  	if (warn && hush == 0 && chng && xchng != chng && value(WARN) && dol > zero) {
  		xchng = chng;
  		vnfl();
  		ex_printf(mesg("[No write]|[No write since last change]"));
  		noonl();
  		flush();
  	} else
  		warn = 0;
  	if (printub) {
  		if (uxb[0] == 0)
  			error("No previous command@to repeat");
  		if (inopen) {
  			splitw++;
  			vclean();
  			vgoto(WECHO, 0);
  		}
  		if (warn)
  			vnfl();
  		if (hush == 0)
  			lprintf("!%s", uxb);
  		if (inopen && Outchar != termchar) {
  			vclreol();
  			vgoto(WECHO, 0);
  		} else
  			putnl();
  		flush();
  	}
  }
  
  /*
   * Do the real work for execution of a shell escape.
   * Mode is like the number passed to open system calls
   * and indicates filtering.  If input is implied, newstdin
   * must have been setup already.
   */
  ttymode
  unixex(char *opt, char *up, int newstdin, int mode)
  {
  	int pvec[2];
  	ttymode f = tty; /* initialize (CK) */
  
  	signal(SIGINT, SIG_IGN);
  #ifdef SIGTSTP
  	if (dosusp)
  		signal(SIGTSTP, SIG_DFL);
  #endif
  	if (inopen)
  		f = setty(normf);
  	if ((mode & 1) && pipe(pvec) < 0) {
  		/* Newstdin should be io so it will be closed */
  		if (inopen)
  			setty(f);
  		error("Can't make pipe for filter");
  	}
  #ifndef VFORK
  	pid = fork();
  #else
  	pid = vfork();
  #endif
  	if (pid < 0) {
  		if (mode & 1) {
  			close(pvec[0]);
  			close(pvec[1]);
  		}
  		setrupt();
  		error("No more processes");
  	}
  	if (pid == 0) {
  		if (up) {
  			char *cp = up;
  			while (*cp)
  				*cp++ &= TRIM;
  		}
  		if (mode & 2) {
  			close(0);
  			dup(newstdin);
  			close(newstdin);
  		}
  		if (mode & 1) {
  			close(pvec[0]);
  			close(1);
  			dup(pvec[1]);
  			if (inopen) {
  				close(2);
  				dup(1);
  			}
  			close(pvec[1]);
  		}
  		if (io)
  			close(io);
  		if (tfile)
  			close(tfile);
  #ifndef VMUNIX
  		close(erfile);
  #endif
  		signal(SIGHUP, oldhup);
  		signal(SIGQUIT, oldquit);
  		if (ruptible)
  			signal(SIGINT, SIG_DFL);
  		execl(svalue(SHELL), "sh", opt, up, (char *) 0);
  		ex_printf("No %s!\n", svalue(SHELL));
  		error(NOSTR);
  	}
  	if (mode & 1) {
  		io = pvec[0];
  		close(pvec[1]);
  	}
  	if (newstdin)
  		close(newstdin);
  	return (f);
  }
  
  /*
   * Wait for the command to complete.
   * F is for restoration of tty mode if from open/visual.
   * C flags suppression of printing.
   */
  void
  unixwt(bool c, ttymode f)
  {
  
  	waitfor();
  #ifdef SIGTSTP
  	if (dosusp)
  		signal(SIGTSTP, onsusp);
  #endif
  	if (inopen)
  		setty(f);
  	setrupt();
  	if (!inopen && c && hush == 0) {
  		ex_printf("!\n");
  		flush();
  		termreset();
  		gettmode();
  	}
  }
  
  /*
   * Setup a pipeline for the filtration implied by mode
   * which is like a open number.  If input is required to
   * the filter, then a child editor is created to write it.
   * If output is catch it from io which is created by unixex.
   */
  void
  filter(int mode)
  {
  	static int pvec[2];
  	ttymode f;	/* mjm: was register */
  	register int lines = lineDOL();
  
  	mode++;
  	if (mode & 2) {
  		signal(SIGINT, SIG_IGN);
  		if (pipe(pvec) < 0)
  			error("Can't make pipe");
  		pid = fork();
  		io = pvec[0];
  		if (pid < 0) {
  			setrupt();
  			close(pvec[1]);
  			error("No more processes");
  		}
  		if (pid == 0) {
  			setrupt();
  			io = pvec[1];
  			close(pvec[0]);
  			putfile(1);
  			ex_exit(0);
  		}
  		close(pvec[1]);
  		io = pvec[0];
  		setrupt();
  	}
  	f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
  	if (mode == 3) {
  		delete(0);
  		addr2 = addr1 - 1;
  	}
  	if (mode & 1) {
  		if(FIXUNDO)
  			undap1 = undap2 = addr2+1;
  		ignore(append(getfile, addr2));
  #ifdef TRACE
  		if (trace)
  			vudump("after append in filter");
  #endif
  	}
  	close(io);
  	io = -1;
  	unixwt(!inopen, f);
  	netchHAD(lines);
  }
  
  /*
   * Set up to do a recover, getting io to be a pipe from
   * the recover process.
   */
  void
  recover(void)
  {
  	static int pvec[2];
  
  	if (pipe(pvec) < 0)
  		error(" Can't make pipe for recovery");
  	pid = fork();
  	io = pvec[0];
  	if (pid < 0) {
  		close(pvec[1]);
  		error(" Can't fork to execute recovery");
  	}
  	if (pid == 0) {
  		close(2);
  		dup(1);
  		close(1);
  		dup(pvec[1]);
  	        close(pvec[1]);
  		execl(EXRECOVER, "exrecover", svalue(DIRECTORY), file, (char *) 0);
  		close(1);
  		dup(2);
  		error(" No recovery routine");
  	}
  	close(pvec[1]);
  }
  
  /*
   * Wait for the process (pid an external) to complete.
   */
  void
  waitfor(void)
  {
  
  	do
  		rpid = wait(&status);
  	while (rpid != pid && rpid != -1);
  	status = (status >> 8) & 0377;
  }
  
  /*
   * The end of a recover operation.  If the process
   * exits non-zero, force not edited; otherwise force
   * a write.
   */
  void
  revocer(void)
  {
  
  	waitfor();
  	if (pid == rpid && status != 0)
  		edited = 0;
  	else
  		change();
  }
  