zsh-workers
 help / color / mirror / code / Atom feed
* Re: bracketed paste mode in xterm and urxvt
       [not found]     ` <mk9dc0$p0$1@ger.gmane.org>
       [not found]       ` <CABZhJg_5p8BLbq82s_wVtsPdD5hVtk-cPg6fNxzbSs4Vg00SOw@mail.gmail.com>
@ 2015-06-03 15:31       ` Oliver Kiddle
  2015-06-03 20:42         ` Stephane Chazelas
  2015-06-05 10:49         ` Yuri D'Elia
  1 sibling, 2 replies; 18+ messages in thread
From: Oliver Kiddle @ 2015-06-03 15:31 UTC (permalink / raw)
  To: zsh-workers

[ moved to -workers ]

Yuri D'Elia wrote:
> On 05/28/2015 10:30 PM, Daniel Hahler wrote:
> > Apart from that I think that this (bracketed paste mode) should be
> > included in Zsh's and get maintained this way.  Then it could also be
> > adjusted for vi-mode.
> 
> I do agree that mainlining this would make a lot of sense, even as a
> setopt. Or at least provide the keymap/functions needed to enable it.

I've been using bracketed paste for a while now and would also agree.
The question is in what form to provide it? Note that I posted an
alternative mechanism in workers/29898. The patch below is a port of
that to C.

This doesn't use a keymap which I don't think is any loss as such?
Quoting is enabled with a numeric argument. I like the idea of using a
Ctrl-X prefix to enable quoting but a small wrapper function can provide
for that.

With the patch as it stands, which is not meant to be final, users must
still manually enable the mode for their terminal with zle-line-init
etc (I actually append the strings to PS1/PS2/POSTEDIT). It'd certainly
possible to add a setopt option to zsh to automatically output the
enable/disable strings for bracketed paste. Testing a few ancient
terminals (xterm and dtterm on Solaris 10), they seem to have no ill
effect. Perhaps it'd be better to apply some sort of heuristics based on
terminfo, however. Any thoughts on this?

What behaviour would you want in vi-mode? What about with the region
active? Replacing the region might make sense but isn't really what
emacs or vim do.

Oliver

diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index b41661a..6a07212 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -28,6 +28,7 @@
 "beginning-of-history", beginningofhistory, 0
 "beginning-of-line", beginningofline, 0
 "beginning-of-line-hist", beginningoflinehist, 0
+"bracketed-paste", bracketedpaste, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index c6fae25..6da31f3 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -1400,6 +1400,10 @@ default_bindings(void)
     bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL);
     bindkey(emap, "\30=",   refthingy(t_whatcursorposition), NULL);
 
+    /* bracketed paste applicable to all keymaps */
+    bindkey(emap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+    bindkey(vmap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+
     /* emacs mode: ESC sequences, all taken from the meta binding table */
     buf[0] = '\33';
     buf[2] = 0;
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4669ef2..2eec8fa 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -737,6 +737,42 @@ yankpop(UNUSED(char **args))
 
 /**/
 int
+bracketedpaste(UNUSED(char **args))
+{
+    static const char endesc[] = "\e[201~";
+    int endpos = 0;
+    size_t psize = 64;
+    char *buf, *pbuf = zalloc(psize);
+    size_t current = 0;
+    int n, next, timeout;
+    ZLE_STRING_T wpaste;
+
+    while (endesc[endpos]) {
+	if ((next = getbyte(1L, &timeout)) == EOF)
+	    break;
+	if (!endpos || next != endesc[endpos++])
+	    endpos = (next == *endesc);
+	if (current + 1 >= psize)
+	    pbuf = zrealloc(pbuf, psize *= 2);
+	if (imeta(next)) {
+	    pbuf[current++] = Meta;
+	    pbuf[current++] = next ^ 32;
+	} else if (next == '\r')
+	    pbuf[current++] = '\n';
+	else
+	    pbuf[current++] = next;
+    }
+    pbuf[current-sizeof(endesc)+1] = '\0';
+    buf = zmult == 1 ? pbuf : quotestring(pbuf, NULL, QT_BACKSLASH);
+    zmult = 1;
+    wpaste = stringaszleline(buf, 0, &n, NULL, NULL);
+    doinsert(wpaste, n);
+    free(pbuf); free(wpaste);
+    return 0;
+}
+
+/**/
+int
 overwritemode(UNUSED(char **args))
 {
     insmode ^= 1;


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-03 15:31       ` Oliver Kiddle
@ 2015-06-03 20:42         ` Stephane Chazelas
  2015-06-03 23:48           ` Oliver Kiddle
  2015-06-05 10:49         ` Yuri D'Elia
  1 sibling, 1 reply; 18+ messages in thread
From: Stephane Chazelas @ 2015-06-03 20:42 UTC (permalink / raw)
  To: zsh-workers

2015-06-03 17:31:31 +0200, Oliver Kiddle:
> [ moved to -workers ]
> 
> Yuri D'Elia wrote:
> > On 05/28/2015 10:30 PM, Daniel Hahler wrote:
> > > Apart from that I think that this (bracketed paste mode) should be
> > > included in Zsh's and get maintained this way.  Then it could also be
> > > adjusted for vi-mode.
> > 
> > I do agree that mainlining this would make a lot of sense, even as a
> > setopt. Or at least provide the keymap/functions needed to enable it.
> 
> I've been using bracketed paste for a while now and would also agree.
> The question is in what form to provide it? Note that I posted an
> alternative mechanism in workers/29898. The patch below is a port of
> that to C.
[...]

Also note that where terminals don't filter out control
characters, there's a potential problem in the way ^C is
handled.

zsh disables all signal keys but "intr". If the data to paste
(which is written in one go to the master side of the pty by
the terminal emulator) contains ^C, then a SIGINT will be sent
to zsh and the data from the start  of the paste up to ^C will
not be available for reading (discarded by the line discipline,
at least on Linux).

So any bracketed paste solution cannot be safe unless either ^C
is removed by the terminal emulator (like newer versions of
xterm), or ISIG is disabled in the terminal line-discipline.

So, for a truly safe paste, zsh should probably do the
equivalent of a stty -isig. That can only be done *before* the
paste, so that means isig must be turned off all the time (at
least when bracketed paste is enabled). Not desirable as CTRL-C
comes handy to stop lengthy processes by the shell.

To sum-up, for a safe bracketed paste, you need either:

- terminal emulator to filter out ^[ and ^C

Or if the terminal doesn't filter out ^[ and ^C both:

 - a different paste mode than xterm's \e[200~<to-paste>\e[201~ which
   doesn't work as <to-paste> may contain \e[201~ (something
   like: insert-formatted("\033[202~%S~%s",
   CLIPBOARD,PRIMARY,CUT_BUFFER0) would do).
 - zsh to disable isig.

Maybe a better approach would be to query the X selection for
instance with xclip/xsel where available. That can also be
integrated with the zsh kill ring. I once implemented a PoC
around that idea together with mouse integration

https://github.com/stephane-chazelas/misc-scripts/blob/master/mouse.zsh

-- 
Stephane


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-03 20:42         ` Stephane Chazelas
@ 2015-06-03 23:48           ` Oliver Kiddle
  2015-06-04  7:15             ` Stephane Chazelas
  0 siblings, 1 reply; 18+ messages in thread
From: Oliver Kiddle @ 2015-06-03 23:48 UTC (permalink / raw)
  To: Stephane Chazelas; +Cc: Zsh workers

