summaryrefslogtreecommitdiff
path: root/Build/source/utils/dialog/dialog-1.1-20110707/ui_getc.c
diff options
context:
space:
mode:
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.c632
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);
+ }
+ }
+ }
+ }
+ }
+}