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.
hedrick@aramis.rutgers.edu (Charles Hedrick) (06/29/88)
I am reluctant to get into an argument about taste. I can only say that Tenex, TOPS-20, various variants of csh, Emacs, and the newest version of ksh (not yet in the toolchest) all agree that completion of a file name simply means just that: completion. If it is impossible to provide a unique completion, they beep. Actually TOPS-20 is stricter than the Unix software. If there is more than one alternative, TOPS-20 will just beep, whereas the Unix software will generally complete that portion on which they all agree. (However there are provisions in TOPS-20 for completing the base name even if there is more than one extension.) E.g. if you have aaabbbccc and aaabbbaaa, and you type aaa<ESC>, most Unix completion software will supply the bbb and then beep. Many of us have gotten used to this behavior and depend upon it. E.g. the Emacs that I use renames the original of an edited file to foo.BAK. So it's common that I have aaa.c and aaa.c.BAK. If I type aa<ESC>, it will complete to aaa.c and then beep. I do not want to see both aaa.c and aaa.c.BAK, which is what the original ksh code did. Similarly, if I have verylongname.c and verylongname.o, and I type very<ESC>, it should complete to verylongname., thus requiring me to type only .c or .o. Completion seems to have been invented in the first place to help users type the long command and file names allowed by Tenex. Since many of those long names exist with multiple extensions, we really do want partial completion, not enumeration of the alternatives. Obviously there are uses for both behaviors, which is why the new ksh code continues to provide the original behavior under ESC *.