1 GL_IO_MODE(3TECLA)            Interactive Command-line Input Library Functions
   5 NAME
   6        gl_io_mode, gl_raw_io, gl_normal_io, gl_tty_signals, gl_abandon_line,
   7        gl_handle_signal, gl_pending_io - use gl_get_line() from an external
   8        event loop
  11        cc [ flag... ] file... -ltecla [ library... ]
  12        #include <libtecla.h>
  14        int gl_io_mode(GetLine *gl, GlIOMode mode);
  17        int gl_raw_io(GetLine *gl);
  20        int gl_normal_io(GetLine *gl);
  23        int gl_tty_signals(void (*term_handler)(int), void (*susp_handler)(int),
  24             void (*cont_handler)(int), void (*size_handler)(int));
  27        void gl_abandon_line(GetLine *gl);
  30        void gl_handle_signal(int signo, GetLine *gl, int ngl);
  33        GlPendingIO gl_pending_io(GetLine *gl);
  37        The gl_get_line(3TECLA) function supports two different I/O modes.
  38        These are selected by calling the gl_io_mode() function. The mode
  39        argument of gl_io_mode() specifies the new I/O mode and must be one of
  40        the following.
  42        GL_NORMAL_MODE
  43                          Select the normal blocking-I/O mode. In this mode
  44                          gl_get_line() does not return until either an error
  45                          occurs of the user finishes entering a new line.
  48        GL_SERVER_MODE
  49                          Select non-blocking server I/O mode. In this mode,
  50                          since non-blocking terminal I/O is used, the entry of
  51                          each new input line typically requires many calls to
  52                          gl_get_line() from an external I/O-driven event loop.
  56        Newly created GetLine objects start in normal I/O mode, so to switch to
  57        non-blocking server mode requires an initial call to gl_io_mode().
  59    Server I/O Mode
  60        In non-blocking server I/O mode, the application is required to have an
  61        event loop that calls gl_get_line() whenever the terminal file
  62        descriptor can perform the type of I/O that gl_get_line() is waiting
  63        for. To determine which type of I/O gl_get_line() is waiting for, the
  64        application calls the gl_pending_io() function.  The return value is
  65        one of the following two enumerated values.
  67        GLP_READ
  68                     gl_get_line() is waiting to write a character to the
  69                     terminal.
  72        GLP_WRITE
  73                     gl_get_line() is waiting to read a character from the
  74                     keyboard.
  78        If the application is using either the select(3C) or poll(2) function
  79        to watch for I/O on a group of file descriptors, then it should call
  80        the gl_pending_io() function before each call to these functions to
  81        determine which direction of I/O it should tell them to watch for, and
  82        configure their arguments accordingly. In the case of the select()
  83        function, this means using the FD_SET() macro to add the terminal file
  84        descriptor either to the set of file descriptors to be watched for
  85        readability or the set to be watched for writability.
  88        As in normal I/O mode, the return value of gl_get_line() is either a
  89        pointer to a completed input line or NULL. However, whereas in normal
  90        I/O mode a NULL return value always means that an error occurred, in
  91        non-blocking server mode, NULL is also returned when gl_get_line()
  92        cannot read or write to the terminal without blocking. Thus in non-
  93        blocking server mode, in order to determine when a NULL return value
  94        signifies that an error occurred or not, it is necessary to call the
  95        gl_return_status() function. If this function returns the enumerated
  96        value GLR_BLOCKED, gl_get_line() is waiting for I/O and no error has
  97        occurred.
 100        When gl_get_line() returns NULL and gl_return_status() indicates that
 101        this is due to blocked terminal I/O, the application should call
 102        gl_get_line() again when the type of I/O reported by gl_pending_io()
 103        becomes possible. The prompt, start_line and start_pos arguments of
 104        gl_get_line() will be ignored on these calls. If you need to change the
 105        prompt of the line that is currently being edited, you can call the
 106        gl_replace_prompt(3TECLA) function between calls to gl_get_line().
 108    Giving Up The Terminal
 109        A complication that is unique to non-blocking server mode is that it
 110        requires that the terminal be left in raw mode between calls to
 111        gl_get_line(). If this were not the case, the external event loop would
 112        not be able to detect individual key-presses, and the basic line
 113        editing implemented by the terminal driver would clash with the editing
 114        provided by gl_get_line(). When the terminal needs to be used for
 115        purposes other than entering a new input line with gl_get_line(), it
 116        needs to be restored to a usable state. In particular, whenever the
 117        process is suspended or terminated, the terminal must be returned to a
 118        normal state. If this is not done, then depending on the
 119        characteristics of the shell that was used to invoke the program, the
 120        user could end up with a hung terminal. To this end, the gl_normal_io()
 121        function is provided for switching the terminal back to the state that
 122        it was in when raw mode was last established.
 125        The gl_normal_io() function first flushes any pending output to the
 126        terminal, then moves the cursor to the start of the terminal line which
 127        follows the end of the incompletely entered input line. At this point
 128        it is safe to suspend or terminate the process, and it is safe for the
 129        application to read and write to the terminal. To resume entry of the
 130        input line, the application should call the gl_raw_io() function.
 133        The gl_normal_io() function starts a new line, redisplays the partially
 134        completed input line (if any), restores the cursor position within this
 135        line to where it was when gl_normal_io() was called, then switches back
 136        to raw, non-blocking terminal mode ready to continue entry of the input
 137        line when gl_get_line() is next called.
 140        Note that in non-blocking server mode, if gl_get_line() is called after
 141        a call to gl_normal_io(), without an intervening call to gl_raw_io(),
 142        gl_get_line() will call gl_raw_mode() itself, and the terminal will
 143        remain in this mode when gl_get_line() returns.
 145    Signal Handling
 146        In the previous section it was pointed out that in non-blocking server
 147        mode, the terminal must be restored to a sane state whenever a signal
 148        is received that either suspends or terminates the process. In normal
 149        I/O mode, this is done for you by gl_get_line(), but in non-blocking
 150        server mode, since the terminal is left in raw mode between calls to
 151        gl_get_line(), this signal handling has to be done by the application.
 152        Since there are many signals that can suspend or terminate a process,
 153        as well as other signals that are important to gl_get_line(), such as
 154        the SIGWINCH signal, which tells it when the terminal size has changed,
 155        the gl_tty_signals() function is provided for installing signal
 156        handlers for all pertinent signals.
 159        The gl_tty_signals() function uses gl_get_line()'s internal list of
 160        signals to assign specified signal handlers to groups of signals. The
 161        arguments of this function are as follows.
 163        term_handler
 164                        This is the signal handler that is used to trap signals
 165                        that by default terminate any process that receives
 166                        them (for example, SIGINT or SIGTERM).
 169        susp_handler
 170                        This is the signal handler that is used to trap signals
 171                        that by default suspend any process that receives them,
 172                        (for example, SIGTSTP or SIGTTOU).
 175        cont_handler
 176                        This is the signal handler that is used to trap signals
 177                        that are usually sent when a process resumes after
 178                        being suspended (usually SIGCONT). Beware that there is
 179                        nothing to stop a user from sending one of these
 180                        signals at other times.
 183        size_handler
 184                        This signal handler is used to trap signals that are
 185                        sent to processes when their controlling terminals are
 186                        resized by the user (for example, SIGWINCH).
 190        These arguments can all be the same, if so desired, and SIG_IGN (ignore
 191        this signal) or SIG_DFL (use the system-provided default signal
 192        handler) can be specified instead of a function where pertinent. In
 193        particular, it is rarely useful to trap SIGCONT, so the cont_handler
 194        argument will usually be SIG_DFL or SIG_IGN.
 197        The gl_tty_signals() function uses the POSIX sigaction(2) function to
 198        install these signal handlers, and it is careful to use the sa_mask
 199        member of each sigaction structure to ensure that only one of these
 200        signals is ever delivered at a time. This guards against different
 201        instances of these signal handlers from simultaneously trying to write
 202        to common global data, such as a shared sigsetjmp(3C) buffer or a
 203        signal-received flag.  The signal handlers installed by this function
 204        should call the gl_handle_signal().
 207        The signo argument tells this function which signal it is being asked
 208        to respond to, and the gl argument should be a pointer to the first
 209        element of an array of ngl GetLine objects. If your application has
 210        only one of these objects, pass its pointer as the gl argument and
 211        specify ngl as 1.
 214        Depending on the signal that is being handled, this function does
 215        different things.
 217    Process termination signals
 218        If the signal that was caught is one of those that by default
 219        terminates any process that receives it, then gl_handle_signal() does
 220        the following steps.
 222            1.     First it blocks the delivery of all signals that can be
 223                   blocked (ie.  SIGKILL and SIGSTOP cannot be blocked).
 225            2.     Next it calls gl_normal_io() for each of the ngl GetLine
 226                   objects. Note that this does nothing to any of the GetLine
 227                   objects that are not currently in raw mode.
 229            3.     Next it sets the signal handler of the signal to its
 230                   default, process-termination disposition.
 232            4.     Next it re-sends the process the signal that was caught.
 234            5.     Finally it unblocks delivery of this signal, which results
 235                   in the process being terminated.
 237    Process suspension signals
 238        If the default disposition of the signal is to suspend the process, the
 239        same steps are executed as for process termination signals, except that
 240        when the process is later resumed, gl_handle_signal() continues, and
 241        does the following steps.
 243            1.     It re-blocks delivery of the signal.
 245            2.     It reinstates the signal handler of the signal to the one
 246                   that was displaced when its default disposition was
 247                   substituted.
 249            3.     For any of the GetLine objects that were in raw mode when
 250                   gl_handle_signal() was called, gl_handle_signal() then calls
 251                   gl_raw_io(), to resume entry of the input lines on those
 252                   terminals.
 254            4.     Finally, it restores the signal process mask to how it was
 255                   when gl_handle_signal() was called.
 258        Note that the process is suspended or terminated using the original
 259        signal that was caught, rather than using the uncatchable SIGSTOP and
 260        SIGKILL signals. This is important, because when a process is suspended
 261        or terminated, the parent of the process may wish to use the status
 262        value returned by the wait system call to figure out which signal was
 263        responsible. In particular, most shells use this information to print a
 264        corresponding message to the terminal.  Users would be rightly confused
 265        if when their process received a SIGPIPE signal, the program responded
 266        by sending itself a SIGKILL signal, and the shell then printed out the
 267        provocative statement, "Killed!".
 269    Interrupting The Event Loop
 270        If a signal is caught and handled when the application's event loop is
 271        waiting in select() or poll(), these functions will be aborted with
 272        errno set to EINTR. When this happens the event loop should call
 273        gl_pending_io() before calling select() or poll() again. It should then
 274        arrange for select() or poll() to wait for the type of I/O that
 275        gl_pending_io() reports. This is necessary because any signal handler
 276        that calls gl_handle_signal() will frequently change the type of I/O
 277        that gl_get_line() is waiting for.
 280        If a signal arrives between the statements that configure the arguments
 281        of select() or poll() and the calls to these functions, the signal will
 282        not be seen by these functions, which will then not be aborted. If
 283        these functions are waiting for keyboard input from the user when the
 284        signal is received, and the signal handler arranges to redraw the input
 285        line to accommodate a terminal resize or the resumption of the process.
 286        This redisplay will be delayed until the user presses the next key.
 287        Apart from puzzling the user, this clearly is not a serious problem.
 288        However there is a way, albeit complicated, to completely avoid this
 289        race condition. The following steps illustrate this.
 291            1.     Block all of the signals that gl_get_line() catches, by
 292                   passing the signal set returned by gl_list_signals() to
 293                   sigprocmask(2).
 295            2.     Call gl_pending_io() and set up the arguments of select() or
 296                   poll() accordingly.
 298            3.     Call sigsetjmp(3C) with a non-zero savemask argument.
 300            4.     Initially this sigsetjmp() statement will return zero,
 301                   indicating that control is not resuming there after a
 302                   matching call to siglongjmp(3C).
 304            5.     Replace all of the handlers of the signals that
 305                   gl_get_line() is configured to catch, with a signal handler
 306                   that first records the number of the signal that was caught,
 307                   in a file-scope variable, then calls siglongjmp() with a
 308                   non-zero val argument, to return execution to the above
 309                   sigsetjmp() statement. Registering these signal handlers can
 310                   conveniently be done using the gl_tty_signals() function.
 312            6.     Set the file-scope variable that the above signal handler
 313                   uses to record any signal that is caught to -1, so that we
 314                   can check whether a signal was caught by seeing if it
 315                   contains a valid signal number.
 317            7.     Now unblock the signals that were blocked in step 1. Any
 318                   signal that was received by the process in between step 1
 319                   and now will now be delivered, and trigger our signal
 320                   handler, as will any signal that is received until we block
 321                   these signals again.
 323            8.     Now call select() or poll().
 325            9.     When select returns, again block the signals that were
 326                   unblocked in step 7.
 328                   If a signal is arrived any time during the above steps, our
 329                   signal handler will be triggered and cause control to return
 330                   to the sigsetjmp() statement, where this time, sigsetjmp()
 331                   will return non-zero, indicating that a signal was caught.
 332                   When this happens we simply skip the above block of
 333                   statements, and continue with the following statements,
 334                   which are executed regardless of whether or not a signal is
 335                   caught. Note that when sigsetjmp() returns, regardless of
 336                   why it returned, the process signal mask is returned to how
 337                   it was when sigsetjmp() was called. Thus the following
 338                   statements are always executed with all of our signals
 339                   blocked.
 341            10.    Reinstate the signal handlers that were displaced in step 5.
 343            11.    Check whether a signal was caught, by checking the file-
 344                   scope variable that the signal handler records signal
 345                   numbers in.
 347            12.    If a signal was caught, send this signal to the application
 348                   again and unblock only this signal so that it invokes the
 349                   signal handler which was just reinstated in step 10.
 351            13.    Unblock all of the signals that were blocked in step 7.
 353    Signals Caught By gl_get_line()
 354        Since the application is expected to handle signals in non-blocking
 355        server mode, gl_get_line() does not attempt to duplicate this when it
 356        is being called. If one of the signals that it is configured to catch
 357        is sent to the application while gl_get_line() is being called,
 358        gl_get_line() reinstates the caller's signal handlers, then immediately
 359        before returning, re-sends the signal to the process to let the
 360        application's signal handler handle it. If the process is not
 361        terminated by this signal, gl_get_line() returns NULL, and a following
 362        call to gl_return_status() returns the enumerated value GLR_SIGNAL.
 364    Aborting Line Input
 365        Often, rather than letting it terminate the process, applications
 366        respond to the SIGINT user-interrupt signal by aborting the current
 367        input line. This can be accomplished in non-blocking server-I/O mode by
 368        not calling gl_handle_signal() when this signal is caught, but by
 369        calling instead the gl_abandon_line() function. This function arranges
 370        that when gl_get_line() is next called, it first flushes any pending
 371        output to the terminal, discards the current input line, outputs a new
 372        prompt on the next line, and finally starts accepting input of a new
 373        input line from the user.
 375    Signal Safe Functions
 376        Provided that certain rules are followed, the gl_normal_io(),
 377        gl_raw_io(), gl_handle_signal(), and gl_abandon_line() functions can be
 378        written to be safely callable from signal handlers. Other functions in
 379        this library should not be called from signal handlers. For this to be
 380        true, all signal handlers that call these functions must be registered
 381        in such a way that only one instance of any one of them can be running
 382        at one time. The way to do this is to use the POSIX sigaction()
 383        function to register all signal handlers, and when doing this, use the
 384        sa_mask member of the corresponding sigaction structure to indicate
 385        that all of the signals whose handlers invoke the above functions
 386        should be blocked when the current signal is being handled. This
 387        prevents two signal handlers from operating on a GetLine object at the
 388        same time.
 391        To prevent signal handlers from accessing a GetLine object while
 392        gl_get_line() or any of its associated public functions are operating
 393        on it, all public functions associated with gl_get_line(), including
 394        gl_get_line() itself, temporarily block the delivery of signals when
 395        they are accessing GetLine objects. Beware that the only signals that
 396        they block are the signals that gl_get_line() is currently configured
 397        to catch, so be sure that if you call any of the above functions from
 398        signal handlers, that the signals that these handlers are assigned to
 399        are configured to be caught by gl_get_line(). See
 400        gl_trap_signal(3TECLA).
 402    Using Timeouts To Poll
 403        If instead of using select() or poll() to wait for I/O your application
 404        needs only to get out of gl_get_line() periodically to briefly do
 405        something else before returning to accept input from the user, use the
 406        gl_inactivity_timeout(3TECLA) function in non-blocking server mode to
 407        specify that a callback function that returns GLTO_CONTINUE should be
 408        called whenever gl_get_line() has been waiting for I/O for more than a
 409        specified amount of time. When this callback is triggered,
 410        gl_get_line() will return NULL and a following call to
 411        gl_return_status() will return GLR_BLOCKED.
 414        The gl_get_line() function will not return until the user has not typed
 415        a key for the specified interval, so if the interval is long and the
 416        user keeps typing, gl_get_line() might not return for a while. There is
 417        no guarantee that it will return in the time specified.
 420        See attributes(5) for descriptions of the following attributes:
 425        +--------------------+-----------------+
 427        +--------------------+-----------------+
 428        |Interface Stability | Evolving        |
 429        +--------------------+-----------------+
 430        |MT-Level            | MT-Safe         |
 431        +--------------------+-----------------+
 434        cpl_complete_word(3TECLA), ef_expand_file(3TECLA), gl_get_line(3TECLA),
 435        libtecla(3LIB), pca_lookup_file(3TECLA), attributes(5), tecla(5)
 439                                January 18, 2020             GL_IO_MODE(3TECLA)