1*e0b8e63eSJohn MarinoThere are six (normally) asynchronous actions about which vi cares: 2*e0b8e63eSJohn MarinoSIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH. 3*e0b8e63eSJohn Marino 4*e0b8e63eSJohn MarinoThe assumptions: 5*e0b8e63eSJohn Marino 1: The DB routines are not reentrant. 6*e0b8e63eSJohn Marino 2: The curses routines may not be reentrant. 7*e0b8e63eSJohn Marino 3: Neither DB nor curses will restart system calls. 8*e0b8e63eSJohn Marino 9*e0b8e63eSJohn MarinoXXX 10*e0b8e63eSJohn MarinoNote, most C library functions don't restart system calls. So, we should 11*e0b8e63eSJohn Marino*probably* start blocking around any imported function that we don't know 12*e0b8e63eSJohn Marinodoesn't make a system call. This is going to be a genuine annoyance... 13*e0b8e63eSJohn Marino 14*e0b8e63eSJohn MarinoSIGHUP, SIGTERM 15*e0b8e63eSJohn Marino Used for file recovery. The DB routines can't be reentered, nor 16*e0b8e63eSJohn Marino can they handle interrupted system calls, so the vi routines that 17*e0b8e63eSJohn Marino call DB block signals. This means that DB routines could be 18*e0b8e63eSJohn Marino called at interrupt time, if necessary. 19*e0b8e63eSJohn Marino 20*e0b8e63eSJohn MarinoSIGQUIT 21*e0b8e63eSJohn Marino Disabled by the signal initialization routines. Historically, ^\ 22*e0b8e63eSJohn Marino switched vi into ex mode, and we continue that practice. 23*e0b8e63eSJohn Marino 24*e0b8e63eSJohn MarinoSIGWINCH: 25*e0b8e63eSJohn Marino The interrupt routine sets a global bit which is checked by the 26*e0b8e63eSJohn Marino key-read routine, so there are no reentrancy issues. This means 27*e0b8e63eSJohn Marino that the screen will not resize until vi runs out of keys, but 28*e0b8e63eSJohn Marino that doesn't seem like a problem. 29*e0b8e63eSJohn Marino 30*e0b8e63eSJohn MarinoSIGINT and SIGTSTP are a much more difficult issue to resolve. Vi has 31*e0b8e63eSJohn Marinoto permit the user to interrupt long-running operations. Generally, a 32*e0b8e63eSJohn Marinosearch, substitution or read/write is done on a large file, or, the user 33*e0b8e63eSJohn Marinocreates a key mapping with an infinite loop. This problem will become 34*e0b8e63eSJohn Marinoworse as more complex semantics are added to vi, especially things like 35*e0b8e63eSJohn Marinomaking it a pure text widget. There are four major solutions on the table, 36*e0b8e63eSJohn Marinoeach of which have minor permutations. 37*e0b8e63eSJohn Marino 38*e0b8e63eSJohn Marino1: Run in raw mode. 39*e0b8e63eSJohn Marino 40*e0b8e63eSJohn Marino The up side is that there's no asynchronous behavior to worry about, 41*e0b8e63eSJohn Marino and obviously no reentrancy problems. The down side is that it's easy 42*e0b8e63eSJohn Marino to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look 43*e0b8e63eSJohn Marino like an interrupt) and it's easy to get into places where we won't see 44*e0b8e63eSJohn Marino interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in 45*e0b8e63eSJohn Marino historic implementations of vi). Periodically reading the terminal 46*e0b8e63eSJohn Marino input buffer might solve the latter problem, but it's not going to be 47*e0b8e63eSJohn Marino pretty. 48*e0b8e63eSJohn Marino 49*e0b8e63eSJohn Marino Also, we're going to be checking for ^C's and ^Z's both, all over 50*e0b8e63eSJohn Marino the place -- I hate to litter the source code with that. For example, 51*e0b8e63eSJohn Marino the historic version of vi didn't permit you to suspend the screen if 52*e0b8e63eSJohn Marino you were on the colon command line. This isn't right. ^Z isn't a vi 53*e0b8e63eSJohn Marino command, it's a terminal event. (Dammit.) 54*e0b8e63eSJohn Marino 55*e0b8e63eSJohn Marino2: Run in cbreak mode. There are two problems in this area. First, the 56*e0b8e63eSJohn Marino current curses implementations (both System V and Berkeley) don't give 57*e0b8e63eSJohn Marino you clean cbreak modes. For example, the IEXTEN bit is left on, turning 58*e0b8e63eSJohn Marino on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with 59*e0b8e63eSJohn Marino the exception that flow control and signals are turned on, and curses 60*e0b8e63eSJohn Marino cbreak mode doesn't give you this. 61*e0b8e63eSJohn Marino 62*e0b8e63eSJohn Marino We can either set raw mode and twiddle the tty, or cbreak mode and 63*e0b8e63eSJohn Marino twiddle the tty. I chose to use raw mode, on the grounds that raw 64*e0b8e63eSJohn Marino mode is better defined and I'm less likely to be surprised by a curses 65*e0b8e63eSJohn Marino implementation down the road. The twiddling consists of setting ISIG, 66*e0b8e63eSJohn Marino IXON/IXOFF, and disabling some of the interrupt characters (see the 67*e0b8e63eSJohn Marino comments in cl_init.c). This is all found in historic System V (SVID 68*e0b8e63eSJohn Marino 3) and POSIX 1003.1-1992, so it should be fairly portable. 69*e0b8e63eSJohn Marino 70*e0b8e63eSJohn Marino The second problem is that vi permits you to enter literal signal 71*e0b8e63eSJohn Marino characters, e.g. ^V^C. There are two possible solutions. First, you 72*e0b8e63eSJohn Marino can turn off signals when you get a ^V, but that means that a network 73*e0b8e63eSJohn Marino packet containing ^V and ^C will lose, since the ^C may take effect 74*e0b8e63eSJohn Marino before vi reads the ^V. (This is particularly problematic if you're 75*e0b8e63eSJohn Marino talking over a protocol that recognizes signals locally and sends OOB 76*e0b8e63eSJohn Marino packets when it sees them.) Second, you can turn the ^C into a literal 77*e0b8e63eSJohn Marino character in vi, but that means that there's a race between entering 78*e0b8e63eSJohn Marino ^V<character>^C, i.e. the sequence may end up being ^V^C<character>. 79*e0b8e63eSJohn Marino Also, the second solution doesn't work for flow control characters, as 80*e0b8e63eSJohn Marino they aren't delivered to the program as signals. 81*e0b8e63eSJohn Marino 82*e0b8e63eSJohn Marino Generally, this is what historic vi did. (It didn't have the curses 83*e0b8e63eSJohn Marino problems because it didn't use curses.) It entered signals following 84*e0b8e63eSJohn Marino ^V characters into the input stream, (which is why there's no way to 85*e0b8e63eSJohn Marino enter a literal flow control character). 86*e0b8e63eSJohn Marino 87*e0b8e63eSJohn Marino3: Run in mostly raw mode; turn signals on when doing an operation the 88*e0b8e63eSJohn Marino user might want to interrupt, but leave them off most of the time. 89*e0b8e63eSJohn Marino 90*e0b8e63eSJohn Marino This works well for things like file reads and writes. This doesn't 91*e0b8e63eSJohn Marino work well for trying to detect infinite maps. The problem is that 92*e0b8e63eSJohn Marino you can write the code so that you don't have to turn on interrupts 93*e0b8e63eSJohn Marino per keystroke, but the code isn't pretty and it's hard to make sure 94*e0b8e63eSJohn Marino that an optimization doesn't cover up an infinite loop. This also 95*e0b8e63eSJohn Marino requires interaction or state between the vi parser and the key 96*e0b8e63eSJohn Marino reading routines, as an infinite loop may still be returning keys 97*e0b8e63eSJohn Marino to the parser. 98*e0b8e63eSJohn Marino 99*e0b8e63eSJohn Marino Also, if the user inserts an interrupt into the tty queue while the 100*e0b8e63eSJohn Marino interrupts are turned off, the key won't be treated as an interrupt, 101*e0b8e63eSJohn Marino and requiring the user to pound the keyboard to catch an interrupt 102*e0b8e63eSJohn Marino window is nasty. 103*e0b8e63eSJohn Marino 104*e0b8e63eSJohn Marino4: Run in mostly raw mode, leaving signals on all of the time. Done 105*e0b8e63eSJohn Marino by setting raw mode, and twiddling the tty's termios ISIG bit. 106*e0b8e63eSJohn Marino 107*e0b8e63eSJohn Marino This works well for the interrupt cases, because the code only has 108*e0b8e63eSJohn Marino to check to see if the interrupt flag has been set, and can otherwise 109*e0b8e63eSJohn Marino ignore signals. It's also less likely that we'll miss a case, and we 110*e0b8e63eSJohn Marino don't have to worry about synchronizing between the vi parser and the 111*e0b8e63eSJohn Marino key read routines. 112*e0b8e63eSJohn Marino 113*e0b8e63eSJohn Marino The down side is that we have to turn signals off if the user wants 114*e0b8e63eSJohn Marino to enter a literal character (e.g. ^V^C). If the user enters the 115*e0b8e63eSJohn Marino combination fast enough, or as part of a single network packet, 116*e0b8e63eSJohn Marino the text input routines will treat it as a signal instead of as a 117*e0b8e63eSJohn Marino literal character. To some extent, we have this problem already, 118*e0b8e63eSJohn Marino since we turn off flow control so that the user can enter literal 119*e0b8e63eSJohn Marino XON/XOFF characters. 120*e0b8e63eSJohn Marino 121*e0b8e63eSJohn Marino This is probably the easiest to code, and provides the smoothest 122*e0b8e63eSJohn Marino programming interface. 123*e0b8e63eSJohn Marino 124*e0b8e63eSJohn MarinoThere are a couple of other problems to consider. 125*e0b8e63eSJohn Marino 126*e0b8e63eSJohn MarinoFirst, System V's curses doesn't handle SIGTSTP correctly. If you use the 127*e0b8e63eSJohn Marinonewterm() interface, the TSTP signal will leave you in raw mode, and the 128*e0b8e63eSJohn Marinofinal endwin() will leave you in the correct shell mode. If you use the 129*e0b8e63eSJohn Marinoinitscr() interface, the TSTP signal will return you to the correct shell 130*e0b8e63eSJohn Marinomode, but the final endwin() will leave you in raw mode. There you have 131*e0b8e63eSJohn Marinoit: proof that drug testing is not making any significant headway in the 132*e0b8e63eSJohn Marinocomputer industry. The 4BSD curses is deficient in that it does not have 133*e0b8e63eSJohn Marinoan interface to the terminal keypad. So, regardless, we have to do our 134*e0b8e63eSJohn Marinoown SIGTSTP handling. 135*e0b8e63eSJohn Marino 136*e0b8e63eSJohn MarinoThe problem with this is that if we do our own SIGTSTP handling, in either 137*e0b8e63eSJohn Marinomodels #3 or #4, we're going to have to call curses routines at interrupt 138*e0b8e63eSJohn Marinotime, which means that we might be reentering curses, which is something we 139*e0b8e63eSJohn Marinodon't want to do. 140*e0b8e63eSJohn Marino 141*e0b8e63eSJohn MarinoSecond, SIGTSTP has its own little problems. It's broadcast to the entire 142*e0b8e63eSJohn Marinoprocess group, not sent to a single process. The scenario goes something 143*e0b8e63eSJohn Marinolike this: the shell execs the mail program, which execs vi. The user hits 144*e0b8e63eSJohn Marino^Z, and all three programs get the signal, in some random order. The mail 145*e0b8e63eSJohn Marinoprogram goes to sleep immediately (since it probably didn't have a SIGTSTP 146*e0b8e63eSJohn Marinohandler in place). The shell gets a SIGCHLD, does a wait, and finds out 147*e0b8e63eSJohn Marinothat the only child in its foreground process group (of which it's aware) 148*e0b8e63eSJohn Marinois asleep. It then optionally resets the terminal (because the modes aren't 149*e0b8e63eSJohn Marinohow it left them), and starts prompting the user for input. The problem is 150*e0b8e63eSJohn Marinothat somewhere in the middle of all of this, vi is resetting the terminal, 151*e0b8e63eSJohn Marinoand getting ready to send a SIGTSTP to the process group in order to put 152*e0b8e63eSJohn Marinoitself to sleep. There's a solution to all of this: when vi starts, it puts 153*e0b8e63eSJohn Marinoitself into its own process group, and then only it (and possible child 154*e0b8e63eSJohn Marinoprocesses) receive the SIGTSTP. This permits it to clean up the terminal 155*e0b8e63eSJohn Marinoand switch back to the original process group, where it sends that process 156*e0b8e63eSJohn Marinogroup a SIGTSTP, putting everyone to sleep and waking the shell. 157*e0b8e63eSJohn Marino 158*e0b8e63eSJohn MarinoThird, handing SIGTSTP asynchronously is further complicated by the child 159*e0b8e63eSJohn Marinoprocesses vi may fork off. If vi calls ex, ex resets the terminal and 160*e0b8e63eSJohn Marinostarts running some filter, and SIGTSTP stops them both, vi has to know 161*e0b8e63eSJohn Marinowhen it restarts that it can't repaint the screen until ex's child has 162*e0b8e63eSJohn Marinofinished running. This is solveable, but it's annoying. 163*e0b8e63eSJohn Marino 164*e0b8e63eSJohn MarinoWell, somebody had to make a decision, and this is the way it's going to be 165*e0b8e63eSJohn Marino(unless I get talked out of it). SIGINT is handled asynchronously, so 166*e0b8e63eSJohn Marinothat we can pretty much guarantee that the user can interrupt any operation 167*e0b8e63eSJohn Marinoat any time. SIGTSTP is handled synchronously, so that we don't have to 168*e0b8e63eSJohn Marinoreenter curses and so that we don't have to play the process group games. 169*e0b8e63eSJohn Marino^Z is recognized in the standard text input and command modes. (^Z should 170*e0b8e63eSJohn Marinoalso be recognized during operations that may potentially take a long time. 171*e0b8e63eSJohn MarinoThe simplest solution is probably to twiddle the tty, install a handler for 172*e0b8e63eSJohn MarinoSIGTSTP, and then restore normal tty modes when the operation is complete.) 173