Stephane Chazelas wrote:
> To sum-up, for a safe bracketed paste, you need either:
> 
> - terminal emulator to filter out ^[ and ^C
> 

^C is only the default interrupt character. From some basic testing, it
seems you can use tcgetattr/tcsetattr on the master side of the pty.
So the terminal could perhaps disable isig before writing the string
and restore it afterwards. I doubt that an ssh would pass that through,
however.

>  - a different paste mode than xterm's \e[200~<to-paste>\e[201~ which
>    doesn't work as <to-paste> may contain \e[201~ (something
>    like: insert-formatted("\033[202~%S~%s",
>    CLIPBOARD,PRIMARY,CUT_BUFFER0) would do).

Would have been better if xterm had done it that way in the first place. 
As it is, stripping a fake end string should do the job.

>  - zsh to disable isig.
> 
> Maybe a better approach would be to query the X selection for
> instance with xclip/xsel where available. That can also be

I've got a widget based on xclip. There isn't always an X connection
back, however. It might be worth remembering for when copying text from
firefox.

The nice thing about getting bracketed paste working is that it
potentially doesn't require users to learn anything new: they already
know how to paste in their terminal. That's also why I think it is worth
trying to include support in a form that doesn't need ohmyzsh plugins or
lines in .zshrc. And putting security aside, there are other benefits
like the single undo event, newlines not being accepted and the option
of manipulating the string such as with shell quoting.

Oliver


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-03 23:48           ` Oliver Kiddle
@ 2015-06-04  7:15             ` Stephane Chazelas
  0 siblings, 0 replies; 18+ messages in thread
From: Stephane Chazelas @ 2015-06-04  7:15 UTC (permalink / raw)
  To: zsh-workers

2015-06-04 01:48:25 +0200, Oliver Kiddle:
> Stephane Chazelas wrote:
> > To sum-up, for a safe bracketed paste, you need either:
> > 
> > - terminal emulator to filter out ^[ and ^C
> > 
> 
> ^C is only the default interrupt character. From some basic testing, it
> seems you can use tcgetattr/tcsetattr on the master side of the pty.
> So the terminal could perhaps disable isig before writing the string
> and restore it afterwards. I doubt that an ssh would pass that through,
> however.

ssh (the client) disables ISIG already. It passes the ^C along
and it's the remote pty line discipline that may send SIGINT to
the process upon receiving that ^C, so there's nothing ssh could
do there.

> >  - a different paste mode than xterm's \e[200~<to-paste>\e[201~ which
> >    doesn't work as <to-paste> may contain \e[201~ (something
> >    like: insert-formatted("\033[202~%S~%s",
> >    CLIPBOARD,PRIMARY,CUT_BUFFER0) would do).
> 
> Would have been better if xterm had done it that way in the first place. 
> As it is, stripping a fake end string should do the job.

zsh could use a heuristic approach similar to the one I use in
the pty wrapper at
http://security.stackexchange.com/questions/39118/how-can-i-protect-myself-from-this-kind-of-clipboard-abuse/52655#52655

That is detect paste by the fact that all characters come at
once. Not foolproof when done by the shell as there's the case
of the user typing or pasting before the prompt is issued.

> >  - zsh to disable isig.
> > 
> > Maybe a better approach would be to query the X selection for
> > instance with xclip/xsel where available. That can also be
> 
> I've got a widget based on xclip. There isn't always an X connection
> back, however. It might be worth remembering for when copying text from
> firefox.
> 
> The nice thing about getting bracketed paste working is that it
> potentially doesn't require users to learn anything new: they already
> know how to paste in their terminal. That's also why I think it is worth
> trying to include support in a form that doesn't need ohmyzsh plugins or
> lines in .zshrc. And putting security aside, there are other benefits
> like the single undo event, newlines not being accepted and the option
> of manipulating the string such as with shell quoting.
[...]

Agreed.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-03 15:31       ` Oliver Kiddle
  2015-06-03 20:42         ` Stephane Chazelas
@ 2015-06-05 10:49         ` Yuri D'Elia
  2015-06-05 13:40           ` Oliver Kiddle
  1 sibling, 1 reply; 18+ messages in thread
From: Yuri D'Elia @ 2015-06-05 10:49 UTC (permalink / raw)
  To: zsh-workers

On 06/03/2015 05:31 PM, Oliver Kiddle wrote:
>> I do agree that mainlining this would make a lot of sense, even as a
>> setopt. Or at least provide the keymap/functions needed to enable it.
> 
> I've been using bracketed paste for a while now and would also agree.
> The question is in what form to provide it? Note that I posted an
> alternative mechanism in workers/29898. The patch below is a port of
> that to C.

Looks good to me.

Actually, with this, couldn't we just disable isig while inserting the
characters?

If the terminal ensures that the end sequence is filtered in a bracketed
paste, the shell can also be sure that anything inbetween is not user input.

> With the patch as it stands, which is not meant to be final, users must
> still manually enable the mode for their terminal with zle-line-init
> etc (I actually append the strings to PS1/PS2/POSTEDIT). It'd certainly
> possible to add a setopt option to zsh to automatically output the
> enable/disable strings for bracketed paste. Testing a few ancient
> terminals (xterm and dtterm on Solaris 10), they seem to have no ill
> effect. Perhaps it'd be better to apply some sort of heuristics based on
> terminfo, however. Any thoughts on this?

I guess we can't use terminfo has no feature for this, right?

> What behaviour would you want in vi-mode? What about with the region
> active? Replacing the region might make sense but isn't really what
> emacs or vim do.

emacs disables the transient selection when pasting (which is inserted
on the current cursor position). For vi I'm not sure what I would
like... maybe the same. You have to enter insert mode normally, so it
would make it identical in behavior. Maybe gvim is different?



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-05 10:49         ` Yuri D'Elia
@ 2015-06-05 13:40           ` Oliver Kiddle
  2015-06-05 14:35             ` Yuri D'Elia
  0 siblings, 1 reply; 18+ messages in thread
From: Oliver Kiddle @ 2015-06-05 13:40 UTC (permalink / raw)
  To: Yuri D'Elia; +Cc: zsh-workers

Yuri D'Elia wrote:
> Actually, with this, couldn't we just disable isig while inserting the
> characters?

Unfortunately not. As Stephane indicated, the string is written in one
go. The terminal driver sees and handles the interrupt character before
zsh has had the chance to interpret the start sequence. It's a feature
that an interrupt doesn't rely on the user program having emptied the
input buffer. It might be possible to do something from a clever SIGINT
handler but I doubt that could be especially reliable. I think I'd
rather have xterm/urxvt strip ^C (plus a few others) and know that I
shouldn't expect pasting to be safe if I use something weird. Unless
someone has some other ideas?

> > etc (I actually append the strings to PS1/PS2/POSTEDIT). It'd certainly
> > possible to add a setopt option to zsh to automatically output the
> > enable/disable strings for bracketed paste. Testing a few ancient
> > terminals (xterm and dtterm on Solaris 10), they seem to have no ill
> > effect. Perhaps it'd be better to apply some sort of heuristics based on
> > terminfo, however. Any thoughts on this?
> 
> I guess we can't use terminfo has no feature for this, right?

Not directly but we might discern some indication from it as to whether
it will regard the sequence as being an escape sequence.

The important thing is that outputting \e[?2004h is harmless. Every
terminal I've tried absorbs the string rather than printing some or all
of it so they presumably recognise \e[? four digit number h as being an
unknown escape sequence. Barring it having some other meaning, that's
all we need. Taking the output of this:
  printf '%s %q\n' ${(kv)terminfo[(R)*[0-9](#c4)[hl]]}
Shows smcup and rmcup as containing that pattern for xterm, urxvt and
screen as $TERM. libvte based things use "xterm" for $TERM. Does anyone
know of any other terminal that may be relevant, perhaps on a mobile
phone? What about putty?

> > What behaviour would you want in vi-mode? What about with the region
> > active? Replacing the region might make sense but isn't really what
> > emacs or vim do.
> 
> emacs disables the transient selection when pasting (which is inserted
> on the current cursor position). For vi I'm not sure what I would
> like... maybe the same. You have to enter insert mode normally, so it
> would make it identical in behavior. Maybe gvim is different?

Replacing the selection is probably the most useful thing. Trying
editors like gedit and nedit, pasting the secondary selection will
replace the currently highlighted selection. GUI emacs is a bit the
odd-one-out, not even grabbing the X selection but it is perhaps trying
to be compatble with emacs run from a tty by default. A p command in vim
does replace the selection while a middle-mouse paste uses the mouse
position which isn't comparable.

Oliver


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-05 13:40           ` Oliver Kiddle
@ 2015-06-05 14:35             ` Yuri D'Elia
  2015-06-10  0:28               ` Oliver Kiddle
  0 siblings, 1 reply; 18+ messages in thread
