[comp.bugs.sys5] more ksh fixes

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

This is a new edition my diffs to ksh to make job control work in System V.
Someone reported a problem that I've also seen.  I was unable to get mail
back to him.  Since others may have seen the same thing, I'm just posting
the result.  The enclosed diffs fix several major problems with job control,
and one with Emacs completion.  They are with respect to the most recent
version of ksh-i from the ATT Toolchest.  Changes since the last posting are
that the interrupt character now works properly, and that it is safe
to ^Z a shell script.  Here are the things fixed:

  with job control in effect, commands involving a pipeline (i.e. '|')
	gave odd effects.  They would not complete properly, would
	display the wrong job number, etc.
  with job control in effect, it was impossible to pipe builtin
	commands, e.g. "history | more".  Output would not come
	out or would be interspersed with other things.  (This bug
	was not in the original, but was actually caused by an
	incorrect attempt to fix the first one.)
  with job control in effect, if you tried to suspend a shell
	script with ^Z (or whatever), the current program would be
	suspended and the shell script would continue.  This is
	presumably some sort of messup with process groups, but
	I couldn't find it.  So I resorted to making the shell
	exec a new copy of itself under situations where it would
	normally longjmp back to the beginning.  The situations
	where this happens are limited enough that it shouldn't
	cause performance problems.
  with job control in effect, the interrupt character (^C or whatever) 
	would not work for commands that didn't fork.  That is, you
	couldn't abort a partially-typed command with the interrupt,
	nor could you interrupt "while" or "for" loops typed to the
	shell.  Scripts would work fine, as would loops typed 
	within ().  The problem was that /dev/sxt000, or whatever
	the controlling sxt turned out to be, wasn't the controlling
	terminal.  The code did a setpgrp, but didn't reopen the
	device so as to establish the proper controlling terminal.
	(Maybe jsh should be doing this, but I was not able to
	make it do so.)  I tried to make it do that, but found
	that I couldn't open /dev/sxt000.  jsh was opening it
	exclusive, but even removing that I couldn't open
	/dev/sxt000.  I finally ended up using the highest numbered
	subchannel for the shell itself, typically /dev/sxt007.
	There's got to be a better way, but I spent two days trying
	to find it and failed.
  in emacs mode, ESC ESC should ring the bell if there is more than
	one possible completion.  ESC * still does as before. (This
	behavior is consistent with newer versions of ksh, and presumably
	will show up in the toolkit version at some point.)
  other minor emacs fixes, e.g. letting it work correctly when
	the erase char is something other than ^H

Here are known problems that are *not* fixed by these edits:
  the problem with the interrupt character noted above will
	continue to happen if ksh is run directly as a top-level
	process.  My conjecture is that getty does a setpgrp,
	and once it has been done for a given process, further
	ones are ignored.  This means that we have no way to
	reset the controlling terminal to /dev/sxt007.
	Currently, my .profile ends with
	   jsh
	   exit
	It used to end with
	   exec /usr/bin/jsh
	This results is using an extra process, but it's fairly
	easy to show that this is unlikely to cause any trouble,
	even in terms of memory and swap space usage.
  when job control is in effect, attempts to run more than 6
	jobs at once will hang the shell.  This is obviously
	just a coding blunder, but I don't have time to fix
	it at the moment.  I'll try to do so eventually.
  typeahead may not behave as expected when job control is in
	effect.  This is a basic problem with the kernel
	support for sxt's, and can't be fixed in the shell.
  you can't suspend a builtin command, unless it is in ().  This
	is a reasonable restriction.

It appears that I am currently the maintainer of this stuff.  I talked to
David Korn.  While he is sympathetic with my desire to have job control, he
regards the System V sxt mechanism as unacceptably buggy, and is now
supporting only Berkeley-style job control.  I am unwilling to wait for
System V release 4.  It appears that even the current sxt support
will be removed from future releases of ksh.

*** 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	Tue Jun  7 11:23:47 1988
***************
*** 360,366
  				continue;
  			}
  			adjust = i - mark;
! 			ungetchar('\b');
  			continue;
  		case cntl(D) :
  			mark = i;

