]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/dialog/ui_getc.c
Upgrade our copy of llvm/clang to r126079, from upstream's trunk.
[FreeBSD/FreeBSD.git] / contrib / dialog / ui_getc.c
1 /*
2  *  $Id: ui_getc.c,v 1.48 2010/01/18 10:24:06 tom Exp $
3  *
4  * ui_getc.c - user interface glue for getc()
5  *
6  * Copyright 2001-2009,2010 Thomas E. Dickey
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License, version 2.1
10  *  as published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful, but
13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this program; if not, write to
19  *      Free Software Foundation, Inc.
20  *      51 Franklin St., Fifth Floor
21  *      Boston, MA 02110, USA.
22  */
23
24 #include <dialog.h>
25 #include <dlg_keys.h>
26
27 #ifdef NEED_WCHAR_H
28 #include <wchar.h>
29 #endif
30
31 #if TIME_WITH_SYS_TIME
32 # include <sys/time.h>
33 # include <time.h>
34 #else
35 # if HAVE_SYS_TIME_H
36 #  include <sys/time.h>
37 # else
38 #  include <time.h>
39 # endif
40 #endif
41
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
45
46 #ifdef __QNX__
47 #include <sys/select.h>
48 #endif
49
50 #ifndef WEXITSTATUS
51 # ifdef HAVE_TYPE_UNIONWAIT
52 #  define       WEXITSTATUS(status)     (status.w_retcode)
53 # else
54 #  define       WEXITSTATUS(status)     (((status) & 0xff00) >> 8)
55 # endif
56 #endif
57
58 #ifndef WTERMSIG
59 # ifdef HAVE_TYPE_UNIONWAIT
60 #  define       WTERMSIG(status)        (status.w_termsig)
61 # else
62 #  define       WTERMSIG(status)        ((status) & 0x7f)
63 # endif
64 #endif
65
66 void
67 dlg_add_callback(DIALOG_CALLBACK * p)
68 {
69     p->next = dialog_state.getc_callbacks;
70     dialog_state.getc_callbacks = p;
71     wtimeout(p->win, WTIMEOUT_VAL);
72 }
73
74 /*
75  * Like dlg_add_callback(), but providing for cleanup of caller's associated
76  * state.
77  */
78 void
79 dlg_add_callback_ref(DIALOG_CALLBACK ** p, DIALOG_FREEBACK freeback)
80 {
81     (*p)->caller = p;
82     (*p)->freeback = freeback;
83     dlg_add_callback(*p);
84 }
85
86 void
87 dlg_remove_callback(DIALOG_CALLBACK * p)
88 {
89     DIALOG_CALLBACK *q;
90
91     if (p->input != 0) {
92         fclose(p->input);
93         if (p->input == dialog_state.pipe_input)
94             dialog_state.pipe_input = 0;
95         p->input = 0;
96     }
97
98     if (!(p->keep_win))
99         dlg_del_window(p->win);
100     if ((q = dialog_state.getc_callbacks) == p) {
101         dialog_state.getc_callbacks = p->next;
102     } else {
103         while (q != 0) {
104             if (q->next == p) {
105                 q->next = p->next;
106                 break;
107             }
108             q = q->next;
109         }
110     }
111
112     /* handle dlg_add_callback_ref cleanup */
113     if (p->freeback != 0)
114         p->freeback(p);
115     if (p->caller != 0)
116         *(p->caller) = 0;
117
118     free(p);
119 }
120
121 /*
122  * FIXME: this could be replaced by a select/poll on several file descriptors
123  */
124 static int
125 dlg_getc_ready(DIALOG_CALLBACK * p)
126 {
127     fd_set read_fds;
128     int fd = fileno(p->input);
129     struct timeval test;
130
131     FD_ZERO(&read_fds);
132     FD_SET(fd, &read_fds);
133
134     test.tv_sec = 0;            /* Seconds.  */
135     test.tv_usec = (isatty(fd)  /* Microseconds.  */
136                     ? (WTIMEOUT_VAL * 1000)
137                     : 1);
138     return (select(fd + 1, &read_fds, (fd_set *) 0, (fd_set *) 0, &test) == 1)
139         && (FD_ISSET(fd, &read_fds));
140 }
141
142 int
143 dlg_getc_callbacks(int ch, int fkey, int *result)
144 {
145     int code = FALSE;
146     DIALOG_CALLBACK *p, *q;
147
148     if ((p = dialog_state.getc_callbacks) != 0) {
149         do {
150             q = p->next;
151             if (dlg_getc_ready(p)) {
152                 if (!(p->handle_getc(p, ch, fkey, result))) {
153                     dlg_remove_callback(p);
154                 }
155             }
156         } while ((p = q) != 0);
157         code = (dialog_state.getc_callbacks != 0);
158     }
159     return code;
160 }
161
162 static void
163 dlg_raise_window(WINDOW *win)
164 {
165     touchwin(win);
166     wmove(win, getcury(win), getcurx(win));
167     wnoutrefresh(win);
168     doupdate();
169 }
170
171 /*
172  * This is a work-around for the case where we actually need the wide-character
173  * code versus a byte stream.
174  */
175 static int last_getc = ERR;
176
177 #ifdef USE_WIDE_CURSES
178 static char last_getc_bytes[80];
179 static int have_last_getc;
180 static int used_last_getc;
181 #endif
182
183 int
184 dlg_last_getc(void)
185 {
186 #ifdef USE_WIDE_CURSES
187     if (used_last_getc != 1)
188         return ERR;             /* not really an error... */
189 #endif
190     return last_getc;
191 }
192
193 void
194 dlg_flush_getc(void)
195 {
196     last_getc = ERR;
197 #ifdef USE_WIDE_CURSES
198     have_last_getc = 0;
199     used_last_getc = 0;
200 #endif
201 }
202
203 /*
204  * Check if the stream has been unexpectedly closed, returning false in that
205  * case.
206  */
207 static bool
208 valid_file(FILE *fp)
209 {
210     bool code = FALSE;
211     int fd = fileno(fp);
212
213     if (fd >= 0) {
214         long result = 0;
215         if ((result = fcntl(fd, F_GETFL, 0)) >= 0) {
216             code = TRUE;
217         }
218     }
219     return code;
220 }
221
222 /*
223  * Read a character from the given window.  Handle repainting here (to simplify
224  * things in the calling application).  Also, if input-callback(s) are set up,
225  * poll the corresponding files and handle the updates, e.g., for displaying a
226  * tailbox.
227  */
228 int
229 dlg_getc(WINDOW *win, int *fkey)
230 {
231     WINDOW *save_win = win;
232     int ch = ERR;
233     int before_lookup;
234     int result;
235     bool done = FALSE;
236     bool literal = FALSE;
237     DIALOG_CALLBACK *p;
238     int interval = dialog_vars.timeout_secs;
239     time_t expired = time((time_t *) 0) + dialog_vars.timeout_secs;
240     time_t current;
241
242     if (dialog_state.getc_callbacks != 0)
243         wtimeout(win, WTIMEOUT_VAL);
244     else if (interval > 0)
245         wtimeout(win, interval);
246
247     while (!done) {
248 #ifdef USE_WIDE_CURSES
249         int code;
250         mbstate_t state;
251         wchar_t my_wchar;
252         wint_t my_wint;
253
254         /*
255          * We get a wide character, translate it to multibyte form to avoid
256          * having to change the rest of the code to use wide-characters.
257          */
258         if (used_last_getc >= have_last_getc) {
259             used_last_getc = 0;
260             have_last_getc = 0;
261             ch = ERR;
262             *fkey = 0;
263             code = wget_wch(win, &my_wint);
264             my_wchar = (wchar_t) my_wint;
265             switch (code) {
266             case KEY_CODE_YES:
267                 ch = *fkey = my_wchar;
268                 last_getc = my_wchar;
269                 break;
270             case OK:
271                 memset(&state, 0, sizeof(state));
272                 have_last_getc = (int) wcrtomb(last_getc_bytes, my_wchar, &state);
273                 if (have_last_getc < 0) {
274                     have_last_getc = used_last_getc = 0;
275                     last_getc_bytes[0] = (char) my_wchar;
276                 }
277                 ch = (int) CharOf(last_getc_bytes[used_last_getc++]);
278                 last_getc = my_wchar;
279                 break;
280             case ERR:
281                 ch = ERR;
282                 last_getc = ERR;
283                 break;
284             default:
285                 break;
286             }
287         } else {
288             ch = (int) CharOf(last_getc_bytes[used_last_getc++]);
289         }
290 #else
291         ch = wgetch(win);
292         last_getc = ch;
293         *fkey = (ch > KEY_MIN && ch < KEY_MAX);
294 #endif
295         if (literal) {
296             done = TRUE;
297             continue;
298         }
299
300         before_lookup = ch;
301         ch = dlg_lookup_key(win, ch, fkey);
302         dlg_trace_chr(ch, *fkey);
303
304         current = time((time_t *) 0);
305
306         switch (ch) {
307         case CHR_LITERAL:
308             if (!literal) {
309                 literal = TRUE;
310                 keypad(win, FALSE);
311                 continue;
312             }
313             break;
314         case CHR_REPAINT:
315             (void) touchwin(win);
316             (void) wrefresh(curscr);
317             break;
318         case ERR:               /* wtimeout() in effect; check for file I/O */
319             if (interval > 0
320                 && current >= expired) {
321                 dlg_exiterr("timeout");
322             }
323             if (dlg_getc_callbacks(ch, *fkey, &result)) {
324                 dlg_raise_window(win);
325             } else {
326                 done = (interval <= 0);
327             }
328             if (!valid_file(stdin)
329                 || !valid_file(dialog_state.screen_output)) {
330                 ch = ESC;
331                 done = TRUE;
332             }
333             break;
334         case DLGK_FIELD_NEXT:
335             /* FALLTHRU */
336         case TAB:
337             /* Handle tab as a special case for traversing between the nominal
338              * "current" window, and other windows having callbacks.  If the
339              * nominal (control) window closes, we'll close the windows with
340              * callbacks.
341              */
342             if (dialog_state.getc_callbacks != 0 &&
343                 before_lookup == TAB) {
344                 if ((p = dialog_state.getc_redirect) != 0) {
345                     p = p->next;
346                 } else {
347                     p = dialog_state.getc_callbacks;
348                 }
349                 if ((dialog_state.getc_redirect = p) != 0) {
350                     win = p->win;
351                 } else {
352                     win = save_win;
353                 }
354                 dlg_raise_window(win);
355                 break;
356             }
357             /* FALLTHRU */
358         default:
359 #ifdef NO_LEAKS
360             if (before_lookup == DLG_CTRL('P')) {
361                 /* for testing, ^P closes the connection */
362                 close(0);
363                 close(1);
364                 close(2);
365                 break;
366             }
367 #endif
368             if ((p = dialog_state.getc_redirect) != 0) {
369                 if (!(p->handle_getc(p, ch, *fkey, &result))) {
370                     dlg_remove_callback(p);
371                     dialog_state.getc_redirect = 0;
372                     win = save_win;
373                 }
374             } else {
375                 done = TRUE;
376             }
377             break;
378 #ifdef HAVE_DLG_TRACE
379         case CHR_TRACE:
380             dlg_trace_win(win);
381             break;
382 #endif
383         }
384     }
385     if (literal)
386         keypad(win, TRUE);
387     return ch;
388 }
389
390 static void
391 finish_bg(int sig GCC_UNUSED)
392 {
393     end_dialog();
394     dlg_exit(DLG_EXIT_ERROR);
395 }
396
397 /*
398  * If we have callbacks active, purge the list of all that are not marked
399  * to keep in the background.  If any remain, run those in a background
400  * process.
401  */
402 void
403 dlg_killall_bg(int *retval)
404 {
405     DIALOG_CALLBACK *cb;
406     int pid;
407 #ifdef HAVE_TYPE_UNIONWAIT
408     union wait wstatus;
409 #else
410     int wstatus;
411 #endif
412
413     if ((cb = dialog_state.getc_callbacks) != 0) {
414         while (cb != 0) {
415             if (cb->keep_bg) {
416                 cb = cb->next;
417             } else {
418                 dlg_remove_callback(cb);
419                 cb = dialog_state.getc_callbacks;
420             }
421         }
422         if (dialog_state.getc_callbacks != 0) {
423
424             refresh();
425             fflush(stdout);
426             fflush(stderr);
427             reset_shell_mode();
428             if ((pid = fork()) != 0) {
429                 _exit(pid > 0 ? DLG_EXIT_OK : DLG_EXIT_ERROR);
430             } else if (pid == 0) {      /* child */
431                 if ((pid = fork()) != 0) {
432                     /*
433                      * Echo the process-id of the grandchild so a shell script
434                      * can read that, and kill that process.  We'll wait around
435                      * until then.  Our parent has already left, leaving us
436                      * temporarily orphaned.
437                      */
438                     if (pid > 0) {      /* parent */
439                         fprintf(stderr, "%d\n", pid);
440                         fflush(stderr);
441                     }
442                     /* wait for child */
443 #ifdef HAVE_WAITPID
444                     while (-1 == waitpid(pid, &wstatus, 0)) {
445 #ifdef EINTR
446                         if (errno == EINTR)
447                             continue;
448 #endif /* EINTR */
449 #ifdef ERESTARTSYS
450                         if (errno == ERESTARTSYS)
451                             continue;
452 #endif /* ERESTARTSYS */
453                         break;
454                     }
455 #else
456                     while (wait(&wstatus) != pid)       /* do nothing */
457                         ;
458 #endif
459                     _exit(WEXITSTATUS(wstatus));
460                 } else if (pid == 0) {
461                     if (!dialog_vars.cant_kill)
462                         (void) signal(SIGHUP, finish_bg);
463                     (void) signal(SIGINT, finish_bg);
464                     (void) signal(SIGQUIT, finish_bg);
465                     (void) signal(SIGSEGV, finish_bg);
466                     while (dialog_state.getc_callbacks != 0) {
467                         int fkey = 0;
468                         dlg_getc_callbacks(ERR, fkey, retval);
469                         napms(1000);
470                     }
471                 }
472             }
473         }
474     }
475 }