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