summaryrefslogtreecommitdiffstats
path: root/contrib/nvi/cl/README.signal
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/nvi/cl/README.signal')
-rw-r--r--contrib/nvi/cl/README.signal174
1 files changed, 174 insertions, 0 deletions
diff --git a/contrib/nvi/cl/README.signal b/contrib/nvi/cl/README.signal
new file mode 100644
index 0000000..7faa456
--- /dev/null
+++ b/contrib/nvi/cl/README.signal
@@ -0,0 +1,174 @@
+# @(#)README.signal 10.1 (Berkeley) 6/23/95
+
+There are six (normally) asynchronous actions about which vi cares:
+SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGTSTP and SIGWINCH.
+
+The assumptions:
+ 1: The DB routines are not reentrant.
+ 2: The curses routines may not be reentrant.
+ 3: Neither DB nor curses will restart system calls.
+
+XXX
+Note, most C library functions don't restart system calls. So, we should
+*probably* start blocking around any imported function that we don't know
+doesn't make a system call. This is going to be a genuine annoyance...
+
+SIGHUP, SIGTERM
+ Used for file recovery. The DB routines can't be reentered, nor
+ can they handle interrupted system calls, so the vi routines that
+ call DB block signals. This means that DB routines could be
+ called at interrupt time, if necessary.
+
+SIGQUIT
+ Disabled by the signal initialization routines. Historically, ^\
+ switched vi into ex mode, and we continue that practice.
+
+SIGWINCH:
+ The interrupt routine sets a global bit which is checked by the
+ key-read routine, so there are no reentrancy issues. This means
+ that the screen will not resize until vi runs out of keys, but
+ that doesn't seem like a problem.
+
+SIGINT and SIGTSTP are a much more difficult issue to resolve. Vi has
+to permit the user to interrupt long-running operations. Generally, a
+search, substitution or read/write is done on a large file, or, the user
+creates a key mapping with an infinite loop. This problem will become
+worse as more complex semantics are added to vi, especially things like
+making it a pure text widget. There are four major solutions on the table,
+each of which have minor permutations.
+
+1: Run in raw mode.
+
+ The up side is that there's no asynchronous behavior to worry about,
+ and obviously no reentrancy problems. The down side is that it's easy
+ to misinterpret characters (e.g. :w big_file^Mi^V^C is going to look
+ like an interrupt) and it's easy to get into places where we won't see
+ interrupt characters (e.g. ":map a ixx^[hxxaXXX" infinitely loops in
+ historic implementations of vi). Periodically reading the terminal
+ input buffer might solve the latter problem, but it's not going to be
+ pretty.
+
+ Also, we're going to be checking for ^C's and ^Z's both, all over
+ the place -- I hate to litter the source code with that. For example,
+ the historic version of vi didn't permit you to suspend the screen if
+ you were on the colon command line. This isn't right. ^Z isn't a vi
+ command, it's a terminal event. (Dammit.)
+
+2: Run in cbreak mode. There are two problems in this area. First, the
+ current curses implementations (both System V and Berkeley) don't give
+ you clean cbreak modes. For example, the IEXTEN bit is left on, turning
+ on DISCARD and LNEXT. To clarify, what vi WANTS is 8-bit clean, with
+ the exception that flow control and signals are turned on, and curses
+ cbreak mode doesn't give you this.
+
+ We can either set raw mode and twiddle the tty, or cbreak mode and
+ twiddle the tty. I chose to use raw mode, on the grounds that raw
+ mode is better defined and I'm less likely to be surprised by a curses
+ implementation down the road. The twiddling consists of setting ISIG,
+ IXON/IXOFF, and disabling some of the interrupt characters (see the
+ comments in cl_init.c). This is all found in historic System V (SVID
+ 3) and POSIX 1003.1-1992, so it should be fairly portable.
+
+ The second problem is that vi permits you to enter literal signal
+ characters, e.g. ^V^C. There are two possible solutions. First, you
+ can turn off signals when you get a ^V, but that means that a network
+ packet containing ^V and ^C will lose, since the ^C may take effect
+ before vi reads the ^V. (This is particularly problematic if you're
+ talking over a protocol that recognizes signals locally and sends OOB
+ packets when it sees them.) Second, you can turn the ^C into a literal
+ character in vi, but that means that there's a race between entering
+ ^V<character>^C, i.e. the sequence may end up being ^V^C<character>.
+ Also, the second solution doesn't work for flow control characters, as
+ they aren't delivered to the program as signals.
+
+ Generally, this is what historic vi did. (It didn't have the curses
+ problems because it didn't use curses.) It entered signals following
+ ^V characters into the input stream, (which is why there's no way to
+ enter a literal flow control character).
+
+3: Run in mostly raw mode; turn signals on when doing an operation the
+ user might want to interrupt, but leave them off most of the time.
+
+ This works well for things like file reads and writes. This doesn't
+ work well for trying to detect infinite maps. The problem is that
+ you can write the code so that you don't have to turn on interrupts
+ per keystroke, but the code isn't pretty and it's hard to make sure
+ that an optimization doesn't cover up an infinite loop. This also
+ requires interaction or state between the vi parser and the key
+ reading routines, as an infinite loop may still be returning keys
+ to the parser.
+
+ Also, if the user inserts an interrupt into the tty queue while the
+ interrupts are turned off, the key won't be treated as an interrupt,
+ and requiring the user to pound the keyboard to catch an interrupt
+ window is nasty.
+
+4: Run in mostly raw mode, leaving signals on all of the time. Done
+ by setting raw mode, and twiddling the tty's termios ISIG bit.
+
+ This works well for the interrupt cases, because the code only has
+ to check to see if the interrupt flag has been set, and can otherwise
+ ignore signals. It's also less likely that we'll miss a case, and we
+ don't have to worry about synchronizing between the vi parser and the
+ key read routines.
+
+ The down side is that we have to turn signals off if the user wants
+ to enter a literal character (e.g. ^V^C). If the user enters the
+ combination fast enough, or as part of a single network packet,
+ the text input routines will treat it as a signal instead of as a
+ literal character. To some extent, we have this problem already,
+ since we turn off flow control so that the user can enter literal
+ XON/XOFF characters.
+
+ This is probably the easiest to code, and provides the smoothest
+ programming interface.
+
+There are a couple of other problems to consider.
+
+First, System V's curses doesn't handle SIGTSTP correctly. If you use the
+newterm() interface, the TSTP signal will leave you in raw mode, and the
+final endwin() will leave you in the correct shell mode. If you use the
+initscr() interface, the TSTP signal will return you to the correct shell
+mode, but the final endwin() will leave you in raw mode. There you have
+it: proof that drug testing is not making any significant headway in the
+computer industry. The 4BSD curses is deficient in that it does not have
+an interface to the terminal keypad. So, regardless, we have to do our
+own SIGTSTP handling.
+
+The problem with this is that if we do our own SIGTSTP handling, in either
+models #3 or #4, we're going to have to call curses routines at interrupt
+time, which means that we might be reentering curses, which is something we
+don't want to do.
+
+Second, SIGTSTP has its own little problems. It's broadcast to the entire
+process group, not sent to a single process. The scenario goes something
+like this: the shell execs the mail program, which execs vi. The user hits
+^Z, and all three programs get the signal, in some random order. The mail
+program goes to sleep immediately (since it probably didn't have a SIGTSTP
+handler in place). The shell gets a SIGCHLD, does a wait, and finds out
+that the only child in its foreground process group (of which it's aware)
+is asleep. It then optionally resets the terminal (because the modes aren't
+how it left them), and starts prompting the user for input. The problem is
+that somewhere in the middle of all of this, vi is resetting the terminal,
+and getting ready to send a SIGTSTP to the process group in order to put
+itself to sleep. There's a solution to all of this: when vi starts, it puts
+itself into its own process group, and then only it (and possible child
+processes) receive the SIGTSTP. This permits it to clean up the terminal
+and switch back to the original process group, where it sends that process
+group a SIGTSTP, putting everyone to sleep and waking the shell.
+
+Third, handing SIGTSTP asynchronously is further complicated by the child
+processes vi may fork off. If vi calls ex, ex resets the terminal and
+starts running some filter, and SIGTSTP stops them both, vi has to know
+when it restarts that it can't repaint the screen until ex's child has
+finished running. This is solveable, but it's annoying.
+
+Well, somebody had to make a decision, and this is the way it's going to be
+(unless I get talked out of it). SIGINT is handled asynchronously, so
+that we can pretty much guarantee that the user can interrupt any operation
+at any time. SIGTSTP is handled synchronously, so that we don't have to
+reenter curses and so that we don't have to play the process group games.
+^Z is recognized in the standard text input and command modes. (^Z should
+also be recognized during operations that may potentially take a long time.
+The simplest solution is probably to twiddle the tty, install a handler for
+SIGTSTP, and then restore normal tty modes when the operation is complete.)
OpenPOWER on IntegriCloud