From: Yuri D'Elia @ 2015-06-05 14:35 UTC (permalink / raw)
  To: zsh-workers

On 06/05/2015 03:40 PM, Oliver Kiddle wrote:
> Unfortunately not. As Stephane indicated, the string is written in one
> go. The terminal driver sees and handles the interrupt character before
> zsh has had the chance to interpret the start sequence. It's a feature
> that an interrupt doesn't rely on the user program having emptied the
> input buffer. It might be possible to do something from a clever SIGINT

What if the terminal itself turns off BREAK processing during paste?

> handler but I doubt that could be especially reliable. I think I'd
> rather have xterm/urxvt strip ^C (plus a few others) and know that I
> shouldn't expect pasting to be safe if I use something weird. Unless
> someone has some other ideas?

I'd rather not go down the xterm route... they've implemented a full
character filtering facility in there. Which I guess is ok, since this
will handle pastes for applications without any bracketing support.. but
I find it to be tacked-on fix.

> The important thing is that outputting \e[?2004h is harmless. Every

Putty also eats it by the way. If that's the case for all terminals, I
would just enabled it by default.

>> emacs disables the transient selection when pasting (which is inserted
>> on the current cursor position). For vi I'm not sure what I would
>> like... maybe the same. You have to enter insert mode normally, so it
>> would make it identical in behavior. Maybe gvim is different?
> 
> Replacing the selection is probably the most useful thing. Trying
> editors like gedit and nedit, pasting the secondary selection will
> replace the currently highlighted selection. GUI emacs is a bit the
> odd-one-out, not even grabbing the X selection but it is perhaps trying
> to be compatble with emacs run from a tty by default. A p command in vim
> does replace the selection while a middle-mouse paste uses the mouse
> position which isn't comparable.

I'm ambivalent to that. Replacing the selection seems pretty sound to me.



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-05 14:35             ` Yuri D'Elia
@ 2015-06-10  0:28               ` Oliver Kiddle
  2015-06-10  4:38                 ` Bart Schaefer
  2015-06-10  9:44                 ` Yuri D'Elia
  0 siblings, 2 replies; 18+ messages in thread
From: Oliver Kiddle @ 2015-06-10  0:28 UTC (permalink / raw)
  To: zsh-workers

On 5 Jun, Yuri D'Elia wrote:
> What if the terminal itself turns off BREAK processing during paste?
You can try. Would be interesting. May not work when there is ssh/sshd,
telnet, screen, tmux etc sat between urxvt and zsh.

> Putty also eats it by the way. If that's the case for all terminals, I
> would just enabled it by default.

We can always add some tests later if it appears to be necessary. Any
objections?
I've tried quite hard to find a terminal that won't eat the sequence and
found none.

This links it to a new BRACKETED_PASTE option. A concern with that
approach is that if logic is needed in init_term() to disable the
feature, the distinction between the user explicitly disabling the
feature and init_term() doing it wouldn't be clear.

Any thoughts on the exact positioning of the code to print the
enable/disable sequences here? Is there any way the disable sequence
could get missed? Doesn't seem necessary to do anything in trashzle().
Unlike zle-line-finish (but like $POSTEDIT), this prints the disable
sequence even if zle is aborted with ^C or ^D. It is also important that
it comes before preexec is run because that is where you might enable
bracketed paste for specific commands.

This includes the previous patch. The region is now replaced if active
and the widget is also bound for vi command mode.

Oliver

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index fa54024..6b32d89 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -2291,6 +2291,20 @@ cindex(enabling the beep)
 item(tt(BEEP) (tt(PLUS()B)) <D>)(
 Beep on error in ZLE.
 )
+pindex(BRACKETED_PASTE)
+pindex(NO_BRACKETED_PASTE)
+pindex(NOBRACKETED_PASTE)
+cindex(bracketed paste)
+cindex(enabling bracketed paste)
+item(tt(BRACKETED_PASTE))(
+Many terminal emulators have a feature that allow applications to
+identify when text is pasted into the terminal rather than being typed
+normally. If this option is set, that feature will be enabled while ZLE
+is active and disabled at other times. In particular, this means that
+special characters such as tabs and newlines will be inserted instead of
+invoking editor commands. Furthermore, pasted text forms a single undo
+event and if the region is active, pasted text will replace the region.
+)
 pindex(COMBINING_CHARS)
 pindex(NO_COMBINING_CHARS)
 pindex(COMBININGCHARS)
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 16d661f..652f996 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -2057,6 +2057,11 @@ tindex(beep)
 item(tt(beep))(
 Beep, unless the tt(BEEP) option is unset.
 )
+tindex(bracketed-paste)
+item(tt(bracketed-paste))(
+This widget is invoked when text is pasted to the terminal emulator.
+See also the BRACKETED_PASTE option.
+)
 tindex(vi-cmd-mode)
 item(tt(vi-cmd-mode) (tt(^X^V)) (unbound) (tt(^[)))(
 Enter command mode; that is, select the `tt(vicmd)' keymap.
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index b41661a..6a07212 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -28,6 +28,7 @@
 "beginning-of-history", beginningofhistory, 0
 "beginning-of-line", beginningofline, 0
 "beginning-of-line-hist", beginningoflinehist, 0
+"bracketed-paste", bracketedpaste, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index c6fae25..d355f41 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -1400,6 +1400,11 @@ default_bindings(void)
     bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL);
     bindkey(emap, "\30=",   refthingy(t_whatcursorposition), NULL);
 
+    /* bracketed paste applicable to all keymaps */
+    bindkey(emap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+    bindkey(vmap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+    bindkey(amap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+
     /* emacs mode: ESC sequences, all taken from the meta binding table */
     buf[0] = '\33';
     buf[2] = 0;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index cec44c0..47b5aa5 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1248,6 +1248,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 
     zlecallhook(init, NULL);
 
+    if (isset(BRACKETEDPASTE))
+        fputs("\e[?2004h", shout);
+
     zrefresh();
 
     zlecore();
@@ -1257,6 +1260,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 		  "ZLE_VARED_ABORTED" :
 		  "ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
 
+    if (isset(BRACKETEDPASTE))
+        fputs("\e[?2004l", shout);
+
     if (done && !exit_pending && !errflag)
 	zlecallhook(finish, NULL);
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4669ef2..873b01b 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -737,6 +737,44 @@ yankpop(UNUSED(char **args))
 
 /**/
 int
+bracketedpaste(UNUSED(char **args))
+{
+    static const char endesc[] = "\e[201~";
+    int endpos = 0;
+    size_t psize = 64;
+    char *buf, *pbuf = zalloc(psize);
+    size_t current = 0;
+    int n, next, timeout;
+    ZLE_STRING_T wpaste;
+
+    while (endesc[endpos]) {
+	if (current + 1 >= psize)
+	    pbuf = zrealloc(pbuf, psize *= 2);
+	if ((next = getbyte(1L, &timeout)) == EOF)
+	    break;
+	if (!endpos || next != endesc[endpos++])
+	    endpos = (next == *endesc);
+	if (imeta(next)) {
+	    pbuf[current++] = Meta;
+	    pbuf[current++] = next ^ 32;
+	} else if (next == '\r')
+	    pbuf[current++] = '\n';
+	else
+	    pbuf[current++] = next;
+    }
+    pbuf[current-endpos] = '\0';
+    buf = zmult == 1 ? pbuf : quotestring(pbuf, NULL, QT_BACKSLASH);
+    zmult = 1;
+    wpaste = stringaszleline(buf, 0, &n, NULL, NULL);
+    if (region_active)
+	killregion(zlenoargs);
+    doinsert(wpaste, n);
+    free(pbuf); free(wpaste);
+    return 0;
+}
+
+/**/
+int
 overwritemode(UNUSED(char **args))
 {
     insmode ^= 1;
diff --git a/Src/options.c b/Src/options.c
index 78f603d..440783f 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -100,6 +100,7 @@ static struct optname optns[] = {
 {{NULL, "beep",		      OPT_ALL},			 BEEP},
 {{NULL, "bgnice",	      OPT_EMULATE|OPT_NONBOURNE},BGNICE},
 {{NULL, "braceccl",	      OPT_EMULATE},		 BRACECCL},
+{{NULL, "bracketedpaste",     OPT_ALL},			 BRACKETEDPASTE},
 {{NULL, "bsdecho",	      OPT_EMULATE|OPT_SH},	 BSDECHO},
 {{NULL, "caseglob",	      OPT_ALL},			 CASEGLOB},
 {{NULL, "casematch",	      OPT_ALL},			 CASEMATCH},
diff --git a/Src/zsh.h b/Src/zsh.h
index c88c2e7..ff3793a 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2075,6 +2075,7 @@ enum {
     BEEP,
     BGNICE,
     BRACECCL,
+    BRACKETEDPASTE,
     BSDECHO,
     CASEGLOB,
     CASEMATCH,


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-10  0:28               ` Oliver Kiddle
@ 2015-06-10  4:38                 ` Bart Schaefer
  2015-06-15 22:11                   ` Oliver Kiddle
  2015-06-10  9:44                 ` Yuri D'Elia
  1 sibling, 1 reply; 18+ messages in thread
From: Bart Schaefer @ 2015-06-10  4:38 UTC (permalink / raw)
  To: zsh-workers

On Jun 10,  2:28am, Oliver Kiddle wrote:
}
} This links it to a new BRACKETED_PASTE option. A concern with that
} approach is that if logic is needed in init_term() to disable the
} feature, the distinction between the user explicitly disabling the
} feature and init_term() doing it wouldn't be clear.

This sounds like the same dilemma that we had with ZLE_RPROMPT_INDENT,
which we addressed with a variable rather than an option.  This would
address several problems:  namespace (ZLE-specific stuff should really
stop leaking into the base shell options); a place to store the string
to use in case it turns out not to be the same for all terminal types;
and distinguishing among unset/set/empty/non-empty semantics.

So I'd vote for scrapping the setopt and adding a ZLE_BRACKETED_PASTE
variable instead.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-10  0:28               ` Oliver Kiddle
  2015-06-10  4:38                 ` Bart Schaefer
@ 2015-06-10  9:44                 ` Yuri D'Elia
  1 sibling, 0 replies; 18+ messages in thread
From: Yuri D'Elia @ 2015-06-10  9:44 UTC (permalink / raw)
  To: zsh-workers

On 06/10/2015 02:28 AM, Oliver Kiddle wrote:
> On 5 Jun, Yuri D'Elia wrote:
>> What if the terminal itself turns off BREAK processing during paste?
>
> You can try. Would be interesting. May not work when there is ssh/sshd,
> telnet, screen, tmux etc sat between urxvt and zsh.

I will give it a try. Doesn't sound complicated to implement (easier
than read ssh source code anyway..).

>From reading termios(8) though, if we disable BREAK a \0 is emitted in
his place.

>> Putty also eats it by the way. If that's the case for all terminals, I
>> would just enabled it by default.
> 
> We can always add some tests later if it appears to be necessary. Any
> objections?
> I've tried quite hard to find a terminal that won't eat the sequence and
> found none.

I have an old SGI Octane sitting somewhere, with IRIX 6.5... but do we
really want to support it ? ;)

I would say it's really better if this was enabled by default and see if
there's any report coming in.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-10  4:38                 ` Bart Schaefer
@ 2015-06-15 22:11                   ` Oliver Kiddle
  2015-06-15 23:09                     ` Mikael Magnusson
  2015-06-16  0:20                     ` Bart Schaefer
  0 siblings, 2 replies; 18+ messages in thread
From: Oliver Kiddle @ 2015-06-15 22:11 UTC (permalink / raw)
  To: zsh-workers

On 9 Jun, Bart wrote:
> This sounds like the same dilemma that we had with ZLE_RPROMPT_INDENT,
> which we addressed with a variable rather than an option.  This would
> address several problems:  namespace (ZLE-specific stuff should really
> stop leaking into the base shell options); a place to store the string
> to use in case it turns out not to be the same for all terminal types;
> and distinguishing among unset/set/empty/non-empty semantics.

That makes sense, thanks.

So this next attempt uses ZLE_BRACKETED_PASTE_ON/OFF though I'm open to
better ideas.
I've not made them special, just initialised them with a call to
setsparam as that seemed simple enough for the purpose. Is that
reasonable or should they be special?

I also looked around for places where keys are read and handled such
that bracketedpaste() would not be called. I've not done anything in
the case of single bytes such as for the vi r and f commands but I have
added handling for the history and completion incremental searches and
for the execute-named-command widget. It would be possible to avoid this
special handling by adding logic to getkeycmd() so that they get a
stream of self-insert widgets. That would somehow defeat the
purpose of bracketed paste, however.

In both the incremental search modes, backspace works like undo so the
entire paste is undone. This same effect is also apparent if you press
backspace after changing search direction with Ctrl-S or Ctrl-R.

Oliver

diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index eb3eb36..48c973a 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1642,6 +1642,25 @@ item(tt(ZDOTDIR))(
 The directory to search for shell startup files (.zshrc, etc),
 if not tt($HOME).
 )
+vindex(ZLE_BRACKETED_PASTE_ON)
+vindex(ZLE_BRACKETED_PASTE_OFF)
+cindex(bracketed paste)
+cindex(enabling bracketed paste)
+xitem(tt(ZLE_BRACKETED_PASTE_ON))
+item(tt(ZLE_BRACKETED_PASTE_OFF))(
+Many terminal emulators have a feature that allows applications to
+identify when text is pasted into the terminal rather than being typed
+normally. For ZLE, this means that special characters such as tabs
+and newlines can be inserted instead of invoking editor commands.
+Furthermore, pasted text forms a single undo event and if the region is
+active, pasted text will replace the region.
+
+These parameters contain the terminal escape sequences for enabling
+and disabling the feature. These escape sequences are used to enable
+bracketed paste when ZLE is active and disable it at other times.
+Unsetting the parameters has the effect of ensuring that bracketed paste
+remains disabled.
+)
 vindex(ZLE_LINE_ABORTED)
 item(tt(ZLE_LINE_ABORTED))(
 This parameter is set by the line editor when an error occurs.  It
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 16d661f..9066681 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -2057,6 +2057,11 @@ tindex(beep)
 item(tt(beep))(
 Beep, unless the tt(BEEP) option is unset.
 )
+tindex(bracketed-paste)
+item(tt(bracketed-paste))(
+This widget is invoked when text is pasted to the terminal emulator.
+See also the ZLE_BRACKETED_PASTE_ON parameter.
+)
 tindex(vi-cmd-mode)
 item(tt(vi-cmd-mode) (tt(^X^V)) (unbound) (tt(^[)))(
 Enter command mode; that is, select the `tt(vicmd)' keymap.
diff --git a/Src/Zle/complist.c b/Src/Zle/complist.c
index f542066..a55567f 100644
--- a/Src/Zle/complist.c
+++ b/Src/Zle/complist.c
@@ -2269,41 +2269,16 @@ msearchpop(int *backp)
 }
 
 static Cmatch **
