From bc8510c4ea9da320e263b2eecd11a7b309b0e11e Mon Sep 17 00:00:00 2001 From: Joel Sherrill Date: Wed, 27 May 2015 13:47:55 -0700 Subject: Add libtecla 1.6.3 --- libtecla/html/gl_io_mode.html | 634 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 634 insertions(+) create mode 100644 libtecla/html/gl_io_mode.html (limited to 'libtecla/html/gl_io_mode.html') diff --git a/libtecla/html/gl_io_mode.html b/libtecla/html/gl_io_mode.html new file mode 100644 index 0000000..42d27dc --- /dev/null +++ b/libtecla/html/gl_io_mode.html @@ -0,0 +1,634 @@ +Content-type: text/html + + +Man page of gl_io_mode + +

gl_io_mode

+Section: C Library Functions (3)
Index +Return to Main Contents
+ +  +

NAME

+ +
 gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line, +
 gl_handle_signal, gl_pending_io - How to use gl_get_line() from an external event loop. +  +

SYNOPSIS

+ +
+#include <libtecla.h>
+
+int gl_io_mode(GetLine *gl, GlIOMode mode);
+
+int gl_raw_io(GetLine *gl);
+
+int gl_normal_io(GetLine *gl);
+
+int gl_tty_signals(void (*term_handler)(int),
+                   void (*susp_handler)(int),
+                   void (*cont_handler)(int),
+                   void (*size_handler)(int));
+
+void gl_abandon_line(GetLine *gl);
+
+void gl_handle_signal(int signo, GetLine *gl, int ngl);
+
+GlPendingIO gl_pending_io(GetLine *gl);
+
+
+ +

+  +

DESCRIPTION

+ +

+The gl_get_line() function, which is documented separately in +the gl_get_line(3) man page, supports two different I/O modes. +These are selected by calling the gl_io_mode() function. +

+

+

+  int gl_io_mode(GetLine *gl, GlIOMode mode);
+
+ +

+

+The mode argument of this function specifies the new I/O mode, +and must be one of the following. +

+

+

+  GL_NORMAL_MODE   -  Select the normal blocking-I/O mode.
+                      In this mode gl_get_line()
+                      doesn't return until either an error
+                      occurs of the user finishes entering a
+                      new line. This mode is the focus of
+                      the gl_get_line(3) man page.
+
+  GL_SERVER_MODE   -  Select non-blocking server I/O mode.
+                      In this mode, since non-blocking
+                      terminal I/O is used, the entry of
+                      each new input line typically requires
+                      many calls to gl_get_line() from
+                      an external I/O-driven event loop.
+                      This mode is the focus of this man
+                      page.
+
+ +

+

+Newly created GetLine objects start in normal I/O +mode, so to switch to non-blocking server mode requires an +initial call to gl_io_mode(). +

+  +

SERVER I/O MODE

+ +

+In non-blocking server I/O mode, the application is required +to have an event loop which calls gl_get_line() +whenever the terminal file descriptor can do the type I/O +that gl_get_line() is waiting for. To determine which +type of I/O gl_get_line() is waiting for, the +application calls the gl_pending_io() function. +

+

+

+  GlPendingIO gl_pending_io(GetLine *gl);
+
+ +

+

+The return value of this function is one of the following two +enumerated values. +

+

+

+  GLP_READ    -  gl_get_line() is waiting to write a
+                 character to the terminal.
+
+  GLP_WRITE   -  gl_get_line() is waiting to read a
+                 character from the keyboad.
+
+ +

+

+If the application is using either the select() or poll() +system calls to watch for I/O on a group of file descriptors, then it +should call the gl_pending_io() function before each call to +these functions to see which direction of I/O it should tell them to +watch for, and configure their arguments accordingly. In the case of +the select() system call, this means using the FD_SET() +macro to add the terminal file descriptor either to the set of file +descriptors to be watched for readability, or the set to be watched +for writability. +

