kernel - cleanup vfs_cache debugging
[dragonfly.git] / contrib / dialog / ui_getc.c
blob33e571fd0e301c1fce4020143ae1d424a5722702
1 /*
2 * $Id: ui_getc.c,v 1.67 2013/03/24 23:53:19 tom Exp $
4 * ui_getc.c - user interface glue for getc()
6 * Copyright 2001-2012,2013 Thomas E. Dickey
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.
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.
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.
24 #include <dialog.h>
25 #include <dlg_keys.h>
27 #ifdef NEED_WCHAR_H
28 #include <wchar.h>
29 #endif
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
42 #ifdef HAVE_SYS_WAIT_H
43 #include <sys/wait.h>
44 #endif
46 #ifdef __QNX__
47 #include <sys/select.h>
48 #endif
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
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
66 void
67 dlg_add_callback(DIALOG_CALLBACK * p)
69 p->next = dialog_state.getc_callbacks;
70 dialog_state.getc_callbacks = p;
71 wtimeout(p->win, WTIMEOUT_VAL);
75 * Like dlg_add_callback(), but providing for cleanup of caller's associated
76 * state.
78 void
79 dlg_add_callback_ref(DIALOG_CALLBACK ** p, DIALOG_FREEBACK freeback)
81 (*p)->caller = p;
82 (*p)->freeback = freeback;
83 dlg_add_callback(*p);
86 void
87 dlg_remove_callback(DIALOG_CALLBACK * p)
89 DIALOG_CALLBACK *q;
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;
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;
108 q = q->next;
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;
118 free(p);
122 * A select() might find more than one input ready for service. Handle them
123 * all.
125 static bool
126 handle_inputs(WINDOW *win)
128 bool result = FALSE;
129 DIALOG_CALLBACK *p;
130 DIALOG_CALLBACK *q;
131 int cur_y, cur_x;
132 int state = ERR;
134 getyx(win, cur_y, cur_x);
135 for (p = dialog_state.getc_callbacks, q = 0; p != 0; p = q) {
136 q = p->next;
137 if ((p->handle_input != 0) && p->input_ready) {
138 p->input_ready = FALSE;
139 if (state == ERR) {
140 state = curs_set(0);
142 if (p->handle_input(p)) {
143 result = TRUE;
147 if (result) {
148 (void) wmove(win, cur_y, cur_x); /* Restore cursor position */
149 wrefresh(win);
150 curs_set(state);
152 return result;
155 static bool
156 may_handle_inputs(void)
158 bool result = FALSE;
160 DIALOG_CALLBACK *p;
162 for (p = dialog_state.getc_callbacks; p != 0; p = p->next) {
163 if (p->input != 0) {
164 result = TRUE;
165 break;
169 return result;
173 * Check any any inputs registered via callbacks, to see if there is any input
174 * available. If there is, return a file-descriptor which should be read.
175 * Otherwise, return -1.
177 static int
178 check_inputs(void)
180 DIALOG_CALLBACK *p;
181 fd_set read_fds;
182 struct timeval test;
183 int last_fd = -1;
184 int fd;
185 int found;
186 int result = -1;
188 if ((p = dialog_state.getc_callbacks) != 0) {
189 FD_ZERO(&read_fds);
191 while (p != 0) {
192 p->input_ready = FALSE;
193 if (p->input != 0 && (fd = fileno(p->input)) >= 0) {
194 FD_SET(fd, &read_fds);
195 if (last_fd < fd)
196 last_fd = fd;
198 p = p->next;
201 test.tv_sec = 0;
202 test.tv_usec = WTIMEOUT_VAL * 1000;
203 found = select(last_fd + 1, &read_fds,
204 (fd_set *) 0,
205 (fd_set *) 0,
206 &test);
208 if (found > 0) {
209 for (p = dialog_state.getc_callbacks; p != 0; p = p->next) {
210 if (p->input != 0
211 && (fd = fileno(p->input)) >= 0
212 && FD_ISSET(fd, &read_fds)) {
213 p->input_ready = TRUE;
214 result = fd;
220 return result;
224 dlg_getc_callbacks(int ch, int fkey, int *result)
226 int code = FALSE;
227 DIALOG_CALLBACK *p, *q;
229 if ((p = dialog_state.getc_callbacks) != 0) {
230 if (check_inputs() >= 0) {
231 do {
232 q = p->next;
233 if (p->input_ready) {
234 if (!(p->handle_getc(p, ch, fkey, result))) {
235 dlg_remove_callback(p);
238 } while ((p = q) != 0);
240 code = (dialog_state.getc_callbacks != 0);
242 return code;
245 static void
246 dlg_raise_window(WINDOW *win)
248 touchwin(win);
249 wmove(win, getcury(win), getcurx(win));
250 wnoutrefresh(win);
251 doupdate();
255 * This is a work-around for the case where we actually need the wide-character
256 * code versus a byte stream.
258 static int last_getc = ERR;
260 #ifdef USE_WIDE_CURSES
261 static char last_getc_bytes[80];
262 static int have_last_getc;
263 static int used_last_getc;
264 #endif
267 dlg_last_getc(void)
269 #ifdef USE_WIDE_CURSES
270 if (used_last_getc != 1)
271 return ERR; /* not really an error... */
272 #endif
273 return last_getc;
276 void
277 dlg_flush_getc(void)
279 last_getc = ERR;
280 #ifdef USE_WIDE_CURSES
281 have_last_getc = 0;
282 used_last_getc = 0;
283 #endif
287 * Report the last key entered by the user. The 'mode' parameter controls
288 * the way it is separated from other results:
289 * -2 (no separator)
290 * -1 (separator after the key name)
291 * 0 (separator is optionally before the key name)
292 * 1 (same as -1)
294 void
295 dlg_add_last_key(int mode)
297 if (dialog_vars.last_key) {
298 if (mode >= 0) {
299 if (mode > 0) {
300 dlg_add_last_key(-1);
301 } else {
302 if (dlg_need_separator())
303 dlg_add_separator();
304 dlg_add_last_key(-2);
306 } else {
307 char temp[80];
308 sprintf(temp, "%d", last_getc);
309 dlg_add_string(temp);
310 if (mode == -1)
311 dlg_add_separator();
317 * Check if the stream has been unexpectedly closed, returning false in that
318 * case.
320 static bool
321 valid_file(FILE *fp)
323 bool code = FALSE;
324 int fd = fileno(fp);
326 if (fd >= 0) {
327 if (fcntl(fd, F_GETFL, 0) >= 0) {
328 code = TRUE;
331 return code;
334 static int
335 really_getch(WINDOW *win, int *fkey)
337 int ch;
338 #ifdef USE_WIDE_CURSES
339 int code;
340 mbstate_t state;
341 wchar_t my_wchar;
342 wint_t my_wint;
345 * We get a wide character, translate it to multibyte form to avoid
346 * having to change the rest of the code to use wide-characters.
348 if (used_last_getc >= have_last_getc) {
349 used_last_getc = 0;
350 have_last_getc = 0;
351 ch = ERR;
352 *fkey = 0;
353 code = wget_wch(win, &my_wint);
354 my_wchar = (wchar_t) my_wint;
355 switch (code) {
356 case KEY_CODE_YES:
357 ch = *fkey = my_wchar;
358 last_getc = my_wchar;
359 break;
360 case OK:
361 memset(&state, 0, sizeof(state));
362 have_last_getc = (int) wcrtomb(last_getc_bytes, my_wchar, &state);
363 if (have_last_getc < 0) {
364 have_last_getc = used_last_getc = 0;
365 last_getc_bytes[0] = (char) my_wchar;
367 ch = (int) CharOf(last_getc_bytes[used_last_getc++]);
368 last_getc = my_wchar;
369 break;
370 case ERR:
371 ch = ERR;
372 last_getc = ERR;
373 break;
374 default:
375 break;
377 } else {
378 ch = (int) CharOf(last_getc_bytes[used_last_getc++]);
380 #else
381 ch = wgetch(win);
382 last_getc = ch;
383 *fkey = (ch > KEY_MIN && ch < KEY_MAX);
384 #endif
385 return ch;
388 static DIALOG_CALLBACK *
389 next_callback(DIALOG_CALLBACK * p)
391 if ((p = dialog_state.getc_redirect) != 0) {
392 p = p->next;
393 } else {
394 p = dialog_state.getc_callbacks;
396 return p;
399 static DIALOG_CALLBACK *
400 prev_callback(DIALOG_CALLBACK * p)
402 DIALOG_CALLBACK *q;
404 if ((p = dialog_state.getc_redirect) != 0) {
405 if (p == dialog_state.getc_callbacks) {
406 for (p = dialog_state.getc_callbacks; p->next != 0; p = p->next) ;
407 } else {
408 for (q = dialog_state.getc_callbacks; q->next != p; q = q->next) ;
409 p = q;
411 } else {
412 p = dialog_state.getc_callbacks;
414 return p;
417 #define isBeforeChr(chr) ((chr) == before_chr && !before_fkey)
418 #define isBeforeFkey(chr) ((chr) == before_chr && before_fkey)
421 * Read a character from the given window. Handle repainting here (to simplify
422 * things in the calling application). Also, if input-callback(s) are set up,
423 * poll the corresponding files and handle the updates, e.g., for displaying a
424 * tailbox.
427 dlg_getc(WINDOW *win, int *fkey)
429 WINDOW *save_win = win;
430 int ch = ERR;
431 int before_chr;
432 int before_fkey;
433 int result;
434 bool done = FALSE;
435 bool literal = FALSE;
436 DIALOG_CALLBACK *p = 0;
437 int interval = (dialog_vars.timeout_secs * 1000);
438 time_t expired = time((time_t *) 0) + dialog_vars.timeout_secs;
439 time_t current;
441 if (may_handle_inputs())
442 wtimeout(win, WTIMEOUT_VAL);
443 else if (interval > 0)
444 wtimeout(win, interval);
446 while (!done) {
447 bool handle_others = FALSE;
450 * If there was no pending file-input, check the keyboard.
452 ch = really_getch(win, fkey);
453 if (literal) {
454 done = TRUE;
455 continue;
458 before_chr = ch;
459 before_fkey = *fkey;
461 ch = dlg_lookup_key(win, ch, fkey);
462 dlg_trace_chr(ch, *fkey);
464 current = time((time_t *) 0);
467 * If we acquired a fkey value, then it is one of dialog's builtin
468 * codes such as DLGK_HELPFILE.
470 if (!*fkey || *fkey != before_fkey) {
471 switch (ch) {
472 case CHR_LITERAL:
473 literal = TRUE;
474 keypad(win, FALSE);
475 continue;
476 case CHR_REPAINT:
477 (void) touchwin(win);
478 (void) wrefresh(curscr);
479 break;
480 case ERR: /* wtimeout() in effect; check for file I/O */
481 if (interval > 0
482 && current >= expired) {
483 dlg_exiterr("timeout");
485 if (!valid_file(stdin)
486 || !valid_file(dialog_state.screen_output)) {
487 ch = ESC;
488 done = TRUE;
489 } else if (check_inputs()) {
490 if (handle_inputs(win))
491 dlg_raise_window(win);
492 else
493 done = TRUE;
494 } else {
495 done = (interval <= 0);
497 break;
498 case DLGK_HELPFILE:
499 if (dialog_vars.help_file) {
500 int yold, xold;
501 getyx(win, yold, xold);
502 dialog_helpfile("HELP", dialog_vars.help_file, 0, 0);
503 dlg_raise_window(win);
504 wmove(win, yold, xold);
506 continue;
507 case DLGK_FIELD_PREV:
508 /* FALLTHRU */
509 case KEY_BTAB:
510 /* FALLTHRU */
511 case DLGK_FIELD_NEXT:
512 /* FALLTHRU */
513 case TAB:
514 /* Handle tab/backtab as a special case for traversing between
515 * the nominal "current" window, and other windows having
516 * callbacks. If the nominal (control) window closes, we'll
517 * close the windows with callbacks.
519 if (dialog_state.getc_callbacks != 0 &&
520 (isBeforeChr(TAB) ||
521 isBeforeFkey(KEY_BTAB))) {
522 p = (isBeforeChr(TAB)
523 ? next_callback(p)
524 : prev_callback(p));
525 if ((dialog_state.getc_redirect = p) != 0) {
526 win = p->win;
527 } else {
528 win = save_win;
530 dlg_raise_window(win);
531 break;
533 /* FALLTHRU */
534 default:
535 #ifdef NO_LEAKS
536 if (isBeforeChr(DLG_CTRL('P'))) {
537 /* for testing, ^P closes the connection */
538 close(0);
539 close(1);
540 close(2);
541 break;
543 #endif
544 handle_others = TRUE;
545 break;
546 #ifdef HAVE_DLG_TRACE
547 case CHR_TRACE:
548 dlg_trace_win(win);
549 break;
550 #endif
552 } else {
553 handle_others = TRUE;
556 if (handle_others) {
557 if ((p = dialog_state.getc_redirect) != 0) {
558 if (!(p->handle_getc(p, ch, *fkey, &result))) {
559 done = (p->win == save_win) && (!p->keep_win);
560 dlg_remove_callback(p);
561 dialog_state.getc_redirect = 0;
562 win = save_win;
564 } else {
565 done = TRUE;
569 if (literal)
570 keypad(win, TRUE);
571 return ch;
574 static void
575 finish_bg(int sig GCC_UNUSED)
577 end_dialog();
578 dlg_exit(DLG_EXIT_ERROR);
582 * If we have callbacks active, purge the list of all that are not marked
583 * to keep in the background. If any remain, run those in a background
584 * process.
586 void
587 dlg_killall_bg(int *retval)
589 DIALOG_CALLBACK *cb;
590 int pid;
591 #ifdef HAVE_TYPE_UNIONWAIT
592 union wait wstatus;
593 #else
594 int wstatus;
595 #endif
597 if ((cb = dialog_state.getc_callbacks) != 0) {
598 while (cb != 0) {
599 if (cb->keep_bg) {
600 cb = cb->next;
601 } else {
602 dlg_remove_callback(cb);
603 cb = dialog_state.getc_callbacks;
606 if (dialog_state.getc_callbacks != 0) {
608 refresh();
609 fflush(stdout);
610 fflush(stderr);
611 reset_shell_mode();
612 if ((pid = fork()) != 0) {
613 _exit(pid > 0 ? DLG_EXIT_OK : DLG_EXIT_ERROR);
614 } else if (pid == 0) { /* child */
615 if ((pid = fork()) != 0) {
617 * Echo the process-id of the grandchild so a shell script
618 * can read that, and kill that process. We'll wait around
619 * until then. Our parent has already left, leaving us
620 * temporarily orphaned.
622 if (pid > 0) { /* parent */
623 fprintf(stderr, "%d\n", pid);
624 fflush(stderr);
626 /* wait for child */
627 #ifdef HAVE_WAITPID
628 while (-1 == waitpid(pid, &wstatus, 0)) {
629 #ifdef EINTR
630 if (errno == EINTR)
631 continue;
632 #endif /* EINTR */
633 #ifdef ERESTARTSYS
634 if (errno == ERESTARTSYS)
635 continue;
636 #endif /* ERESTARTSYS */
637 break;
639 #else
640 while (wait(&wstatus) != pid) /* do nothing */
642 #endif
643 _exit(WEXITSTATUS(wstatus));
644 } else if (pid == 0) {
645 if (!dialog_vars.cant_kill)
646 (void) signal(SIGHUP, finish_bg);
647 (void) signal(SIGINT, finish_bg);
648 (void) signal(SIGQUIT, finish_bg);
649 (void) signal(SIGSEGV, finish_bg);
650 while (dialog_state.getc_callbacks != 0) {
651 int fkey = 0;
652 dlg_getc_callbacks(ERR, fkey, retval);
653 napms(1000);