[comp.unix.microport] diffs for ksh for Emacs editing, BSD-style job control

hedrick@athos.rutgers.edu (Charles Hedrick) (06/04/88)

Having gotten tired of waiting for a ksh binary with emacs enabled to
show up on the Uport BBS, I decided to build one myself.
Unfortunately, licensing considerations prevent me from posting the
whole thing.  But I have some advice to others who are trying to do
the same thing.  (Hopefully the kind folks at Microport will follow
these instructions and generate an emacs version of ksh for the BBS.)
These instructions also tell you how to get BSD-style job control to
work.  The capability is there is the ksh source, but a couple of
fixes and some instructions are needed to make it work.  These
instructions apply to what is called "ksh-i", from the Toolchest.
There are no version numbers that I can find, but files seem to be
created on Oct 27, 1987.  This applies to SV/AT.  Presumably ksh
and the job control stuff also works on the 386 version, but I have
no way to test it.

In order to build a ksh with emacs editing and job control, you
have to be in the src directory, and do 
   make OPTIONS="+v +s"
This turns off vi and the ability to do suid scripts, to save space in
the core image. (This would be unnecessary on the 386 version.  I
haven't had a chance to try building a large-model version on SV/AT.)
vi and emacs do not fit at once.  (That's why the uport copy doesn't
include emacs: you have to choose, and the default choice is vi.)

If you want job control, you will then want to go into the jsh
subdirectory and do
   make install
Note that you have to be root to do this install, as jsh has to be
installed setuid root (in order to be able to change ownership of the
sxt devices).  You will have to be running a kernel that has the sxt
devices in it.  By default uport kernels do not, but I believe the
"large" kernel does, and you can build one for yourself using the
linkkit.  /usr/bin/jsh is a jacket for ksh.  It sets things up to use
the appropriate set of sxt devices, and then calls /bin/sh (actually
it calls whatever shell is pointed to by the environment variable
SHELL, but I've only been able to make it work with the default
configuration where the Korn shell is installed as /bin/sh).

The changes below do the following:
  - make file name completion work right.  In the original ksh,
	ESC ESC  causes a partly-typed file name to be replaced
	by *all* file names that match it.  That is not what any
	other system means by filename recognition.  If there
	are several files beginning with what you typed, you
	want to complete the part that is the same for all of
	them and beep, rather than putting them all in your
	command line.  I've made ESC ESC do this, and left ESC *
	doing the old version.
  - ESC ? is an alternative to ESC = for listing all files that match
	what has been typed.  I found ESC = impossible to remember.
  - ESC u   uppercases the next word.  They took ESC l, but for
	some reason not ESC u.  (They called it ESC c, which at
	least on my version of Emacs capitalizes the next word,
	i.e. changes the first letter only to upper-case.)
  - fixed ^W to work if you have changed the erase character.
  - fixed the make file to enable the sxt code.  (You should just
	be able to remove the comments from 3 lines that are there
	already.  For reasons I didn't take time to look at, this
	didn't work for me, so I had to do what you see below.)
  - fixed the code for job control so that it works with pipelines.
	Previously if you typed "diff a b | more", the job would
	hang.

It is very clear that nobody has used job control seriously on System
V.  Almost no implementations of the sxt code work correctly, so
probably Korn was unable to test his code very much.  (Interestingly,
SV/AT seems to have a solid implementation of sxt's.)  The code to
implement job control using sxt's is by default disabled in ksh.
However I haven't run into any serious problems other than the fact
that pipelines hang, which is fixed below.  I *really* like getting
job control back.  I almost feel like this is real Unix now!  (My
primary editor, jove, has code in it to allow ^Z to suspend it.  This
was part of the diffs I posted a couple of weeks ago.  Probably
similar code should be put into "more" and any other programs that use
the raw terminal.)

The way I use job control is as follows:  in my .profile, the last
line is
  exec /usr/bin/jsh
This causes the login shell to be replaced by jsh, which then
replaces itself by ksh again (actually /bin/sh in my case, but that is
ksh on my system), but in a mode that allows it to do job control.  
This gives job control at the top level.  If you find yourself in
a recursively-spawned shell later (e.g. emacs spawns a shell), I
don't know of any way to get jsh to provide job control at that
level.  I tried setting SHELL to /usr/bin/jsh so that jsh is used
by any program that starts a shell, but that didn't work.  However
having it even at the top level is a welcome relief.

Now and then I find that something resets my tty modes to something
odd, so I have a script "reset" that puts back the appropriate
characters.  In case you find that suddenly job control has stopped
working, take a look at "stty".  If SWTCH is suddenly ^`, you will
need to do "stty swtch '^z'" to get it back to ^Z.  (This is part of
my reset script, which is probably a better approach.)

*** edit.c.ORIG	Fri Jun  3 20:34:18 1988
--- edit.c	Fri Jun  3 04:43:37 1988
***************
*** 628,634
   * file name generation for edit modes
   * non-zero exit for error, <0 ring bell
   * don't search back past <start> character of the buffer
!  * mode is '*' for inline expansion, otherwise files are listed in select format
   */
  
  q_expand(outbuff,cur,eol,start,mode)

--- 628,636 -----
   * file name generation for edit modes
   * non-zero exit for error, <0 ring bell
   * don't search back past <start> character of the buffer
!  * mode is '*' for inline expansion,
!  *         '?' for listing all files in select format
!  * otherwise unique completion
   */
  
  q_expand(outbuff,cur,eol,start,mode)
***************
*** 716,722
  		endstak(ptr);
  		last = ptr-1;
  	}
! 	if(mode!='*')
  		on_option(MARKDIR);
  	{
  		register char **com;

--- 718,724 -----
  		endstak(ptr);
  		last = ptr-1;
  	}
! 	if(mode=='?')
  		on_option(MARKDIR);
  	{
  		register char **com;
***************
*** 736,742
  				goto done;
  			}
  		}
! 		if(mode!='*')
  		{
  			if (strip)
  			{

--- 738,744 -----
  				goto done;
  			}
  		}
! 		if(mode=='?')
  		{
  			if (strip)
  			{
***************
*** 751,756
  			p_flush();
  			goto done;
  		}
  		/* see if there is enough room */
  		size = *eol - (out-begin);
  		size += narg;

--- 753,777 -----
  			p_flush();
  			goto done;
  		}
+ /* if normal completion, and not unique, beep and make it unique */
+ 		if(mode!='*' && narg > 1) {
+ 			char **nextcom = com+1;
+ 			char *firstcom;
+ 			char *thiscom;
+ 
+ 			e_ringbell();
+ 			while (*nextcom) {
+ 				thiscom = *nextcom;
+ 				firstcom = *com;
+ 				while (*thiscom++ == *firstcom)
+ 					firstcom++;
+ 				*firstcom = 0;
+ 				nextcom++;
+ 			}
+ 			narg = 1;
+ 			com[1] = 0;
+ 		}
+ 
  		/* see if there is enough room */
  		size = *eol - (out-begin);
  		size += narg;
*** emacs.c.ORIG	Fri Jun  3 20:32:06 1988
--- emacs.c	Fri Jun  3 05:19:54 1988
***************
*** 360,366
  				continue;
  			}
  			adjust = i - mark;
! 			ungetchar('\b');
  			continue;
  		case cntl(D) :
  			mark = i;

--- 360,366 -----
  				continue;
  			}
  			adjust = i - mark;
! 			ungetchar(usrerase);
  			continue;
  		case cntl(D) :
  			mark = i;
***************
*** 626,631
  
  		case 'l':	/* M-l == lower-case */
  		case 'd':
  		case 'c':
  		case 'f':
  		{

--- 626,632 -----
  
  		case 'l':	/* M-l == lower-case */
  		case 'd':
+ 		case 'u':
  		case 'c':
  		case 'f':
  		{
***************
*** 664,670
  				draw(UPDATE);
  				return(-1);
  			}
! 			else if(ch=='c')
  			{
  				ungetchar(cntl(C));
  				return(i-cur);

--- 665,671 -----
  				draw(UPDATE);
  				return(-1);
  			}
! 			else if(ch=='c' || ch=='u')
  			{
  				ungetchar(cntl(C));
  				return(i-cur);
***************
*** 751,760
  		}
  
  		/* file name expansion */
! 		case cntl([) :	/* easier to type */
! 			i = '*';
! 		case '*':
! 		case '=':	/* escape = - list all matching file names */
  			mark = cur;
  			if(q_expand(out,&cur,&eol,plen,i) < 0)
  				beep();

--- 752,762 -----
  		}
  
  		/* file name expansion */
! 		case '=': i = '?';  /* ? and = are list */
! 		case '?':
! 		case cntl([) :	/* unique completion */
! 		case '*':  /* complete all */
! 
  			mark = cur;
  			if(q_expand(out,&cur,&eol,plen,i) < 0)
  				beep();
***************
*** 758,764
  			mark = cur;
  			if(q_expand(out,&cur,&eol,plen,i) < 0)
  				beep();
! 			else if(i=='*')
  				draw(UPDATE);
  			else
  				draw(REFRESH);

--- 760,766 -----
  			mark = cur;
  			if(q_expand(out,&cur,&eol,plen,i) < 0)
  				beep();
! 			else if(i!='?')
  				draw(UPDATE);
  			else
  				draw(REFRESH);
*** makesh.ORIG	Fri Jun  3 22:38:30 1988
--- makesh	Fri Jun  3 13:37:52 1988
***************
*** 79,87
  if	test -d /dev/fd		# new research UNIX feature
  then	Options="$Options DEVFD=-DDEVFD"
  fi
! #if	test -d /dev/sxt	# sxt driver available
! #then	Options="$Options SXT=-DSXT"
! #fi
  if	test -f /vmunix -o "$SYSTYPE" = bsd4.1 -o "$SYSTYPE" = bsd4.2 # true for BSD unix
  then	JOBLIB=-ljobs Options="$Options DBSD=-DBSD" JOBS=jobs.o LFLAGS=-z
  	if	test -f /etc/networks #BSD 4.2

--- 79,88 -----
  if	test -d /dev/fd		# new research UNIX feature
  then	Options="$Options DEVFD=-DDEVFD"
  fi
  #if	test -d /dev/sxt	# sxt driver available
  #then	Options="$Options SXT=-DSXT"
  #fi
! Options="$Options SXT=-DSXT"
  if	test -f /vmunix -o "$SYSTYPE" = bsd4.1 -o "$SYSTYPE" = bsd4.2 # true for BSD unix
  then	JOBLIB=-ljobs Options="$Options DBSD=-DBSD" JOBS=jobs.o LFLAGS=-z
  	if	test -f /etc/networks #BSD 4.2
*** xec.c.ORIG	Fri Jun  3 22:15:12 1988
--- xec.c	Fri Jun  3 22:15:13 1988
***************
*** 256,261
  				int no_fork;
  				sync_io();
  #ifdef SXT
  				/* find job number and create synchronization pipe */
  				if((jobstat.cur_job = next_job()) < jobstat.maxjob)
  					if(pipe(jobstat.pipe)<0)

--- 256,262 -----
  				int no_fork;
  				sync_io();
  #ifdef SXT
+ 				if(jobstat.j_flag==0) {
  				/* find job number and create synchronization pipe */
  				if((jobstat.cur_job = next_job()) < jobstat.maxjob)
  					if(pipe(jobstat.pipe)<0)
***************
*** 260,265
  				if((jobstat.cur_job = next_job()) < jobstat.maxjob)
  					if(pipe(jobstat.pipe)<0)
  						jobstat.maxjob = 0;
  #endif	/* SXT */
  				no_fork = (execflg&1) && (type&(FAMP|FPOU))==0;
  				if(no_fork)

--- 261,267 -----
  				if((jobstat.cur_job = next_job()) < jobstat.maxjob)
  					if(pipe(jobstat.pipe)<0)
  						jobstat.maxjob = 0;
+ 				}
  #endif	/* SXT */
  				no_fork = (execflg&1) && (type&(FAMP|FPOU))==0;
  				if(no_fork)