On Wed, Jul 26, 2023, 16:06 Chet Ramey <chet.ra...@case.edu> wrote: > > On 7/24/23 1:13 PM, Chet Ramey wrote: > > > You could do it if you allowed, say > > > > bind -x '"\eX": \"command with spaces\" \"x\"' > > > > and then stripped the backslashes before calling rl_generic_bind, but > > that's not exactly backwards compatible either. > > Thinking about it some more, you can do it like this: > > bind -x $'"\\eX": \'"command with spaces" "x"\'' > > since bind -x allows single-quoted strings as the command to execute, > and $'...' allows backslash-escaped single quotes. > > If we ran the command string through rl_translate_keyseq, it would allow > backslash-escaped double quotes and strip the backslashes, but you get > the rest of the backslash processing that you probably don't want. > > It's just not transitive.
Another issue I didn't think of with printing the unquoted translated command is that it can include newlines, which is a problem since you have to read the `bind -X' output one line at a time to reuse it with `bind -x'. If there isn't a backwards compatible way to produce output that is reusable given the current input format, I wonder if we can leverage a format that's not currently valid as input. `bind -x' currently requires a colon following the key sequence but we could change it to also allow input without it and use rl_macro_bind instead of rl_generic_bind if we get such input. If we have `bind -X' produce untranslated output as it did before, but without the `:', everything should match up and existing valid `bind -X' commands will be unaffected. --- diff --git a/bashline.c b/bashline.c index 5dac2e9e..9d99c536 100644 --- a/bashline.c +++ b/bashline.c @@ -4702,7 +4702,7 @@ bind_keyseq_to_unix_command (char *line) { Keymap kmap, cmd_xmap; char *kseq, *value; - int i, kstart; + int i, kstart, translate; kmap = rl_get_keymap (); @@ -4716,16 +4716,13 @@ bind_keyseq_to_unix_command (char *line) /* Create the key sequence string to pass to rl_generic_bind */ kseq = substring (line, kstart, i); - for ( ; line[i] && line[i] != ':'; i++) + /* Advance to the colon (:) or whitespace which separates the two objects. */ + for ( ; line[i] && line[i] != ':' && line[i] != ' ' && line[i] != '\t'; i++) ; - if (line[i] != ':') - { - builtin_error (_("%s: missing colon separator"), line); - FREE (kseq); - return -1; - } - i = isolate_sequence (line, i + 1, 0, &kstart); + translate = (line[i] != ':'); + + i = isolate_sequence (line, i + 1, translate, &kstart); if (i < 0) { FREE (kseq); @@ -4737,7 +4734,10 @@ bind_keyseq_to_unix_command (char *line) /* Save the command to execute and the key sequence in the CMD_XMAP */ cmd_xmap = get_cmd_xmap_from_keymap (kmap); - rl_generic_bind (ISMACR, kseq, value, cmd_xmap); + if (translate) + rl_macro_bind (kseq, value, cmd_xmap); + else + rl_generic_bind (ISMACR, kseq, value, cmd_xmap); /* and bind the key sequence in the current keymap to a function that understands how to execute from CMD_XMAP */ diff --git a/lib/readline/bind.c b/lib/readline/bind.c index dc30dd84..9d4817a3 100644 --- a/lib/readline/bind.c +++ b/lib/readline/bind.c @@ -2861,18 +2861,12 @@ _rl_macro_dumper_internal (int print_readably, Keymap map, char *prefix) { case ISMACR: keyname = _rl_get_keyname (key); - if (print_readably < 0) - out = savestring ((char *)map[key].function); - else - out = _rl_untranslate_macro_value ((char *)map[key].function, 0); + out = _rl_untranslate_macro_value ((char *)map[key].function, 0); - if (print_readably < 0) - fprintf (rl_outstream, "\"%s%s\": %s\n", prefix ? prefix : "", - keyname, - out ? out : ""); - else if (print_readably > 0) - fprintf (rl_outstream, "\"%s%s\": \"%s\"\n", prefix ? prefix : "", + if (print_readably) + fprintf (rl_outstream, "\"%s%s\"%s \"%s\"\n", prefix ? prefix : "", keyname, + print_readably > 0 ? ":" : "", out ? out : ""); else fprintf (rl_outstream, "%s%s outputs %s\n", prefix ? prefix : "",