peterh/liner

Ctrl-Z (SIGTSTP) Should Be Handled

Closed this issue · 8 comments

  • Operating System: Ubuntu Linux
  • Terminal Emulator: GNOME Terminal running bash
  • Bug behaviour: Joker, built with (forked version of) liner, ignores Ctrl-Z (SIGTSTP) from terminal
  • Expected behaviour: Handles (respects) Ctrl-Z as SIGTSTP

Note that kill -TSTP <pid>, from another terminal, works, as does a subsequent fg from the original terminal (which is then sitting at a bash prompt).

If it's not intended for this to work (why not, since it should for non-shells that take interactive input?), please document that clearly in the README.md. Ditto if there's some configurable that needs to be set (by Joker, in my case). Currently README.md doesn't mention Ctrl-Z that I can see.

If it's not intended for this to work (why not, since it should for non-shells that take interactive input?),

The non-shell comment puzzles me. I think of liner as a shell. It doesn't do anything when you press Ctrl-Z for the same reason that bash doesn't do anything (at the command prompt) when you press Ctrl-Z.

Your application should add a suspend command if it wants to have a way to suspend from the liner prompt. If the application is performing a long-running operation and is going to be away from the command prompt for a long time, it can restore the terminal mode using liner.TerminalMode() (don't forget to restore the terminal mode before re-entering liner).

If it's not intended for this to work (why not, since it should for non-shells that take interactive input?),

The non-shell comment puzzles me. I think of liner as a shell. It doesn't do anything when you press Ctrl-Z for the same reason that bash doesn't do anything (at the command prompt) when you press Ctrl-Z.

Is liner not intended for ordinary programs to use as their own command-line-input handlers then? That's what Joker uses it for -- a (Lisp-like) REPL.

Joker used the predecessor, readline, in the same way, and it supported Ctrl-Z as expected.

I guess I'm not sure exactly what distinguishes a "shell" versus "non-shell" line-reading library. Are most/all programs, that liner would be used for, expected to substitute for bash as shells?

Your application should add a suspend command if it wants to have a way to suspend from the liner prompt. If the application is performing a long-running operation and is going to be away from the command prompt for a long time, it can restore the terminal mode using liner.TerminalMode() (don't forget to restore the terminal mode before re-entering liner).

That's something I've been thinking about adding, since Joker recently added an (exit) function to provide a consistent, elegant way to exit. (Ctrl-D doesn't work on Windows or via a socket session such as telnet, Ctrl-C isn't particularly elegant, etc.)

E.g. Joker could provide a (suspend) or similar function.

Does liner try to handle SIGTSTP itself? Given your caveats, it seems like it must, since that worked for me. I.e. suspending a Joker session from another terminal session and then re-entering it seemed to handle changing terminal state just fine. But maybe I didn't experiment enough to find the missing elements?

Does liner try to handle SIGTSTP itself?

No, the only signal liner tries to handle itself is SIGWINCH (for obvious reasons).

What you're experiencing is liner changing the terminal mode to no longer automatically convert control codes into signals. I'm surprised that readline behaves differently; readline makes similar modifications to the tty state. (Upon closer inspection, it looks like liner clears the IEXTEN flag, and readline does not. That could be the difference).

Does liner try to handle SIGTSTP itself?

No, the only signal liner tries to handle itself is SIGWINCH (for obvious reasons).

...which is great!

What you're experiencing is liner changing the terminal mode to no longer automatically convert control codes into signals. I'm surprised that readline behaves differently; readline makes similar modifications to the tty state. (Upon closer inspection, it looks like liner clears the IEXTEN flag, and readline does not. That could be the difference).

Is it possible that could be made a configurable?

I think what I'm missing is what makes liner a shell instead of a library (like GNU Readline).

Wouldn't it normally be up to the client of a line-reading library to decide whether it's a shell?

E.g. if Bash uses GNU Readline (I don't know whether it does), it makes sense that it'd disable automatic conversion of control codes into signals.

Whereas I would think most clients of GNU Readline would not so disable. E.g. programs like ftp, telnet, and so on, in their command-prompt modes, typically support Control-Z. (Again, I don't know which clients use GNU Readline offhand.)

Is it possible that could be made a configurable?

I prefer not to make things configurable if we can avoid it. What happens if you leave ISIG and IEXTEN alone, but instead disable VINTR and VKILL (somehow, it's way too late at night and I'm having trouble reading the termios manual page).

Keep in mind that readline is far more configurable than liner is intended to be. If you need all the options (including ctrl-Z without adding your own command), readline may be a better choice. liner is more directly inspired by linenoise, not readline (and linenoise masks off both IEXTEN and ISIG, so I'm guessing it has the same Ctrl-Z behaviour as we do. But it's way too late at night and I didn't test).

After careful consideration, I have decided to decline this suggestion. Sorry about that. The correct way to put a process in the background (according to liner) is to use the platform-specific tools available to you. For example, Ctrl-Shift-t (Windows Terminal, konsole, gnome-terminal), Cmd-t (macOS), Ctrl-b, d (tmux), or Ctrl-a, d (screen).

Liner is stubbornly cross-platform, where "cross platform" means that every feature must include support for both Windows and Linux, and Ctrl-Z doesn't work (and as far as I can tell, can never work) on Windows.

After careful consideration, I have decided to decline this suggestion. Sorry about that. The correct way to put a process in the background (according to liner) is to use the platform-specific tools available to you. For example, Ctrl-Shift-t (Windows Terminal, konsole, gnome-terminal), Cmd-t (macOS), Ctrl-b, d (tmux), or Ctrl-a, d (screen).

Liner is stubbornly cross-platform, where "cross platform" means that every feature must include support for both Windows and Linux, and Ctrl-Z doesn't work (and as far as I can tell, can never work) on Windows.

Okay, thanks for your thoughtful response! Might be good to (if you haven't already) clarify, in the README.md, the intended audience (type of program) for liner, and (if you like) point to any forks, of which you're aware, that try to provide more Unix-utility-like behavior as others might prefer.

As of now, the only such fork of which I'm aware is https://github.com/candid82/liner/, but it was created solely as a more-dropin-like replacement for readline in Joker, which strives to provide a Unix-like experience on Unix machines, Windows-like on Windows machines, etc. And that fork isn't (as of this writing) otherwise advertised as being supported for any other uses; so I'm not sure it's a great candidate for mentioning in your README.md, though perhaps as an example of how other needs might be met?

Also, I think I said, long ago, that I'd break my proposed changes out into multiple distinct PRs. That didn't happen, guess I got too busy with other things (having the aforementioned fork available in Joker surely affected my prioritization); perhaps that's just as well, at this point.

Thanks again for the work you put into liner in the first place! Roman Bataev, the author of Joker, discovered your package and switched over to it, improving its utility substantially; that's how I learned of it in the first place.

Might be good to (if you haven't already) clarify, in the README.md, the intended audience (type of program) for liner,

I wrote liner because I was replacing Java and Perl programs that run on multiple platforms, and I needed a command-line editor that is easy to cross-compile and behaves the same way on every (supported) platform.

I thought there was already a note about the cgo-free cross-platform nature of Liner in the README, but apparently not. I propose e8226af ; let me know if that's clear enough or if you have any other suggestions.

and (if you like) point to any forks, of which you're aware, that try to provide more Unix-utility-like behaviour as others might prefer.

I would be happy to add links to forks (or even other alternatives that are not based on Liner). I haven't had the need to look for any since I wrote Liner, although I'm sure that many have been written in the intervening (checks notes)... nearly a decade already? Where does the time go? A pull request to add such a list to the README would be welcome.