+As in normal I/O mode, the return value of gl_get_line() is +either a pointer to a completed input line, or NULL. However, +whereas in normal I/O mode a NULL return value always means that +an error occurred, in non-blocking server mode, NULL is also +returned when gl_get_line() can't read or write to the terminal +without blocking. Thus in non-blocking server mode, in order to +determine when a NULL return value signifies that an error +occurred or not, it is necessary to call the gl_return_status() +function. If this function returns the enumerated value, +GLR_BLOCKED, as documented in the gl_get_line(3) man page, +this means that gl_get_line() is waiting for I/O, and no error +has occurred. +

+When gl_get_line() returns NULL and +gl_return_status() indicates that this is due to blocked +terminal I/O, the application should call gl_get_line() again +when the type of I/O reported by gl_pending_io() becomes +possible. The prompt, start_line and start_pos +arguments of gl_get_line() will be ignored on these calls. If +you need to change the prompt of the line that is currently being +edited, then you can call the gl_replace_prompt() function +(documented in the gl_get_line(3) man page) between calls to +gl_get_line(). +

+  +

GIVING UP THE TERMINAL

+ +

+A complication that is unique to non-blocking server mode is that it +requires that the terminal be left in raw mode between calls to +gl_get_line(). If this weren't the case, the external event loop +wouldn't be able to detect individual key-presses, and the basic line +editing implemented by the terminal driver would clash with the +editing provided by gl_get_line(). What this means is that any +time that the terminal needs to be used for other things than entering +a new input line with gl_get_line(), it needs to be restored to +a usable state. In particular, whenever the process is suspended or +terminated, the terminal must be returned to a normal state. If this +isn't done, then depending on the characteristics of the shell that +was used to invoke the program, the user may end up with a hung +terminal. To this end, the gl_normal_io() function is provided +for switching the terminal back to the state that it was in when raw +mode was last established. +

+

+

+  int gl_normal_io(GetLine *gl);
+
+ +

+

+What this function does is first flush any pending output to the +terminal, then move the cursor to the start of the terminal line which +follows the end of the incompletely entered input line. At this point +it is safe to suspend or terminate the process, and it is safe for the +application to read and write to the terminal. To resume entry of the +input line, the application should call the gl_raw_io() +function. +

+

+

+  int gl_raw_io(GetLine *gl);
+
+ +

+

+This function starts a new line, redisplays the partially completed +input line (if any), restores the cursor position within this line to +where it was when gl_normal_io() was called, then switches back +to raw, non-blocking terminal mode ready to continue entry of the +input line when gl_get_line() is next called. +

+Note that in non-blocking server mode, if gl_get_line() is +called after a call to gl_normal_io(), without an intervening +call to gl_raw_io(), gl_get_line() will call +gl_raw_mode() itself, and the terminal will remain in this mode +when gl_get_line() returns. +

+  +

SIGNAL HANDLING

+ +

+In the previous section it was pointed out that in non-blocking server +mode, the terminal must be restored to a sane state whenever a signal +is received that either suspends or terminates the process. In normal +I/O mode, this is done for you by gl_get_line(), but in +non-blocking server mode, since the terminal is left in raw mode +between calls to gl_get_line(), this signal handling has to be +done by the application. Since there are many signals that can suspend +or terminate a process, as well as other signals that are important to +gl_get_line(), such as the SIGWINCH signal, which tells it +when the terminal size has changed, the gl_tty_signals() +function is provided for installing signal handlers for all pertinent +signals. +

+

+

+  int gl_tty_signals(void (*term_handler)(int),
+                     void (*susp_handler)(int),
+                     void (*cont_handler)(int),
+                     void (*size_handler)(int));
+
+ +

+

+What this does is use gl_get_line()'s internal list of signals +to assign specified signal handlers to groups of signals. The +arguments of this function are as follows. +

+

+

+  term_handler  -  This is the signal handler that is to be
+                   used to trap signals that by default
+                   terminate any process that receives
+                   them (eg. SIGINT or SIGTERM).
+
+  susp_handler  -  This is the signal handler that is to be
+                   used to trap signals that by default
+                   suspend any process that receives them,
+                   (eg. SIGTSTP or SIGTTOU).
+
+  cont_handler  -  This is the signal handler that is to be
+                   used to trap signals that are usually
+                   sent when a process resumes after being
+                   suspended (usually SIGCONT). Beware that there is
+                   nothing to stop a user from sending one of these
+                   signals at other times.
+
+  size_handler  -  This signal handler is used to trap
+                   signals that are sent to processes when
+                   their controlling terminals are resized
+                   by the user (eg. SIGWINCH).
+
+ +

