mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-27 15:43:18 +02:00
application-settable readline timeouts
This commit is contained in:
@@ -9757,3 +9757,72 @@ builtins/read.def
|
||||
using SIGALRM
|
||||
- edit_line: simulate receiving SIGALRM if readline times out (not
|
||||
used yet)
|
||||
|
||||
3/11
|
||||
----
|
||||
lib/readline/readline.c
|
||||
- rl_initialize: call _rl_timeout_init to set things up for any timeout
|
||||
that was set with rl_set_timeout
|
||||
- readline_internal_charloop: if we longjmped because of a timeout,
|
||||
make sure to set rl_done/RL_STATE_DONE and return; we are
|
||||
abandoning this call to readline(). The readline timeout changes
|
||||
were based on work contributed by Koichi Murase
|
||||
<myoga.murase@gmail.com>
|
||||
|
||||
lib/readline/readline.h
|
||||
- extern declarations for new timeout functions and hook
|
||||
- rl_clear_timeout: new define
|
||||
|
||||
lib/readline/callback.c
|
||||
- rl_callback_read_char: if we longjmped because of a timeout,
|
||||
make sure to set rl_done/RL_STATE_DONE and return; we are
|
||||
abandoning this call to readline()
|
||||
|
||||
lib/readline/util.c
|
||||
- _rl_abort_internal: if we time out, don't ring the bell; let the
|
||||
caller handle it
|
||||
|
||||
lib/readline/input.c
|
||||
- extern declarations for public and readline-library-private functions
|
||||
and hooks to implement timeouts
|
||||
- rl_set_timeout,rl_timeout_remaining: new public functions
|
||||
- _rl_timeout_select: new function, uses select/pselect to implement
|
||||
read timeouts that take timeouts set with rl_set_timeout into account;
|
||||
calling hook function if a timeout occurs
|
||||
- rl_gather_tyi, _rl_input_available: use _rl_timeout_select, taking
|
||||
any existing timeout into consideration if it expires before the
|
||||
timeout passed as an argument
|
||||
- rl_getc: use _rl_timeout_select and handle any timeouts by calling
|
||||
_rl_timehout_handle
|
||||
- set_alarm,reset_alarm: new functions to implement timeouts using
|
||||
SIGALRM for systems that lack a working select/pselect
|
||||
- _rl_timeout_init: new function, sets things up for reading input
|
||||
with a specified timeout
|
||||
- _rl_timeout_handle: a timeout handler; calls any event hook and
|
||||
sets up to abort the current readline() call
|
||||
- _rl_timeout_handle_sigalrm: a timeout handler for systems using
|
||||
SIGALRM to implement timeouts
|
||||
|
||||
lib/readline/parens.c
|
||||
- rl_insert_close: use _rl_timeout_select to take timeouts into account
|
||||
|
||||
lib/readline/rlprivate.h
|
||||
- extern declarations for readline-library-private timeout functions
|
||||
|
||||
lib/readline/rltty.c
|
||||
- rl_deprep_terminal: don't print a newline after the bracketed paste
|
||||
disable sequence if we timed out
|
||||
|
||||
lib/readline/signals.c
|
||||
- _rl_handle_signal: if sig is SIGALRM, call _rl_timeout_handle_sigalrm()
|
||||
|
||||
lib/readline/doc/rltech.texi
|
||||
- rl_set_timeout,rl_timeout_remaining: document new public functions
|
||||
- RL_STATE_TIMEOUT: document new possible state value for rl_readline_state
|
||||
- rl_timeout_event_hook: document new hook function, called when
|
||||
readline times out
|
||||
|
||||
builtins/read.def
|
||||
- read_builtin: changes to use the readline timeout functions to
|
||||
implement timeouts with `read -e'; these use rl_set_timeout and
|
||||
sh_timer structs together
|
||||
|
||||
@@ -126,6 +126,7 @@ static int set_itext PARAMS((void));
|
||||
static char *edit_line PARAMS((char *, char *));
|
||||
static void set_eol_delim PARAMS((int));
|
||||
static void reset_eol_delim PARAMS((char *));
|
||||
static void set_readline_timeout PARAMS((sh_timer *t, time_t, long));
|
||||
#endif
|
||||
static SHELL_VAR *bind_read_variable PARAMS((char *, char *));
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
@@ -511,7 +512,9 @@ read_builtin (list)
|
||||
{
|
||||
add_unwind_protect (reset_attempted_completion_function, (char *)NULL);
|
||||
add_unwind_protect (bashline_reset_event_hook, (char *)NULL);
|
||||
set_readline_timeout (read_timeout, tmsec, tmusec);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
shtimer_set (read_timeout, tmsec, tmusec);
|
||||
}
|
||||
@@ -1209,6 +1212,17 @@ edit_line (p, itext)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
set_readline_timeout (t, sec, usec)
|
||||
sh_timer *t;
|
||||
time_t sec;
|
||||
long usec;
|
||||
{
|
||||
t->tmout.tv_sec = sec;
|
||||
t->tmout.tv_usec = usec;
|
||||
rl_set_timeout (sec, usec);
|
||||
}
|
||||
|
||||
static int old_delim_ctype;
|
||||
static rl_command_func_t *old_delim_func;
|
||||
static int old_newline_ctype;
|
||||
|
||||
+1
-1
@@ -10870,7 +10870,7 @@ or
|
||||
are unset, they lose their special properties, even if they are
|
||||
subsequently reset. The exit status is true unless a
|
||||
.I name
|
||||
is readonly.
|
||||
is readonly or may not be unset.
|
||||
.TP
|
||||
\fBwait\fP [\fB\-fn\fP] [\fP\-p\fP \fIvarname\fP] [\fIid ...\fP]
|
||||
Wait for each specified child process and return its termination status.
|
||||
|
||||
+1
-1
@@ -4033,7 +4033,7 @@ unset.
|
||||
Readonly variables and functions may not be unset.
|
||||
Some shell variables lose their special behavior if they are unset; such
|
||||
behavior is noted in the description of the individual variables.
|
||||
The return status is zero unless a @var{name} is readonly.
|
||||
The return status is zero unless a @var{name} is readonly or may not be unset.
|
||||
@end table
|
||||
|
||||
@node Bash Builtins
|
||||
|
||||
@@ -147,6 +147,14 @@ rl_callback_read_char (void)
|
||||
(*rl_redisplay_function) ();
|
||||
_rl_want_redisplay = 0;
|
||||
memcpy ((void *)_rl_top_level, (void *)olevel, sizeof (procenv_t));
|
||||
|
||||
/* If we longjmped because of a timeout, handle it here. */
|
||||
if (RL_ISSTATE (RL_STATE_TIMEOUT))
|
||||
{
|
||||
RL_SETSTATE (RL_STATE_DONE);
|
||||
rl_done = 1;
|
||||
}
|
||||
|
||||
CALLBACK_READ_RETURN ();
|
||||
}
|
||||
|
||||
|
||||
@@ -455,6 +455,11 @@ If non-zero, this is the address of a function to call if a read system
|
||||
call is interrupted when Readline is reading terminal input.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar {rl_hook_func_t *} rl_timeout_event_hook
|
||||
If non-zero, this is the address of a function to call if Readline times
|
||||
out while reading input.
|
||||
@end deftypevar
|
||||
|
||||
@deftypevar {rl_hook_func_t *} rl_input_available_hook
|
||||
If non-zero, Readline will use this function's return value when it needs
|
||||
to determine whether or not there is available input on the current input
|
||||
@@ -588,6 +593,10 @@ the current call to @code{readline()}.
|
||||
@item RL_STATE_DONE
|
||||
Readline has read a key sequence bound to @code{accept-line}
|
||||
and is about to return the line to the caller.
|
||||
@item RL_STATE_TIMEOUT
|
||||
Readline has timed out (it did not receive a line or specified number of
|
||||
characters before the timeout duration specified by @code{rl_set_timeout}
|
||||
elapsed) and is returning that status to the caller.
|
||||
@end table
|
||||
|
||||
@end deftypevar
|
||||
@@ -1151,6 +1160,27 @@ The default waiting period is one-tenth of a second.
|
||||
Returns the old timeout value.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int rl_set_timeout (unsigned int secs, unsigned int usecs)
|
||||
Set a timeout for subsequent calls to @code{readline()}. If Readline does
|
||||
not read a complete line, or the number of characters specified by
|
||||
@code{rl_num_chars_to_read}, before the duration specfied by @var{secs}
|
||||
(in seconds) and @var{usecs} (microseconds), it returns and sets
|
||||
@code{RL_STATE_TIMEOUT} in @code{rl_readline_state}.
|
||||
Passing 0 for @code{secs} and @code{usecs} cancels any previously set
|
||||
timeout; the convenience macro @code{rl_clear_timeout()} is shorthand
|
||||
for this.
|
||||
Returns 0 if the timeout is set successfully.
|
||||
@end deftypefun
|
||||
|
||||
@deftypefun int rl_timeout_remaining (unsigned int *secs, unsigned int *usecs)
|
||||
Return the number of seconds and microseconds remaining in the current
|
||||
timeout duration in @code{*secs} and @code{*usecs}, respectively.
|
||||
Returns -1 on error or when there is no timeout set, 0 when the timeout has
|
||||
expired (leaving @code{*secs} and @code{*usecs} unchanged), and 1 if the
|
||||
timeout has not expired. If @code{secs} and @code{usecs} are @code{NULL},
|
||||
the return value indicates whether the timeout has expired.
|
||||
@end deftypefun
|
||||
|
||||
@node Terminal Management
|
||||
@subsection Terminal Management
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@ignore
|
||||
Copyright (C) 1988-2020 Free Software Foundation, Inc.
|
||||
Copyright (C) 1988-2021 Free Software Foundation, Inc.
|
||||
@end ignore
|
||||
|
||||
@set EDITION 8.1
|
||||
@set VERSION 8.1
|
||||
@set UPDATED 29 October 2020
|
||||
@set UPDATED-MONTH October 2020
|
||||
@set UPDATED 10 March 2021
|
||||
@set UPDATED-MONTH March 2021
|
||||
|
||||
@set LASTCHANGE Thu Oct 29 16:49:01 EDT 2020
|
||||
@set LASTCHANGE Wed Mar 10 15:43:49 EST 2021
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
./rl-timeout readline1 0.5
|
||||
./rl-timeout readline2 0.25
|
||||
|
||||
./rl-timeout callback1 0.5
|
||||
./rl-timeout callback2 0.5
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/* rl-timeout: test various readline builtin timeouts. */
|
||||
|
||||
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of the GNU Readline Library (Readline), a library for
|
||||
reading lines of text with interactive input and history editing.
|
||||
|
||||
Readline is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Readline 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with readline. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Standard include files. stdio.h is required. */
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
/* Used for select(2) */
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Standard readline include files. */
|
||||
#if defined (READLINE_LIBRARY)
|
||||
# include "readline.h"
|
||||
# include "history.h"
|
||||
#else
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
#endif
|
||||
|
||||
extern int errno;
|
||||
|
||||
static void cb_linehandler (char *);
|
||||
|
||||
int timeout_secs = 1, timeout_usecs = 0;
|
||||
int running;
|
||||
const char *prompt = "rl-timeout$ ";
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Example 1: readline () with rl_readline_state */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
void
|
||||
rltest_timeout_readline1 ()
|
||||
{
|
||||
const char *temp;
|
||||
|
||||
rl_set_timeout (timeout_secs, timeout_usecs);
|
||||
temp = readline (prompt);
|
||||
if (RL_ISSTATE (RL_STATE_TIMEOUT))
|
||||
printf ("timeout\n");
|
||||
else if (temp == NULL)
|
||||
printf ("no input line\n");
|
||||
else
|
||||
printf ("input line: %s\n", temp);
|
||||
free ((void *) temp);
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Example 2: readline () with rl_timeout_event_hook */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
static int
|
||||
timeout_handler ()
|
||||
{
|
||||
printf ("timeout\n");
|
||||
return READERR;
|
||||
}
|
||||
|
||||
void
|
||||
rltest_timeout_readline2 ()
|
||||
{
|
||||
const char *temp;
|
||||
|
||||
rl_set_timeout (timeout_secs, timeout_usecs);
|
||||
rl_timeout_event_hook = timeout_handler;
|
||||
temp = readline (prompt);
|
||||
if (temp == NULL)
|
||||
printf ("no input line\n");
|
||||
else
|
||||
printf ("input line: %s\n", temp);
|
||||
free ((void *)temp);
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Example 3: rl_callback_* () with rl_timeout_remaining */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
/* Callback function called for each line when accept-line executed, EOF
|
||||
seen, or EOF character read. This sets a flag and returns; it could
|
||||
also call exit(3). */
|
||||
static void
|
||||
cb_linehandler (char *line)
|
||||
{
|
||||
/* Can use ^D (stty eof) or `exit' to exit. */
|
||||
if (line == NULL || strcmp (line, "exit") == 0)
|
||||
{
|
||||
if (line == 0)
|
||||
printf ("\n");
|
||||
printf ("exit\n");
|
||||
/* This function needs to be called to reset the terminal settings,
|
||||
and calling it from the line handler keeps one extra prompt from
|
||||
being displayed. */
|
||||
rl_callback_handler_remove ();
|
||||
|
||||
running = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*line)
|
||||
add_history (line);
|
||||
printf ("input line: %s\n", line);
|
||||
free (line);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rltest_timeout_callback1 ()
|
||||
{
|
||||
fd_set fds;
|
||||
int r;
|
||||
unsigned sec, usec;
|
||||
|
||||
rl_set_timeout (timeout_secs, timeout_usecs);
|
||||
rl_callback_handler_install (prompt, cb_linehandler);
|
||||
running = 1;
|
||||
while (running)
|
||||
{
|
||||
FD_ZERO (&fds);
|
||||
FD_SET (fileno (rl_instream), &fds);
|
||||
r = rl_timeout_remaining (&sec, &usec);
|
||||
if (r == 1)
|
||||
{
|
||||
struct timeval timeout = {sec, usec};
|
||||
r = select (FD_SETSIZE, &fds, NULL, NULL, &timeout);
|
||||
}
|
||||
if (r < 0 && errno != EINTR)
|
||||
{
|
||||
perror ("rl-timeout: select");
|
||||
rl_callback_handler_remove ();
|
||||
break;
|
||||
}
|
||||
else if (r == 0)
|
||||
{
|
||||
printf ("rl-timeout: timeout\n");
|
||||
rl_callback_handler_remove ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET (fileno (rl_instream), &fds))
|
||||
rl_callback_read_char ();
|
||||
}
|
||||
|
||||
printf ("rl-timeout: Event loop has exited\n");
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Example 4: rl_callback_* () with rl_timeout_event_hook */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
static int
|
||||
cb_timeouthandler ()
|
||||
{
|
||||
printf ("timeout\n");
|
||||
rl_callback_handler_remove ();
|
||||
running = 0;
|
||||
return READERR;
|
||||
}
|
||||
|
||||
void
|
||||
rltest_timeout_callback2 ()
|
||||
{
|
||||
int r;
|
||||
|
||||
rl_set_timeout (timeout_secs, timeout_usecs);
|
||||
rl_timeout_event_hook = cb_timeouthandler;
|
||||
rl_callback_handler_install (prompt, cb_linehandler);
|
||||
running = 1;
|
||||
while (running)
|
||||
rl_callback_read_char ();
|
||||
|
||||
printf ("rl-timeout: Event loop has exited\n");
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
if (argc >= 2)
|
||||
{
|
||||
if (argc >= 3)
|
||||
{
|
||||
double timeout = atof (argv[2]);
|
||||
if (timeout <= 0.0)
|
||||
{
|
||||
fprintf (stderr, "rl-timeout: specify a positive number for timeout.\n");
|
||||
return 2;
|
||||
}
|
||||
else if (timeout > UINT_MAX)
|
||||
{
|
||||
fprintf (stderr, "rl-timeout: timeout too large.\n");
|
||||
return 2;
|
||||
}
|
||||
timeout_secs = (unsigned) timeout;
|
||||
timeout_usecs = (unsigned) ((timeout - timeout_secs) * 1000000 + 0.5);
|
||||
}
|
||||
|
||||
if (strcmp (argv[1], "readline1") == 0)
|
||||
rltest_timeout_readline1 ();
|
||||
else if (strcmp (argv[1], "readline2") == 0)
|
||||
rltest_timeout_readline2 ();
|
||||
else if (strcmp (argv[1], "callback1") == 0)
|
||||
rltest_timeout_callback1 ();
|
||||
else if (strcmp (argv[1], "callback2") == 0)
|
||||
rltest_timeout_callback2 ();
|
||||
else
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "usage: rl-timeout [readline1 | readline2 | callback1 | callback2] [timeout]\n");
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+295
-11
@@ -50,6 +50,7 @@
|
||||
#include <signal.h>
|
||||
|
||||
#include "posixselect.h"
|
||||
#include "posixtime.h"
|
||||
|
||||
#if defined (FIONREAD_IN_SYS_IOCTL)
|
||||
# include <sys/ioctl.h>
|
||||
@@ -89,6 +90,9 @@ rl_hook_func_t *rl_event_hook = (rl_hook_func_t *)NULL;
|
||||
/* A function to call if a read(2) is interrupted by a signal. */
|
||||
rl_hook_func_t *rl_signal_event_hook = (rl_hook_func_t *)NULL;
|
||||
|
||||
/* A function to call when readline times out after a time is specified. */
|
||||
rl_hook_func_t *rl_timeout_event_hook = (rl_hook_func_t *)NULL;
|
||||
|
||||
/* A function to replace _rl_input_available for applications using the
|
||||
callback interface. */
|
||||
rl_hook_func_t *rl_input_available_hook = (rl_hook_func_t *)NULL;
|
||||
@@ -132,6 +136,36 @@ win32_isatty (int fd)
|
||||
#define isatty(x) win32_isatty(x)
|
||||
#endif
|
||||
|
||||
/* Readline timeouts */
|
||||
|
||||
/* I don't know how to set a timeout for _getch() in MinGW32, so we use
|
||||
SIGALRM. */
|
||||
#if (defined (HAVE_PSELECT) || defined (HAVE_SELECT)) && !defined (__MINGW32__)
|
||||
# define RL_TIMEOUT_USE_SELECT
|
||||
#else
|
||||
# define RL_TIMEOUT_USE_SIGALRM
|
||||
#endif
|
||||
|
||||
int rl_set_timeout (unsigned int, unsigned int);
|
||||
int rl_timeout_remaining (unsigned int *, unsigned int *);
|
||||
|
||||
int _rl_timeout_init (void);
|
||||
int _rl_timeout_sigalrm_handler (void);
|
||||
int _rl_timeout_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *, const sigset_t *);
|
||||
|
||||
static void _rl_timeout_handle (void);
|
||||
#if defined (RL_TIMEOUT_USE_SIGALRM)
|
||||
static int set_alarm (unsigned int *, unsigned int *);
|
||||
static void reset_alarm (void);
|
||||
#endif
|
||||
|
||||
/* We implement timeouts as a future time using a supplied interval
|
||||
(timeout_duration) from when the timeout is set (timeout_point).
|
||||
That allows us to easily determine whether the timeout has occurred
|
||||
and compute the time remaining until it does. */
|
||||
static struct timeval timeout_point;
|
||||
static struct timeval timeout_duration;
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Character Input Buffering */
|
||||
@@ -223,13 +257,17 @@ rl_gather_tyi (void)
|
||||
input = 0;
|
||||
tty = fileno (rl_instream);
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
|
||||
FD_ZERO (&readfds);
|
||||
FD_ZERO (&exceptfds);
|
||||
FD_SET (tty, &readfds);
|
||||
FD_SET (tty, &exceptfds);
|
||||
USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout);
|
||||
#if defined (RL_TIMEOUT_USE_SELECT)
|
||||
result = _rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL);
|
||||
#else
|
||||
result = select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout);
|
||||
#endif
|
||||
if (result <= 0)
|
||||
return 0; /* Nothing to read. */
|
||||
#endif
|
||||
@@ -330,11 +368,11 @@ rl_set_keyboard_input_timeout (int u)
|
||||
int
|
||||
_rl_input_available (void)
|
||||
{
|
||||
#if defined(HAVE_SELECT)
|
||||
#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
|
||||
fd_set readfds, exceptfds;
|
||||
struct timeval timeout;
|
||||
#endif
|
||||
#if !defined (HAVE_SELECT) && defined(FIONREAD)
|
||||
#if !defined (HAVE_SELECT) && defined (FIONREAD)
|
||||
int chars_avail;
|
||||
#endif
|
||||
int tty;
|
||||
@@ -344,13 +382,17 @@ _rl_input_available (void)
|
||||
|
||||
tty = fileno (rl_instream);
|
||||
|
||||
#if defined (HAVE_SELECT)
|
||||
#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
|
||||
FD_ZERO (&readfds);
|
||||
FD_ZERO (&exceptfds);
|
||||
FD_SET (tty, &readfds);
|
||||
FD_SET (tty, &exceptfds);
|
||||
USEC_TO_TIMEVAL (_keyboard_input_timeout, timeout);
|
||||
# if defined (RL_TIMEOUT_USE_SELECT)
|
||||
return (_rl_timeout_select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout, NULL) > 0);
|
||||
# else
|
||||
return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0);
|
||||
# endif
|
||||
#else
|
||||
|
||||
#if defined (FIONREAD)
|
||||
@@ -463,6 +505,242 @@ rl_clear_pending_input (void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Timeout utility */
|
||||
/* */
|
||||
/* **************************************************************** */
|
||||
|
||||
#if defined (RL_TIMEOUT_USE_SIGALRM)
|
||||
# if defined (HAVE_SETITIMER)
|
||||
|
||||
static int
|
||||
set_alarm (unsigned int *secs, unsigned int *usecs)
|
||||
{
|
||||
struct itimerval it;
|
||||
|
||||
timerclear (&it.it_interval);
|
||||
timerset (&it.it_value, *secs, *usecs);
|
||||
return setitimer (ITIMER_REAL, &it, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_alarm ()
|
||||
{
|
||||
struct itimerval it;
|
||||
|
||||
timerclear (&it.it_interval);
|
||||
timerclear (&it.it_value);
|
||||
setitimer (ITIMER_REAL, &it, NULL);
|
||||
}
|
||||
# else
|
||||
static int
|
||||
set_alarm (unsigned int *secs, unsigned int *usecs)
|
||||
{
|
||||
if (*secs == 0 || *usecs >= USEC_PER_SEC / 2)
|
||||
(*secs)++;
|
||||
*usecs = 0;
|
||||
|
||||
return alarm (*secs);
|
||||
}
|
||||
static void
|
||||
reset_alarm ()
|
||||
{
|
||||
alarm (0);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Set a timeout which will be used for the next call of `readline
|
||||
()'. When (0, 0) are specified the timeout is cleared. */
|
||||
int
|
||||
rl_set_timeout (unsigned int secs, unsigned int usecs)
|
||||
{
|
||||
timeout_duration.tv_sec = secs + usecs / USEC_PER_SEC;
|
||||
timeout_duration.tv_usec = usecs % USEC_PER_SEC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start measuring the time. Returns 0 on success. Returns -1 on
|
||||
error. */
|
||||
int
|
||||
_rl_timeout_init (void)
|
||||
{
|
||||
unsigned int secs, usecs;
|
||||
|
||||
/* Clear the timeout state of the previous edit */
|
||||
RL_UNSETSTATE(RL_STATE_TIMEOUT);
|
||||
timerclear (&timeout_point);
|
||||
|
||||
/* Return 0 when timeout is unset. */
|
||||
if (timerisunset (&timeout_duration))
|
||||
return 0;
|
||||
|
||||
/* Return -1 on gettimeofday error. */
|
||||
if (gettimeofday(&timeout_point, 0) != 0)
|
||||
{
|
||||
timerclear (&timeout_point);
|
||||
return -1;
|
||||
}
|
||||
|
||||
secs = timeout_duration.tv_sec;
|
||||
usecs = timeout_duration.tv_usec;
|
||||
|
||||
#if defined (RL_TIMEOUT_USE_SIGALRM)
|
||||
/* If select(2)/pselect(2) is unavailable, use SIGALRM. */
|
||||
if (set_alarm (&secs, &usecs) < 0)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
timeout_point.tv_sec += secs;
|
||||
timeout_point.tv_usec += usecs;
|
||||
if (timeout_point.tv_usec >= USEC_PER_SEC)
|
||||
{
|
||||
timeout_point.tv_sec++;
|
||||
timeout_point.tv_usec -= USEC_PER_SEC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get the remaining time until the scheduled timeout. Returns -1 on
|
||||
error or no timeout set with secs and usecs unchanged. Returns 0
|
||||
on an expired timeout with secs and usecs unchanged. Returns 1
|
||||
when the timeout has not yet expired. The remaining time is stored
|
||||
in secs and usecs. When NULL is specified to either of the
|
||||
arguments, just the expiration is tested. */
|
||||
int
|
||||
rl_timeout_remaining (unsigned int *secs, unsigned int *usecs)
|
||||
{
|
||||
struct timeval current_time;
|
||||
|
||||
/* Return -1 when timeout is unset. */
|
||||
if (timerisunset (&timeout_point))
|
||||
{
|
||||
errno = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return -1 on error. errno is set by gettimeofday. */
|
||||
if (gettimeofday(¤t_time, 0) != 0)
|
||||
return -1;
|
||||
|
||||
/* Return 0 when timeout has already expired. */
|
||||
/* could use timercmp (&timeout_point, ¤t_time, <) here */
|
||||
if (current_time.tv_sec > timeout_point.tv_sec ||
|
||||
(current_time.tv_sec == timeout_point.tv_sec &&
|
||||
current_time.tv_usec >= timeout_point.tv_usec))
|
||||
return 0;
|
||||
|
||||
if (secs && usecs)
|
||||
{
|
||||
*secs = timeout_point.tv_sec - current_time.tv_sec;
|
||||
*usecs = timeout_point.tv_usec - current_time.tv_usec;
|
||||
if (timeout_point.tv_usec < current_time.tv_usec)
|
||||
{
|
||||
(*secs)--;
|
||||
*usecs += USEC_PER_SEC;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* This should only be called if RL_TIMEOUT_USE_SELECT is defined. */
|
||||
|
||||
#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
|
||||
int
|
||||
_rl_timeout_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout, const sigset_t *sigmask)
|
||||
{
|
||||
int result;
|
||||
#if defined (HAVE_PSELECT)
|
||||
struct timespec ts;
|
||||
#else
|
||||
sigset_t origmask;
|
||||
struct timeval tv;
|
||||
#endif
|
||||
int tmout_status;
|
||||
struct timeval tmout;
|
||||
unsigned int sec, usec;
|
||||
|
||||
/* When the remaining time for rl_timeout is shorter than the
|
||||
keyboard input timeout, replace `timeout' with the remaining time
|
||||
for `rl_timeout' and set `tmout_status = 1'. */
|
||||
tmout_status = rl_timeout_remaining (&sec, &usec);
|
||||
tmout.tv_sec = sec;
|
||||
tmout.tv_usec = usec;
|
||||
|
||||
if (tmout_status == 0)
|
||||
_rl_timeout_handle ();
|
||||
else if (tmout_status == 1)
|
||||
{
|
||||
if (timeout == NULL || timercmp (&tmout, timeout, <))
|
||||
timeout = &tmout;
|
||||
else
|
||||
tmout_status = -1;
|
||||
}
|
||||
|
||||
#if defined (HAVE_PSELECT)
|
||||
if (timeout)
|
||||
{
|
||||
TIMEVAL_TO_TIMESPEC (timeout, &ts);
|
||||
result = pselect (nfds, readfds, writefds, exceptfds, &ts, sigmask);
|
||||
}
|
||||
else
|
||||
result = pselect (nfds, readfds, writefds, exceptfds, NULL, sigmask);
|
||||
#else
|
||||
if (sigmask)
|
||||
sigprocmask (SIG_SETMASK, sigmask, &origmask);
|
||||
|
||||
if (timeout)
|
||||
{
|
||||
tv.tv_sec = timeout->tv_sec;
|
||||
tv.tv_usec = timeout->tv_usec;
|
||||
result = select (nfds, readfds, writefds, exceptfds, &tv);
|
||||
}
|
||||
else
|
||||
result = select (nfds, readfds, writefds, exceptfds, NULL);
|
||||
|
||||
if (sigmask)
|
||||
sigprocmask (SIG_SETMASK, &origmask, NULL);
|
||||
#endif
|
||||
|
||||
if (tmout_status == 1 && result == 0)
|
||||
_rl_timeout_handle ();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
_rl_timeout_handle ()
|
||||
{
|
||||
if (rl_timeout_event_hook)
|
||||
(*rl_timeout_event_hook) ();
|
||||
|
||||
RL_SETSTATE(RL_STATE_TIMEOUT);
|
||||
_rl_abort_internal ();
|
||||
}
|
||||
|
||||
int
|
||||
_rl_timeout_handle_sigalrm ()
|
||||
{
|
||||
#if defined (RL_TIMEOUT_USE_SIGALRM)
|
||||
if (timerisunset (&timeout_point))
|
||||
return -1;
|
||||
|
||||
/* Reset `timeout_point' to the current time to ensure that later
|
||||
calls of `rl_timeout_pending ()' return 0 (timeout expired). */
|
||||
if (gettimeofday(&timeout_point, 0) != 0)
|
||||
timerclear (&timeout_point);
|
||||
|
||||
reset_alarm ();
|
||||
|
||||
_rl_timeout_handle ();
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
/* **************************************************************** */
|
||||
/* */
|
||||
/* Character Input */
|
||||
@@ -526,11 +804,13 @@ rl_getc (FILE *stream)
|
||||
{
|
||||
int result;
|
||||
unsigned char c;
|
||||
int fd;
|
||||
#if defined (HAVE_PSELECT)
|
||||
sigset_t empty_set;
|
||||
fd_set readfds;
|
||||
#endif
|
||||
|
||||
fd = fileno (stream);
|
||||
while (1)
|
||||
{
|
||||
RL_CHECK_SIGNALS ();
|
||||
@@ -538,23 +818,27 @@ rl_getc (FILE *stream)
|
||||
/* We know at this point that _rl_caught_signal == 0 */
|
||||
|
||||
#if defined (__MINGW32__)
|
||||
if (isatty (fileno (stream)))
|
||||
if (isatty (fd)
|
||||
return (_getch ()); /* "There is no error return." */
|
||||
#endif
|
||||
result = 0;
|
||||
#if defined (HAVE_PSELECT)
|
||||
#if defined (HAVE_PSELECT) || defined (HAVE_SELECT)
|
||||
/* At this point, if we have pselect, we're using select/pselect for the
|
||||
timeouts. We handled MinGW above. */
|
||||
FD_ZERO (&readfds);
|
||||
FD_SET (fileno (stream), &readfds);
|
||||
FD_SET (fd, &readfds);
|
||||
# if defined (HANDLE_SIGNALS)
|
||||
result = pselect (fileno (stream) + 1, &readfds, NULL, NULL, NULL, &_rl_orig_sigset);
|
||||
result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &_rl_orig_sigset);
|
||||
# else
|
||||
sigemptyset (&empty_set);
|
||||
sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &empty_set);
|
||||
result = pselect (fileno (stream) + 1, &readfds, NULL, NULL, NULL, &empty_set);
|
||||
result = _rl_timeout_select (fd + 1, &readfds, NULL, NULL, NULL, &empty_set);
|
||||
# endif /* HANDLE_SIGNALS */
|
||||
if (result == 0)
|
||||
_rl_timeout_handle (); /* check the timeout */
|
||||
#endif
|
||||
if (result >= 0)
|
||||
result = read (fileno (stream), &c, sizeof (unsigned char));
|
||||
result = read (fd, &c, sizeof (unsigned char));
|
||||
|
||||
if (result == sizeof (unsigned char))
|
||||
return (c);
|
||||
@@ -583,7 +867,7 @@ rl_getc (FILE *stream)
|
||||
|
||||
if (errno == X_EWOULDBLOCK || errno == X_EAGAIN)
|
||||
{
|
||||
if (sh_unset_nodelay_mode (fileno (stream)) < 0)
|
||||
if (sh_unset_nodelay_mode (fd) < 0)
|
||||
return (EOF);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -135,7 +135,11 @@ rl_insert_close (int count, int invoking_key)
|
||||
orig_point = rl_point;
|
||||
rl_point = match_point;
|
||||
(*rl_redisplay_function) ();
|
||||
# if defined (RL_TIMEOUT_USE_SELECT)
|
||||
ready = _rl_timeout_select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer, NULL);
|
||||
# else
|
||||
ready = select (1, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timer);
|
||||
# endif
|
||||
rl_point = orig_point;
|
||||
#else /* !HAVE_SELECT */
|
||||
_rl_insert_char (count, invoking_key);
|
||||
|
||||
@@ -577,6 +577,15 @@ readline_internal_charloop (void)
|
||||
{
|
||||
(*rl_redisplay_function) ();
|
||||
_rl_want_redisplay = 0;
|
||||
|
||||
/* If we longjmped because of a timeout, handle it here. */
|
||||
if (RL_ISSTATE (RL_STATE_TIMEOUT))
|
||||
{
|
||||
RL_SETSTATE (RL_STATE_DONE);
|
||||
rl_done = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we get here, we're not being called from something dispatched
|
||||
from _rl_callback_read_char(), which sets up its own value of
|
||||
_rl_top_level (saving and restoring the old, of course), so
|
||||
@@ -1148,6 +1157,9 @@ _rl_subseq_result (int r, Keymap map, int key, int got_subseq)
|
||||
int
|
||||
rl_initialize (void)
|
||||
{
|
||||
/* Initialize the timeout first to get the precise start time. */
|
||||
_rl_timeout_init ();
|
||||
|
||||
/* If we have never been called before, initialize the
|
||||
terminal and data structures. */
|
||||
if (rl_initialized == 0)
|
||||
|
||||
+10
-1
@@ -448,6 +448,13 @@ extern int rl_read_key (void);
|
||||
extern int rl_getc (FILE *);
|
||||
extern int rl_set_keyboard_input_timeout (int);
|
||||
|
||||
/* Functions to set and reset timeouts. */
|
||||
extern int rl_set_timeout (unsigned int, unsigned int);
|
||||
extern int rl_timeout_remaining (unsigned int *, unsigned int *);
|
||||
|
||||
#undef rl_clear_timeout
|
||||
#define rl_clear_timeout() rl_set_timeout (0, 0)
|
||||
|
||||
/* `Public' utility functions . */
|
||||
extern void rl_extend_line_buffer (int);
|
||||
extern int rl_ding (void);
|
||||
@@ -599,6 +606,8 @@ extern rl_hook_func_t *rl_event_hook;
|
||||
/* The address of a function to call if a read is interrupted by a signal. */
|
||||
extern rl_hook_func_t *rl_signal_event_hook;
|
||||
|
||||
extern rl_hook_func_t *rl_timeout_event_hook;
|
||||
|
||||
/* The address of a function to call if Readline needs to know whether or not
|
||||
there is data available from the current input source. */
|
||||
extern rl_hook_func_t *rl_input_available_hook;
|
||||
@@ -906,7 +915,7 @@ extern int rl_persistent_signal_handlers;
|
||||
#define RL_STATE_REDISPLAYING 0x1000000 /* updating terminal display */
|
||||
|
||||
#define RL_STATE_DONE 0x2000000 /* done; accepted line */
|
||||
#define RL_STATE_TIMEOUT 0x4000000 /* readline() timed out */
|
||||
#define RL_STATE_TIMEOUT 0x4000000
|
||||
|
||||
#define RL_SETSTATE(x) (rl_readline_state |= (x))
|
||||
#define RL_UNSETSTATE(x) (rl_readline_state &= ~(x))
|
||||
|
||||
@@ -301,6 +301,13 @@ extern void _rl_insert_typein (int);
|
||||
extern int _rl_unget_char (int);
|
||||
extern int _rl_pushed_input_available (void);
|
||||
|
||||
extern int _rl_timeout_init (void);
|
||||
extern int _rl_timeout_handle_sigalrm (void);
|
||||
#if defined (_POSIXSELECT_H_)
|
||||
/* use as a sentinel for fd_set, struct timeval, and sigset_t definitions */
|
||||
extern int _rl_timeout_select (int, fd_set *, fd_set *, fd_set *, const struct timeval *, const sigset_t *);
|
||||
#endif
|
||||
|
||||
/* isearch.c */
|
||||
extern _rl_search_cxt *_rl_scxt_alloc (int, int);
|
||||
extern void _rl_scxt_dispose (_rl_search_cxt *, int);
|
||||
|
||||
@@ -692,7 +692,7 @@ rl_deprep_terminal (void)
|
||||
if (terminal_prepped & TPX_BRACKPASTE)
|
||||
{
|
||||
fprintf (rl_outstream, BRACK_PASTE_FINI);
|
||||
if (_rl_eof_found)
|
||||
if (_rl_eof_found && (RL_ISSTATE (RL_STATE_TIMEOUT) == 0))
|
||||
fprintf (rl_outstream, "\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -267,6 +267,8 @@ _rl_handle_signal (int sig)
|
||||
case SIGTERM:
|
||||
#if defined (SIGALRM)
|
||||
case SIGALRM:
|
||||
if (sig == SIGALRM)
|
||||
_rl_timeout_handle_sigalrm ();
|
||||
#endif
|
||||
#if defined (SIGQUIT)
|
||||
case SIGQUIT:
|
||||
|
||||
+2
-1
@@ -98,7 +98,8 @@ _rl_walphabetic (wchar_t wc)
|
||||
int
|
||||
_rl_abort_internal (void)
|
||||
{
|
||||
rl_ding ();
|
||||
if (RL_ISSTATE (RL_STATE_TIMEOUT) == 0)
|
||||
rl_ding (); /* Don't ring the bell on a timeout */
|
||||
rl_clear_message ();
|
||||
_rl_reset_argument ();
|
||||
rl_clear_pending_input ();
|
||||
|
||||
@@ -67,3 +67,12 @@ FOO
|
||||
0
|
||||
0
|
||||
1
|
||||
[?2004h[?2004l
|
||||
timeout 1: ok
|
||||
unset or null 1
|
||||
[?2004h[?2004l
|
||||
timeout 2: ok
|
||||
unset or null 2
|
||||
[?2004h[?2004l
|
||||
timeout 3: ok
|
||||
unset or null 3
|
||||
|
||||
+1
-1
@@ -111,4 +111,4 @@ ${THIS_SH} ./read5.sub
|
||||
${THIS_SH} ./read6.sub
|
||||
|
||||
# test behavior of readline timeouts
|
||||
# ${THIS_SH} ./read7.sub
|
||||
${THIS_SH} ./read7.sub
|
||||
|
||||
@@ -53,3 +53,6 @@ echo abcde | {
|
||||
read -e -t 0.5 a
|
||||
echo $a
|
||||
}
|
||||
|
||||
read -e -t .0001 a <<<abcde
|
||||
echo $a
|
||||
|
||||
Reference in New Issue
Block a user