Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

automatic tty redirection should not statically target /dev/tty, but the current pty/pts #4242

Open
6 of 10 tasks
andreaskremser opened this issue Feb 14, 2025 · 9 comments
Open
6 of 10 tasks

Comments

@andreaskremser
Copy link

andreaskremser commented Feb 14, 2025

Checklist

  • I have read through the manual page (man fzf)
  • I have searched through the existing issues
  • For bug reports, I have checked if the bug is reproducible in the latest version of fzf

Output of fzf --version

0.59.0 (bbe1721)

OS

  • Linux
  • macOS
  • Windows
  • Etc.

Shell

  • bash
  • zsh
  • fish

Problem / Steps to reproduce

Since 0.53.0 fzf will automatically redirect execute actions to /dev/tty,

so that the usual hack

ls | fzf --bind 'space:execute:vim {} < /dev/tty >/dev/tty'  > selected

could be simplified, without worrying about any redirection

ls | fzf --bind `space:execute:vim {}' > selected

but the static /dev/tty is not universally understood/not always a working solution.
A more robust approach would be to query the current pty (e.g. with /usr/bin/tty), which would return something like /dev/pts/4

The more general solution, that also works with programs like emacsclient, should probably be equivalent to something like this:

CURRENT_TTY=$(tty)
ls | fzf --bind "space:execute:emacsclient -t -a \"\" -c {} < $CURRENT_TTY > $CURRENT_TTY" > selected
@junegunn
Copy link
Owner

Have you actually had the problem while using emacsclient? If so, can you describe what happens in that case?

It's odd because fzf writes directly to /dev/tty, so if it's not available, fzf shouldn't run at all, let alone execute.

const consoleDevice string = "/dev/tty"

@andreaskremser
Copy link
Author

andreaskremser commented Feb 15, 2025

Yes emacsclient will always fail with simple /dev/tty redirection.
Reporting

error: could not open file: /dev/tty

I guess the problem is, that it connects to the emacsdaemon, which no longer has access to a controlling terminal, but still can make use of the concrete pty device node you can obtain by asking $(tty). This is probably also the reason why the non-deamon version of emacs will work in this case.

This is not specific to emacsclient though, it's just the most likely candidate for me to observe this problem.
The point is, a controlling terminal is typically available, that's why /dev/tty usually works, but in some cases it will fail. On the other hand, accessing the correct unix.Ttyname(os.Stdin.Fd()) device node should work reliably.

@junegunn
Copy link
Owner

Can you explain how I can test fzf in emacs? I've never used emacs before.

@junegunn
Copy link
Owner

junegunn commented Feb 15, 2025

unix.Ttyname(os.Stdin.Fd()) device node should work reliably.

Unfortunately, this approach can't be used. Because fzf is a filter, stdin is usually not the tty device.

$ ls | fzf

$ tty
/dev/ttys009

$ ls | tty
not a tty

We could make fzf check if a variable named FZF_TTY is set and use it instead of /dev/tty.

@andreaskremser
Copy link
Author

Ah right, sorry good point. Since fzf requires to have a controlling terminal, we could just keep using that to learn the pty, that we need to provide to the children

fd, err := unix.Open("/dev/tty", unix.O_RDWR, 0)
consoleDevice, err := unix.Ttyname(fd)

@junegunn
Copy link
Owner

Can you show me how I can reproduce the problem and see if the patch helps? Like I said, no experience in Emacs.

@andreaskremser
Copy link
Author

andreaskremser commented Feb 15, 2025

Sure, i really should have added this to the first message already, sorry:

  • First you need to start emacs as a daemon.
emacs --daemon
  • launching emacslcient from fzf with the actual pty works as exepcted:
    emacs will allow you to edit a selected file after pressing space.
    You can exit emacs with ctrl+(x,c). That is pressing and holding ctrl, then typing "x", then typing "c", while still holding ctrl.
CURRENT_TTY=$(tty)
ls | fzf --bind "space:execute:emacsclient -t -a \"\" -c {} < $CURRENT_TTY > $CURRENT_TTY" > selected
  • trying to launch emacsclient with /dev/tty will fail, because emacsdaemon has no access to it.
ls | fzf --bind "space:execute:emacsclient  -t -a \"\" -c {} < /dev/tty > /dev/tty" > selected
*ERROR*: Could not open file: /dev/tty

@junegunn
Copy link
Owner

junegunn commented Feb 16, 2025

Thanks, I can reproduce the problem. One other thing I noticed is that CURRENT_TTY=$(tty) approach doesn't really work nicely with --tmux option as shown below.

Image

So, fzf should get the device name after starting fzf inside tmux popup.

@junegunn
Copy link
Owner

fd, err := unix.Open("/dev/tty", unix.O_RDWR, 0)
consoleDevice, err := unix.Ttyname(fd)

Does this code work? There is no unix.Ttyname function. I've tested the approach using our ttyname implementation slightly changing it to take a file descriptor (instead of using hard-coded 2).

func ttyname() string {
if cached := tty.Load(); cached != nil {
return cached.(string)
}
var stderr syscall.Stat_t
if syscall.Fstat(2, &stderr) != nil {

But I couldn't get it to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants