Hi Chet,
Thanks for your reply.
> Thanks for the reminder. The example needs to be updated for SIGWINCH,
> but simply exits -- as most programs do -- on SIGINT. If you want to
> handle SIGINT, you're already doing it: you already have a functioning
> signal handler. There are a couple of things you need to add to it to
> make it clean up the readline state if you're not exiting:
>
> rl_free_line_state (); /* clean up the line buffer */
> rl_callback_sigcleanup (); /* dispose of internal readline state */
>
> Since readline isn't active, you don't need to call rl_cleanup_after_signal().
>
> That second function is new in readline-7.0; it fixes a bug in previous
> readline versions.
I don't think we'll have the luxury of using readline-7.0 yet in R,
but I'm not sure. I'd like a solution that works for the past few
versions at least.
I have a short example program that I attached. I haven't added
SIGWINCH handling yet. You can see that I use siglongjmp to get out of
the SIGINT handler - a tip I found on stackoverflow - and then I try a
few miscellaneous Readline cleanup functions. I'm trying to get
behavior like Bash - ^C should cancel anything I've entered on a line,
an give me a new prompt. (I notice that GDB has different, somewhat
annoying behavior, of keeping the input line on ^C)
The example that I wrote has a bug which both R and Python suffer
from. The problematic behavior is that when the user hits ^C during a
reverse-isearch, the last isearch proposal is kept as a "hidden" input.
For R, I reported this bug in
<https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16603>.
I'm a bit curious to know if this bug has always been present, or if
it appeared with Readline 6.3 or some other recent version.
Here is an example. I type "test^n^rea^c" and see this:
$ ./test-callback 2>/dev/null
rltest$ test
input line: test
(failed reverse-i-search)`ea': test
rltest$
Note that the prompt appears and there is no text following the
prompt. Now, I hit enter and voila:
$ ./test-callback 2>/dev/null
rltest$ test
input line: test
(failed reverse-i-search)`ea': test
rltest$ test
input line: test
The text "test" suddenly appears after the prompt, and is accepted as
an input line. Somewhat dangerous if "test" meant "delete my files"! I
haven't had time to look at the Bash source for the correct way to
interface with Readline in this case. Do you happen to know what the
problem is here?
Thanks,
Frederick
On Tue, Apr 26, 2016 at 10:14:55AM -0400, Chet Ramey wrote:
> On 4/25/16 2:59 PM, [email protected] wrote:
> > Dear Readline people (and Chet Ramey),
> >
> > I'm trying to help fix some bugs in the R interpreter, involving
> > signals not being received by GNU Readline.
> >
> > R currently has problems which stem from ignoring SIGWINCH
> > <https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16604>, and
> > failing to correctly reset the Readline state upon receiving SIGINT
> > <https://bugs.r-project.org/bugzilla/show_bug.cgi?id=16603>.
>
> I believe that this is correct, in that these signals arrive when
> readline is not reading and processing a character, and therefore
> does not have its signal handlers installed.
>
> > I understand from reading about a similar bug in Python
> > <https://bugs.python.org/issue23735> that the problem may stem from
> > signal handling changes made in Readline 6.3.
> >
> > According to the Python bug (which quotes Chet), the solution is for
> > the application itself to trap SIGWINCH, and then set a flag to call
> > "rl_resize_terminal()" before the next "select()".
>
> Yes, something like
>
> static void
> sighandler (int sig)
> {
> rl_resize_terminal ();
> }
>
> (Technically this is unsafe; you should, as you note, set a flag and call
> rl_resize_terminal from your program's main loop if that flag is set.
> But you have the idea.)
>
> >
> > However, the need to do this is apparently not yet noted in the
> > Readline documentation, for instance the "Alternate interface example"
> > <http://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC43>
> > seems to lack the extra code for SIGWINCH (and, presumably, SIGINT).
>
> Thanks for the reminder. The example needs to be updated for SIGWINCH,
> but simply exits -- as most programs do -- on SIGINT. If you want to
> handle SIGINT, you're already doing it: you already have a functioning
> signal handler. There are a couple of things you need to add to it to
> make it clean up the readline state if you're not exiting:
>
> rl_free_line_state (); /* clean up the line buffer */
> rl_callback_sigcleanup (); /* dispose of internal readline state */
>
> Since readline isn't active, you don't need to call rl_cleanup_after_signal().
>
> That second function is new in readline-7.0; it fixes a bug in previous
> readline versions.
>
> > Delving a bit deeper, I'm wondering why it was necessary for Readline
> > to make application writers do all this extra work. For instance, why
> > not go back to installing signal handlers (via "rl_set_signals()") in
> > "rl_callback_handler_install()", and extend the callback interface
> > with a function - e.g. "rl_check_for_signal()" - that queries
> > _rl_caught_signal to see if a signal is pending?
>
> This is an interesting approach, but it's years too late, and would not
> have satisfied the requirements being presented at the time: the
> applications already had existing signal handling functions they needed
> to have called when signals arrived.
>
> Chet
> --
> ``The lyf so short, the craft so long to lerne.'' - Chaucer
> ``Ars longa, vita brevis'' - Hippocrates
> Chet Ramey, ITS, CWRU [email protected] http://cnswww.cns.cwru.edu/~chet/
>
/*
FHE 26 Apr 2016
Readline callback interface example copied from readline documentation
and modified.
Compile with:
gcc -g -Wall test-callback.c -lreadline -o test-callback
*/
/* Standard include files. stdio.h is required. */
#include <stdlib.h>
#include <unistd.h>
/* Used for select(2) */
#include <sys/types.h>
#include <sys/select.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
/* Standard readline include files. */
#include <readline/readline.h>
#include <readline/history.h>
static void cb_linehandler (char *);
int running;
const char *prompt = "rltest$ ";
/******************************************************************/
/* Copied from rlprivate.h */
#define RL_CHECK_SIGNALS() \
do { \
if (_rl_caught_signal) _rl_signal_handler (_rl_caught_signal); \
} while (0)
extern void _rl_signal_handler (int);
extern int volatile _rl_caught_signal;
void _rl_reset_completion_state ();
/******************************************************************/
/* 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);
}
}
sigjmp_buf env;
void handle_signal(int sig) {
/* rl_reset_line_state(); */
/* rl_reset_after_signal (); */
/* rl_callback_sigcleanup () */
fprintf(stderr,"Got signal: %d\n", sig);
siglongjmp(env,1);
}
int
main (int c, char **v)
{
struct sigaction act;
act.sa_handler=handle_signal;
int res;
res = sigaction(SIGINT, &act, NULL);
if(res!=0) {
perror("sigaction");
exit(1);
}
if(sigsetjmp(env, 1)) {
rl_free_line_state ();
rl_cleanup_after_signal ();
rl_callback_handler_remove ();
printf("\n");
}
fd_set fds;
int r;
/* Install the line handler. */
rl_callback_handler_install (prompt, cb_linehandler);
rl_set_signals();
/* Enter a simple event loop. This waits until something is available
to read on readline's input stream (defaults to standard input) and
calls the builtin character read callback to read it. It does not
have to modify the user's terminal settings. */
running = 1;
while (running)
{
FD_ZERO (&fds);
FD_SET (fileno (rl_instream), &fds);
r = select (FD_SETSIZE, &fds, NULL, NULL, NULL);
if (r < 0 && errno != EINTR)
{
perror ("rltest: select");
rl_callback_handler_remove ();
break;
}
fprintf(stderr, "r=%d\n",r);
/* RL_CHECK_SIGNALS(); */
if (FD_ISSET (fileno (rl_instream), &fds))
rl_callback_read_char ();
}
printf ("rltest: Event loop has exited\n");
return 0;
}
_______________________________________________
Bug-readline mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/bug-readline