help-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: how to save/restore (all?) bindings


From: Koichi Murase
Subject: Re: how to save/restore (all?) bindings
Date: Fri, 20 Oct 2023 15:56:21 +0900

> Wow, thanks for the effort you put into this.
> It looks... complicated ^^

> At first I wondered, what all that level of complexity was needed for,
> when my much simpler version (or Grisha's) already seemed to work.

In addition to the workarounds for the past Bash bugs, it also unsets
the bindings that haven't existed before `bind -r'.  As Grisha has
mentioned in his first reply, the simpler version doesn't reset
temporal bindings unless it is overwritten by the original bindings.

> I personally don't really feel the need to support ancient versions.

I don't think Bash 5.0 is considered an ancient version. Also, if you
would like to contribute to fzf with this, it's not you who determines
whether a specific version is ancient or not. You need to support Bash
3.2 for macOS as the current fzf setting does.

> But isn't bind -X documented to use a "format that can be reused as
> input."?
>
> What is happening there?

It's simply a bug.

> Well, whether something like that can happen was part of my question.
>
> When I do:
> $ bind -x '"\ec": sleep 1; echo func; sleep 1'
>
> And press Alt-c twice (fast)... it already runs func twice.
> So it seems that while executing bind -x’s shell code, bash doesn't
> somehow ignore furter keyseqs, right?

Right, and it's expected.

> So could be that my function, that is bound to \ec, sets READLINE_LINE
> to the new cd command, prints \e[5n, then bash processes another keyseq
> the user may have typed (and which e.g. inserts `; rm -rf /`), only
> then the  \e[0n arrives and my code executes altogether?

Yes.

> Is that the problem you think about?

Yes.

> Translated to my use case you'd do:
>
> bind '"\ec":"\xC0\a\xC0\r\xC0\n"' <= \ec starts the whole thing off
> bind -x '"\xC0\a": store_and_set' <= store_and_set would run fzf, store
>                                      the old READLINE_* run fzf and set
>                                      the new (to cd something)
> bind '"\xC0\r":accept-line'       <= would execute the current
>                                      READLINE_* and thus the cd
> bind -x '"\xC0\n": store_and_set' <= would restore the old READLINE_*
>
> Is that about the idea?

Right. It should be noted that if you are going to include it in a
framework (such as fzf) that can be shared by many users, instead of
\xC0\a, \xC0\r, etc., you need to choose a longer invalid UTF-8 that
is unlikely to conflict with the keybindings of users or other
frameworks.

> 1) I assume that this would also prevent the problem that some other
>    keyseq could break the whole thing like above?
>    I.e. once \ec is recognised, it first runs \xC0\a then \xC0\r and
>    then \xC0\n and nothing can come in between?

Right. Readline macros *replace* byte sequences. Macros are not
something prepending byte sequences.

> 2) But the downside is, I couldn't set/restore the keybindings other
>   than the "main" one, right?!

You shouldn't set/restore keybindings just to call a readline function
from `bind -x'. If you would like to call `accept-line' to make sure
that the prompt is recalculated, you don't have to even change the
keybindings (as suggested above).

What is your final goal? Is it to reset keybindings or to update the
directory name in the prompt after changing directory in `bind -x'? If
it is the former you could pursue changing the keybindings in `bind
-x', but if it is the latter there is no reason to use fragile
keybinding resets.

>   So as soon as the user would use any of these keyseqs for anything
>   else, either that would break, or mine.

Please read my first reply. First, invalid UTF-8 sequences will never
happen in the input stream from the terminial. Next, the code space of
invalid UTF-8 sequence is large, so you can choose a larger code that
is unlikely to conflict. For example, the codespace of overlong UTF-8
encodings [4] has the size of (128 + 2048 + 65536 =) 67712. If you
think 67712 is not enough, you can use one of them with sufficiently
large code as an introducer to an arbitrary length sequence, then you
have effectively an infinite size of the codespace.

[4] https://en.wikipedia.org/wiki/UTF-8#Overlong_encodings

> 3) The whole idea of allowing a user to easily set up many such
>    bindings with different parameters would be more difficult, and each
>    would need further keybindings that replace "\xC0\a" with different
>    parameters?

The same as the reply to the previous point. You can use any invalid
UTF-8 sequences.

> So whatever I do it seems I have to live with possible breakage because
> of keyseqs:
> - either (when using "my" approach) because there are other keyseqs
>   in-between, that break my own

No. It doesn't happen with macros.