-msearch(Cmatch **ptr, int ins, int back, int rep, int *wrapp)
+msearch(Cmatch **ptr, char *ins, int back, int rep, int *wrapp)
 {
-#ifdef MULTIBYTE_SUPPORT
-    /* MB_CUR_MAX may not be constant */
-    VARARR(char, s, MB_CUR_MAX+1);
-#else
-    char s[2];
-#endif
     Cmatch **p, *l = NULL, m;
     int x = mcol, y = mline;
     int ex, ey, wrap = 0, owrap = (msearchstate & MS_WRAPPED);
 
     msearchpush(ptr, back);
 
-    if (ins) {
-#ifdef MULTIBYTE_SUPPORT
-	if (lastchar_wide_valid)
-	{
-	    mbstate_t mbs;
-	    int len;
-
-	    memset(&mbs, 0, sizeof(mbs));
-	    len = wcrtomb(s, lastchar_wide, &mbs);
-	    if (len < 0)
-		len = 0;
-	    s[len] = '\0';
-	} else
-#endif
-	{
-	    s[0] = lastchar;
-	    s[1] = '\0';
-	}
-
-        msearchstr = dyncat(msearchstr, s);
-    }
+    if (ins)
+        msearchstr = dyncat(msearchstr, ins);
     if (back) {
         ex = mcols - 1;
         ey = -1;
@@ -3273,14 +3248,23 @@ domenuselect(Hookdef dummy, Chdata dat)
                    cmd == Th(z_historyincrementalsearchbackward) ||
                    ((mode == MM_FSEARCH || mode == MM_BSEARCH) &&
                     (cmd == Th(z_selfinsert) ||
-                     cmd == Th(z_selfinsertunmeta)))) {
+                     cmd == Th(z_selfinsertunmeta) ||
+		     cmd == Th(z_bracketedpaste)))) {
             Cmatch **np, **op = p;
             int was = (mode == MM_FSEARCH || mode == MM_BSEARCH);
-            int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta));
+            int ins = (cmd == Th(z_selfinsert) || cmd == Th(z_selfinsertunmeta) ||
+		cmd == Th(z_bracketedpaste));
             int back = (cmd == Th(z_historyincrementalsearchbackward));
             int wrap;
 
             do {
+		char *toins = NULL;
+#ifdef MULTIBYTE_SUPPORT
+		/* MB_CUR_MAX may not be constant */
+		VARARR(char, insert, MB_CUR_MAX+1);
+#else
+		char insert[2];
+#endif
                 if (was) {
                     p += wishcol - mcol;
                     mcol = wishcol;
@@ -3294,17 +3278,43 @@ domenuselect(Hookdef dummy, Chdata dat)
                     } else {
                         msearchstr = "";
                         msearchstack = NULL;
+			msearchstate = MS_OK;
                     }
-                }
-                if (cmd == Th(z_selfinsertunmeta)) {
-		    fixunmeta();
-                }
+                } else {
+		    if (cmd == Th(z_selfinsertunmeta)) {
+			fixunmeta();
+		    }
+		    if (cmd == Th(z_bracketedpaste)) {
+			toins = bracketedstring();
+		    } else {
+			toins = insert;
+#ifdef MULTIBYTE_SUPPORT
+			if (lastchar_wide_valid)
+			{
+			    mbstate_t mbs;
+			    int len;
+
+			    memset(&mbs, 0, sizeof(mbs));
+			    len = wcrtomb(s, lastchar_wide, &mbs);
+			    if (len < 0)
+				len = 0;
+			    insert[len] = '\0';
+			} else
+#endif
+			{
+			    insert[0] = lastchar;
+			    insert[1] = '\0';
+			}
+		    }
+		}
                 wrap = 0;
