Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using rlwrap with Node.js REPL, how could node '.break' (Ctrl-C) not be interpreted as a SIGINT by rlwrap?

Following this discussion on how to preserve command line history between sessions, I defined the following alias:

alias node='env NODE_NO_READLINE=1 rlwrap node'

It works perfectly for the history persistance but now, everytime I do a Ctrl-C to send node's '.break' command, rlwrap takes it too but as a SIGINT: it cleans everything and suicides (as described in its man page), thus forcing me to restart a node session (having to recall my var, funcs, requires etc), while I just wanted to '.break'...

Any way to get back the classic node behaviour?

  • Ctrl-C : breaks
  • Ctrl-C again (or on a blank line): exit
like image 924
maxlath Avatar asked Feb 18 '14 19:02

maxlath


1 Answers

Avoiding SIGINT

node changes the meaning of CTRL-C by unsetting its interrupt character VINTR (usually CTRL-C) to avoid the interrupt signal that it would otherwise get.

After starting up, rlwrap is sleeping all the time, until something happens on your terminal or on the pseudo-terminal (pty) used by e.g. node. This "something" can be a keypress by you, or output from node.

Every time this happens, rlwrap will copy nodess terminal settings (including VINTR) ) to its own tty.

However, if node only changes its terminal settings, this, by itself, won't wake up rlwrap, which will thus keep the old settings on its own tty. Transparency will then be broken: When you press CTRL-C rlwrap will still interpret it as a SIGINT, while node would have understood a .break command.

There ia a special, very obscure, pty mode (EXTPROC) that allows the pty master (rlwrap) to be woken up by a slave's changes in terminal settings, but this is very un-portable. This is why, since version 0.41, rlwrap has the much less elegant --polling option that makes it wake up every 40 milliseconds and copy the slave's terminal settings.

Forward CTRL-C

Starting with version 0.43, rlwrap can directly forward special keys even when in readline mode by binding such a key to rlwrap-direct-keypress in ~/.inputrc:

$if node
   "\C-c": rlwrap-direct-keypress
$endif

However, node only gives CTRL+C special treatment when it itself uses readline (try NODE_NO_READLINE=1 node and then type CTRL-C) to see what I mean)

In such cases (i.e. when a command does its own line editing), one has to force rlwrap into readline mode:

$ rlwrap --always-readline node

This has the unfortunate and unavoidable drawback that whanever a command asks for single keypresses (Continue? Y/N) one has to type an extra Enter.

And then there still is the problem sketched above: if the terminal's interrupt character is not changed, node will never see the CTRL-C (but get a SIGINT instead)

There are two solutions. Either:

stty intr undef # disable interrupt character
rlwrap --always-readline node
stty intr '^c'  # re-enable CTRL-C

or:

 rlwrap --polling --always-readline node # --polling means: continually wake up and wacth  node's interrupt character

Wrapping up

To make a long story short:

  • Add "\C-c": rlwrap-direct-keypress to your inputc
  • Temporarily unset the terminal's interrupt character, or use rlwrap --polling --always-readline as above
  • Try to live with the extra Enter for single keypresses
like image 175
Hans Lub Avatar answered Sep 19 '22 20:09

Hans Lub