--- 368,374 -----
  				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':
  		{

--- 634,640 -----
  
  		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);

--- 673,679 -----
  				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();

--- 760,770 -----
  		}
  
  		/* 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);

--- 768,774 -----
  			mark = cur;
  			if(q_expand(out,&cur,&eol,plen,i) < 0)
  				beep();
! 			else if(i!='?')
  				draw(UPDATE);
  			else
  				draw(REFRESH);
*** jobs.c.ORIG	Sat Jun 25 20:25:38 1988
--- jobs.c	Sat Jun 25 22:50:42 1988
***************
*** 279,284
  #ifdef SXT
  static char sxt[] = "/dev/sxt/000";
  static struct sxtblock status1,status2;
  
  int init_jobs(lflag)
  {

--- 279,287 -----
  #ifdef SXT
  static char sxt[] = "/dev/sxt/000";
  static struct sxtblock status1,status2;
+ static char *open_mode[3] = {"r","w","w+"};
+ static int ctlchn = 2;
+ #define MAIN_CHAN MAXPCHAN - 1
  
  int init_jobs(lflag)
  {
***************
*** 302,311
  	sxt[10] = '0' + (dev%10);
  	sxt[9] = '0' + dev/10;
  	my_stty.c_cc[VSWTCH] = CSWTCH;
! 	if(ioctl(2,TCSETAF,&my_stty) < 0)
! 		return(-1);
! 	setpgrp(pid,pid);
! 	jobstat.maxjob = MAXPCHAN;
  	on_option(MONITOR);
  	return(0);
  }

--- 305,336 -----
  	sxt[10] = '0' + (dev%10);
  	sxt[9] = '0' + dev/10;
  	my_stty.c_cc[VSWTCH] = CSWTCH;
! 	{
! 		register int fd;
! 		register int i;
! 		sxt[11] = '0' + MAIN_CHAN;
! 		ctlchn = dup(2);
! 		setpgrp();		
! 		fd = open(sxt,2);
! 		if(ioctl(fd,TCSETA,&my_stty)<0 || fd < 0)
! 		{
! 			return(0);
! 		}
! 		for(i=0;i<3;i++)
! 		{
! 			if(isatty(i))
! 			{
! 				close(i);
! 				dup(fd);
! 			}
! 		}
! 		close(fd);
! 		if(ioctl(ctlchn,SXTIOCSWTCH,MAIN_CHAN)!=0)
! 		{
! 			return(-1);
! 		}
! 	}
! 	jobstat.maxjob = MAXPCHAN - 1;
  	on_option(MONITOR);
  	return(0);
  }
***************
*** 369,375
  		jobstat.pipe[0] = 0;
  	}
  	if(pw->p_job < jobstat.maxjob)
! 		if(ioctl(2,SXTIOCSWTCH,pw->p_job)!=0)
  		{
  			return(-1);
  		}

--- 394,400 -----
  		jobstat.pipe[0] = 0;
  	}
  	if(pw->p_job < jobstat.maxjob)
! 		if(ioctl(ctlchn,SXTIOCSWTCH,pw->p_job)!=0)
  		{
  			return(-1);
  		}
***************
*** 417,423
  		fd_chan = -1;
  		/* grab control in channel 0 */
  		ioctl(2,TCSETAW,&my_stty);
! 		if(ioctl(2,SXTIOCSWTCH,0)!=0)
  		{
  			return(-1);
  		}

--- 442,448 -----
  		fd_chan = -1;
  		/* grab control in channel 0 */
  		ioctl(2,TCSETAW,&my_stty);
! 		if(ioctl(ctlchn,SXTIOCSWTCH,MAIN_CHAN)!=0)
  		{
  			return(-1);
  		}
***************
*** 1345,1351
   * open up the input streams for a new channel
   */
  
