diff options
Diffstat (limited to 'Build/source/utils/dialog/dialog-1.1-20110707/ui_getc.c')
-rw-r--r-- | Build/source/utils/dialog/dialog-1.1-20110707/ui_getc.c | 632 |
1 files changed, 632 insertions, 0 deletions
diff --git a/Build/source/utils/dialog/dialog-1.1-20110707/ui_getc.c b/Build/source/utils/dialog/dialog-1.1-20110707/ui_getc.c new file mode 100644 index 00000000000..d9d5a93a00e --- /dev/null +++ b/Build/source/utils/dialog/dialog-1.1-20110707/ui_getc.c @@ -0,0 +1,632 @@ +/* + * $Id: ui_getc.c,v 1.63 2011/07/07 22:05:58 tom Exp $ + * + * ui_getc.c - user interface glue for getc() + * + * Copyright 2001-2010,2011 Thomas E. Dickey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License, version 2.1 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to + * Free Software Foundation, Inc. + * 51 Franklin St., Fifth Floor + * Boston, MA 02110, USA. + */ + +#include <dialog.h> +#include <dlg_keys.h> + +#ifdef NEED_WCHAR_H +#include <wchar.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#ifdef __QNX__ +#include <sys/select.h> +#endif + +#ifndef WEXITSTATUS +# ifdef HAVE_TYPE_UNIONWAIT +# define WEXITSTATUS(status) (status.w_retcode) +# else +# define WEXITSTATUS(status) (((status) & 0xff00) >> 8) +# endif +#endif + +#ifndef WTERMSIG +# ifdef HAVE_TYPE_UNIONWAIT +# define WTERMSIG(status) (status.w_termsig) +# else +# define WTERMSIG(status) ((status) & 0x7f) +# endif +#endif + +void +dlg_add_callback(DIALOG_CALLBACK * p) +{ + p->next = dialog_state.getc_callbacks; + dialog_state.getc_callbacks = p; + wtimeout(p->win, WTIMEOUT_VAL); +} + +/* + * Like dlg_add_callback(), but providing for cleanup of caller's associated + * state. + */ +void +dlg_add_callback_ref(DIALOG_CALLBACK ** p, DIALOG_FREEBACK freeback) +{ + (*p)->caller = p; + (*p)->freeback = freeback; + dlg_add_callback(*p); +} + +void +dlg_remove_callback(DIALOG_CALLBACK * p) +{ + DIALOG_CALLBACK *q; + + if (p->input != 0) { + fclose(p->input); + if (p->input == dialog_state.pipe_input) + dialog_state.pipe_input = 0; + p->input = 0; + } + + if (!(p->keep_win)) + dlg_del_window(p->win); + if ((q = dialog_state.getc_callbacks) == p) { + dialog_state.getc_callbacks = p->next; + } else { + while (q != 0) { + if (q->next == p) { + q->next = p->next; + break; + } + q = q->next; + } + } + + /* handle dlg_add_callback_ref cleanup */ + if (p->freeback != 0) + p->freeback(p); + if (p->caller != 0) + *(p->caller) = 0; + + free(p); +} + +/* + * A select() might find more than one input ready for service. Handle them + * all. + */ +static bool +handle_inputs(WINDOW *win) +{ + bool result = FALSE; + DIALOG_CALLBACK *p; + DIALOG_CALLBACK *q; + int cur_y, cur_x; + int state = ERR; + + getyx(win, cur_y, cur_x); + for (p = dialog_state.getc_callbacks, q = 0; p != 0; p = q) { + q = p->next; + if ((p->handle_input != 0) && p->input_ready) { + p->input_ready = FALSE; + if (state == ERR) { + state = curs_set(0); + } + if (p->handle_input(p)) { + result = TRUE; + } + } + } + if (result) { + (void) wmove(win, cur_y, cur_x); /* Restore cursor position */ + wrefresh(win); + curs_set(state); + } + return result; +} + +static bool +may_handle_inputs(void) +{ + bool result = FALSE; + + DIALOG_CALLBACK *p; + + for (p = dialog_state.getc_callbacks; p != 0; p = p->next) { + if (p->input != 0) { + result = TRUE; + break; + } + } + + return result; +} + +/* + * Check any any inputs registered via callbacks, to see if there is any input + * available. If there is, return a file-descriptor which should be read. + * Otherwise, return -1. + */ +static int +check_inputs(void) +{ + DIALOG_CALLBACK *p; + fd_set read_fds; + struct timeval test; + int last_fd = -1; + int fd; + int found; + int result = -1; + + if ((p = dialog_state.getc_callbacks) != 0) { + FD_ZERO(&read_fds); + + while (p != 0) { + p->input_ready = FALSE; + if (p->input != 0 && (fd = fileno(p->input)) >= 0) { + FD_SET(fd, &read_fds); + if (last_fd < fd) + last_fd = fd; + } + p = p->next; + } + + test.tv_sec = 0; + test.tv_usec = WTIMEOUT_VAL * 1000; + found = select(last_fd + 1, &read_fds, + (fd_set *) 0, + (fd_set *) 0, + &test); + + if (found > 0) { + for (p = dialog_state.getc_callbacks; p != 0; p = p->next) { + if (p->input != 0 + && (fd = fileno(p->input)) >= 0 + && FD_ISSET(fd, &read_fds)) { + p->input_ready = TRUE; + result = fd; + } + } + } + } + + return result; +} + +int +dlg_getc_callbacks(int ch, int fkey, int *result) +{ + int code = FALSE; + DIALOG_CALLBACK *p, *q; + + if ((p = dialog_state.getc_callbacks) != 0) { + if (check_inputs() >= 0) { + do { + q = p->next; + if (p->input_ready) { + if (!(p->handle_getc(p, ch, fkey, result))) { + dlg_remove_callback(p); + } + } + } while ((p = q) != 0); + } + code = (dialog_state.getc_callbacks != 0); + } + return code; +} + +static void +dlg_raise_window(WINDOW *win) +{ + touchwin(win); + wmove(win, getcury(win), getcurx(win)); + wnoutrefresh(win); + doupdate(); +} + +/* + * This is a work-around for the case where we actually need the wide-character + * code versus a byte stream. + */ +static int last_getc = ERR; + +#ifdef USE_WIDE_CURSES +static char last_getc_bytes[80]; +static int have_last_getc; +static int used_last_getc; +#endif + +int +dlg_last_getc(void) +{ +#ifdef USE_WIDE_CURSES + if (used_last_getc != 1) + return ERR; /* not really an error... */ +#endif + return last_getc; +} + +void +dlg_flush_getc(void) +{ + last_getc = ERR; +#ifdef USE_WIDE_CURSES + have_last_getc = 0; + used_last_getc = 0; +#endif +} + +/* + * Check if the stream has been unexpectedly closed, returning false in that + * case. + */ +static bool +valid_file(FILE *fp) +{ + bool code = FALSE; + int fd = fileno(fp); + + if (fd >= 0) { + long result = 0; + if ((result = fcntl(fd, F_GETFL, 0)) >= 0) { + code = TRUE; + } + } + return code; +} + +static int +really_getch(WINDOW *win, int *fkey) +{ + int ch; +#ifdef USE_WIDE_CURSES + int code; + mbstate_t state; + wchar_t my_wchar; + wint_t my_wint; + + /* + * We get a wide character, translate it to multibyte form to avoid + * having to change the rest of the code to use wide-characters. + */ + if (used_last_getc >= have_last_getc) { + used_last_getc = 0; + have_last_getc = 0; + ch = ERR; + *fkey = 0; + code = wget_wch(win, &my_wint); + my_wchar = (wchar_t) my_wint; + switch (code) { + case KEY_CODE_YES: + ch = *fkey = my_wchar; + last_getc = my_wchar; + break; + case OK: + memset(&state, 0, sizeof(state)); + have_last_getc = (int) wcrtomb(last_getc_bytes, my_wchar, &state); + if (have_last_getc < 0) { + have_last_getc = used_last_getc = 0; + last_getc_bytes[0] = (char) my_wchar; + } + ch = (int) CharOf(last_getc_bytes[used_last_getc++]); + last_getc = my_wchar; + break; + case ERR: + ch = ERR; + last_getc = ERR; + break; + default: + break; + } + } else { + ch = (int) CharOf(last_getc_bytes[used_last_getc++]); + } +#else + ch = wgetch(win); + last_getc = ch; + *fkey = (ch > KEY_MIN && ch < KEY_MAX); +#endif + return ch; +} + +static DIALOG_CALLBACK * +next_callback(DIALOG_CALLBACK * p) +{ + if ((p = dialog_state.getc_redirect) != 0) { + p = p->next; + } else { + p = dialog_state.getc_callbacks; + } + return p; +} + +static DIALOG_CALLBACK * +prev_callback(DIALOG_CALLBACK * p) +{ + DIALOG_CALLBACK *q; + + if ((p = dialog_state.getc_redirect) != 0) { + if (p == dialog_state.getc_callbacks) { + for (p = dialog_state.getc_callbacks; p->next != 0; p = p->next) ; + } else { + for (q = dialog_state.getc_callbacks; q->next != p; q = q->next) ; + p = q; + } + } else { + p = dialog_state.getc_callbacks; + } + return p; +} + +#define isBeforeChr(chr) ((chr) == before_chr && !before_fkey) +#define isBeforeFkey(chr) ((chr) == before_chr && before_fkey) + +/* + * Read a character from the given window. Handle repainting here (to simplify + * things in the calling application). Also, if input-callback(s) are set up, + * poll the corresponding files and handle the updates, e.g., for displaying a + * tailbox. + */ +int +dlg_getc(WINDOW *win, int *fkey) +{ + WINDOW *save_win = win; + int ch = ERR; + int before_chr; + int before_fkey; + int result; + bool done = FALSE; + bool literal = FALSE; + DIALOG_CALLBACK *p = 0; + int interval = (dialog_vars.timeout_secs * 1000); + time_t expired = time((time_t *) 0) + dialog_vars.timeout_secs; + time_t current; + + if (may_handle_inputs()) + wtimeout(win, WTIMEOUT_VAL); + else if (interval > 0) + wtimeout(win, interval); + + while (!done) { + bool handle_others = FALSE; + + /* + * If there was no pending file-input, check the keyboard. + */ + ch = really_getch(win, fkey); + if (literal) { + done = TRUE; + continue; + } + + before_chr = ch; + before_fkey = *fkey; + + ch = dlg_lookup_key(win, ch, fkey); + dlg_trace_chr(ch, *fkey); + + current = time((time_t *) 0); + + /* + * If we acquired a fkey value, then it is one of dialog's builtin + * codes such as DLGK_HELPFILE. + */ + if (!*fkey || *fkey != before_fkey) { + switch (ch) { + case CHR_LITERAL: + if (!literal) { + literal = TRUE; + keypad(win, FALSE); + continue; + } + break; + case CHR_REPAINT: + (void) touchwin(win); + (void) wrefresh(curscr); + break; + case ERR: /* wtimeout() in effect; check for file I/O */ + if (interval > 0 + && current >= expired) { + dlg_exiterr("timeout"); + } + if (!valid_file(stdin) + || !valid_file(dialog_state.screen_output)) { + ch = ESC; + done = TRUE; + } else if (check_inputs()) { + if (handle_inputs(win)) + dlg_raise_window(win); + else + done = TRUE; + } else { + done = (interval <= 0); + } + break; + case DLGK_HELPFILE: + if (dialog_vars.help_file) { + int yold, xold; + getyx(win, yold, xold); + dialog_helpfile("HELP", dialog_vars.help_file, 0, 0); + dlg_raise_window(win); + wmove(win, yold, xold); + } + continue; + case DLGK_FIELD_PREV: + /* FALLTHRU */ + case KEY_BTAB: + /* FALLTHRU */ + case DLGK_FIELD_NEXT: + /* FALLTHRU */ + case TAB: + /* Handle tab/backtab as a special case for traversing between + * the nominal "current" window, and other windows having + * callbacks. If the nominal (control) window closes, we'll + * close the windows with callbacks. + */ + if (dialog_state.getc_callbacks != 0 && + (isBeforeChr(TAB) || + isBeforeFkey(KEY_BTAB))) { + p = (isBeforeChr(TAB) + ? next_callback(p) + : prev_callback(p)); + if ((dialog_state.getc_redirect = p) != 0) { + win = p->win; + } else { + win = save_win; + } + dlg_raise_window(win); + break; + } + /* FALLTHRU */ + default: +#ifdef NO_LEAKS + if (isBeforeChr(DLG_CTRL('P'))) { + /* for testing, ^P closes the connection */ + close(0); + close(1); + close(2); + break; + } +#endif + handle_others = TRUE; + break; +#ifdef HAVE_DLG_TRACE + case CHR_TRACE: + dlg_trace_win(win); + break; +#endif + } + } else { + handle_others = TRUE; + } + + if (handle_others) { + if ((p = dialog_state.getc_redirect) != 0) { + if (!(p->handle_getc(p, ch, *fkey, &result))) { + dlg_remove_callback(p); + dialog_state.getc_redirect = 0; + win = save_win; + } + } else { + done = TRUE; + } + } + } + if (literal) + keypad(win, TRUE); + return ch; +} + +static void +finish_bg(int sig GCC_UNUSED) +{ + end_dialog(); + dlg_exit(DLG_EXIT_ERROR); +} + +/* + * If we have callbacks active, purge the list of all that are not marked + * to keep in the background. If any remain, run those in a background + * process. + */ +void +dlg_killall_bg(int *retval) +{ + DIALOG_CALLBACK *cb; + int pid; +#ifdef HAVE_TYPE_UNIONWAIT + union wait wstatus; +#else + int wstatus; +#endif + + if ((cb = dialog_state.getc_callbacks) != 0) { + while (cb != 0) { + if (cb->keep_bg) { + cb = cb->next; + } else { + dlg_remove_callback(cb); + cb = dialog_state.getc_callbacks; + } + } + if (dialog_state.getc_callbacks != 0) { + + refresh(); + fflush(stdout); + fflush(stderr); + reset_shell_mode(); + if ((pid = fork()) != 0) { + _exit(pid > 0 ? DLG_EXIT_OK : DLG_EXIT_ERROR); + } else if (pid == 0) { /* child */ + if ((pid = fork()) != 0) { + /* + * Echo the process-id of the grandchild so a shell script + * can read that, and kill that process. We'll wait around + * until then. Our parent has already left, leaving us + * temporarily orphaned. + */ + if (pid > 0) { /* parent */ + fprintf(stderr, "%d\n", pid); + fflush(stderr); + } + /* wait for child */ +#ifdef HAVE_WAITPID + while (-1 == waitpid(pid, &wstatus, 0)) { +#ifdef EINTR + if (errno == EINTR) + continue; +#endif /* EINTR */ +#ifdef ERESTARTSYS + if (errno == ERESTARTSYS) + continue; +#endif /* ERESTARTSYS */ + break; + } +#else + while (wait(&wstatus) != pid) /* do nothing */ + ; +#endif + _exit(WEXITSTATUS(wstatus)); + } else if (pid == 0) { + if (!dialog_vars.cant_kill) + (void) signal(SIGHUP, finish_bg); + (void) signal(SIGINT, finish_bg); + (void) signal(SIGQUIT, finish_bg); + (void) signal(SIGSEGV, finish_bg); + while (dialog_state.getc_callbacks != 0) { + int fkey = 0; + dlg_getc_callbacks(ERR, fkey, retval); + napms(1000); + } + } + } + } + } +} |