> - or (when using your approach) because I may break other keybindings
>  or others may break mine?

If you would like to avoid conflicts, you should choose a larger code
for the invalid UTF-8 sequences. There is still a possibility of
conflicts but it's unlikely to happen unless the user tries to make
the conflict on purpose.

> The user would only set:
>  bind -x '"\ec": __fzf_bash_integration_widget_cd -s / -o -f ...'
>
> In this mode, when called with neither `-.` nor `-_`,
> __fzf_bash_integration_widget_cd would *not* run find/fzf
> but:
>
> 1) store the current key bindings in some global variable

Why do you need to save the keybindings?

> 2) set up these key bindings:
>   bind    '"\e[0n":  "\xC0\a\xC0\r\xC0\n"'

This is not needed.

>   bind -x '"\xC0\a": __fzf_bash_integration_widget_cd -. <the same
> options from above>'
>    bind    '"\xC0\r": accept-line'
>    bind -x '"\xC0\n": __fzf_bash_integration_widget_cd -_'

These should be static keybindings, i.e., they should be set up in
.bashrc and do not need to be dynamically bound.

> 3) printf '\e[5n'

This causes the problem that I mentioned. The sequence is not ensured.

> Printing the \e[5n causes the \e[0n, which executes the first bind (and
> AFAIU, that one is guaranteed to run a row and nothing can come in
> between, right?

No. It's not guaranteed. Why do you think it can be guaranteed? The
bindings of "\xC0?" and the terminal response is unrelated.

> Now I thoroughly tried to betray myself ;-) ... cause even if
> everything from the \xC0\a\xC0\r\xC0\n would truly run without anything
> being able to come in between, it's still possible in theory, that
> after the \e[5n, but before the \e[0n, somehing reset part of my binds.

Yes. If you solve the question by yourself, could you please remove it
from the reply?

> So... I continued to think and what if in step (2) I don't just set the
> bindings I need, but completely remove any others (before doing the
> printf '\e[5n')?

If you stick to \e[5n and \e[0n, these bindings are not needed at all.
I provided the solution with macros bound to the invalid UTF-8
sequences as an alternative way to invoke a readline function. There
is really no reason to do both of two solutions to achieve the same
thing.

> Even if some keyseq is already in the queue, e.g. because the user did
> a fast \ecXXXX, XXXX, whatever it is, should(?) have no effect when
> it's finally processed after the function returns after the
> printf '\e[5n'

XXXX may invoke the temporal binding that you set before sending
\e[5n.  I'm not sure if I should answer these trivial questions. Are
you asking it to yourself and going to answer it yourself?

> Does that mean, that I can even do my original schema (which is the
> code from __fzf_bash_integration_widget_cd() as given here:
> [...]
> and be save?

You can if it is fine that user inputs fed before \e[5n arrives.
However, there are probably more such situations than you think. When
the user paste a text into the terminal, many bytes will be sent at
the same time in a packet. In this case, when the shell receives the
initial sequence (\ec), the following bytes arrives in the shell even
before the `bind -x' function bound to \ec is run. For another
example, when the user is connecting to a remote host with a slow
connection, the user inputs can also be combined into a single packet
again.

Even if there was no sequence issue, utilizing the terminal's response
doesn't seem right to me. The input processing can be something
completed within the shell. When the user connects to a remote host,
requiring the terminal's response means requiring network
communications over the internet. An approach requiring internet
communications just to process keybindings seems incorrect.

> *If* all the above would work as hoped and *if* what Grisha said in
> https://lists.gnu.org/archive/html/help-bash/2023-10/msg00035.html
> means that I can indeed (with the next version) bind -ps’s and bind -
> X’s output "as is" for restoring (which would be much simpler than
> Koichi's complex function - which I rather don't understand ^^)...
>
> ... then all I'd be left in need with would be a way to remove all
> bindings.
> Which seems more difficult than expected:

The feature to remove all bindings is already included in the script I
provided in my first reply [1,2]. I suggest you to again read all the
code comments of Ref. [2].

----

Anyway, I still strongly suggest discarding the idea of using the
terminal responses. Instead, you can use the invalid UTF-8 sequences
of a larger code that will never conflict. If you want to make sure
that it will never conflict, you might create a repository that
maintains the existing codes in use in the wild and register your code
there to
reserve the code for your use. But basically, I don't think it is
needed because the codespace is sufficiently large.

--
Koichi



reply via email to

[Prev in Thread] Current Thread [Next in Thread]