- static char *open_mode[3] = {"r","w","w+"};
  j_new_chan()
  {
  	register FILE* fd;

--- 1370,1375 -----
   * open up the input streams for a new channel
   */
  
  j_new_chan()
  {
  	register FILE* fd;
*** makesh.ORIG	Fri Jun  3 22:38:30 1988
--- makesh	Mon Jun 13 03:26:08 1988
***************
*** 1,3
  CMD=${CMD-/bin/make}
  ARK=${ARK-lib.a}
  #

--- 1,4 -----
  CMD=${CMD-/bin/make}
  ARK=${ARK-lib.a}
  #
***************
*** 82,87
  #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

--- 83,89 -----
  #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
*** service.c.ORIG	Mon Jun 13 13:48:46 1988
--- service.c	Tue Jun 14 00:44:06 1988
***************
*** 460,465
  	if(ex_xenix(p))
  #endif	/* XENIX */
  	execve(p, &t[0] ,xecenv);
  	switch(errno)
  	{
  		case ENOEXEC:

--- 460,475 -----
  	if(ex_xenix(p))
  #endif	/* XENIX */
  	execve(p, &t[0] ,xecenv);
+ 	if (errno == ENOEXEC && (is_option(MONITOR))) {
+ 	   char *localarg[256]; int i;
+ 
+ 	   for (i = 0; t[i]; i++)
+ 		localarg[i+1] = t[i];
+ 	   localarg[i+1] = 0;
+ 	   localarg[0] = t[0];
+ 	   localarg[1] = p;
+ 	   execve("/bin/sh", &localarg[0], xecenv);
+ 	}
  	switch(errno)
  	{
  		case ENOEXEC:
*** xec.c.ORIG	Fri Jun  3 22:15:12 1988
--- xec.c	Mon Jun 13 14:15:14 1988
***************
*** 138,143
  		type = t->tretyp;
  		oldexit=exitval;
  		exitval=0;
  		switch(type&COMMSK)
  		{
  			case TCOM:

--- 138,152 -----
  		type = t->tretyp;
  		oldexit=exitval;
  		exitval=0;
+ 
+ #ifdef clh
+   p_num(getpid(),' ');
+   p_num(execflg,' ');
+   if (states&MONITOR) p_str("monitor",' ');
+   p_num(t->tretyp,'\n');
+   p_flush();
+ #endif
+ 
  		switch(type&COMMSK)
  		{
  			case TCOM:
***************
*** 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)

--- 265,272 -----
  				int no_fork;
  				sync_io();
  #ifdef SXT
+ 				if(jobstat.j_flag==0)
+ 					jobstat.cur_job = next_job();
  				/* find job number and create synchronization pipe */
  				if(jobstat.cur_job < jobstat.maxjob)
  					if(pipe(jobstat.pipe)<0)
***************
*** 257,263
  				sync_io();
  #ifdef SXT
  				/* find job number and create synchronization pipe */
! 				if((jobstat.cur_job = next_job()) < jobstat.maxjob)
  					if(pipe(jobstat.pipe)<0)
  						jobstat.maxjob = 0;
  #endif	/* SXT */

--- 268,274 -----
  				if(jobstat.j_flag==0)
  					jobstat.cur_job = next_job();
  				/* find job number and create synchronization pipe */
! 				if(jobstat.cur_job < jobstat.maxjob)
  					if(pipe(jobstat.pipe)<0)
  						jobstat.maxjob = 0;
  #endif	/* SXT */
***************
*** 290,295
  						vfork_save();
  					while((parent=(v_fork?vfork():fork())) == -1)
  #else
  					while((parent=fork()) == -1)
  #endif	/* VFORK */
  					{

--- 301,312 -----
  						vfork_save();
  					while((parent=(v_fork?vfork():fork())) == -1)
  #else
+ #ifdef clh
+ if (states&MONITOR) {
+   p_str("fork",'\n');
+   p_flush();
+ }
+ #endif
  					while((parent=fork()) == -1)
  #endif	/* VFORK */
  					{

twb@hoqax.UUCP (T.W. Beattie) (06/27/88)

In article <Jun.26.00.16.07.1988.23795@athos.rutgers.edu>, hedrick@athos.rutgers.edu (Charles Hedrick) writes:
>   in emacs mode, ESC ESC should ring the bell if there is more than
> 	one possible completion.

Why?
I prefer it to make the expansion of multiple matches (as it does).
The expansion should only beep if it fails.

Tombo.