+

+These arguments can all be the same, if so desired, and you can +specify SIG_IGN (ignore this signal) or SIG_DFL (use the +system-provided default signal handler) instead of a function where +pertinent. In particular, it is rarely useful to trap SIGCONT, +so the cont_handler argument will usually be SIG_DFL or +SIG_IGN. +

+The gl_tty_signals() function uses the POSIX sigaction() +function to install these signal handlers, and it is careful to use +the sa_mask member of each sigaction structure to ensure that +only one of these signals is ever delivered at a time. This guards +against different instances of these signal handlers from +simultaneously trying to write to common global data, such as a shared +sigsetjmp() buffer or a signal-received flag. +

+The signal handlers that are installed by this function, should call +the gl_handle_signal(). +

+

+

+  void gl_handle_signal(int signo, GetLine *gl, int ngl);
+
+ +

+

+The signo argument tells this function which signal it is being +asked to respond to, and the gl argument should be a pointer to +the first element of an array of ngl GetLine objects. If +your application only has one of these objects, just pass its pointer +as the gl argument and specify ngl as 1. +

+Depending on the signal that is being handled, this function does +different things. +

+  +

Terminal resize signals (SIGWINCH)

+ +

+If the signal indicates that the terminal was resized, then it +arranges for the next call to gl_get_line() to ask the terminal +for its new size and redraw the input line accordingly. In order that +gl_get_line() be called as soon as possible to do this, +gl_handle_signal() also arranges that the next call to +gl_pending_io() will return GLP_WRITE. Thus if the +application waits for I/O in select() or poll(), then the +application needs to ensure that these functions will be reliably +aborted when a signal is caught and handled by the application. More +on this below. +

+  +

Process termination signals.

+ +

+If the signal that was caught is one of those that by default +terminates any process that receives it, then gl_handle_signal() +does the following steps. +