-                np = msearch(p, ins, (ins ? (mode == MM_BSEARCH) : back),
+                np = msearch(p, toins, (ins ? (mode == MM_BSEARCH) : back),
                              (was && !ins), &wrap);
 
                 if (!ins)
                     mode = (back ? MM_BSEARCH : MM_FSEARCH);
+		else if (cmd == Th(z_bracketedpaste))
+		    free(toins);
 
                 if (*msearchstr) {
                     zsfree(lastsearch);
diff --git a/Src/Zle/iwidgets.list b/Src/Zle/iwidgets.list
index b41661a..6a07212 100644
--- a/Src/Zle/iwidgets.list
+++ b/Src/Zle/iwidgets.list
@@ -28,6 +28,7 @@
 "beginning-of-history", beginningofhistory, 0
 "beginning-of-line", beginningofline, 0
 "beginning-of-line-hist", beginningoflinehist, 0
+"bracketed-paste", bracketedpaste, ZLE_MENUCMP | ZLE_KEEPSUFFIX
 "capitalize-word", capitalizeword, 0
 "clear-screen", clearscreen, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_LASTCOL | ZLE_NOTCOMMAND
 "complete-word", completeword, ZLE_MENUCMP | ZLE_KEEPSUFFIX | ZLE_ISCOMP
diff --git a/Src/Zle/zle_hist.c b/Src/Zle/zle_hist.c
index cc66f99..f8aab20 100644
--- a/Src/Zle/zle_hist.c
+++ b/Src/Zle/zle_hist.c
@@ -1620,6 +1620,21 @@ doisearch(char **args, int dir, int pattern)
 		feep = 1;
 	    else
 		goto ins;
+	} else if (cmd == Th(z_bracketedpaste)) {
+	    char *paste = bracketedstring();
+	    set_isrch_spot(top_spot++, hl, pos, pat_hl, pat_pos, end_pos,
+			   zlemetacs, sbptr, dir, nomatch);
+	    size_t pastelen = strlen(paste);
+	    if (sbptr + pastelen >= sibuf - FIRST_SEARCH_CHAR - 2) {
+		int oldsize = sibuf;
+		sibuf += (pastelen >= sibuf) ? pastelen + 1 : sibuf;
+		ibuf = hrealloc(ibuf, oldsize, sibuf);
+		sbuf = ibuf + FIRST_SEARCH_CHAR;
+	    }
+	    strcpy(sbuf + sbptr, paste);
+	    sbptr += pastelen;
+	    patprog = NULL;
+	    free(paste);
 	} else if (cmd == Th(z_acceptsearch)) {
 	    break;
 	} else {
diff --git a/Src/Zle/zle_keymap.c b/Src/Zle/zle_keymap.c
index c6fae25..d355f41 100644
--- a/Src/Zle/zle_keymap.c
+++ b/Src/Zle/zle_keymap.c
@@ -1400,6 +1400,11 @@ default_bindings(void)
     bindkey(emap, "\30\30", refthingy(t_exchangepointandmark), NULL);
     bindkey(emap, "\30=",   refthingy(t_whatcursorposition), NULL);
 
+    /* bracketed paste applicable to all keymaps */
+    bindkey(emap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+    bindkey(vmap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+    bindkey(amap, "\33[200~", refthingy(t_bracketedpaste), NULL);
+
     /* emacs mode: ESC sequences, all taken from the meta binding table */
     buf[0] = '\33';
     buf[2] = 0;
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index cec44c0..62ed157 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1119,7 +1119,7 @@ zlecore(void)
 char *
 zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 {
-    char *s;
+    char *s, *bracket;
     int old_errno = errno;
     int tmout = getiparam("TMOUT");
 
@@ -1248,6 +1248,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 
     zlecallhook(init, NULL);
 
+    if ((bracket = getsparam("ZLE_BRACKETED_PASTE_ON")))
+	fputs(bracket, shout);
+
     zrefresh();
 
     zlecore();
@@ -1257,6 +1260,9 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 		  "ZLE_VARED_ABORTED" :
 		  "ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
 
+    if ((bracket = getsparam("ZLE_BRACKETED_PASTE_OFF")))
+	fputs(bracket, shout);
+
     if (done && !exit_pending && !errflag)
 	zlecallhook(finish, NULL);
 
@@ -2028,6 +2034,9 @@ setup_(UNUSED(Module m))
 
     clwords = (char **) zshcalloc((clwsize = 16) * sizeof(char *));
 
+    setsparam("ZLE_BRACKETED_PASTE_ON", ztrdup("\033[?2004h"));
+    setsparam("ZLE_BRACKETED_PASTE_OFF", ztrdup("\033[?2004l"));
+
     return 0;
 }
 
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index 4669ef2..ef9d0a8 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -736,6 +736,54 @@ yankpop(UNUSED(char **args))
 }
 
 /**/
+char *
+bracketedstring()
+{
+    static const char endesc[] = "\033[201~";
+    int endpos = 0;
+    size_t psize = 64;
+    char *pbuf = zalloc(psize);
+    size_t current = 0;
+    int next, timeout;
+
+    while (endesc[endpos]) {
+	if (current + 1 >= psize)
+	    pbuf = zrealloc(pbuf, psize *= 2);
+	if ((next = getbyte(1L, &timeout)) == EOF)
+	    break;
+	if (!endpos || next != endesc[endpos++])
+	    endpos = (next == *endesc);
+	if (imeta(next)) {
+	    pbuf[current++] = Meta;
+	    pbuf[current++] = next ^ 32;
+	} else if (next == '\r')
+	    pbuf[current++] = '\n';
+	else
+	    pbuf[current++] = next;
+    }
+    pbuf[current-endpos] = '\0';
+    return pbuf;
+}
+
+/**/
+int
+bracketedpaste(UNUSED(char **args))
+{
+    int n;
+    ZLE_STRING_T wpaste;
+    char *pbuf = bracketedstring();
+
+    wpaste = stringaszleline((zmult == 1) ? pbuf :
+	quotestring(pbuf, NULL, QT_BACKSLASH), 0, &n, NULL, NULL);
+    zmult = 1;
+    if (region_active)
+	killregion(zlenoargs);
+    doinsert(wpaste, n);
+    free(pbuf); free(wpaste);
+    return 0;
+}
+
+/**/
 int
 overwritemode(UNUSED(char **args))
 {
@@ -1264,6 +1312,22 @@ executenamedcommand(char *prmt)
 	    if (listed)
 		clearlist = listshown = 1;
 	    curlist = 0;
+	} else if (cmd == Th(z_bracketedpaste)) {
+	    char *insert = bracketedstring();
+	    size_t inslen = strlen(insert);
+	    if (len + inslen > NAMLEN)
+		feep = 1;
+	    else {
+		strcpy(ptr, insert);
+		len += inslen;
+		ptr += inslen;
+		if (listed) {
+		    clearlist = listshown = 1;
+		    listed = 0;
+		} else
+		    curlist = 0;
+	    }
+	    free(insert);
 	} else {
 	    if(cmd == Th(z_acceptline) || cmd == Th(z_vicmdmode)) {
 		Thingy r;


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-15 22:11                   ` Oliver Kiddle
@ 2015-06-15 23:09                     ` Mikael Magnusson
  2015-06-16  0:20                     ` Bart Schaefer
  1 sibling, 0 replies; 18+ messages in thread
From: Mikael Magnusson @ 2015-06-15 23:09 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: zsh workers

On Tue, Jun 16, 2015 at 12:11 AM, Oliver Kiddle <okiddle@yahoo.co.uk> wrote:
> On 9 Jun, Bart wrote:
>> This sounds like the same dilemma that we had with ZLE_RPROMPT_INDENT,
>> which we addressed with a variable rather than an option.  This would
>> address several problems:  namespace (ZLE-specific stuff should really
>> stop leaking into the base shell options); a place to store the string
>> to use in case it turns out not to be the same for all terminal types;
>> and distinguishing among unset/set/empty/non-empty semantics.
>
> That makes sense, thanks.
>
> So this next attempt uses ZLE_BRACKETED_PASTE_ON/OFF though I'm open to
> better ideas.
> I've not made them special, just initialised them with a call to
> setsparam as that seemed simple enough for the purpose. Is that
> reasonable or should they be special?
>
> I also looked around for places where keys are read and handled such
> that bracketedpaste() would not be called. I've not done anything in
> the case of single bytes such as for the vi r and f commands but I have
> added handling for the history and completion incremental searches and
> for the execute-named-command widget. It would be possible to avoid this
> special handling by adding logic to getkeycmd() so that they get a
> stream of self-insert widgets. That would somehow defeat the
> purpose of bracketed paste, however.
>
> In both the incremental search modes, backspace works like undo so the
> entire paste is undone. This same effect is also apparent if you press
> backspace after changing search direction with Ctrl-S or Ctrl-R.
>
> +int
> +bracketedpaste(UNUSED(char **args))
> +{
> +    int n;
> +    ZLE_STRING_T wpaste;
> +    char *pbuf = bracketedstring();
> +
> +    wpaste = stringaszleline((zmult == 1) ? pbuf :
> +       quotestring(pbuf, NULL, QT_BACKSLASH), 0, &n, NULL, NULL);
> +    zmult = 1;

This feature appears to be undocumented? I also think the
documentation for the widget should list the default bindings, as
other widgets do.

It might also be nice if the widget could take a parameter as an
argument to store the text in, rather than insert it into the command
line always.

-- 
Mikael Magnusson


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-15 22:11                   ` Oliver Kiddle
  2015-06-15 23:09                     ` Mikael Magnusson
@ 2015-06-16  0:20                     ` Bart Schaefer
  2015-06-16 17:12                       ` Oliver Kiddle
  1 sibling, 1 reply; 18+ messages in thread
From: Bart Schaefer @ 2015-06-16  0:20 UTC (permalink / raw)
  To: zsh-workers

On Jun 16, 12:11am, Oliver Kiddle wrote:
}
} So this next attempt uses ZLE_BRACKETED_PASTE_ON/OFF though I'm open to
} better ideas.

I might have chosen a single two-valued array and require it to either
be empty or to have two values (whether "exactly" or "at least" is less
important) so that it's harder e.g. to accidentally begin bracketed paste
mode and never exit from it.

} I've not made them special, just initialised them with a call to
} setsparam as that seemed simple enough for the purpose. Is that
} reasonable or should they be special?

Mostly, variables only need to be special when they have a custom GSU
structure, e.g., ZLE_RROMPT_INDENT keeps the internal rprompt_indent
value updated because it's accessed frequently (so calling getsparam()
is too inefficient), so it is special.


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-16  0:20                     ` Bart Schaefer
@ 2015-06-16 17:12                       ` Oliver Kiddle
  2015-06-16 20:26                         ` Bart Schaefer
  0 siblings, 1 reply; 18+ messages in thread
From: Oliver Kiddle @ 2015-06-16 17:12 UTC (permalink / raw)
  To: zsh-workers

Bart wrote:
> I might have chosen a single two-valued array and require it to either
> be empty or to have two values (whether "exactly" or "at least" is less
> important) so that it's harder e.g. to accidentally begin bracketed paste
> mode and never exit from it.

That does sound better. As an array, should the name be lowercase -
zle_bracketed_paste?

Mikael wrote:
> This feature appears to be undocumented? I also think the
> documentation for the widget should list the default bindings, as
> other widgets do.

I didn't list the bindings because they don't correspond to actual
keys. It seems more of an implementation detail that may be adapted in
future if other terminal emulators do things differently. I'm not
especially bothered, however.

> It might also be nice if the widget could take a parameter as an
> argument to store the text in, rather than insert it into the command
> line always.

That's a good idea. Makes it quite easy to write a custom widget in
terms of the default one. Besides the existing quoting use case, the
only other idea that comes to mind is to strip comments out so that
scripts can be copied without having to set interactive_comments.

This patch goes on top of the previous one rather than replacing it.

Thanks for the feedback.

Oliver

diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 48c973a..e209162 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1642,12 +1642,10 @@ item(tt(ZDOTDIR))(
 The directory to search for shell startup files (.zshrc, etc),
 if not tt($HOME).
 )
-vindex(ZLE_BRACKETED_PASTE_ON)
-vindex(ZLE_BRACKETED_PASTE_OFF)
+vindex(zle_bracketed_paste)
 cindex(bracketed paste)
 cindex(enabling bracketed paste)
-xitem(tt(ZLE_BRACKETED_PASTE_ON))
-item(tt(ZLE_BRACKETED_PASTE_OFF))(
+item(tt(zle_bracketed_paste))(
 Many terminal emulators have a feature that allows applications to
 identify when text is pasted into the terminal rather than being typed
 normally. For ZLE, this means that special characters such as tabs
@@ -1655,10 +1653,10 @@ and newlines can be inserted instead of invoking editor commands.
 Furthermore, pasted text forms a single undo event and if the region is
 active, pasted text will replace the region.
 
-These parameters contain the terminal escape sequences for enabling
-and disabling the feature. These escape sequences are used to enable
-bracketed paste when ZLE is active and disable it at other times.
-Unsetting the parameters has the effect of ensuring that bracketed paste
+This two-element array contains the terminal escape sequences for
+enabling and disabling the feature. These escape sequences are used to
+enable bracketed paste when ZLE is active and disable it at other times.
+Unsetting the parameter has the effect of ensuring that bracketed paste
 remains disabled.
 )
 vindex(ZLE_LINE_ABORTED)
diff --git a/Doc/Zsh/zle.yo b/Doc/Zsh/zle.yo
index 9066681..30675b4 100644
--- a/Doc/Zsh/zle.yo
+++ b/Doc/Zsh/zle.yo
@@ -2060,7 +2060,12 @@ Beep, unless the tt(BEEP) option is unset.
 tindex(bracketed-paste)
 item(tt(bracketed-paste))(
 This widget is invoked when text is pasted to the terminal emulator.
-See also the ZLE_BRACKETED_PASTE_ON parameter.
+If a numeric argument is given, shell quoting will be applied to the
+pasted text before it is inserted. When called from a widget function,
+an argument can be given to specify a variable to which pasted text is
+assigned.
+
+See also the zle_bracketed_paste parameter.
 )
 tindex(vi-cmd-mode)
 item(tt(vi-cmd-mode) (tt(^X^V)) (unbound) (tt(^[)))(
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 62ed157..7ccfb68 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1119,7 +1119,7 @@ zlecore(void)
 char *
 zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 {
-    char *s, *bracket;
+    char *s, **bracket;
     int old_errno = errno;
     int tmout = getiparam("TMOUT");
 
@@ -1248,8 +1248,8 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 
     zlecallhook(init, NULL);
 
-    if ((bracket = getsparam("ZLE_BRACKETED_PASTE_ON")))
-	fputs(bracket, shout);
+    if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+	fputs(*bracket, shout);
 
     zrefresh();
 
@@ -1260,8 +1260,8 @@ zleread(char **lp, char **rp, int flags, int context, char *init, char *finish)
 		  "ZLE_VARED_ABORTED" :
 		  "ZLE_LINE_ABORTED", zlegetline(NULL, NULL));
 
-    if ((bracket = getsparam("ZLE_BRACKETED_PASTE_OFF")))
-	fputs(bracket, shout);
+    if ((bracket = getaparam("zle_bracketed_paste")) && arrlen(bracket) == 2)
+	fputs(bracket[1], shout);
 
     if (done && !exit_pending && !errflag)
 	zlecallhook(finish, NULL);
@@ -2010,6 +2010,8 @@ static struct features module_features = {
 int
 setup_(UNUSED(Module m))
 {
+    char **bpaste;
+
     /* Set up editor entry points */
     zle_entry_ptr = zle_main_entry;
     zle_load_state = 1;
@@ -2034,8 +2036,10 @@ setup_(UNUSED(Module m))
 
     clwords = (char **) zshcalloc((clwsize = 16) * sizeof(char *));
 
-    setsparam("ZLE_BRACKETED_PASTE_ON", ztrdup("\033[?2004h"));
-    setsparam("ZLE_BRACKETED_PASTE_OFF", ztrdup("\033[?2004l"));
+    bpaste = zshcalloc(3*sizeof(char *));
+    bpaste[0] = ztrdup("\033[?2004h");
+    bpaste[1] = ztrdup("\033[?2004l");
+    setaparam("zle_bracketed_paste", bpaste);
 
     return 0;
 }
diff --git a/Src/Zle/zle_misc.c b/Src/Zle/zle_misc.c
index ef9d0a8..c2fb2e7 100644
--- a/Src/Zle/zle_misc.c
+++ b/Src/Zle/zle_misc.c
@@ -767,19 +767,23 @@ bracketedstring()
 
 /**/
 int
-bracketedpaste(UNUSED(char **args))
+bracketedpaste(char **args)
 {
-    int n;
-    ZLE_STRING_T wpaste;
     char *pbuf = bracketedstring();
 
-    wpaste = stringaszleline((zmult == 1) ? pbuf :
-	quotestring(pbuf, NULL, QT_BACKSLASH), 0, &n, NULL, NULL);
-    zmult = 1;
-    if (region_active)
-	killregion(zlenoargs);
-    doinsert(wpaste, n);
-    free(pbuf); free(wpaste);
+    if (*args) {
+	setsparam(*args, pbuf);
+    } else {
+	int n;
+	ZLE_STRING_T wpaste;
+	wpaste = stringaszleline((zmult == 1) ? pbuf :
+	    quotestring(pbuf, NULL, QT_BACKSLASH), 0, &n, NULL, NULL);
+	zmult = 1;
+	if (region_active)
+	    killregion(zlenoargs);
+	doinsert(wpaste, n);
+	free(pbuf); free(wpaste);
+    }
     return 0;
 }
 


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-16 17:12                       ` Oliver Kiddle
@ 2015-06-16 20:26                         ` Bart Schaefer
  2015-06-17 10:45                           ` Oliver Kiddle
  0 siblings, 1 reply; 18+ messages in thread
From: Bart Schaefer @ 2015-06-16 20:26 UTC (permalink / raw)
  To: zsh-workers

On Jun 16,  7:12pm, Oliver Kiddle wrote:
} Subject: Re: bracketed paste mode in xterm and urxvt
}
} Bart wrote:
} > I might have chosen a single two-valued array
} 
} That does sound better. As an array, should the name be lowercase -
} zle_bracketed_paste?

I don't have a strong opinion -- lowercase is probably fine.

} Mikael wrote:
} > documentation for the widget should list the default bindings
} 
} I didn't list the bindings because they don't correspond to actual
} keys.
[...]
} This patch goes on top of the previous one rather than replacing it.

Speaking of the bindings in the previous patch -- Src/Zle/zle_keymap.c
-- should those be hardwired?  Perhaps an optional third element of the
zle_bracketed_paste array could be the string for the key binding.

-- 
Barton E. Schaefer


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-16 20:26                         ` Bart Schaefer
@ 2015-06-17 10:45                           ` Oliver Kiddle
  2015-06-17 15:04                             ` Bart Schaefer
  0 siblings, 1 reply; 18+ messages in thread
From: Oliver Kiddle @ 2015-06-17 10:45 UTC (permalink / raw)
  To: zsh-workers

Bart wrote:
> Speaking of the bindings in the previous patch -- Src/Zle/zle_keymap.c
> -- should those be hardwired?  Perhaps an optional third element of the
> zle_bracketed_paste array could be the string for the key binding.

Given a situation of someone wanting to assign to just that third
element to have a different key binding, we would have to initialise
zle_bracketed_paste outside of the zle module in order for it to occur
earlier than both the assignment in .zshrc and the key binding. Or you'd
have to dynamically update the bindings when the variable is set. That
doesn't really add anything given that changing key bindings is easily
done directly.

I'm not really sure that it would achieve anything other than allow us
to feel less bad about the hardwired escape sequences.

Oliver


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
  2015-06-17 10:45                           ` Oliver Kiddle
@ 2015-06-17 15:04                             ` Bart Schaefer
  0 siblings, 0 replies; 18+ messages in thread
From: Bart Schaefer @ 2015-06-17 15:04 UTC (permalink / raw)
  To: zsh-workers

On Jun 17, 12:45pm, Oliver Kiddle wrote:
}
} Bart wrote:
} > Perhaps an optional third element of the
} > zle_bracketed_paste array could be the string for the key binding.
} 
} doesn't really add anything given that changing key bindings is easily
} done directly.

OK, I buy that.  Document under the widget that changing the binding is
how you do this?


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: bracketed paste mode in xterm and urxvt
       [not found]           ` <CAHYJk3T3dVdN5qDMecPAH_ALLBYNntW0QVdPMh50Lo_ULeWP6w__21110.9288772152$1433333265$gmane$org@mail.gmail.com>
@ 2015-06-03 12:43             ` Stephane Chazelas
  0 siblings, 0 replies; 18+ messages in thread
From: Stephane Chazelas @ 2015-06-03 12:43 UTC (permalink / raw)
  To: zsh-workers

2015-06-03 14:06:05 +0200, Mikael Magnusson:
[...]
> It's probably worth noting that 'safe-paste' is a bad name for this,
> since the pasted text can include the end-paste escape code, causing
> the rest of the paste to appear to the shell as typed by the user.
> 
> This page has an example attack against the plugin,
> https://thejh.net/misc/website-terminal-copy-paste
> 
> Hm, seems newer xterm prohibits pasting raw escape codes, so if you
> have one of those versions, you are safe.
[...]

Yes, see:

https://security.stackexchange.com/questions/39118/how-can-i-protect-myself-from-this-kind-of-clipboard-abuse/52655#52655

for details.

Note that with xterm, it's also possible to configure it
to do another safe type of bracketed paste like:

xterm -xrm 'XTerm.VT100.translations: #override Shift <KeyPress> space: insert-formatted("\033[202~%S~%s", CLIPBOARD)'

For the CLIPBOARD selection to be inserted as ^[[202~3~abc (here
upon Shift+Space).

That is with the content of the selection prefixed with its
length (in bytes)

-- 
Stephane


^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2015-06-17 15:04 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <BANLkTikh_-+L2W5=Yfu7h7iAe5CcpP6fxw@mail.gmail.com>
     [not found] ` <CAFOazAOfk=Sq-smkMGzJKO4b7jMb_1_m4vXXn8twoVA2wV55YA@mail.gmail.com>
     [not found]   ` <55677AF5.50709@thequod.de>
     [not found]     ` <mk9dc0$p0$1@ger.gmane.org>
     [not found]       ` <CABZhJg_5p8BLbq82s_wVtsPdD5hVtk-cPg6fNxzbSs4Vg00SOw@mail.gmail.com>
     [not found]         ` <mkmjfu$3h0$1@ger.gmane.org>
     [not found]           ` <CAHYJk3T3dVdN5qDMecPAH_ALLBYNntW0QVdPMh50Lo_ULeWP6w__21110.9288772152$1433333265$gmane$org@mail.gmail.com>
2015-06-03 12:43             ` bracketed paste mode in xterm and urxvt Stephane Chazelas
2015-06-03 15:31       ` Oliver Kiddle
2015-06-03 20:42         ` Stephane Chazelas
2015-06-03 23:48           ` Oliver Kiddle
2015-06-04  7:15             ` Stephane Chazelas
2015-06-05 10:49         ` Yuri D'Elia
2015-06-05 13:40           ` Oliver Kiddle
2015-06-05 14:35             ` Yuri D'Elia
2015-06-10  0:28               ` Oliver Kiddle
2015-06-10  4:38                 ` Bart Schaefer
2015-06-15 22:11                   ` Oliver Kiddle
2015-06-15 23:09                     ` Mikael Magnusson
2015-06-16  0:20                     ` Bart Schaefer
2015-06-16 17:12                       ` Oliver Kiddle
2015-06-16 20:26                         ` Bart Schaefer
2015-06-17 10:45                           ` Oliver Kiddle
2015-06-17 15:04                             ` Bart Schaefer
2015-06-10  9:44                 ` Yuri D'Elia

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).