+1. First it blocks the delivery of all signals that can be +
   blocked (ie. SIGKILL and SIGSTOP can't be blocked) +

+2. Next it calls gl_normal_io() for each of the ngl +
   GetLine objects. Note that this does nothing to any of the +
   GetLine objects that aren't currently in raw mode. +

+3. Next it sets the signal handler of the signal to its default, +
   process-termination disposition. +

+4. Next it re-sends the process the signal that was caught. +

+5. Finally it unblocks delivery of this signal, which +
   results in the process being terminated. +

+  +

Process suspension signals.

+ +

+If the default disposition of the signal is to suspend the process, +the same steps are executed as for process termination signals, except +that when the process is later resumed, gl_handle_signal() +continues, and does the following steps. +

+6. It re-blocks delivery of the signal. +

+7. It reinstates the signal handler of the signal to the one +
   that was displaced when its default disposition was substituted. +

+8. For any of the GetLine objects that were in raw mode when +
   gl_handle_signal() was called, gl_handle_signal() then +
   calls gl_raw_io(), to resume entry of the input lines on +
   those terminals. +

+9. Finally, it restores the signal process mask to how it +
   was when gl_handle_signal() was called. +

+Note that the process is suspended or terminated using the original +signal that was caught, rather than using the uncatchable +SIGSTOP and SIGKILL signals. This is important, because +when a process is suspended or terminated, the parent of the process +may wish to use the status value returned by the wait() system +call to figure out which signal was responsible. In particular, most +shells use this information to print a corresponding message to the +terminal. Users would be rightly confused if when their process +received a SIGPIPE signal, the program responded by sending +itself a SIGKILL signal, and the shell then printed out the +provocative statement, "Killed!". +

+  +

INTERRUPTING THE EVENT LOOP

+ +

+If a signal is caught and handled when the application's event loop is +waiting in select() or poll(), these functions will be +aborted with errno set to EINTR. When this happens the +event loop should call gl_pending_io(), before calling +select() or poll() again. It should then arrange for +select() or poll() to wait for the type of I/O that this +reports. This is necessary, because any signal handler which calls +gl_handle_signal(), will frequently change the type of I/O that +gl_get_line() is waiting for. +

+Unfortunately, if a signal arrives between the statements which +configure the arguments of select() or poll() and the +calls to these functions, then the signal will not be seen by these +functions, which will then not be aborted. If these functions are +waiting for keyboard input from the user when the signal is received, +and the signal handler arranges to redraw the input line to accomodate +a terminal resize or the resumption of the process, then this +redisplay will be end up being delayed until the user hits the next +key. Apart from puzzling the user, this clearly isn't a serious +problem. However there is a way, albeit complicated, to completely +avoid this race condition. The following steps illustrate this. +

+1. Block all of the signals that gl_get_line() catches, +
   by passing the signal set returned by gl_list_signals() to +
   sigprocmask(). +

+2. Call gl_pending_io() and set up the arguments of +
   select() or poll() accordingly. +

+3. Call sigsetjmp() with a non-zero savesigs argument. +

+4. Initially this sigsetjmp() statement will return zero, +
   indicating that control isn't resuming there after a matching +
   call to siglongjmp(). +

+5. Replace all of the handlers of the signals that gl_get_line() +
   is configured to catch, with a signal handler that first records +
   the number of the signal that was caught, in a file-scope variable, +
   then calls siglongjmp() with a non-zero value argument, to +
   return execution to the above sigsetjmp() +
   statement.  Registering these signal handlers can conveniently be +
   done using the gl_tty_signals() function. +

+6. Set the file-scope variable that the above signal handler uses to +
   record any signal that is caught to -1, so that we can check +
   whether a signal was caught by seeing if it contains a valid signal +
   number. +

+7. Now unblock the signals that were blocked in step 1. Any signal +
   that was received by the process in between step 1 and now will +
   now be delivered, and trigger our signal handler, as will any +
   signal that is received until we block these signals again. +

+8. Now call select() or poll(). +

+9. When select() returns, again block the signals that were +
   unblocked in step 7. +

+If a signal is arrived any time during the above steps, our signal +handler will be triggered and cause control to return to the +sigsetjmp() statement, where this time, sigsetjmp() will +return non-zero, indicating that a signal was caught. When this +happens we simply skip the above block of statements, and continue +with the following statements, which are executed regardless of +whether or not a signal is caught. Note that when sigsetjmp() +returns, regardless of why it returned, the process signal mask is +returned to how it was when sigsetjmp() was called. Thus the +following statements are always executed with all of our signals +blocked. +

+9. Reinstate the signal handlers that were displaced in step 5. +

+10. Check wether a signal was caught, by checking the file-scope +
    variable that the signal handler records signal numbers in. +

+11. If a signal was caught, send this signal to the application +
    again, and unblock just this signal, so that it invokes the +
    signal handler which we just reinstated in step 10. +

+12. Unblock all of the signals that were blocked in step 7. +

+Since this is complicated, note that demo3.c includes a working +example of how to do this. The method used there however, is more +general than the above. What it provides is a wrapper function around +select() which encompasses steps 3 to 11. In this wrapper, +rather than use gl_list_signals() to figure out the signals to +block, and and gl_tty_signals() to assign and revert signal +handlers, one of its arguments is a sigset_t which specifies +which signals to block and assign signal handlers to. This function +thus doesn't depend on gl_get_line() and can thus be used in +other situations where race-condition-free signal handling is +required. +

+  +

SIGNALS CAUGHT BY GL_GET_LINE

+ +

+Since the application is expected to handle signals in non-blocking +server mode, gl_get_line() doesn't attempt to duplicate this +when it is being called. If one of the signals that it is configured +to catch is sent to the application while gl_get_line() is being +called, gl_get_line() reinstates the caller's signal handlers, +then just before returning, re-sends the signal to the process to let +the application's signal handler handle it. If the process isn't +terminated by this signal, gl_get_line() returns NULL, and +a following call to gl_return_status() returns the enumerated +value GLR_SIGNAL. +

+  +

ABORTING LINE INPUT

+ +

+Often, rather than letting it terminate the process, applications +respond to the SIGINT user-interrupt signal by aborting the current +input line. The way to do this in non-blocking server-I/O mode is to +not call gl_handle_signal() when this signal is caught, but +instead to call the gl_abandon_line(). +

+

+

+  void gl_abandon_line(GetLine *gl);
+
+ +

+

+This function arranges that when gl_get_line() is next called, +it first flushes any pending output to the terminal, then discardes +the current input line, outputs a new prompt on the next line, and +finally starts accepting input of a new input line from the user. +

+  +

SIGNAL SAFE FUNCTIONS

+ +

+Provided that certain rules are followed, the following functions can +have been written to be safely callable from signal handlers. Other +functions in this library should not be called from signal handlers. +

+

+

+  gl_normal_io()
+  gl_raw_io()
+  gl_handle_signal()
+  gl_abandon_line()
+
+ +

+

+In order for this to be true, all signal handlers that call these +functions must be registered in such a way that only one instance of +any one of them can be running at one time. The way to do this is to +use the POSIX sigaction() function to register all signal +handlers, and when doing this, use the sa_mask member of the +corresponding sigaction structure, to indicate that all of the signals +who's handlers invoke the above functions, should be blocked when the +current signal is being handled. This prevents two signal handlers +from operating on a GetLine object at the same time. +

+To prevent signal handlers from accessing a GetLine object while +gl_get_line() or any of its associated public functions are +operating on it, all public functions associated with +gl_get_line(), including gl_get_line() itself, temporarily +block the delivery of signals when they are accessing GetLine +objects. Beware that the only signals that they block are the signals +that gl_get_line() is currently configured to catch, so be sure +that if you call any of the above functions from signal handlers, that +the signals that these handlers are assigned to are configured to be +caught by gl_get_line() (see gl_trap_signal()). +

+  +

USING TIMEOUTS TO POLL

+ +

+If instead of using select() or poll() to wait for I/O, +your application just needs to get out of gl_get_line() +periodically to briefly do something else before returning to accept +input from the user, this can be done in non-blocking server mode by +using the gl_inactivity_timeout() function (see +gl_get_line(3)), to specify that a callback function that +returns GLTO_CONTINUE should be called whenever +gl_get_line() has been waiting for I/O for more than a specified +amount of time. +

+When this callback is triggered, gl_get_line() will return +NULL, and a following call to gl_return_status() will +return GLR_BLOCKED. +

+Beware that gl_get_line() won't return until the user +hasn't typed a key for the specified interval, so if the +interval is long, and the user keeps typing, +gl_get_line() may not return for a while. In other +words there is no guarantee that it will return in the time +specified. +

+  +

THE SERVER DEMO PROGRAM

+ +

+The demo3 program that is distributed with the library, provides +a working example of how to use non-blocking server I/O mode in a real +program. As far as the user is concerned, this program operates +identically to the main demo program (called demo), except that +whereas the main demo program uses the normal blocking I/O mode, +demo3 using non-blocking I/O and an external event loop. The +source code can be found in demo3.c, and the comments therein +explain the various steps. +

+  +

FILES

+ +
+libtecla.a      -    The tecla library
+libtecla.h      -    The tecla header file.
+
+ +

+  +

SEE ALSO

+ +

+

+libtecla(3), gl_get_line(3), tecla(7), ef_expand_file(3),
+cpl_complete_word(3), pca_lookup_file(3)
+
+ +

+  +

AUTHOR

+ +Martin Shepherd (mcs@astro.caltech.edu) +

+ +


+ 

Index

+
+
NAME
+
SYNOPSIS
+
DESCRIPTION
+
SERVER I/O MODE
+
GIVING UP THE TERMINAL
+
SIGNAL HANDLING
+
+
Terminal resize signals (SIGWINCH)
+
+
Process termination signals.
+
Process suspension signals.
+
INTERRUPTING THE EVENT LOOP
+
SIGNALS CAUGHT BY GL_GET_LINE
+
ABORTING LINE INPUT
+
SIGNAL SAFE FUNCTIONS
+
USING TIMEOUTS TO POLL
+
THE SERVER DEMO PROGRAM
+
FILES
+
SEE ALSO
+
AUTHOR
+
+
+This document was created by +man2html, +using the manual pages.
+Time: 22:21:57 GMT, November 09, 2014 + + -- cgit v1.2.3